clang-tools  8.0.0
StringFindStartswithCheck.cpp
Go to the documentation of this file.
1 //===--- StringFindStartswithCheck.cc - clang-tidy---------------*- C++ -*-===//
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 
12 #include "../utils/OptionsUtils.h"
13 #include "clang/AST/ASTContext.h"
14 #include "clang/ASTMatchers/ASTMatchers.h"
15 #include "clang/Frontend/CompilerInstance.h"
16 
17 #include <cassert>
18 
19 using namespace clang::ast_matchers;
20 
21 namespace clang {
22 namespace tidy {
23 namespace abseil {
24 
25 StringFindStartswithCheck::StringFindStartswithCheck(StringRef Name,
26  ClangTidyContext *Context)
27  : ClangTidyCheck(Name, Context),
28  StringLikeClasses(utils::options::parseStringList(
29  Options.get("StringLikeClasses", "::std::basic_string"))),
30  IncludeStyle(utils::IncludeSorter::parseIncludeStyle(
31  Options.getLocalOrGlobal("IncludeStyle", "llvm"))),
32  AbseilStringsMatchHeader(
33  Options.get("AbseilStringsMatchHeader", "absl/strings/match.h")) {}
34 
36  auto ZeroLiteral = integerLiteral(equals(0));
37  auto StringClassMatcher = cxxRecordDecl(hasAnyName(SmallVector<StringRef, 4>(
38  StringLikeClasses.begin(), StringLikeClasses.end())));
39  auto StringType = hasUnqualifiedDesugaredType(
40  recordType(hasDeclaration(StringClassMatcher)));
41 
42  auto StringFind = cxxMemberCallExpr(
43  // .find()-call on a string...
44  callee(cxxMethodDecl(hasName("find"))),
45  on(hasType(StringType)),
46  // ... with some search expression ...
47  hasArgument(0, expr().bind("needle")),
48  // ... and either "0" as second argument or the default argument (also 0).
49  anyOf(hasArgument(1, ZeroLiteral), hasArgument(1, cxxDefaultArgExpr())));
50 
51  Finder->addMatcher(
52  // Match [=!]= with a zero on one side and a string.find on the other.
53  binaryOperator(
54  anyOf(hasOperatorName("=="), hasOperatorName("!=")),
55  hasEitherOperand(ignoringParenImpCasts(ZeroLiteral)),
56  hasEitherOperand(ignoringParenImpCasts(StringFind.bind("findexpr"))))
57  .bind("expr"),
58  this);
59 }
60 
61 void StringFindStartswithCheck::check(const MatchFinder::MatchResult &Result) {
62  const ASTContext &Context = *Result.Context;
63  const SourceManager &Source = Context.getSourceManager();
64 
65  // Extract matching (sub)expressions
66  const auto *ComparisonExpr = Result.Nodes.getNodeAs<BinaryOperator>("expr");
67  assert(ComparisonExpr != nullptr);
68  const auto *Needle = Result.Nodes.getNodeAs<Expr>("needle");
69  assert(Needle != nullptr);
70  const Expr *Haystack = Result.Nodes.getNodeAs<CXXMemberCallExpr>("findexpr")
71  ->getImplicitObjectArgument();
72  assert(Haystack != nullptr);
73 
74  if (ComparisonExpr->getBeginLoc().isMacroID())
75  return;
76 
77  // Get the source code blocks (as characters) for both the string object
78  // and the search expression
79  const StringRef NeedleExprCode = Lexer::getSourceText(
80  CharSourceRange::getTokenRange(Needle->getSourceRange()), Source,
81  Context.getLangOpts());
82  const StringRef HaystackExprCode = Lexer::getSourceText(
83  CharSourceRange::getTokenRange(Haystack->getSourceRange()), Source,
84  Context.getLangOpts());
85 
86  // Create the StartsWith string, negating if comparison was "!=".
87  bool Neg = ComparisonExpr->getOpcodeStr() == "!=";
88  StringRef StartswithStr;
89  if (Neg) {
90  StartswithStr = "!absl::StartsWith";
91  } else {
92  StartswithStr = "absl::StartsWith";
93  }
94 
95  // Create the warning message and a FixIt hint replacing the original expr.
96  auto Diagnostic =
97  diag(ComparisonExpr->getBeginLoc(),
98  (StringRef("use ") + StartswithStr + " instead of find() " +
99  ComparisonExpr->getOpcodeStr() + " 0")
100  .str());
101 
102  Diagnostic << FixItHint::CreateReplacement(
103  ComparisonExpr->getSourceRange(),
104  (StartswithStr + "(" + HaystackExprCode + ", " + NeedleExprCode + ")")
105  .str());
106 
107  // Create a preprocessor #include FixIt hint (CreateIncludeInsertion checks
108  // whether this already exists).
109  auto IncludeHint = IncludeInserter->CreateIncludeInsertion(
110  Source.getFileID(ComparisonExpr->getBeginLoc()), AbseilStringsMatchHeader,
111  false);
112  if (IncludeHint) {
113  Diagnostic << *IncludeHint;
114  }
115 }
116 
118  CompilerInstance &Compiler) {
119  IncludeInserter = llvm::make_unique<clang::tidy::utils::IncludeInserter>(
120  Compiler.getSourceManager(), Compiler.getLangOpts(), IncludeStyle);
121  Compiler.getPreprocessor().addPPCallbacks(
122  IncludeInserter->CreatePPCallbacks());
123 }
124 
127  Options.store(Opts, "StringLikeClasses",
128  utils::options::serializeStringList(StringLikeClasses));
129  Options.store(Opts, "IncludeStyle",
130  utils::IncludeSorter::toString(IncludeStyle));
131  Options.store(Opts, "AbseilStringsMatchHeader", AbseilStringsMatchHeader);
132 }
133 
134 } // namespace abseil
135 } // namespace tidy
136 } // namespace clang
void store(ClangTidyOptions::OptionMap &Options, StringRef LocalName, StringRef Value) const
Stores an option with the check-local name LocalName with string value Value to Options.
Definition: ClangTidy.cpp:473
void check(const ast_matchers::MatchFinder::MatchResult &Result) override
ClangTidyChecks that register ASTMatchers should do the actual work in here.
std::string serializeStringList(ArrayRef< std::string > Strings)
Serialize a sequence of names that can be parsed by parseStringList.
void storeOptions(ClangTidyOptions::OptionMap &Opts) override
Should store all options supported by this check with their current values or default values for opti...
static StringRef toString(IncludeStyle Style)
Converts IncludeStyle to string representation.
Base class for all clang-tidy checks.
Definition: ClangTidy.h:127
std::vector< std::string > parseStringList(StringRef Option)
Parse a semicolon separated list of strings.
static constexpr llvm::StringLiteral Name
std::map< std::string, std::string > OptionMap
llvm::Optional< llvm::Expected< tooling::AtomicChanges > > Result
void registerPPCallbacks(CompilerInstance &Compiler) override
Override this to register PPCallbacks with Compiler.
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
Every ClangTidyCheck reports errors through a DiagnosticsEngine provided by this context.
DiagnosticBuilder diag(SourceLocation Loc, StringRef Description, DiagnosticIDs::Level Level=DiagnosticIDs::Warning)
Add a diagnostic with the check&#39;s name.
Definition: ClangTidy.cpp:438
void registerMatchers(ast_matchers::MatchFinder *Finder) override
Override this to register AST matchers with Finder.