blob: 276ffd8245a3a5260ed5b1aa8d6cbea4ebcf6853 [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"
15
16using namespace clang::ast_matchers;
17
18namespace clang {
19namespace tidy {
20namespace performance {
21
22namespace {
23
24std::string paramNameOrIndex(StringRef Name, size_t Index) {
25 return (Name.empty() ? llvm::Twine('#') + llvm::Twine(Index + 1)
26 : llvm::Twine('\'') + Name + llvm::Twine('\''))
27 .str();
28}
29
30} // namespace
31
32void UnnecessaryValueParamCheck::registerMatchers(MatchFinder *Finder) {
33 const auto ExpensiveValueParamDecl =
34 parmVarDecl(hasType(hasCanonicalType(allOf(matchers::isExpensiveToCopy(),
35 unless(referenceType())))),
36 decl().bind("param"));
37 Finder->addMatcher(
38 functionDecl(isDefinition(), unless(cxxMethodDecl(isOverride())),
39 unless(isInstantiated()),
40 has(typeLoc(forEach(ExpensiveValueParamDecl))),
41 decl().bind("functionDecl")),
42 this);
43}
44
45void UnnecessaryValueParamCheck::check(const MatchFinder::MatchResult &Result) {
46 const auto *Param = Result.Nodes.getNodeAs<ParmVarDecl>("param");
47 const auto *Function = Result.Nodes.getNodeAs<FunctionDecl>("functionDecl");
48 const size_t Index = std::find(Function->parameters().begin(),
49 Function->parameters().end(), Param) -
50 Function->parameters().begin();
51 bool IsConstQualified =
52 Param->getType().getCanonicalType().isConstQualified();
53
Etienne Bergeron2c82ebe2016-04-07 14:58:13 +000054 // Skip declarations delayed by late template parsing without a body.
55 if (!Function->getBody())
56 return;
57
Felix Berger3c8edde2016-03-29 02:42:38 +000058 // Do not trigger on non-const value parameters when:
59 // 1. they are in a constructor definition since they can likely trigger
60 // misc-move-constructor-init which will suggest to move the argument.
61 // 2. they are not only used as const.
62 if (!IsConstQualified && (llvm::isa<CXXConstructorDecl>(Function) ||
63 !Function->doesThisDeclarationHaveABody() ||
Etienne Bergeron2a4c00f2016-05-03 02:54:05 +000064 !utils::decl_ref_expr::isOnlyUsedAsConst(
Felix Berger3c8edde2016-03-29 02:42:38 +000065 *Param, *Function->getBody(), *Result.Context)))
66 return;
67 auto Diag =
68 diag(Param->getLocation(),
69 IsConstQualified ? "the const qualified parameter %0 is "
70 "copied for each invocation; consider "
71 "making it a reference"
72 : "the parameter %0 is copied for each "
73 "invocation but only used as a const reference; "
74 "consider making it a const reference")
75 << paramNameOrIndex(Param->getName(), Index);
76 // Do not propose fixes in macros since we cannot place them correctly.
77 if (Param->getLocStart().isMacroID())
78 return;
79 for (const auto *FunctionDecl = Function; FunctionDecl != nullptr;
80 FunctionDecl = FunctionDecl->getPreviousDecl()) {
81 const auto &CurrentParam = *FunctionDecl->getParamDecl(Index);
Etienne Bergeron2a4c00f2016-05-03 02:54:05 +000082 Diag << utils::fixit::changeVarDeclToReference(CurrentParam,
Felix Berger3c8edde2016-03-29 02:42:38 +000083 *Result.Context);
84 if (!IsConstQualified)
Etienne Bergeron2a4c00f2016-05-03 02:54:05 +000085 Diag << utils::fixit::changeVarDeclToConst(CurrentParam);
Felix Berger3c8edde2016-03-29 02:42:38 +000086 }
87}
88
89} // namespace performance
90} // namespace tidy
91} // namespace clang