blob: abdcdf49e8655caf14fcb4d85505de469982b987 [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"
Felix Bergeradfdc142016-03-05 21:17:58 +000011#include "../utils/DeclRefExprUtils.h"
12#include "../utils/FixItHintUtils.h"
Alexander Kornienko5aebfe22016-01-29 15:54:26 +000013#include "../utils/TypeTraits.h"
Alexander Kornienko5aebfe22016-01-29 15:54:26 +000014
15namespace clang {
16namespace tidy {
17namespace performance {
18
19using namespace ::clang::ast_matchers;
Alexander Kornienko5aebfe22016-01-29 15:54:26 +000020
21ForRangeCopyCheck::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.
44 if (Var->getLocStart().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
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 =
62 type_traits::isExpensiveToCopy(LoopVar.getType(), Context);
63 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")
Felix Bergeradfdc142016-03-05 21:17:58 +000069 << utils::create_fix_it::changeVarDeclToReference(LoopVar, Context);
Alexander Kornienko5aebfe22016-01-29 15:54:26 +000070 if (!LoopVar.getType().isConstQualified())
Felix Bergeradfdc142016-03-05 21:17:58 +000071 Diagnostic << utils::create_fix_it::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 =
79 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;
Felix Bergeradfdc142016-03-05 21:17:58 +000082 if (!decl_ref_expr_utils::isOnlyUsedAsConst(LoopVar, *ForRange.getBody(), Context))
Alexander Kornienko5aebfe22016-01-29 15:54:26 +000083 return false;
84 diag(LoopVar.getLocation(),
85 "loop variable is copied but only used as const reference; consider "
86 "making it a const reference")
Felix Bergeradfdc142016-03-05 21:17:58 +000087 << utils::create_fix_it::changeVarDeclToConst(LoopVar)
88 << utils::create_fix_it::changeVarDeclToReference(LoopVar, Context);
Alexander Kornienko5aebfe22016-01-29 15:54:26 +000089 return true;
90}
91
92} // namespace performance
93} // namespace tidy
94} // namespace clang