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