brintos

brintos / llvm-project-archived public Read only

0
0
Text · 5.3 KiB · b58e716 Raw
159 lines · cpp
1//===----------------------------------------------------------------------===//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-exception6//7//===----------------------------------------------------------------------===//8 9#include "TransformerClangTidyCheck.h"10#include "clang/Basic/DiagnosticIDs.h"11#include "clang/Lex/Preprocessor.h"12#include "llvm/ADT/STLExtras.h"13#include <optional>14 15namespace clang::tidy::utils {16using transformer::RewriteRuleWith;17 18#ifndef NDEBUG19static bool hasGenerator(const transformer::Generator<std::string> &G) {20  return G != nullptr;21}22#endif23 24static 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, Clang31// will claim the string is ill-formed and assert-fail. This function escapes32// such strings so they can be safely used in diagnostics.33static std::string escapeForDiagnostic(std::string ToEscape) {34  // Optimize for the common case that the string does not contain `%` at the35  // cost of an extra scan over the string in the slow case.36  auto Pos = ToEscape.find('%');37  if (Pos == std::string::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 57TransformerClangTidyCheck::TransformerClangTidyCheck(StringRef Name,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, in64// order to get meaningful results from `getLangOpts` and `Options`, we need the65// `ClangTidyCheck()` constructor to have been called. If we were to dispatch,66// we would be accessing `getLangOpts` and `Options` before the underlying67// `ClangTidyCheck` instance was properly initialized.68TransformerClangTidyCheck::TransformerClangTidyCheck(69    llvm::function_ref<std::optional<RewriteRuleWith<std::string>>(70        const LangOptions &, const OptionsView &)>71        MakeRule,72    StringRef Name, ClangTidyContext *Context)73    : TransformerClangTidyCheck(Name, Context) {74  if (std::optional<RewriteRuleWith<std::string>> R =75          MakeRule(getLangOpts(), Options))76    setRule(std::move(*R));77}78 79TransformerClangTidyCheck::TransformerClangTidyCheck(80    RewriteRuleWith<std::string> R, StringRef Name, ClangTidyContext *Context)81    : TransformerClangTidyCheck(Name, Context) {82  setRule(std::move(R));83}84 85void TransformerClangTidyCheck::setRule(86    transformer::RewriteRuleWith<std::string> R) {87  verifyRule(R);88  Rule = std::move(R);89}90 91void TransformerClangTidyCheck::registerPPCallbacks(92    const SourceManager &SM, Preprocessor *PP, Preprocessor *ModuleExpanderPP) {93  Inserter.registerPreprocessor(PP);94}95 96void TransformerClangTidyCheck::registerMatchers(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 103void TransformerClangTidyCheck::check(104    const ast_matchers::MatchFinder::MatchResult &Result) {105  if (Result.Context->getDiagnostics().hasErrorOccurred())106    return;107 108  const 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  {130    const DiagnosticBuilder Diag =131        diag((*Edits)[0].Range.getBegin(), escapeForDiagnostic(*Explanation));132    for (const auto &T : *Edits) {133      switch (T.Kind) {134      case transformer::EditKind::Range:135        Diag << FixItHint::CreateReplacement(T.Range, T.Replacement);136        break;137      case transformer::EditKind::AddInclude:138        Diag << Inserter.createIncludeInsertion(139            Result.SourceManager->getFileID(T.Range.getBegin()), T.Replacement);140        break;141      }142    }143  }144  // Emit potential notes.145  for (const auto &T : *Edits) {146    if (!T.Note.empty()) {147      diag(T.Range.getBegin(), escapeForDiagnostic(T.Note),148           DiagnosticIDs::Note);149    }150  }151}152 153void TransformerClangTidyCheck::storeOptions(154    ClangTidyOptions::OptionMap &Opts) {155  Options.store(Opts, "IncludeStyle", Inserter.getStyle());156}157 158} // namespace clang::tidy::utils159