clang-tools  7.0.0svn
MoveConstArgCheck.cpp
Go to the documentation of this file.
1 //===--- MoveConstArgCheck.cpp - clang-tidy -----------------------===//
2 //
3 // The LLVM Compiler Infrastructure
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9 
10 #include "MoveConstArgCheck.h"
11 
12 #include "clang/Lex/Lexer.h"
13 
14 using namespace clang::ast_matchers;
15 
16 namespace clang {
17 namespace tidy {
18 namespace performance {
19 
20 static void ReplaceCallWithArg(const CallExpr *Call, DiagnosticBuilder &Diag,
21  const SourceManager &SM,
22  const LangOptions &LangOpts) {
23  const Expr *Arg = Call->getArg(0);
24 
25  CharSourceRange BeforeArgumentsRange = Lexer::makeFileCharRange(
26  CharSourceRange::getCharRange(Call->getLocStart(), Arg->getLocStart()),
27  SM, LangOpts);
28  CharSourceRange AfterArgumentsRange = Lexer::makeFileCharRange(
29  CharSourceRange::getCharRange(Call->getLocEnd(),
30  Call->getLocEnd().getLocWithOffset(1)),
31  SM, LangOpts);
32 
33  if (BeforeArgumentsRange.isValid() && AfterArgumentsRange.isValid()) {
34  Diag << FixItHint::CreateRemoval(BeforeArgumentsRange)
35  << FixItHint::CreateRemoval(AfterArgumentsRange);
36  }
37 }
38 
39 void MoveConstArgCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
40  Options.store(Opts, "CheckTriviallyCopyableMove", CheckTriviallyCopyableMove);
41 }
42 
43 void MoveConstArgCheck::registerMatchers(MatchFinder *Finder) {
44  if (!getLangOpts().CPlusPlus)
45  return;
46 
47  auto MoveCallMatcher =
48  callExpr(callee(functionDecl(hasName("::std::move"))), argumentCountIs(1),
49  unless(isInTemplateInstantiation()))
50  .bind("call-move");
51 
52  Finder->addMatcher(MoveCallMatcher, this);
53 
54  auto ConstParamMatcher = forEachArgumentWithParam(
55  MoveCallMatcher, parmVarDecl(hasType(references(isConstQualified()))));
56 
57  Finder->addMatcher(callExpr(ConstParamMatcher).bind("receiving-expr"), this);
58  Finder->addMatcher(cxxConstructExpr(ConstParamMatcher).bind("receiving-expr"),
59  this);
60 }
61 
62 void MoveConstArgCheck::check(const MatchFinder::MatchResult &Result) {
63  const auto *CallMove = Result.Nodes.getNodeAs<CallExpr>("call-move");
64  const auto *ReceivingExpr = Result.Nodes.getNodeAs<Expr>("receiving-expr");
65  const Expr *Arg = CallMove->getArg(0);
66  SourceManager &SM = Result.Context->getSourceManager();
67 
68  CharSourceRange MoveRange =
69  CharSourceRange::getCharRange(CallMove->getSourceRange());
70  CharSourceRange FileMoveRange =
71  Lexer::makeFileCharRange(MoveRange, SM, getLangOpts());
72  if (!FileMoveRange.isValid())
73  return;
74 
75  bool IsConstArg = Arg->getType().isConstQualified();
76  bool IsTriviallyCopyable =
77  Arg->getType().isTriviallyCopyableType(*Result.Context);
78 
79  if (IsConstArg || IsTriviallyCopyable) {
80  if (const CXXRecordDecl *R = Arg->getType()->getAsCXXRecordDecl()) {
81  // According to [expr.prim.lambda]p3, "whether the closure type is
82  // trivially copyable" property can be changed by the implementation of
83  // the language, so we shouldn't rely on it when issuing diagnostics.
84  if (R->isLambda())
85  return;
86  // Don't warn when the type is not copyable.
87  for (const auto *Ctor : R->ctors()) {
88  if (Ctor->isCopyConstructor() && Ctor->isDeleted())
89  return;
90  }
91  }
92 
93  if (!IsConstArg && IsTriviallyCopyable && !CheckTriviallyCopyableMove)
94  return;
95 
96  bool IsVariable = isa<DeclRefExpr>(Arg);
97  const auto *Var =
98  IsVariable ? dyn_cast<DeclRefExpr>(Arg)->getDecl() : nullptr;
99  auto Diag = diag(FileMoveRange.getBegin(),
100  "std::move of the %select{|const }0"
101  "%select{expression|variable %4}1 "
102  "%select{|of the trivially-copyable type %5 }2"
103  "has no effect; remove std::move()"
104  "%select{| or make the variable non-const}3")
105  << IsConstArg << IsVariable << IsTriviallyCopyable
106  << (IsConstArg && IsVariable && !IsTriviallyCopyable) << Var
107  << Arg->getType();
108 
109  ReplaceCallWithArg(CallMove, Diag, SM, getLangOpts());
110  } else if (ReceivingExpr) {
111  auto Diag = diag(FileMoveRange.getBegin(),
112  "passing result of std::move() as a const reference "
113  "argument; no move will actually happen");
114 
115  ReplaceCallWithArg(CallMove, Diag, SM, getLangOpts());
116  }
117 }
118 
119 } // namespace performance
120 } // namespace tidy
121 } // namespace clang
std::map< std::string, std::string > OptionMap
static void ReplaceCallWithArg(const CallExpr *Call, DiagnosticBuilder &Diag, const SourceManager &SM, const LangOptions &LangOpts)