blob: c64769f6c3e5cf661b2237e739c2bb7152389ea6 [file] [log] [blame]
Alexander Kornienko1bfcba82017-11-28 16:41:03 +00001//===--- MoveConstArgCheck.cpp - clang-tidy -----------------------===//
Alexander Kornienkoe4ac60d2015-11-25 15:56:11 +00002//
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
Alexander Kornienko1bfcba82017-11-28 16:41:03 +000010#include "MoveConstArgCheck.h"
Alexander Kornienkoe4ac60d2015-11-25 15:56:11 +000011
Etienne Bergeron456177b2016-05-02 18:00:29 +000012#include "clang/Lex/Lexer.h"
13
14using namespace clang::ast_matchers;
Alexander Kornienkoe4ac60d2015-11-25 15:56:11 +000015
16namespace clang {
17namespace tidy {
Alexander Kornienko1bfcba82017-11-28 16:41:03 +000018namespace performance {
Alexander Kornienkoe4ac60d2015-11-25 15:56:11 +000019
Alexander Kornienko28b34b02016-06-16 14:32:41 +000020static 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(
Stephen Kelly43465bf2018-08-09 22:42:26 +000026 CharSourceRange::getCharRange(Call->getBeginLoc(), Arg->getBeginLoc()),
Alexander Kornienko28b34b02016-06-16 14:32:41 +000027 SM, LangOpts);
28 CharSourceRange AfterArgumentsRange = Lexer::makeFileCharRange(
Stephen Kellyc09197e2018-08-09 22:43:02 +000029 CharSourceRange::getCharRange(Call->getEndLoc(),
30 Call->getEndLoc().getLocWithOffset(1)),
Alexander Kornienko28b34b02016-06-16 14:32:41 +000031 SM, LangOpts);
32
33 if (BeforeArgumentsRange.isValid() && AfterArgumentsRange.isValid()) {
34 Diag << FixItHint::CreateRemoval(BeforeArgumentsRange)
35 << FixItHint::CreateRemoval(AfterArgumentsRange);
36 }
37}
38
Alexander Kornienko1bfcba82017-11-28 16:41:03 +000039void MoveConstArgCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
Aaron Ballmanc5661392017-11-27 22:59:33 +000040 Options.store(Opts, "CheckTriviallyCopyableMove", CheckTriviallyCopyableMove);
41}
42
Alexander Kornienko1bfcba82017-11-28 16:41:03 +000043void MoveConstArgCheck::registerMatchers(MatchFinder *Finder) {
Alexander Kornienkoe4ac60d2015-11-25 15:56:11 +000044 if (!getLangOpts().CPlusPlus)
45 return;
Alexander Kornienko28b34b02016-06-16 14:32:41 +000046
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"),
Alexander Kornienkoe4ac60d2015-11-25 15:56:11 +000059 this);
60}
61
Alexander Kornienko1bfcba82017-11-28 16:41:03 +000062void MoveConstArgCheck::check(const MatchFinder::MatchResult &Result) {
Alexander Kornienkoe4ac60d2015-11-25 15:56:11 +000063 const auto *CallMove = Result.Nodes.getNodeAs<CallExpr>("call-move");
Alexander Kornienko28b34b02016-06-16 14:32:41 +000064 const auto *ReceivingExpr = Result.Nodes.getNodeAs<Expr>("receiving-expr");
Alexander Kornienkoe4ac60d2015-11-25 15:56:11 +000065 const Expr *Arg = CallMove->getArg(0);
66 SourceManager &SM = Result.Context->getSourceManager();
67
Alexander Kornienko28b34b02016-06-16 14:32:41 +000068 CharSourceRange MoveRange =
69 CharSourceRange::getCharRange(CallMove->getSourceRange());
70 CharSourceRange FileMoveRange =
71 Lexer::makeFileCharRange(MoveRange, SM, getLangOpts());
72 if (!FileMoveRange.isValid())
73 return;
74
Alexander Kornienkoe4ac60d2015-11-25 15:56:11 +000075 bool IsConstArg = Arg->getType().isConstQualified();
76 bool IsTriviallyCopyable =
77 Arg->getType().isTriviallyCopyableType(*Result.Context);
78
79 if (IsConstArg || IsTriviallyCopyable) {
Alexander Kornienkob4898672017-05-05 17:33:49 +000080 if (const CXXRecordDecl *R = Arg->getType()->getAsCXXRecordDecl()) {
Alexander Kornienkof2dc6492017-05-22 14:30:14 +000081 // 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.
Alexander Kornienkob4898672017-05-05 17:33:49 +000087 for (const auto *Ctor : R->ctors()) {
88 if (Ctor->isCopyConstructor() && Ctor->isDeleted())
89 return;
90 }
91 }
Aaron Ballmanc5661392017-11-27 22:59:33 +000092
93 if (!IsConstArg && IsTriviallyCopyable && !CheckTriviallyCopyableMove)
94 return;
95
Alexander Kornienkoe4ac60d2015-11-25 15:56:11 +000096 bool IsVariable = isa<DeclRefExpr>(Arg);
Alexander Kornienkoeedcd9c2016-08-24 21:23:24 +000097 const auto *Var =
98 IsVariable ? dyn_cast<DeclRefExpr>(Arg)->getDecl() : nullptr;
Alexander Kornienko900cadd2016-04-26 19:33:49 +000099 auto Diag = diag(FileMoveRange.getBegin(),
100 "std::move of the %select{|const }0"
Alexander Kornienkoeedcd9c2016-08-24 21:23:24 +0000101 "%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
Mandeep Singh Grang7c7ea7d2016-11-08 07:50:19 +0000106 << (IsConstArg && IsVariable && !IsTriviallyCopyable) << Var
107 << Arg->getType();
Alexander Kornienkoe4ac60d2015-11-25 15:56:11 +0000108
Alexander Kornienko28b34b02016-06-16 14:32:41 +0000109 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");
Alexander Kornienkoe4ac60d2015-11-25 15:56:11 +0000114
Alexander Kornienko28b34b02016-06-16 14:32:41 +0000115 ReplaceCallWithArg(CallMove, Diag, SM, getLangOpts());
Alexander Kornienkoe4ac60d2015-11-25 15:56:11 +0000116 }
117}
118
Alexander Kornienko1bfcba82017-11-28 16:41:03 +0000119} // namespace performance
Alexander Kornienkoe4ac60d2015-11-25 15:56:11 +0000120} // namespace tidy
121} // namespace clang