blob: d3e4c7fb87f40e5f459bbd72be42bf60e0d596dd [file] [log] [blame]
Felix Berger3c8edde2016-03-29 02:42:38 +00001//===--- UnnecessaryValueParamCheck.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 "UnnecessaryValueParamCheck.h"
11
12#include "../utils/DeclRefExprUtils.h"
13#include "../utils/FixItHintUtils.h"
14#include "../utils/Matchers.h"
Felix Berger7f882752016-07-01 20:12:15 +000015#include "../utils/TypeTraits.h"
16#include "clang/Frontend/CompilerInstance.h"
17#include "clang/Lex/Lexer.h"
18#include "clang/Lex/Preprocessor.h"
Felix Berger3c8edde2016-03-29 02:42:38 +000019
20using namespace clang::ast_matchers;
21
22namespace clang {
23namespace tidy {
24namespace performance {
25
26namespace {
27
28std::string paramNameOrIndex(StringRef Name, size_t Index) {
29 return (Name.empty() ? llvm::Twine('#') + llvm::Twine(Index + 1)
30 : llvm::Twine('\'') + Name + llvm::Twine('\''))
31 .str();
32}
33
Felix Berger7f882752016-07-01 20:12:15 +000034template <typename S>
35bool isSubset(const S &SubsetCandidate, const S &SupersetCandidate) {
36 for (const auto &E : SubsetCandidate)
37 if (SupersetCandidate.count(E) == 0)
38 return false;
39 return true;
40}
41
Felix Berger3c8edde2016-03-29 02:42:38 +000042} // namespace
43
Felix Berger7f882752016-07-01 20:12:15 +000044UnnecessaryValueParamCheck::UnnecessaryValueParamCheck(
45 StringRef Name, ClangTidyContext *Context)
46 : ClangTidyCheck(Name, Context),
47 IncludeStyle(utils::IncludeSorter::parseIncludeStyle(
48 Options.get("IncludeStyle", "llvm"))) {}
49
Felix Berger3c8edde2016-03-29 02:42:38 +000050void UnnecessaryValueParamCheck::registerMatchers(MatchFinder *Finder) {
51 const auto ExpensiveValueParamDecl =
52 parmVarDecl(hasType(hasCanonicalType(allOf(matchers::isExpensiveToCopy(),
53 unless(referenceType())))),
54 decl().bind("param"));
55 Finder->addMatcher(
56 functionDecl(isDefinition(), unless(cxxMethodDecl(isOverride())),
57 unless(isInstantiated()),
58 has(typeLoc(forEach(ExpensiveValueParamDecl))),
59 decl().bind("functionDecl")),
60 this);
61}
62
63void UnnecessaryValueParamCheck::check(const MatchFinder::MatchResult &Result) {
64 const auto *Param = Result.Nodes.getNodeAs<ParmVarDecl>("param");
65 const auto *Function = Result.Nodes.getNodeAs<FunctionDecl>("functionDecl");
66 const size_t Index = std::find(Function->parameters().begin(),
67 Function->parameters().end(), Param) -
68 Function->parameters().begin();
69 bool IsConstQualified =
70 Param->getType().getCanonicalType().isConstQualified();
71
Etienne Bergeron2c82ebe2016-04-07 14:58:13 +000072 // Skip declarations delayed by late template parsing without a body.
73 if (!Function->getBody())
74 return;
75
Felix Berger3c8edde2016-03-29 02:42:38 +000076 // Do not trigger on non-const value parameters when:
77 // 1. they are in a constructor definition since they can likely trigger
78 // misc-move-constructor-init which will suggest to move the argument.
Felix Berger3c8edde2016-03-29 02:42:38 +000079 if (!IsConstQualified && (llvm::isa<CXXConstructorDecl>(Function) ||
Felix Berger7f882752016-07-01 20:12:15 +000080 !Function->doesThisDeclarationHaveABody()))
Felix Berger3c8edde2016-03-29 02:42:38 +000081 return;
Felix Berger7f882752016-07-01 20:12:15 +000082
83 auto AllDeclRefExprs = utils::decl_ref_expr::allDeclRefExprs(
84 *Param, *Function->getBody(), *Result.Context);
85 auto ConstDeclRefExprs = utils::decl_ref_expr::constReferenceDeclRefExprs(
86 *Param, *Function->getBody(), *Result.Context);
87 // 2. they are not only used as const.
88 if (!isSubset(AllDeclRefExprs, ConstDeclRefExprs))
89 return;
90
91 // If the parameter is non-const, check if it has a move constructor and is
92 // only referenced once to copy-construct another object or whether it has a
93 // move assignment operator and is only referenced once when copy-assigned.
94 // In this case wrap DeclRefExpr with std::move() to avoid the unnecessary
95 // copy.
96 if (!IsConstQualified) {
97 auto CanonicalType = Param->getType().getCanonicalType();
98 if (AllDeclRefExprs.size() == 1 &&
99 ((utils::type_traits::hasNonTrivialMoveConstructor(CanonicalType) &&
100 utils::decl_ref_expr::isCopyConstructorArgument(
101 **AllDeclRefExprs.begin(), *Function->getBody(),
102 *Result.Context)) ||
103 (utils::type_traits::hasNonTrivialMoveAssignment(CanonicalType) &&
104 utils::decl_ref_expr::isCopyAssignmentArgument(
105 **AllDeclRefExprs.begin(), *Function->getBody(),
106 *Result.Context)))) {
107 handleMoveFix(*Param, **AllDeclRefExprs.begin(), *Result.Context);
108 return;
109 }
110 }
111
Felix Berger3c8edde2016-03-29 02:42:38 +0000112 auto Diag =
113 diag(Param->getLocation(),
114 IsConstQualified ? "the const qualified parameter %0 is "
115 "copied for each invocation; consider "
116 "making it a reference"
117 : "the parameter %0 is copied for each "
118 "invocation but only used as a const reference; "
119 "consider making it a const reference")
120 << paramNameOrIndex(Param->getName(), Index);
121 // Do not propose fixes in macros since we cannot place them correctly.
122 if (Param->getLocStart().isMacroID())
123 return;
124 for (const auto *FunctionDecl = Function; FunctionDecl != nullptr;
125 FunctionDecl = FunctionDecl->getPreviousDecl()) {
126 const auto &CurrentParam = *FunctionDecl->getParamDecl(Index);
Etienne Bergeron2a4c00f2016-05-03 02:54:05 +0000127 Diag << utils::fixit::changeVarDeclToReference(CurrentParam,
Felix Berger3c8edde2016-03-29 02:42:38 +0000128 *Result.Context);
129 if (!IsConstQualified)
Etienne Bergeron2a4c00f2016-05-03 02:54:05 +0000130 Diag << utils::fixit::changeVarDeclToConst(CurrentParam);
Felix Berger3c8edde2016-03-29 02:42:38 +0000131 }
132}
133
Felix Berger7f882752016-07-01 20:12:15 +0000134void UnnecessaryValueParamCheck::registerPPCallbacks(
135 CompilerInstance &Compiler) {
136 Inserter.reset(new utils::IncludeInserter(
137 Compiler.getSourceManager(), Compiler.getLangOpts(), IncludeStyle));
138 Compiler.getPreprocessor().addPPCallbacks(Inserter->CreatePPCallbacks());
139}
140
141void UnnecessaryValueParamCheck::storeOptions(
142 ClangTidyOptions::OptionMap &Opts) {
143 Options.store(Opts, "IncludeStyle",
144 utils::IncludeSorter::toString(IncludeStyle));
145}
146
147void UnnecessaryValueParamCheck::handleMoveFix(const ParmVarDecl &Var,
148 const DeclRefExpr &CopyArgument,
149 const ASTContext &Context) {
150 auto Diag = diag(CopyArgument.getLocStart(),
151 "parameter %0 is passed by value and only copied once; "
152 "consider moving it to avoid unnecessary copies")
153 << &Var;
154 // Do not propose fixes in macros since we cannot place them correctly.
155 if (CopyArgument.getLocStart().isMacroID())
156 return;
157 const auto &SM = Context.getSourceManager();
158 auto EndLoc = Lexer::getLocForEndOfToken(CopyArgument.getLocation(), 0, SM,
159 Context.getLangOpts());
160 Diag << FixItHint::CreateInsertion(CopyArgument.getLocStart(), "std::move(")
161 << FixItHint::CreateInsertion(EndLoc, ")");
162 if (auto IncludeFixit = Inserter->CreateIncludeInsertion(
163 SM.getFileID(CopyArgument.getLocStart()), "utility",
164 /*IsAngled=*/true))
165 Diag << *IncludeFixit;
166}
167
Felix Berger3c8edde2016-03-29 02:42:38 +0000168} // namespace performance
169} // namespace tidy
170} // namespace clang