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
ReplaceCallWithArg(const CallExpr * Call,DiagnosticBuilder & Diag,const SourceManager & SM,const LangOptions & LangOpts)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->getBeginLoc(), Arg->getBeginLoc()),
27 SM, LangOpts);
28 CharSourceRange AfterArgumentsRange = Lexer::makeFileCharRange(
29 CharSourceRange::getCharRange(Call->getEndLoc(),
30 Call->getEndLoc().getLocWithOffset(1)),
31 SM, LangOpts);
32
33 if (BeforeArgumentsRange.isValid() && AfterArgumentsRange.isValid()) {
34 Diag << FixItHint::CreateRemoval(BeforeArgumentsRange)
35 << FixItHint::CreateRemoval(AfterArgumentsRange);
36 }
37 }
38
storeOptions(ClangTidyOptions::OptionMap & Opts)39 void MoveConstArgCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
40 Options.store(Opts, "CheckTriviallyCopyableMove", CheckTriviallyCopyableMove);
41 }
42
registerMatchers(MatchFinder * Finder)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
check(const MatchFinder::MatchResult & Result)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
122