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
10 #include "FasterStrsplitDelimiterCheck.h"
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
clang::ast_matchers
Definition: AbseilMatcher.h:15
clang::tidy::matchers::AST_MATCHER
AST_MATCHER(BinaryOperator, isAssignmentOperator)
Definition: Matchers.h:20
FasterStrsplitDelimiterCheck.h
Result
llvm::Optional< llvm::Expected< tooling::AtomicChanges > > Result
Definition: ClangdServer.cpp:60
Pos
Position Pos
Definition: CodeComplete.cpp:1008
clang
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
Definition: ChangeNamespace.cpp:18
Range
CharSourceRange Range
SourceRange for the file name.
Definition: IncludeOrderCheck.cpp:38