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 
replaceCallWithArg(const CallExpr * Call,DiagnosticBuilder & Diag,const SourceManager & SM,const LangOptions & LangOpts)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 
storeOptions(ClangTidyOptions::OptionMap & Opts)38 void MoveConstArgCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
39   Options.store(Opts, "CheckTriviallyCopyableMove", CheckTriviallyCopyableMove);
40 }
41 
registerMatchers(MatchFinder * Finder)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   Finder->addMatcher(
51       invocation(forEachArgumentWithParam(
52                      MoveCallMatcher,
53                      parmVarDecl(hasType(references(isConstQualified())))))
54           .bind("receiving-expr"),
55       this);
56 }
57 
check(const MatchFinder::MatchResult & Result)58 void MoveConstArgCheck::check(const MatchFinder::MatchResult &Result) {
59   const auto *CallMove = Result.Nodes.getNodeAs<CallExpr>("call-move");
60   const auto *ReceivingExpr = Result.Nodes.getNodeAs<Expr>("receiving-expr");
61   const Expr *Arg = CallMove->getArg(0);
62   SourceManager &SM = Result.Context->getSourceManager();
63 
64   CharSourceRange MoveRange =
65       CharSourceRange::getCharRange(CallMove->getSourceRange());
66   CharSourceRange FileMoveRange =
67       Lexer::makeFileCharRange(MoveRange, SM, getLangOpts());
68   if (!FileMoveRange.isValid())
69     return;
70 
71   bool IsConstArg = Arg->getType().isConstQualified();
72   bool IsTriviallyCopyable =
73       Arg->getType().isTriviallyCopyableType(*Result.Context);
74 
75   if (IsConstArg || IsTriviallyCopyable) {
76     if (const CXXRecordDecl *R = Arg->getType()->getAsCXXRecordDecl()) {
77       // According to [expr.prim.lambda]p3, "whether the closure type is
78       // trivially copyable" property can be changed by the implementation of
79       // the language, so we shouldn't rely on it when issuing diagnostics.
80       if (R->isLambda())
81         return;
82       // Don't warn when the type is not copyable.
83       for (const auto *Ctor : R->ctors()) {
84         if (Ctor->isCopyConstructor() && Ctor->isDeleted())
85           return;
86       }
87     }
88 
89     if (!IsConstArg && IsTriviallyCopyable && !CheckTriviallyCopyableMove)
90       return;
91 
92     bool IsVariable = isa<DeclRefExpr>(Arg);
93     const auto *Var =
94         IsVariable ? dyn_cast<DeclRefExpr>(Arg)->getDecl() : nullptr;
95     auto Diag = diag(FileMoveRange.getBegin(),
96                      "std::move of the %select{|const }0"
97                      "%select{expression|variable %4}1 "
98                      "%select{|of the trivially-copyable type %5 }2"
99                      "has no effect; remove std::move()"
100                      "%select{| or make the variable non-const}3")
101                 << IsConstArg << IsVariable << IsTriviallyCopyable
102                 << (IsConstArg && IsVariable && !IsTriviallyCopyable) << Var
103                 << Arg->getType();
104 
105     replaceCallWithArg(CallMove, Diag, SM, getLangOpts());
106   } else if (ReceivingExpr) {
107     auto Diag = diag(FileMoveRange.getBegin(),
108                      "passing result of std::move() as a const reference "
109                      "argument; no move will actually happen");
110 
111     replaceCallWithArg(CallMove, Diag, SM, getLangOpts());
112   }
113 }
114 
115 } // namespace performance
116 } // namespace tidy
117 } // namespace clang
118