brintos

brintos / llvm-project-archived public Read only

0
0
Text · 8.9 KiB · 80e78e2 Raw
242 lines · cpp
1//===--- RefactoringCallbacks.cpp - Structural query framework ------------===//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//10//===----------------------------------------------------------------------===//11#include "clang/Tooling/RefactoringCallbacks.h"12#include "clang/ASTMatchers/ASTMatchFinder.h"13#include "clang/Basic/SourceLocation.h"14#include "clang/Lex/Lexer.h"15 16using llvm::StringError;17using llvm::make_error;18 19namespace clang {20namespace tooling {21 22RefactoringCallback::RefactoringCallback() {}23tooling::Replacements &RefactoringCallback::getReplacements() {24  return Replace;25}26 27ASTMatchRefactorer::ASTMatchRefactorer(28    std::map<std::string, Replacements> &FileToReplaces)29    : FileToReplaces(FileToReplaces) {}30 31void ASTMatchRefactorer::addDynamicMatcher(32    const ast_matchers::internal::DynTypedMatcher &Matcher,33    RefactoringCallback *Callback) {34  MatchFinder.addDynamicMatcher(Matcher, Callback);35  Callbacks.push_back(Callback);36}37 38class RefactoringASTConsumer : public ASTConsumer {39public:40  explicit RefactoringASTConsumer(ASTMatchRefactorer &Refactoring)41      : Refactoring(Refactoring) {}42 43  void HandleTranslationUnit(ASTContext &Context) override {44    // The ASTMatchRefactorer is re-used between translation units.45    // Clear the matchers so that each Replacement is only emitted once.46    for (const auto &Callback : Refactoring.Callbacks) {47      Callback->getReplacements().clear();48    }49    Refactoring.MatchFinder.matchAST(Context);50    for (const auto &Callback : Refactoring.Callbacks) {51      for (const auto &Replacement : Callback->getReplacements()) {52        llvm::Error Err =53            Refactoring.FileToReplaces[std::string(Replacement.getFilePath())]54                .add(Replacement);55        if (Err) {56          llvm::errs() << "Skipping replacement " << Replacement.toString()57                       << " due to this error:\n"58                       << toString(std::move(Err)) << "\n";59        }60      }61    }62  }63 64private:65  ASTMatchRefactorer &Refactoring;66};67 68std::unique_ptr<ASTConsumer> ASTMatchRefactorer::newASTConsumer() {69  return std::make_unique<RefactoringASTConsumer>(*this);70}71 72static Replacement replaceStmtWithText(SourceManager &Sources, const Stmt &From,73                                       StringRef Text) {74  return tooling::Replacement(75      Sources, CharSourceRange::getTokenRange(From.getSourceRange()), Text);76}77static Replacement replaceStmtWithStmt(SourceManager &Sources, const Stmt &From,78                                       const Stmt &To) {79  return replaceStmtWithText(80      Sources, From,81      Lexer::getSourceText(CharSourceRange::getTokenRange(To.getSourceRange()),82                           Sources, LangOptions()));83}84 85ReplaceStmtWithText::ReplaceStmtWithText(StringRef FromId, StringRef ToText)86    : FromId(std::string(FromId)), ToText(std::string(ToText)) {}87 88void ReplaceStmtWithText::run(89    const ast_matchers::MatchFinder::MatchResult &Result) {90  if (const Stmt *FromMatch = Result.Nodes.getNodeAs<Stmt>(FromId)) {91    auto Err = Replace.add(tooling::Replacement(92        *Result.SourceManager,93        CharSourceRange::getTokenRange(FromMatch->getSourceRange()), ToText));94    // FIXME: better error handling. For now, just print error message in the95    // release version.96    if (Err) {97      llvm::errs() << llvm::toString(std::move(Err)) << "\n";98      assert(false);99    }100  }101}102 103ReplaceStmtWithStmt::ReplaceStmtWithStmt(StringRef FromId, StringRef ToId)104    : FromId(std::string(FromId)), ToId(std::string(ToId)) {}105 106void ReplaceStmtWithStmt::run(107    const ast_matchers::MatchFinder::MatchResult &Result) {108  const Stmt *FromMatch = Result.Nodes.getNodeAs<Stmt>(FromId);109  const Stmt *ToMatch = Result.Nodes.getNodeAs<Stmt>(ToId);110  if (FromMatch && ToMatch) {111    auto Err = Replace.add(112        replaceStmtWithStmt(*Result.SourceManager, *FromMatch, *ToMatch));113    // FIXME: better error handling. For now, just print error message in the114    // release version.115    if (Err) {116      llvm::errs() << llvm::toString(std::move(Err)) << "\n";117      assert(false);118    }119  }120}121 122ReplaceIfStmtWithItsBody::ReplaceIfStmtWithItsBody(StringRef Id,123                                                   bool PickTrueBranch)124    : Id(std::string(Id)), PickTrueBranch(PickTrueBranch) {}125 126void ReplaceIfStmtWithItsBody::run(127    const ast_matchers::MatchFinder::MatchResult &Result) {128  if (const IfStmt *Node = Result.Nodes.getNodeAs<IfStmt>(Id)) {129    const Stmt *Body = PickTrueBranch ? Node->getThen() : Node->getElse();130    if (Body) {131      auto Err =132          Replace.add(replaceStmtWithStmt(*Result.SourceManager, *Node, *Body));133      // FIXME: better error handling. For now, just print error message in the134      // release version.135      if (Err) {136        llvm::errs() << llvm::toString(std::move(Err)) << "\n";137        assert(false);138      }139    } else if (!PickTrueBranch) {140      // If we want to use the 'else'-branch, but it doesn't exist, delete141      // the whole 'if'.142      auto Err =143          Replace.add(replaceStmtWithText(*Result.SourceManager, *Node, ""));144      // FIXME: better error handling. For now, just print error message in the145      // release version.146      if (Err) {147        llvm::errs() << llvm::toString(std::move(Err)) << "\n";148        assert(false);149      }150    }151  }152}153 154ReplaceNodeWithTemplate::ReplaceNodeWithTemplate(155    llvm::StringRef FromId, std::vector<TemplateElement> Template)156    : FromId(std::string(FromId)), Template(std::move(Template)) {}157 158llvm::Expected<std::unique_ptr<ReplaceNodeWithTemplate>>159ReplaceNodeWithTemplate::create(StringRef FromId, StringRef ToTemplate) {160  std::vector<TemplateElement> ParsedTemplate;161  for (size_t Index = 0; Index < ToTemplate.size();) {162    if (ToTemplate[Index] == '$') {163      if (ToTemplate.substr(Index, 2) == "$$") {164        Index += 2;165        ParsedTemplate.push_back(166            TemplateElement{TemplateElement::Literal, "$"});167      } else if (ToTemplate.substr(Index, 2) == "${") {168        size_t EndOfIdentifier = ToTemplate.find("}", Index);169        if (EndOfIdentifier == std::string::npos) {170          return make_error<StringError>(171              "Unterminated ${...} in replacement template near " +172                  ToTemplate.substr(Index),173              llvm::inconvertibleErrorCode());174        }175        std::string SourceNodeName = std::string(176            ToTemplate.substr(Index + 2, EndOfIdentifier - Index - 2));177        ParsedTemplate.push_back(178            TemplateElement{TemplateElement::Identifier, SourceNodeName});179        Index = EndOfIdentifier + 1;180      } else {181        return make_error<StringError>(182            "Invalid $ in replacement template near " +183                ToTemplate.substr(Index),184            llvm::inconvertibleErrorCode());185      }186    } else {187      size_t NextIndex = ToTemplate.find('$', Index + 1);188      ParsedTemplate.push_back(TemplateElement{189          TemplateElement::Literal,190          std::string(ToTemplate.substr(Index, NextIndex - Index))});191      Index = NextIndex;192    }193  }194  return std::unique_ptr<ReplaceNodeWithTemplate>(195      new ReplaceNodeWithTemplate(FromId, std::move(ParsedTemplate)));196}197 198void ReplaceNodeWithTemplate::run(199    const ast_matchers::MatchFinder::MatchResult &Result) {200  const auto &NodeMap = Result.Nodes.getMap();201 202  std::string ToText;203  for (const auto &Element : Template) {204    switch (Element.Type) {205    case TemplateElement::Literal:206      ToText += Element.Value;207      break;208    case TemplateElement::Identifier: {209      auto NodeIter = NodeMap.find(Element.Value);210      if (NodeIter == NodeMap.end()) {211        llvm::errs() << "Node " << Element.Value212                     << " used in replacement template not bound in Matcher \n";213        llvm::report_fatal_error("Unbound node in replacement template.");214      }215      CharSourceRange Source =216          CharSourceRange::getTokenRange(NodeIter->second.getSourceRange());217      ToText += Lexer::getSourceText(Source, *Result.SourceManager,218                                     Result.Context->getLangOpts());219      break;220    }221    }222  }223  auto It = NodeMap.find(FromId);224  if (It == NodeMap.end()) {225    llvm::errs() << "Node to be replaced " << FromId226                 << " not bound in query.\n";227    llvm::report_fatal_error("FromId node not bound in MatchResult");228  }229  auto Replacement =230      tooling::Replacement(*Result.SourceManager, &It->second, ToText,231                           Result.Context->getLangOpts());232  llvm::Error Err = Replace.add(Replacement);233  if (Err) {234    llvm::errs() << "Query and replace failed in " << Replacement.getFilePath()235                 << "! " << llvm::toString(std::move(Err)) << "\n";236    llvm::report_fatal_error("Replacement failed");237  }238}239 240} // end namespace tooling241} // end namespace clang242