blob: 4b90df43aed753dd9cec2c5f4deb1dcedbe05568 [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"
Felix Bergeradfdc142016-03-05 21:17:58 +000012#include "../utils/FixItHintUtils.h"
Adam Baloghabd72e92018-10-12 13:05:21 +000013#include "../utils/Matchers.h"
14#include "../utils/OptionsUtils.h"
Alexander Kornienko5aebfe22016-01-29 15:54:26 +000015#include "../utils/TypeTraits.h"
Shuai Wangf21e8eb2018-09-11 22:59:46 +000016#include "clang/Analysis/Analyses/ExprMutationAnalyzer.h"
Alexander Kornienko5aebfe22016-01-29 15:54:26 +000017
Etienne Bergeron456177b2016-05-02 18:00:29 +000018using namespace clang::ast_matchers;
19
Alexander Kornienko5aebfe22016-01-29 15:54:26 +000020namespace clang {
21namespace tidy {
22namespace performance {
23
Alexander Kornienko5aebfe22016-01-29 15:54:26 +000024ForRangeCopyCheck::ForRangeCopyCheck(StringRef Name, ClangTidyContext *Context)
25 : ClangTidyCheck(Name, Context),
Adam Baloghabd72e92018-10-12 13:05:21 +000026 WarnOnAllAutoCopies(Options.get("WarnOnAllAutoCopies", 0)),
27 AllowedTypes(
28 utils::options::parseStringList(Options.get("AllowedTypes", ""))) {}
Alexander Kornienko5aebfe22016-01-29 15:54:26 +000029
30void ForRangeCopyCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
31 Options.store(Opts, "WarnOnAllAutoCopies", WarnOnAllAutoCopies);
Adam Baloghabd72e92018-10-12 13:05:21 +000032 Options.store(Opts, "AllowedTypes",
33 utils::options::serializeStringList(AllowedTypes));
Alexander Kornienko5aebfe22016-01-29 15:54:26 +000034}
35
36void ForRangeCopyCheck::registerMatchers(MatchFinder *Finder) {
37 // Match loop variables that are not references or pointers or are already
38 // initialized through MaterializeTemporaryExpr which indicates a type
39 // conversion.
40 auto LoopVar = varDecl(
Adam Baloghabd72e92018-10-12 13:05:21 +000041 hasType(qualType(
42 unless(anyOf(hasCanonicalType(anyOf(referenceType(), pointerType())),
43 hasDeclaration(namedDecl(
44 matchers::matchesAnyListedName(AllowedTypes))))))),
Alexander Kornienko5aebfe22016-01-29 15:54:26 +000045 unless(hasInitializer(expr(hasDescendant(materializeTemporaryExpr())))));
46 Finder->addMatcher(cxxForRangeStmt(hasLoopVariable(LoopVar.bind("loopVar")))
47 .bind("forRange"),
48 this);
49}
50
51void ForRangeCopyCheck::check(const MatchFinder::MatchResult &Result) {
52 const auto *Var = Result.Nodes.getNodeAs<VarDecl>("loopVar");
Adam Baloghabd72e92018-10-12 13:05:21 +000053
Alexander Kornienko5aebfe22016-01-29 15:54:26 +000054 // Ignore code in macros since we can't place the fixes correctly.
Stephen Kelly43465bf2018-08-09 22:42:26 +000055 if (Var->getBeginLoc().isMacroID())
Alexander Kornienko5aebfe22016-01-29 15:54:26 +000056 return;
57 if (handleConstValueCopy(*Var, *Result.Context))
58 return;
59 const auto *ForRange = Result.Nodes.getNodeAs<CXXForRangeStmt>("forRange");
60 handleCopyIsOnlyConstReferenced(*Var, *ForRange, *Result.Context);
61}
62
63bool ForRangeCopyCheck::handleConstValueCopy(const VarDecl &LoopVar,
64 ASTContext &Context) {
65 if (WarnOnAllAutoCopies) {
66 // For aggressive check just test that loop variable has auto type.
67 if (!isa<AutoType>(LoopVar.getType()))
68 return false;
69 } else if (!LoopVar.getType().isConstQualified()) {
70 return false;
71 }
Felix Bergerf4fdc8a2016-02-15 03:36:23 +000072 llvm::Optional<bool> Expensive =
Etienne Bergeron2a4c00f2016-05-03 02:54:05 +000073 utils::type_traits::isExpensiveToCopy(LoopVar.getType(), Context);
Felix Bergerf4fdc8a2016-02-15 03:36:23 +000074 if (!Expensive || !*Expensive)
Alexander Kornienko5aebfe22016-01-29 15:54:26 +000075 return false;
76 auto Diagnostic =
77 diag(LoopVar.getLocation(),
78 "the loop variable's type is not a reference type; this creates a "
79 "copy in each iteration; consider making this a reference")
Etienne Bergeron2a4c00f2016-05-03 02:54:05 +000080 << utils::fixit::changeVarDeclToReference(LoopVar, Context);
Alexander Kornienko5aebfe22016-01-29 15:54:26 +000081 if (!LoopVar.getType().isConstQualified())
Etienne Bergeron2a4c00f2016-05-03 02:54:05 +000082 Diagnostic << utils::fixit::changeVarDeclToConst(LoopVar);
Alexander Kornienko5aebfe22016-01-29 15:54:26 +000083 return true;
84}
85
86bool ForRangeCopyCheck::handleCopyIsOnlyConstReferenced(
87 const VarDecl &LoopVar, const CXXForRangeStmt &ForRange,
88 ASTContext &Context) {
Felix Bergerf4fdc8a2016-02-15 03:36:23 +000089 llvm::Optional<bool> Expensive =
Etienne Bergeron2a4c00f2016-05-03 02:54:05 +000090 utils::type_traits::isExpensiveToCopy(LoopVar.getType(), Context);
Felix Bergeradfdc142016-03-05 21:17:58 +000091 if (LoopVar.getType().isConstQualified() || !Expensive || !*Expensive)
Alexander Kornienko5aebfe22016-01-29 15:54:26 +000092 return false;
Haojian Wu25efa0f2018-08-10 08:25:51 +000093 // We omit the case where the loop variable is not used in the loop body. E.g.
94 //
95 // for (auto _ : benchmark_state) {
96 // }
97 //
98 // Because the fix (changing to `const auto &`) will introduce an unused
99 // compiler warning which can't be suppressed.
100 // Since this case is very rare, it is safe to ignore it.
Shuai Wangf21e8eb2018-09-11 22:59:46 +0000101 if (!ExprMutationAnalyzer(*ForRange.getBody(), Context).isMutated(&LoopVar) &&
Haojian Wu25efa0f2018-08-10 08:25:51 +0000102 !utils::decl_ref_expr::allDeclRefExprs(LoopVar, *ForRange.getBody(),
103 Context)
104 .empty()) {
105 diag(LoopVar.getLocation(),
106 "loop variable is copied but only used as const reference; consider "
107 "making it a const reference")
108 << utils::fixit::changeVarDeclToConst(LoopVar)
109 << utils::fixit::changeVarDeclToReference(LoopVar, Context);
110 return true;
111 }
112 return false;
Alexander Kornienko5aebfe22016-01-29 15:54:26 +0000113}
114
115} // namespace performance
116} // namespace tidy
117} // namespace clang