clang-tools  15.0.0git
TransformerClangTidyCheck.cpp
Go to the documentation of this file.
1 //===---------- TransformerClangTidyCheck.cpp - clang-tidy ----------------===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 
10 #include "clang/Lex/Preprocessor.h"
11 #include "llvm/ADT/STLExtras.h"
12 
13 namespace clang {
14 namespace tidy {
15 namespace utils {
16 using transformer::RewriteRuleWith;
17 
18 #ifndef NDEBUG
19 static bool hasGenerator(const transformer::Generator<std::string> &G) {
20  return G != nullptr;
21 }
22 #endif
23 
24 static void verifyRule(const RewriteRuleWith<std::string> &Rule) {
25  assert(llvm::all_of(Rule.Metadata, hasGenerator) &&
26  "clang-tidy checks must have an explanation by default;"
27  " explicitly provide an empty explanation if none is desired");
28 }
29 
30 // If a string unintentionally containing '%' is passed as a diagnostic, Clang
31 // will claim the string is ill-formed and assert-fail. This function escapes
32 // such strings so they can be safely used in diagnostics.
33 std::string escapeForDiagnostic(std::string ToEscape) {
34  // Optimize for the common case that the string does not contain `%` at the
35  // cost of an extra scan over the string in the slow case.
36  auto Pos = ToEscape.find('%');
37  if (Pos == ToEscape.npos)
38  return ToEscape;
39 
40  std::string Result;
41  Result.reserve(ToEscape.size());
42  // Convert position to a count.
43  ++Pos;
44  Result.append(ToEscape, 0, Pos);
45  Result += '%';
46 
47  for (auto N = ToEscape.size(); Pos < N; ++Pos) {
48  const char C = ToEscape.at(Pos);
49  Result += C;
50  if (C == '%')
51  Result += '%';
52  }
53 
54  return Result;
55 }
56 
58  ClangTidyContext *Context)
59  : ClangTidyCheck(Name, Context),
60  Inserter(Options.getLocalOrGlobal("IncludeStyle", IncludeSorter::IS_LLVM),
61  areDiagsSelfContained()) {}
62 
63 // This constructor cannot dispatch to the simpler one (below), because, in
64 // order to get meaningful results from `getLangOpts` and `Options`, we need the
65 // `ClangTidyCheck()` constructor to have been called. If we were to dispatch,
66 // we would be accessing `getLangOpts` and `Options` before the underlying
67 // `ClangTidyCheck` instance was properly initialized.
69  std::function<Optional<RewriteRuleWith<std::string>>(const LangOptions &,
70  const OptionsView &)>
71  MakeRule,
72  StringRef Name, ClangTidyContext *Context)
73  : TransformerClangTidyCheck(Name, Context) {
74  if (Optional<RewriteRuleWith<std::string>> R =
75  MakeRule(getLangOpts(), Options))
76  setRule(std::move(*R));
77 }
78 
80  RewriteRuleWith<std::string> R, StringRef Name, ClangTidyContext *Context)
81  : TransformerClangTidyCheck(Name, Context) {
82  setRule(std::move(R));
83 }
84 
86  transformer::RewriteRuleWith<std::string> R) {
87  verifyRule(R);
88  Rule = std::move(R);
89 }
90 
92  const SourceManager &SM, Preprocessor *PP, Preprocessor *ModuleExpanderPP) {
93  Inserter.registerPreprocessor(PP);
94 }
95 
97  ast_matchers::MatchFinder *Finder) {
98  if (!Rule.Cases.empty())
99  for (auto &Matcher : transformer::detail::buildMatchers(Rule))
100  Finder->addDynamicMatcher(Matcher, this);
101 }
102 
104  const ast_matchers::MatchFinder::MatchResult &Result) {
105  if (Result.Context->getDiagnostics().hasErrorOccurred())
106  return;
107 
108  size_t I = transformer::detail::findSelectedCase(Result, Rule);
109  Expected<SmallVector<transformer::Edit, 1>> Edits =
110  Rule.Cases[I].Edits(Result);
111  if (!Edits) {
112  llvm::errs() << "Rewrite failed: " << llvm::toString(Edits.takeError())
113  << "\n";
114  return;
115  }
116 
117  // No rewrite applied, but no error encountered either.
118  if (Edits->empty())
119  return;
120 
121  Expected<std::string> Explanation = Rule.Metadata[I]->eval(Result);
122  if (!Explanation) {
123  llvm::errs() << "Error in explanation: "
124  << llvm::toString(Explanation.takeError()) << "\n";
125  return;
126  }
127 
128  // Associate the diagnostic with the location of the first change.
129  DiagnosticBuilder Diag =
130  diag((*Edits)[0].Range.getBegin(), escapeForDiagnostic(*Explanation));
131  for (const auto &T : *Edits)
132  switch (T.Kind) {
134  Diag << FixItHint::CreateReplacement(T.Range, T.Replacement);
135  break;
136  case transformer::EditKind::AddInclude:
137  Diag << Inserter.createIncludeInsertion(
138  Result.SourceManager->getFileID(T.Range.getBegin()), T.Replacement);
139  break;
140  }
141 }
142 
145  Options.store(Opts, "IncludeStyle", Inserter.getStyle());
146 }
147 
148 } // namespace utils
149 } // namespace tidy
150 } // namespace clang
Range
CharSourceRange Range
SourceRange for the file name.
Definition: IncludeOrderCheck.cpp:38
clang::tidy::ClangTidyOptions::OptionMap
llvm::StringMap< ClangTidyValue > OptionMap
Definition: ClangTidyOptions.h:115
clang::tidy::utils::IncludeSorter
Class used by IncludeInserterCallback to record the names of the inclusions in a given source file be...
Definition: IncludeSorter.h:23
TransformerClangTidyCheck.h
clang::tidy::utils::hasGenerator
static bool hasGenerator(const transformer::Generator< std::string > &G)
Definition: TransformerClangTidyCheck.cpp:19
clang::tidy::utils::TransformerClangTidyCheck::check
void check(const ast_matchers::MatchFinder::MatchResult &Result) final
ClangTidyChecks that register ASTMatchers should do the actual work in here.
Definition: TransformerClangTidyCheck.cpp:103
clang::tidy::utils::IncludeInserter::registerPreprocessor
void registerPreprocessor(Preprocessor *PP)
Registers this with the Preprocessor PP, must be called before this class is used.
Definition: IncludeInserter.cpp:43
clang::tidy::ClangTidyCheck
Base class for all clang-tidy checks.
Definition: ClangTidyCheck.h:53
clang::tidy::utils::TransformerClangTidyCheck::registerMatchers
void registerMatchers(ast_matchers::MatchFinder *Finder) final
Override this to register AST matchers with Finder.
Definition: TransformerClangTidyCheck.cpp:96
clang::tidy::ClangTidyCheck::getLangOpts
const LangOptions & getLangOpts() const
Returns the language options from the context.
Definition: ClangTidyCheck.h:419
clang::tidy::utils::verifyRule
static void verifyRule(const RewriteRuleWith< std::string > &Rule)
Definition: TransformerClangTidyCheck.cpp:24
Pos
size_t Pos
Definition: NoLintDirectiveHandler.cpp:97
clang::tidy::utils::TransformerClangTidyCheck::TransformerClangTidyCheck
TransformerClangTidyCheck(StringRef Name, ClangTidyContext *Context)
Definition: TransformerClangTidyCheck.cpp:57
clang::tidy::ClangTidyCheck::Options
OptionsView Options
Definition: ClangTidyCheck.h:415
clang::tidy::ClangTidyContext
Every ClangTidyCheck reports errors through a DiagnosticsEngine provided by this context.
Definition: ClangTidyDiagnosticConsumer.h:67
clang::tidy::utils::TransformerClangTidyCheck::registerPPCallbacks
void registerPPCallbacks(const SourceManager &SM, Preprocessor *PP, Preprocessor *ModuleExpanderPP) override
Override this to register PPCallbacks in the preprocessor.
Definition: TransformerClangTidyCheck.cpp:91
clang::tidy::ClangTidyCheck::diag
DiagnosticBuilder diag(SourceLocation Loc, StringRef Description, DiagnosticIDs::Level Level=DiagnosticIDs::Warning)
Add a diagnostic with the check's name.
Definition: ClangTidyCheck.cpp:25
Name
Token Name
Definition: MacroToEnumCheck.cpp:89
clang::tidy::bugprone::PP
static Preprocessor * PP
Definition: BadSignalToKillThreadCheck.cpp:29
clang::tidy::utils::IncludeInserter::getStyle
IncludeSorter::IncludeStyle getStyle() const
Definition: IncludeInserter.h:85
clang::tidy::utils::TransformerClangTidyCheck
A base class for defining a ClangTidy check based on a RewriteRule.
Definition: TransformerClangTidyCheck.h:39
C
const Criteria C
Definition: FunctionCognitiveComplexityCheck.cpp:93
clang
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
Definition: ApplyReplacements.h:27
clang::tidy::utils::TransformerClangTidyCheck::setRule
void setRule(transformer::RewriteRuleWith< std::string > R)
Set the rule that this check implements.
Definition: TransformerClangTidyCheck.cpp:85
clang::tidy::utils::TransformerClangTidyCheck::storeOptions
void storeOptions(ClangTidyOptions::OptionMap &Opts) override
Derived classes that override this function should call this method from the overridden method.
Definition: TransformerClangTidyCheck.cpp:143
clang::tidy::cppcoreguidelines::toString
static llvm::StringRef toString(SpecialMemberFunctionsCheck::SpecialMemberFunctionKind K)
Definition: SpecialMemberFunctionsCheck.cpp:55
clang::tidy::utils::IncludeInserter::createIncludeInsertion
llvm::Optional< FixItHint > createIncludeInsertion(FileID FileID, llvm::StringRef Header)
Creates a Header inclusion directive fixit in the File FileID.
Definition: IncludeInserter.cpp:71
clang::tidy::ClangTidyCheck::OptionsView::store
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: ClangTidyCheck.cpp:129
clang::tidy::utils::escapeForDiagnostic
std::string escapeForDiagnostic(std::string ToEscape)
Definition: TransformerClangTidyCheck.cpp:33