blob: 867d95d8edd30dfe9f08ab6c731de909eb87fe26 [file] [log] [blame]
Alexander Kornienko5aebfe22016-01-29 15:54:26 +00001//===--- ForRangeCopyCheck.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 "ForRangeCopyCheck.h"
Haojian Wu25efa0f2018-08-10 08:25:51 +000011#include "../utils/DeclRefExprUtils.h"
Shuai Wang0ed0febb2018-07-10 22:51:06 +000012#include "../utils/ExprMutationAnalyzer.h"
Felix Bergeradfdc142016-03-05 21:17:58 +000013#include "../utils/FixItHintUtils.h"
Alexander Kornienko5aebfe22016-01-29 15:54:26 +000014#include "../utils/TypeTraits.h"
Alexander Kornienko5aebfe22016-01-29 15:54:26 +000015
Etienne Bergeron456177b2016-05-02 18:00:29 +000016using namespace clang::ast_matchers;
17
Alexander Kornienko5aebfe22016-01-29 15:54:26 +000018namespace clang {
19namespace tidy {
20namespace performance {
21
Alexander Kornienko5aebfe22016-01-29 15:54:26 +000022ForRangeCopyCheck::ForRangeCopyCheck(StringRef Name, ClangTidyContext *Context)
23 : ClangTidyCheck(Name, Context),
24 WarnOnAllAutoCopies(Options.get("WarnOnAllAutoCopies", 0)) {}
25
26void ForRangeCopyCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
27 Options.store(Opts, "WarnOnAllAutoCopies", WarnOnAllAutoCopies);
28}
29
30void ForRangeCopyCheck::registerMatchers(MatchFinder *Finder) {
31 // Match loop variables that are not references or pointers or are already
32 // initialized through MaterializeTemporaryExpr which indicates a type
33 // conversion.
34 auto LoopVar = varDecl(
35 hasType(hasCanonicalType(unless(anyOf(referenceType(), pointerType())))),
36 unless(hasInitializer(expr(hasDescendant(materializeTemporaryExpr())))));
37 Finder->addMatcher(cxxForRangeStmt(hasLoopVariable(LoopVar.bind("loopVar")))
38 .bind("forRange"),
39 this);
40}
41
42void ForRangeCopyCheck::check(const MatchFinder::MatchResult &Result) {
43 const auto *Var = Result.Nodes.getNodeAs<VarDecl>("loopVar");
44 // Ignore code in macros since we can't place the fixes correctly.
Stephen Kelly43465bf2018-08-09 22:42:26 +000045 if (Var->getBeginLoc().isMacroID())
Alexander Kornienko5aebfe22016-01-29 15:54:26 +000046 return;
47 if (handleConstValueCopy(*Var, *Result.Context))
48 return;
49 const auto *ForRange = Result.Nodes.getNodeAs<CXXForRangeStmt>("forRange");
50 handleCopyIsOnlyConstReferenced(*Var, *ForRange, *Result.Context);
51}
52
53bool ForRangeCopyCheck::handleConstValueCopy(const VarDecl &LoopVar,
54 ASTContext &Context) {
55 if (WarnOnAllAutoCopies) {
56 // For aggressive check just test that loop variable has auto type.
57 if (!isa<AutoType>(LoopVar.getType()))
58 return false;
59 } else if (!LoopVar.getType().isConstQualified()) {
60 return false;
61 }
Felix Bergerf4fdc8a2016-02-15 03:36:23 +000062 llvm::Optional<bool> Expensive =
Etienne Bergeron2a4c00f2016-05-03 02:54:05 +000063 utils::type_traits::isExpensiveToCopy(LoopVar.getType(), Context);
Felix Bergerf4fdc8a2016-02-15 03:36:23 +000064 if (!Expensive || !*Expensive)
Alexander Kornienko5aebfe22016-01-29 15:54:26 +000065 return false;
66 auto Diagnostic =
67 diag(LoopVar.getLocation(),
68 "the loop variable's type is not a reference type; this creates a "
69 "copy in each iteration; consider making this a reference")
Etienne Bergeron2a4c00f2016-05-03 02:54:05 +000070 << utils::fixit::changeVarDeclToReference(LoopVar, Context);
Alexander Kornienko5aebfe22016-01-29 15:54:26 +000071 if (!LoopVar.getType().isConstQualified())
Etienne Bergeron2a4c00f2016-05-03 02:54:05 +000072 Diagnostic << utils::fixit::changeVarDeclToConst(LoopVar);
Alexander Kornienko5aebfe22016-01-29 15:54:26 +000073 return true;
74}
75
76bool ForRangeCopyCheck::handleCopyIsOnlyConstReferenced(
77 const VarDecl &LoopVar, const CXXForRangeStmt &ForRange,
78 ASTContext &Context) {
Felix Bergerf4fdc8a2016-02-15 03:36:23 +000079 llvm::Optional<bool> Expensive =
Etienne Bergeron2a4c00f2016-05-03 02:54:05 +000080 utils::type_traits::isExpensiveToCopy(LoopVar.getType(), Context);
Felix Bergeradfdc142016-03-05 21:17:58 +000081 if (LoopVar.getType().isConstQualified() || !Expensive || !*Expensive)
Alexander Kornienko5aebfe22016-01-29 15:54:26 +000082 return false;
Haojian Wu25efa0f2018-08-10 08:25:51 +000083 // We omit the case where the loop variable is not used in the loop body. E.g.
84 //
85 // for (auto _ : benchmark_state) {
86 // }
87 //
88 // Because the fix (changing to `const auto &`) will introduce an unused
89 // compiler warning which can't be suppressed.
90 // Since this case is very rare, it is safe to ignore it.
91 if (!utils::ExprMutationAnalyzer(ForRange.getBody(), &Context)
92 .isMutated(&LoopVar) &&
93 !utils::decl_ref_expr::allDeclRefExprs(LoopVar, *ForRange.getBody(),
94 Context)
95 .empty()) {
96 diag(LoopVar.getLocation(),
97 "loop variable is copied but only used as const reference; consider "
98 "making it a const reference")
99 << utils::fixit::changeVarDeclToConst(LoopVar)
100 << utils::fixit::changeVarDeclToReference(LoopVar, Context);
101 return true;
102 }
103 return false;
Alexander Kornienko5aebfe22016-01-29 15:54:26 +0000104}
105
106} // namespace performance
107} // namespace tidy
108} // namespace clang