123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240 |
- //===--- RefactoringCallbacks.cpp - Structural query framework ------------===//
- //
- // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
- // See https://llvm.org/LICENSE.txt for license information.
- // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
- //
- //===----------------------------------------------------------------------===//
- //
- //
- //===----------------------------------------------------------------------===//
- #include "clang/Tooling/RefactoringCallbacks.h"
- #include "clang/ASTMatchers/ASTMatchFinder.h"
- #include "clang/Basic/SourceLocation.h"
- #include "clang/Lex/Lexer.h"
- using llvm::StringError;
- using llvm::make_error;
- namespace clang {
- namespace tooling {
- RefactoringCallback::RefactoringCallback() {}
- tooling::Replacements &RefactoringCallback::getReplacements() {
- return Replace;
- }
- ASTMatchRefactorer::ASTMatchRefactorer(
- std::map<std::string, Replacements> &FileToReplaces)
- : FileToReplaces(FileToReplaces) {}
- void ASTMatchRefactorer::addDynamicMatcher(
- const ast_matchers::internal::DynTypedMatcher &Matcher,
- RefactoringCallback *Callback) {
- MatchFinder.addDynamicMatcher(Matcher, Callback);
- Callbacks.push_back(Callback);
- }
- class RefactoringASTConsumer : public ASTConsumer {
- public:
- explicit RefactoringASTConsumer(ASTMatchRefactorer &Refactoring)
- : Refactoring(Refactoring) {}
- void HandleTranslationUnit(ASTContext &Context) override {
- // The ASTMatchRefactorer is re-used between translation units.
- // Clear the matchers so that each Replacement is only emitted once.
- for (const auto &Callback : Refactoring.Callbacks) {
- Callback->getReplacements().clear();
- }
- Refactoring.MatchFinder.matchAST(Context);
- for (const auto &Callback : Refactoring.Callbacks) {
- for (const auto &Replacement : Callback->getReplacements()) {
- llvm::Error Err =
- Refactoring.FileToReplaces[std::string(Replacement.getFilePath())]
- .add(Replacement);
- if (Err) {
- llvm::errs() << "Skipping replacement " << Replacement.toString()
- << " due to this error:\n"
- << toString(std::move(Err)) << "\n";
- }
- }
- }
- }
- private:
- ASTMatchRefactorer &Refactoring;
- };
- std::unique_ptr<ASTConsumer> ASTMatchRefactorer::newASTConsumer() {
- return std::make_unique<RefactoringASTConsumer>(*this);
- }
- static Replacement replaceStmtWithText(SourceManager &Sources, const Stmt &From,
- StringRef Text) {
- return tooling::Replacement(
- Sources, CharSourceRange::getTokenRange(From.getSourceRange()), Text);
- }
- static Replacement replaceStmtWithStmt(SourceManager &Sources, const Stmt &From,
- const Stmt &To) {
- return replaceStmtWithText(
- Sources, From,
- Lexer::getSourceText(CharSourceRange::getTokenRange(To.getSourceRange()),
- Sources, LangOptions()));
- }
- ReplaceStmtWithText::ReplaceStmtWithText(StringRef FromId, StringRef ToText)
- : FromId(std::string(FromId)), ToText(std::string(ToText)) {}
- void ReplaceStmtWithText::run(
- const ast_matchers::MatchFinder::MatchResult &Result) {
- if (const Stmt *FromMatch = Result.Nodes.getNodeAs<Stmt>(FromId)) {
- auto Err = Replace.add(tooling::Replacement(
- *Result.SourceManager,
- CharSourceRange::getTokenRange(FromMatch->getSourceRange()), ToText));
- // FIXME: better error handling. For now, just print error message in the
- // release version.
- if (Err) {
- llvm::errs() << llvm::toString(std::move(Err)) << "\n";
- assert(false);
- }
- }
- }
- ReplaceStmtWithStmt::ReplaceStmtWithStmt(StringRef FromId, StringRef ToId)
- : FromId(std::string(FromId)), ToId(std::string(ToId)) {}
- void ReplaceStmtWithStmt::run(
- const ast_matchers::MatchFinder::MatchResult &Result) {
- const Stmt *FromMatch = Result.Nodes.getNodeAs<Stmt>(FromId);
- const Stmt *ToMatch = Result.Nodes.getNodeAs<Stmt>(ToId);
- if (FromMatch && ToMatch) {
- auto Err = Replace.add(
- replaceStmtWithStmt(*Result.SourceManager, *FromMatch, *ToMatch));
- // FIXME: better error handling. For now, just print error message in the
- // release version.
- if (Err) {
- llvm::errs() << llvm::toString(std::move(Err)) << "\n";
- assert(false);
- }
- }
- }
- ReplaceIfStmtWithItsBody::ReplaceIfStmtWithItsBody(StringRef Id,
- bool PickTrueBranch)
- : Id(std::string(Id)), PickTrueBranch(PickTrueBranch) {}
- void ReplaceIfStmtWithItsBody::run(
- const ast_matchers::MatchFinder::MatchResult &Result) {
- if (const IfStmt *Node = Result.Nodes.getNodeAs<IfStmt>(Id)) {
- const Stmt *Body = PickTrueBranch ? Node->getThen() : Node->getElse();
- if (Body) {
- auto Err =
- Replace.add(replaceStmtWithStmt(*Result.SourceManager, *Node, *Body));
- // FIXME: better error handling. For now, just print error message in the
- // release version.
- if (Err) {
- llvm::errs() << llvm::toString(std::move(Err)) << "\n";
- assert(false);
- }
- } else if (!PickTrueBranch) {
- // If we want to use the 'else'-branch, but it doesn't exist, delete
- // the whole 'if'.
- auto Err =
- Replace.add(replaceStmtWithText(*Result.SourceManager, *Node, ""));
- // FIXME: better error handling. For now, just print error message in the
- // release version.
- if (Err) {
- llvm::errs() << llvm::toString(std::move(Err)) << "\n";
- assert(false);
- }
- }
- }
- }
- ReplaceNodeWithTemplate::ReplaceNodeWithTemplate(
- llvm::StringRef FromId, std::vector<TemplateElement> Template)
- : FromId(std::string(FromId)), Template(std::move(Template)) {}
- llvm::Expected<std::unique_ptr<ReplaceNodeWithTemplate>>
- ReplaceNodeWithTemplate::create(StringRef FromId, StringRef ToTemplate) {
- std::vector<TemplateElement> ParsedTemplate;
- for (size_t Index = 0; Index < ToTemplate.size();) {
- if (ToTemplate[Index] == '$') {
- if (ToTemplate.substr(Index, 2) == "$$") {
- Index += 2;
- ParsedTemplate.push_back(
- TemplateElement{TemplateElement::Literal, "$"});
- } else if (ToTemplate.substr(Index, 2) == "${") {
- size_t EndOfIdentifier = ToTemplate.find("}", Index);
- if (EndOfIdentifier == std::string::npos) {
- return make_error<StringError>(
- "Unterminated ${...} in replacement template near " +
- ToTemplate.substr(Index),
- llvm::inconvertibleErrorCode());
- }
- std::string SourceNodeName = std::string(
- ToTemplate.substr(Index + 2, EndOfIdentifier - Index - 2));
- ParsedTemplate.push_back(
- TemplateElement{TemplateElement::Identifier, SourceNodeName});
- Index = EndOfIdentifier + 1;
- } else {
- return make_error<StringError>(
- "Invalid $ in replacement template near " +
- ToTemplate.substr(Index),
- llvm::inconvertibleErrorCode());
- }
- } else {
- size_t NextIndex = ToTemplate.find('$', Index + 1);
- ParsedTemplate.push_back(TemplateElement{
- TemplateElement::Literal,
- std::string(ToTemplate.substr(Index, NextIndex - Index))});
- Index = NextIndex;
- }
- }
- return std::unique_ptr<ReplaceNodeWithTemplate>(
- new ReplaceNodeWithTemplate(FromId, std::move(ParsedTemplate)));
- }
- void ReplaceNodeWithTemplate::run(
- const ast_matchers::MatchFinder::MatchResult &Result) {
- const auto &NodeMap = Result.Nodes.getMap();
- std::string ToText;
- for (const auto &Element : Template) {
- switch (Element.Type) {
- case TemplateElement::Literal:
- ToText += Element.Value;
- break;
- case TemplateElement::Identifier: {
- auto NodeIter = NodeMap.find(Element.Value);
- if (NodeIter == NodeMap.end()) {
- llvm::errs() << "Node " << Element.Value
- << " used in replacement template not bound in Matcher \n";
- llvm::report_fatal_error("Unbound node in replacement template.");
- }
- CharSourceRange Source =
- CharSourceRange::getTokenRange(NodeIter->second.getSourceRange());
- ToText += Lexer::getSourceText(Source, *Result.SourceManager,
- Result.Context->getLangOpts());
- break;
- }
- }
- }
- if (NodeMap.count(FromId) == 0) {
- llvm::errs() << "Node to be replaced " << FromId
- << " not bound in query.\n";
- llvm::report_fatal_error("FromId node not bound in MatchResult");
- }
- auto Replacement =
- tooling::Replacement(*Result.SourceManager, &NodeMap.at(FromId), ToText,
- Result.Context->getLangOpts());
- llvm::Error Err = Replace.add(Replacement);
- if (Err) {
- llvm::errs() << "Query and replace failed in " << Replacement.getFilePath()
- << "! " << llvm::toString(std::move(Err)) << "\n";
- llvm::report_fatal_error("Replacement failed");
- }
- }
- } // end namespace tooling
- } // end namespace clang
|