Home | History | Annotate | Line # | Download | only in Tooling
      1 //===--- Refactoring.cpp - Framework for clang refactoring tools ----------===//
      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 //
      9 //  Implements tools to support refactorings.
     10 //
     11 //===----------------------------------------------------------------------===//
     12 
     13 #include "clang/Tooling/Refactoring.h"
     14 #include "clang/Basic/DiagnosticOptions.h"
     15 #include "clang/Basic/FileManager.h"
     16 #include "clang/Basic/SourceManager.h"
     17 #include "clang/Format/Format.h"
     18 #include "clang/Frontend/TextDiagnosticPrinter.h"
     19 #include "clang/Lex/Lexer.h"
     20 #include "clang/Rewrite/Core/Rewriter.h"
     21 #include "llvm/Support/Path.h"
     22 #include "llvm/Support/raw_os_ostream.h"
     23 
     24 namespace clang {
     25 namespace tooling {
     26 
     27 RefactoringTool::RefactoringTool(
     28     const CompilationDatabase &Compilations, ArrayRef<std::string> SourcePaths,
     29     std::shared_ptr<PCHContainerOperations> PCHContainerOps)
     30     : ClangTool(Compilations, SourcePaths, std::move(PCHContainerOps)) {}
     31 
     32 std::map<std::string, Replacements> &RefactoringTool::getReplacements() {
     33   return FileToReplaces;
     34 }
     35 
     36 int RefactoringTool::runAndSave(FrontendActionFactory *ActionFactory) {
     37   if (int Result = run(ActionFactory)) {
     38     return Result;
     39   }
     40 
     41   LangOptions DefaultLangOptions;
     42   IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts = new DiagnosticOptions();
     43   TextDiagnosticPrinter DiagnosticPrinter(llvm::errs(), &*DiagOpts);
     44   DiagnosticsEngine Diagnostics(
     45       IntrusiveRefCntPtr<DiagnosticIDs>(new DiagnosticIDs()),
     46       &*DiagOpts, &DiagnosticPrinter, false);
     47   SourceManager Sources(Diagnostics, getFiles());
     48   Rewriter Rewrite(Sources, DefaultLangOptions);
     49 
     50   if (!applyAllReplacements(Rewrite)) {
     51     llvm::errs() << "Skipped some replacements.\n";
     52   }
     53 
     54   return saveRewrittenFiles(Rewrite);
     55 }
     56 
     57 bool RefactoringTool::applyAllReplacements(Rewriter &Rewrite) {
     58   bool Result = true;
     59   for (const auto &Entry : groupReplacementsByFile(
     60            Rewrite.getSourceMgr().getFileManager(), FileToReplaces))
     61     Result = tooling::applyAllReplacements(Entry.second, Rewrite) && Result;
     62   return Result;
     63 }
     64 
     65 int RefactoringTool::saveRewrittenFiles(Rewriter &Rewrite) {
     66   return Rewrite.overwriteChangedFiles() ? 1 : 0;
     67 }
     68 
     69 bool formatAndApplyAllReplacements(
     70     const std::map<std::string, Replacements> &FileToReplaces,
     71     Rewriter &Rewrite, StringRef Style) {
     72   SourceManager &SM = Rewrite.getSourceMgr();
     73   FileManager &Files = SM.getFileManager();
     74 
     75   bool Result = true;
     76   for (const auto &FileAndReplaces : groupReplacementsByFile(
     77            Rewrite.getSourceMgr().getFileManager(), FileToReplaces)) {
     78     const std::string &FilePath = FileAndReplaces.first;
     79     auto &CurReplaces = FileAndReplaces.second;
     80 
     81     const FileEntry *Entry = nullptr;
     82     if (auto File = Files.getFile(FilePath))
     83       Entry = *File;
     84 
     85     FileID ID = SM.getOrCreateFileID(Entry, SrcMgr::C_User);
     86     StringRef Code = SM.getBufferData(ID);
     87 
     88     auto CurStyle = format::getStyle(Style, FilePath, "LLVM");
     89     if (!CurStyle) {
     90       llvm::errs() << llvm::toString(CurStyle.takeError()) << "\n";
     91       return false;
     92     }
     93 
     94     auto NewReplacements =
     95         format::formatReplacements(Code, CurReplaces, *CurStyle);
     96     if (!NewReplacements) {
     97       llvm::errs() << llvm::toString(NewReplacements.takeError()) << "\n";
     98       return false;
     99     }
    100     Result = applyAllReplacements(*NewReplacements, Rewrite) && Result;
    101   }
    102   return Result;
    103 }
    104 
    105 } // end namespace tooling
    106 } // end namespace clang
    107