xref: /llvm-project/clang-tools-extra/clang-tidy/performance/ForRangeCopyCheck.cpp (revision 43465bf3fd6cca715187ee7286c881cb210fc3c4)
1 //===--- 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"
11 #include "../utils/ExprMutationAnalyzer.h"
12 #include "../utils/FixItHintUtils.h"
13 #include "../utils/TypeTraits.h"
14 
15 using namespace clang::ast_matchers;
16 
17 namespace clang {
18 namespace tidy {
19 namespace performance {
20 
21 ForRangeCopyCheck::ForRangeCopyCheck(StringRef Name, ClangTidyContext *Context)
22     : ClangTidyCheck(Name, Context),
23       WarnOnAllAutoCopies(Options.get("WarnOnAllAutoCopies", 0)) {}
24 
25 void ForRangeCopyCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
26   Options.store(Opts, "WarnOnAllAutoCopies", WarnOnAllAutoCopies);
27 }
28 
29 void ForRangeCopyCheck::registerMatchers(MatchFinder *Finder) {
30   // Match loop variables that are not references or pointers or are already
31   // initialized through MaterializeTemporaryExpr which indicates a type
32   // conversion.
33   auto LoopVar = varDecl(
34       hasType(hasCanonicalType(unless(anyOf(referenceType(), pointerType())))),
35       unless(hasInitializer(expr(hasDescendant(materializeTemporaryExpr())))));
36   Finder->addMatcher(cxxForRangeStmt(hasLoopVariable(LoopVar.bind("loopVar")))
37                          .bind("forRange"),
38                      this);
39 }
40 
41 void ForRangeCopyCheck::check(const MatchFinder::MatchResult &Result) {
42   const auto *Var = Result.Nodes.getNodeAs<VarDecl>("loopVar");
43   // Ignore code in macros since we can't place the fixes correctly.
44   if (Var->getBeginLoc().isMacroID())
45     return;
46   if (handleConstValueCopy(*Var, *Result.Context))
47     return;
48   const auto *ForRange = Result.Nodes.getNodeAs<CXXForRangeStmt>("forRange");
49   handleCopyIsOnlyConstReferenced(*Var, *ForRange, *Result.Context);
50 }
51 
52 bool ForRangeCopyCheck::handleConstValueCopy(const VarDecl &LoopVar,
53                                              ASTContext &Context) {
54   if (WarnOnAllAutoCopies) {
55     // For aggressive check just test that loop variable has auto type.
56     if (!isa<AutoType>(LoopVar.getType()))
57       return false;
58   } else if (!LoopVar.getType().isConstQualified()) {
59     return false;
60   }
61   llvm::Optional<bool> Expensive =
62       utils::type_traits::isExpensiveToCopy(LoopVar.getType(), Context);
63   if (!Expensive || !*Expensive)
64     return false;
65   auto Diagnostic =
66       diag(LoopVar.getLocation(),
67            "the loop variable's type is not a reference type; this creates a "
68            "copy in each iteration; consider making this a reference")
69       << utils::fixit::changeVarDeclToReference(LoopVar, Context);
70   if (!LoopVar.getType().isConstQualified())
71     Diagnostic << utils::fixit::changeVarDeclToConst(LoopVar);
72   return true;
73 }
74 
75 bool ForRangeCopyCheck::handleCopyIsOnlyConstReferenced(
76     const VarDecl &LoopVar, const CXXForRangeStmt &ForRange,
77     ASTContext &Context) {
78   llvm::Optional<bool> Expensive =
79       utils::type_traits::isExpensiveToCopy(LoopVar.getType(), Context);
80   if (LoopVar.getType().isConstQualified() || !Expensive || !*Expensive)
81     return false;
82   if (utils::ExprMutationAnalyzer(ForRange.getBody(), &Context)
83           .isMutated(&LoopVar))
84     return false;
85   diag(LoopVar.getLocation(),
86        "loop variable is copied but only used as const reference; consider "
87        "making it a const reference")
88       << utils::fixit::changeVarDeclToConst(LoopVar)
89       << utils::fixit::changeVarDeclToReference(LoopVar, Context);
90   return true;
91 }
92 
93 } // namespace performance
94 } // namespace tidy
95 } // namespace clang
96