blob: 0bcb2a016ba22b1e43a9035593bdd97f5779caf5 [file] [log] [blame]
Alexander Kornienkoe94b7c22015-01-23 15:10:37 +00001//===--- ShrinkToFitCheck.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 "ShrinkToFitCheck.h"
Alexander Kornienkoe94b7c22015-01-23 15:10:37 +000011#include "clang/AST/ASTContext.h"
12#include "clang/ASTMatchers/ASTMatchFinder.h"
13#include "clang/Lex/Lexer.h"
Chandler Carruthf7662782015-02-13 09:07:58 +000014#include "llvm/ADT/StringRef.h"
Alexander Kornienkoe94b7c22015-01-23 15:10:37 +000015
16using namespace clang::ast_matchers;
17
Alexander Kornienko50d7f4612015-06-17 13:11:37 +000018namespace clang {
19namespace {
20bool isShrinkableContainer(llvm::StringRef ClassName) {
Craig Topper45857d42015-10-18 05:14:41 +000021 static const char *const Shrinkables[] = {
Benjamin Kramer73d27492015-04-17 13:52:08 +000022 "std::basic_string",
23 "std::deque",
24 "std::vector"
25 };
26 return std::binary_search(std::begin(Shrinkables), std::end(Shrinkables),
27 ClassName);
Alexander Kornienkoe94b7c22015-01-23 15:10:37 +000028}
Alexander Kornienkoe94b7c22015-01-23 15:10:37 +000029
Alexander Kornienkoe94b7c22015-01-23 15:10:37 +000030AST_MATCHER(NamedDecl, stlShrinkableContainer) {
31 return isShrinkableContainer(Node.getQualifiedNameAsString());
32}
Alexander Kornienko50d7f4612015-06-17 13:11:37 +000033} // namespace
Alexander Kornienkoe94b7c22015-01-23 15:10:37 +000034
Alexander Kornienkoe94b7c22015-01-23 15:10:37 +000035namespace tidy {
Alexander Kornienko0ed6c472015-08-31 13:17:43 +000036namespace modernize {
Alexander Kornienkoe94b7c22015-01-23 15:10:37 +000037
38void ShrinkToFitCheck::registerMatchers(MatchFinder *Finder) {
39 // Swap as a function need not to be considered, because rvalue can not
40 // be bound to a non-const reference.
41 const auto ShrinkableAsMember =
42 memberExpr(member(valueDecl().bind("ContainerDecl")));
43 const auto ShrinkableAsDecl =
44 declRefExpr(hasDeclaration(valueDecl().bind("ContainerDecl")));
Aaron Ballmanb9ea09c2015-09-17 13:31:25 +000045 const auto CopyCtorCall = cxxConstructExpr(
Alexander Kornienkoe94b7c22015-01-23 15:10:37 +000046 hasArgument(0, anyOf(ShrinkableAsMember, ShrinkableAsDecl,
47 unaryOperator(has(ShrinkableAsMember)),
48 unaryOperator(has(ShrinkableAsDecl)))));
49 const auto SwapParam = expr(anyOf(
50 memberExpr(member(equalsBoundNode("ContainerDecl"))),
51 declRefExpr(hasDeclaration(equalsBoundNode("ContainerDecl"))),
52 unaryOperator(has(memberExpr(member(equalsBoundNode("ContainerDecl"))))),
53 unaryOperator(
54 has(declRefExpr(hasDeclaration(equalsBoundNode("ContainerDecl")))))));
55
56 Finder->addMatcher(
Aaron Ballmanb9ea09c2015-09-17 13:31:25 +000057 cxxMemberCallExpr(on(hasType(namedDecl(stlShrinkableContainer()))),
58 callee(cxxMethodDecl(hasName("swap"))),
59 has(memberExpr(hasDescendant(CopyCtorCall))),
60 hasArgument(0, SwapParam.bind("ContainerToShrink")),
61 unless(isInTemplateInstantiation()))
Alexander Kornienkoe94b7c22015-01-23 15:10:37 +000062 .bind("CopyAndSwapTrick"),
63 this);
64}
65
66void ShrinkToFitCheck::check(const MatchFinder::MatchResult &Result) {
67 const LangOptions &Opts = Result.Context->getLangOpts();
68
69 if (!Opts.CPlusPlus11)
70 return;
71
72 const auto *MemberCall =
73 Result.Nodes.getNodeAs<CXXMemberCallExpr>("CopyAndSwapTrick");
74 const auto *Container = Result.Nodes.getNodeAs<Expr>("ContainerToShrink");
75 FixItHint Hint;
76
77 if (!MemberCall->getLocStart().isMacroID()) {
78 std::string ReplacementText;
79 if (const auto *UnaryOp = llvm::dyn_cast<UnaryOperator>(Container)) {
80 ReplacementText =
81 Lexer::getSourceText(CharSourceRange::getTokenRange(
82 UnaryOp->getSubExpr()->getSourceRange()),
83 *Result.SourceManager, Opts);
84 ReplacementText += "->shrink_to_fit()";
85 } else {
86 ReplacementText = Lexer::getSourceText(
87 CharSourceRange::getTokenRange(Container->getSourceRange()),
88 *Result.SourceManager, Opts);
89 ReplacementText += ".shrink_to_fit()";
90 }
91
92 Hint = FixItHint::CreateReplacement(MemberCall->getSourceRange(),
93 ReplacementText);
94 }
95
96 diag(MemberCall->getLocStart(), "the shrink_to_fit method should be used "
97 "to reduce the capacity of a shrinkable "
98 "container")
99 << Hint;
100}
101
Alexander Kornienko0ed6c472015-08-31 13:17:43 +0000102} // namespace modernize
Alexander Kornienkoe94b7c22015-01-23 15:10:37 +0000103} // namespace tidy
104} // namespace clang