blob: c8c90638255ca7b425c48c82ace3f1b3be3aafaa [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"
Shuai Wang0ed0febb2018-07-10 22:51:06 +000011#include "../utils/ExprMutationAnalyzer.h"
Felix Bergeradfdc142016-03-05 21:17:58 +000012#include "../utils/FixItHintUtils.h"
Alexander Kornienko5aebfe22016-01-29 15:54:26 +000013#include "../utils/TypeTraits.h"
Alexander Kornienko5aebfe22016-01-29 15:54:26 +000014
Etienne Bergeron456177b2016-05-02 18:00:29 +000015using namespace clang::ast_matchers;
16
Alexander Kornienko5aebfe22016-01-29 15:54:26 +000017namespace clang {
18namespace tidy {
19namespace performance {
20
Alexander Kornienko5aebfe22016-01-29 15:54:26 +000021ForRangeCopyCheck::ForRangeCopyCheck(StringRef Name, ClangTidyContext *Context)
22 : ClangTidyCheck(Name, Context),
23 WarnOnAllAutoCopies(Options.get("WarnOnAllAutoCopies", 0)) {}
24
25void ForRangeCopyCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
26 Options.store(Opts, "WarnOnAllAutoCopies", WarnOnAllAutoCopies);
27}
28
29void 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
41void 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.
Stephen Kelly43465bf2018-08-09 22:42:26 +000044 if (Var->getBeginLoc().isMacroID())
Alexander Kornienko5aebfe22016-01-29 15:54:26 +000045 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
52bool 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 }
Felix Bergerf4fdc8a2016-02-15 03:36:23 +000061 llvm::Optional<bool> Expensive =
Etienne Bergeron2a4c00f2016-05-03 02:54:05 +000062 utils::type_traits::isExpensiveToCopy(LoopVar.getType(), Context);
Felix Bergerf4fdc8a2016-02-15 03:36:23 +000063 if (!Expensive || !*Expensive)
Alexander Kornienko5aebfe22016-01-29 15:54:26 +000064 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")
Etienne Bergeron2a4c00f2016-05-03 02:54:05 +000069 << utils::fixit::changeVarDeclToReference(LoopVar, Context);
Alexander Kornienko5aebfe22016-01-29 15:54:26 +000070 if (!LoopVar.getType().isConstQualified())
Etienne Bergeron2a4c00f2016-05-03 02:54:05 +000071 Diagnostic << utils::fixit::changeVarDeclToConst(LoopVar);
Alexander Kornienko5aebfe22016-01-29 15:54:26 +000072 return true;
73}
74
75bool ForRangeCopyCheck::handleCopyIsOnlyConstReferenced(
76 const VarDecl &LoopVar, const CXXForRangeStmt &ForRange,
77 ASTContext &Context) {
Felix Bergerf4fdc8a2016-02-15 03:36:23 +000078 llvm::Optional<bool> Expensive =
Etienne Bergeron2a4c00f2016-05-03 02:54:05 +000079 utils::type_traits::isExpensiveToCopy(LoopVar.getType(), Context);
Felix Bergeradfdc142016-03-05 21:17:58 +000080 if (LoopVar.getType().isConstQualified() || !Expensive || !*Expensive)
Alexander Kornienko5aebfe22016-01-29 15:54:26 +000081 return false;
Shuai Wang0ed0febb2018-07-10 22:51:06 +000082 if (utils::ExprMutationAnalyzer(ForRange.getBody(), &Context)
83 .isMutated(&LoopVar))
Alexander Kornienko5aebfe22016-01-29 15:54:26 +000084 return false;
85 diag(LoopVar.getLocation(),
86 "loop variable is copied but only used as const reference; consider "
87 "making it a const reference")
Etienne Bergeron2a4c00f2016-05-03 02:54:05 +000088 << utils::fixit::changeVarDeclToConst(LoopVar)
89 << utils::fixit::changeVarDeclToReference(LoopVar, Context);
Alexander Kornienko5aebfe22016-01-29 15:54:26 +000090 return true;
91}
92
93} // namespace performance
94} // namespace tidy
95} // namespace clang