clang-tools  8.0.0
FasterStrsplitDelimiterCheck.cpp
Go to the documentation of this file.
1 //===--- FasterStrsplitDelimiterCheck.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 
11 #include "clang/AST/ASTContext.h"
12 #include "clang/ASTMatchers/ASTMatchFinder.h"
13 
14 using namespace clang::ast_matchers;
15 
16 namespace clang {
17 namespace tidy {
18 namespace abseil {
19 
20 namespace {
21 
22 AST_MATCHER(StringLiteral, lengthIsOne) { return Node.getLength() == 1; }
23 
24 ::internal::Matcher<Expr>
25 constructExprWithArg(llvm::StringRef ClassName,
26  const ::internal::Matcher<Expr> &Arg) {
27  auto ConstrExpr = cxxConstructExpr(hasType(recordDecl(hasName(ClassName))),
28  hasArgument(0, ignoringParenCasts(Arg)));
29 
30  return anyOf(ConstrExpr, cxxBindTemporaryExpr(has(ConstrExpr)));
31 }
32 
33 ::internal::Matcher<Expr>
34 copyConstructExprWithArg(llvm::StringRef ClassName,
35  const ::internal::Matcher<Expr> &Arg) {
36  return constructExprWithArg(ClassName, constructExprWithArg(ClassName, Arg));
37 }
38 
39 llvm::Optional<std::string> makeCharacterLiteral(const StringLiteral *Literal) {
40  std::string Result;
41  {
42  llvm::raw_string_ostream Stream(Result);
43  Literal->outputString(Stream);
44  }
45 
46  // Special case: If the string contains a single quote, we just need to return
47  // a character of the single quote. This is a special case because we need to
48  // escape it in the character literal.
49  if (Result == R"("'")")
50  return std::string(R"('\'')");
51 
52  assert(Result.size() == 3 || (Result.size() == 4 && Result.substr(0, 2) == "\"\\"));
53 
54  // Now replace the " with '.
55  auto Pos = Result.find_first_of('"');
56  if (Pos == Result.npos)
57  return llvm::None;
58  Result[Pos] = '\'';
59  Pos = Result.find_last_of('"');
60  if (Pos == Result.npos)
61  return llvm::None;
62  Result[Pos] = '\'';
63  return Result;
64 }
65 
66 } // anonymous namespace
67 
68 void FasterStrsplitDelimiterCheck::registerMatchers(MatchFinder *Finder) {
69  if (!getLangOpts().CPlusPlus)
70  return;
71 
72  // Binds to one character string literals.
73  const auto SingleChar =
74  expr(ignoringParenCasts(stringLiteral(lengthIsOne()).bind("Literal")));
75 
76  // Binds to a string_view (either absl or std) that was passed by value and
77  // contructed from string literal.
78  auto StringViewArg =
79  copyConstructExprWithArg("::absl::string_view", SingleChar);
80 
81  auto ByAnyCharArg =
82  expr(copyConstructExprWithArg("::absl::ByAnyChar", StringViewArg))
83  .bind("ByAnyChar");
84 
85  // Find uses of absl::StrSplit(..., "x") and absl::StrSplit(...,
86  // absl::ByAnyChar("x")) to transform them into absl::StrSplit(..., 'x').
87  Finder->addMatcher(callExpr(callee(functionDecl(hasName("::absl::StrSplit"))),
88  hasArgument(1, anyOf(ByAnyCharArg, SingleChar)),
89  unless(isInTemplateInstantiation()))
90  .bind("StrSplit"),
91  this);
92 
93  // Find uses of absl::MaxSplits("x", N) and
94  // absl::MaxSplits(absl::ByAnyChar("x"), N) to transform them into
95  // absl::MaxSplits('x', N).
96  Finder->addMatcher(
97  callExpr(
98  callee(functionDecl(hasName("::absl::MaxSplits"))),
99  hasArgument(0, anyOf(ByAnyCharArg, ignoringParenCasts(SingleChar))),
100  unless(isInTemplateInstantiation())),
101  this);
102 }
103 
104 void FasterStrsplitDelimiterCheck::check(
105  const MatchFinder::MatchResult &Result) {
106  const auto *Literal = Result.Nodes.getNodeAs<StringLiteral>("Literal");
107 
108  if (Literal->getBeginLoc().isMacroID() || Literal->getEndLoc().isMacroID())
109  return;
110 
111  llvm::Optional<std::string> Replacement = makeCharacterLiteral(Literal);
112  if (!Replacement)
113  return;
114  SourceRange Range = Literal->getSourceRange();
115 
116  if (const auto *ByAnyChar = Result.Nodes.getNodeAs<Expr>("ByAnyChar"))
117  Range = ByAnyChar->getSourceRange();
118 
119  diag(
120  Literal->getBeginLoc(),
121  "%select{absl::StrSplit()|absl::MaxSplits()}0 called with a string "
122  "literal "
123  "consisting of a single character; consider using the character overload")
124  << (Result.Nodes.getNodeAs<CallExpr>("StrSplit") ? 0 : 1)
125  << FixItHint::CreateReplacement(CharSourceRange::getTokenRange(Range),
126  *Replacement);
127 }
128 
129 } // namespace abseil
130 } // namespace tidy
131 } // namespace clang
AST_MATCHER(BinaryOperator, isAssignmentOperator)
Definition: Matchers.h:20
llvm::Optional< llvm::Expected< tooling::AtomicChanges > > Result
Position Pos
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
CharSourceRange Range
SourceRange for the file name.