Alexander Kornienko | 1bfcba8 | 2017-11-28 16:41:03 +0000 | [diff] [blame] | 1 | //===--- MoveConstArgCheck.cpp - clang-tidy -----------------------===// |
Alexander Kornienko | e4ac60d | 2015-11-25 15:56:11 +0000 | [diff] [blame] | 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 | |
Alexander Kornienko | 1bfcba8 | 2017-11-28 16:41:03 +0000 | [diff] [blame] | 10 | #include "MoveConstArgCheck.h" |
Alexander Kornienko | e4ac60d | 2015-11-25 15:56:11 +0000 | [diff] [blame] | 11 | |
Etienne Bergeron | 456177b | 2016-05-02 18:00:29 +0000 | [diff] [blame] | 12 | #include "clang/Lex/Lexer.h" |
| 13 | |
| 14 | using namespace clang::ast_matchers; |
Alexander Kornienko | e4ac60d | 2015-11-25 15:56:11 +0000 | [diff] [blame] | 15 | |
| 16 | namespace clang { |
| 17 | namespace tidy { |
Alexander Kornienko | 1bfcba8 | 2017-11-28 16:41:03 +0000 | [diff] [blame] | 18 | namespace performance { |
Alexander Kornienko | e4ac60d | 2015-11-25 15:56:11 +0000 | [diff] [blame] | 19 | |
Alexander Kornienko | 28b34b0 | 2016-06-16 14:32:41 +0000 | [diff] [blame] | 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( |
Stephen Kelly | 43465bf | 2018-08-09 22:42:26 +0000 | [diff] [blame] | 26 | CharSourceRange::getCharRange(Call->getBeginLoc(), Arg->getBeginLoc()), |
Alexander Kornienko | 28b34b0 | 2016-06-16 14:32:41 +0000 | [diff] [blame] | 27 | SM, LangOpts); |
| 28 | CharSourceRange AfterArgumentsRange = Lexer::makeFileCharRange( |
Stephen Kelly | c09197e | 2018-08-09 22:43:02 +0000 | [diff] [blame] | 29 | CharSourceRange::getCharRange(Call->getEndLoc(), |
| 30 | Call->getEndLoc().getLocWithOffset(1)), |
Alexander Kornienko | 28b34b0 | 2016-06-16 14:32:41 +0000 | [diff] [blame] | 31 | SM, LangOpts); |
| 32 | |
| 33 | if (BeforeArgumentsRange.isValid() && AfterArgumentsRange.isValid()) { |
| 34 | Diag << FixItHint::CreateRemoval(BeforeArgumentsRange) |
| 35 | << FixItHint::CreateRemoval(AfterArgumentsRange); |
| 36 | } |
| 37 | } |
| 38 | |
Alexander Kornienko | 1bfcba8 | 2017-11-28 16:41:03 +0000 | [diff] [blame] | 39 | void MoveConstArgCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) { |
Aaron Ballman | c566139 | 2017-11-27 22:59:33 +0000 | [diff] [blame] | 40 | Options.store(Opts, "CheckTriviallyCopyableMove", CheckTriviallyCopyableMove); |
| 41 | } |
| 42 | |
Alexander Kornienko | 1bfcba8 | 2017-11-28 16:41:03 +0000 | [diff] [blame] | 43 | void MoveConstArgCheck::registerMatchers(MatchFinder *Finder) { |
Alexander Kornienko | e4ac60d | 2015-11-25 15:56:11 +0000 | [diff] [blame] | 44 | if (!getLangOpts().CPlusPlus) |
| 45 | return; |
Alexander Kornienko | 28b34b0 | 2016-06-16 14:32:41 +0000 | [diff] [blame] | 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"), |
Alexander Kornienko | e4ac60d | 2015-11-25 15:56:11 +0000 | [diff] [blame] | 59 | this); |
| 60 | } |
| 61 | |
Alexander Kornienko | 1bfcba8 | 2017-11-28 16:41:03 +0000 | [diff] [blame] | 62 | void MoveConstArgCheck::check(const MatchFinder::MatchResult &Result) { |
Alexander Kornienko | e4ac60d | 2015-11-25 15:56:11 +0000 | [diff] [blame] | 63 | const auto *CallMove = Result.Nodes.getNodeAs<CallExpr>("call-move"); |
Alexander Kornienko | 28b34b0 | 2016-06-16 14:32:41 +0000 | [diff] [blame] | 64 | const auto *ReceivingExpr = Result.Nodes.getNodeAs<Expr>("receiving-expr"); |
Alexander Kornienko | e4ac60d | 2015-11-25 15:56:11 +0000 | [diff] [blame] | 65 | const Expr *Arg = CallMove->getArg(0); |
| 66 | SourceManager &SM = Result.Context->getSourceManager(); |
| 67 | |
Alexander Kornienko | 28b34b0 | 2016-06-16 14:32:41 +0000 | [diff] [blame] | 68 | CharSourceRange MoveRange = |
| 69 | CharSourceRange::getCharRange(CallMove->getSourceRange()); |
| 70 | CharSourceRange FileMoveRange = |
| 71 | Lexer::makeFileCharRange(MoveRange, SM, getLangOpts()); |
| 72 | if (!FileMoveRange.isValid()) |
| 73 | return; |
| 74 | |
Alexander Kornienko | e4ac60d | 2015-11-25 15:56:11 +0000 | [diff] [blame] | 75 | bool IsConstArg = Arg->getType().isConstQualified(); |
| 76 | bool IsTriviallyCopyable = |
| 77 | Arg->getType().isTriviallyCopyableType(*Result.Context); |
| 78 | |
| 79 | if (IsConstArg || IsTriviallyCopyable) { |
Alexander Kornienko | b489867 | 2017-05-05 17:33:49 +0000 | [diff] [blame] | 80 | if (const CXXRecordDecl *R = Arg->getType()->getAsCXXRecordDecl()) { |
Alexander Kornienko | f2dc649 | 2017-05-22 14:30:14 +0000 | [diff] [blame] | 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. |
Alexander Kornienko | b489867 | 2017-05-05 17:33:49 +0000 | [diff] [blame] | 87 | for (const auto *Ctor : R->ctors()) { |
| 88 | if (Ctor->isCopyConstructor() && Ctor->isDeleted()) |
| 89 | return; |
| 90 | } |
| 91 | } |
Aaron Ballman | c566139 | 2017-11-27 22:59:33 +0000 | [diff] [blame] | 92 | |
| 93 | if (!IsConstArg && IsTriviallyCopyable && !CheckTriviallyCopyableMove) |
| 94 | return; |
| 95 | |
Alexander Kornienko | e4ac60d | 2015-11-25 15:56:11 +0000 | [diff] [blame] | 96 | bool IsVariable = isa<DeclRefExpr>(Arg); |
Alexander Kornienko | eedcd9c | 2016-08-24 21:23:24 +0000 | [diff] [blame] | 97 | const auto *Var = |
| 98 | IsVariable ? dyn_cast<DeclRefExpr>(Arg)->getDecl() : nullptr; |
Alexander Kornienko | 900cadd | 2016-04-26 19:33:49 +0000 | [diff] [blame] | 99 | auto Diag = diag(FileMoveRange.getBegin(), |
| 100 | "std::move of the %select{|const }0" |
Alexander Kornienko | eedcd9c | 2016-08-24 21:23:24 +0000 | [diff] [blame] | 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 |
Mandeep Singh Grang | 7c7ea7d | 2016-11-08 07:50:19 +0000 | [diff] [blame] | 106 | << (IsConstArg && IsVariable && !IsTriviallyCopyable) << Var |
| 107 | << Arg->getType(); |
Alexander Kornienko | e4ac60d | 2015-11-25 15:56:11 +0000 | [diff] [blame] | 108 | |
Alexander Kornienko | 28b34b0 | 2016-06-16 14:32:41 +0000 | [diff] [blame] | 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"); |
Alexander Kornienko | e4ac60d | 2015-11-25 15:56:11 +0000 | [diff] [blame] | 114 | |
Alexander Kornienko | 28b34b0 | 2016-06-16 14:32:41 +0000 | [diff] [blame] | 115 | ReplaceCallWithArg(CallMove, Diag, SM, getLangOpts()); |
Alexander Kornienko | e4ac60d | 2015-11-25 15:56:11 +0000 | [diff] [blame] | 116 | } |
| 117 | } |
| 118 | |
Alexander Kornienko | 1bfcba8 | 2017-11-28 16:41:03 +0000 | [diff] [blame] | 119 | } // namespace performance |
Alexander Kornienko | e4ac60d | 2015-11-25 15:56:11 +0000 | [diff] [blame] | 120 | } // namespace tidy |
| 121 | } // namespace clang |