blob: be7e31e978831758e4891b4f629079ae622a57e2 [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"
15#include "llvm/ADT/StringSet.h"
Alexander Kornienkoe94b7c22015-01-23 15:10:37 +000016
17using namespace clang::ast_matchers;
18
19namespace {
20bool isShrinkableContainer(llvm::StringRef ClassName) {
21 static const llvm::StringSet<> Shrinkables = [] {
22 llvm::StringSet<> RetVal;
23 RetVal.insert("std::deque");
24 RetVal.insert("std::basic_string");
25 RetVal.insert("std::vector");
26 return RetVal;
27 }();
28 return Shrinkables.find(ClassName) != Shrinkables.end();
29}
30}
31
32namespace clang {
33namespace ast_matchers {
34AST_MATCHER(NamedDecl, stlShrinkableContainer) {
35 return isShrinkableContainer(Node.getQualifiedNameAsString());
36}
37} // namespace ast_matchesr
38} // namespace clang
39
40namespace clang {
41namespace tidy {
42
43void ShrinkToFitCheck::registerMatchers(MatchFinder *Finder) {
44 // Swap as a function need not to be considered, because rvalue can not
45 // be bound to a non-const reference.
46 const auto ShrinkableAsMember =
47 memberExpr(member(valueDecl().bind("ContainerDecl")));
48 const auto ShrinkableAsDecl =
49 declRefExpr(hasDeclaration(valueDecl().bind("ContainerDecl")));
50 const auto CopyCtorCall = constructExpr(
51 hasArgument(0, anyOf(ShrinkableAsMember, ShrinkableAsDecl,
52 unaryOperator(has(ShrinkableAsMember)),
53 unaryOperator(has(ShrinkableAsDecl)))));
54 const auto SwapParam = expr(anyOf(
55 memberExpr(member(equalsBoundNode("ContainerDecl"))),
56 declRefExpr(hasDeclaration(equalsBoundNode("ContainerDecl"))),
57 unaryOperator(has(memberExpr(member(equalsBoundNode("ContainerDecl"))))),
58 unaryOperator(
59 has(declRefExpr(hasDeclaration(equalsBoundNode("ContainerDecl")))))));
60
61 Finder->addMatcher(
62 memberCallExpr(on(hasType(namedDecl(stlShrinkableContainer()))),
63 callee(methodDecl(hasName("swap"))),
64 has(memberExpr(hasDescendant(CopyCtorCall))),
65 hasArgument(0, SwapParam.bind("ContainerToShrink")),
66 unless(isInTemplateInstantiation()))
67 .bind("CopyAndSwapTrick"),
68 this);
69}
70
71void ShrinkToFitCheck::check(const MatchFinder::MatchResult &Result) {
72 const LangOptions &Opts = Result.Context->getLangOpts();
73
74 if (!Opts.CPlusPlus11)
75 return;
76
77 const auto *MemberCall =
78 Result.Nodes.getNodeAs<CXXMemberCallExpr>("CopyAndSwapTrick");
79 const auto *Container = Result.Nodes.getNodeAs<Expr>("ContainerToShrink");
80 FixItHint Hint;
81
82 if (!MemberCall->getLocStart().isMacroID()) {
83 std::string ReplacementText;
84 if (const auto *UnaryOp = llvm::dyn_cast<UnaryOperator>(Container)) {
85 ReplacementText =
86 Lexer::getSourceText(CharSourceRange::getTokenRange(
87 UnaryOp->getSubExpr()->getSourceRange()),
88 *Result.SourceManager, Opts);
89 ReplacementText += "->shrink_to_fit()";
90 } else {
91 ReplacementText = Lexer::getSourceText(
92 CharSourceRange::getTokenRange(Container->getSourceRange()),
93 *Result.SourceManager, Opts);
94 ReplacementText += ".shrink_to_fit()";
95 }
96
97 Hint = FixItHint::CreateReplacement(MemberCall->getSourceRange(),
98 ReplacementText);
99 }
100
101 diag(MemberCall->getLocStart(), "the shrink_to_fit method should be used "
102 "to reduce the capacity of a shrinkable "
103 "container")
104 << Hint;
105}
106
107} // namespace tidy
108} // namespace clang