clang-tools 10.0.0

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