1 //===--- RenamingAction.cpp - Clang refactoring library -------------------===//
2 //
3 //                     The LLVM Compiler Infrastructure
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9 ///
10 /// \file
11 /// \brief Provides an action to rename every symbol at a point.
12 ///
13 //===----------------------------------------------------------------------===//
14 
15 #include "clang/Tooling/Refactoring/Rename/RenamingAction.h"
16 #include "clang/AST/ASTConsumer.h"
17 #include "clang/AST/ASTContext.h"
18 #include "clang/Basic/FileManager.h"
19 #include "clang/Frontend/CompilerInstance.h"
20 #include "clang/Frontend/FrontendAction.h"
21 #include "clang/Lex/Lexer.h"
22 #include "clang/Lex/Preprocessor.h"
23 #include "clang/Tooling/CommonOptionsParser.h"
24 #include "clang/Tooling/Refactoring.h"
25 #include "clang/Tooling/Refactoring/Rename/USRLocFinder.h"
26 #include "clang/Tooling/Tooling.h"
27 #include "llvm/ADT/STLExtras.h"
28 #include <string>
29 #include <vector>
30 
31 using namespace llvm;
32 
33 namespace clang {
34 namespace tooling {
35 
36 Expected<std::vector<AtomicChange>>
37 createRenameReplacements(const SymbolOccurrences &Occurrences,
38                          const SourceManager &SM,
39                          ArrayRef<StringRef> NewNameStrings) {
40   // FIXME: A true local rename can use just one AtomicChange.
41   std::vector<AtomicChange> Changes;
42   for (const auto &Occurrence : Occurrences) {
43     ArrayRef<SourceRange> Ranges = Occurrence.getNameRanges();
44     assert(NewNameStrings.size() == Ranges.size() &&
45            "Mismatching number of ranges and name pieces");
46     AtomicChange Change(SM, Ranges[0].getBegin());
47     for (const auto &Range : llvm::enumerate(Ranges)) {
48       auto Error =
49           Change.replace(SM, CharSourceRange::getCharRange(Range.value()),
50                          NewNameStrings[Range.index()]);
51       if (Error)
52         return std::move(Error);
53     }
54     Changes.push_back(std::move(Change));
55   }
56   return std::move(Changes);
57 }
58 
59 /// Takes each atomic change and inserts its replacements into the set of
60 /// replacements that belong to the appropriate file.
61 static void convertChangesToFileReplacements(
62     ArrayRef<AtomicChange> AtomicChanges,
63     std::map<std::string, tooling::Replacements> *FileToReplaces) {
64   for (const auto &AtomicChange : AtomicChanges) {
65     for (const auto &Replace : AtomicChange.getReplacements()) {
66       llvm::Error Err = (*FileToReplaces)[Replace.getFilePath()].add(Replace);
67       if (Err) {
68         llvm::errs() << "Renaming failed in " << Replace.getFilePath() << "! "
69                      << llvm::toString(std::move(Err)) << "\n";
70       }
71     }
72   }
73 }
74 
75 class RenamingASTConsumer : public ASTConsumer {
76 public:
77   RenamingASTConsumer(
78       const std::vector<std::string> &NewNames,
79       const std::vector<std::string> &PrevNames,
80       const std::vector<std::vector<std::string>> &USRList,
81       std::map<std::string, tooling::Replacements> &FileToReplaces,
82       bool PrintLocations)
83       : NewNames(NewNames), PrevNames(PrevNames), USRList(USRList),
84         FileToReplaces(FileToReplaces), PrintLocations(PrintLocations) {}
85 
86   void HandleTranslationUnit(ASTContext &Context) override {
87     for (unsigned I = 0; I < NewNames.size(); ++I)
88       HandleOneRename(Context, NewNames[I], PrevNames[I], USRList[I]);
89   }
90 
91   void HandleOneRename(ASTContext &Context, const std::string &NewName,
92                        const std::string &PrevName,
93                        const std::vector<std::string> &USRs) {
94     const SourceManager &SourceMgr = Context.getSourceManager();
95 
96     SymbolOccurrences Occurrences = tooling::getOccurrencesOfUSRs(
97         USRs, PrevName, Context.getTranslationUnitDecl());
98     if (PrintLocations) {
99       for (const auto &Occurrence : Occurrences) {
100         FullSourceLoc FullLoc(Occurrence.getNameRanges()[0].getBegin(),
101                               SourceMgr);
102         errs() << "clang-rename: renamed at: " << SourceMgr.getFilename(FullLoc)
103                << ":" << FullLoc.getSpellingLineNumber() << ":"
104                << FullLoc.getSpellingColumnNumber() << "\n";
105       }
106     }
107     // FIXME: Support multi-piece names.
108     // FIXME: better error handling (propagate error out).
109     StringRef NewNameRef = NewName;
110     Expected<std::vector<AtomicChange>> Change =
111         createRenameReplacements(Occurrences, SourceMgr, NewNameRef);
112     if (!Change) {
113       llvm::errs() << "Failed to create renaming replacements for '" << PrevName
114                    << "'! " << llvm::toString(Change.takeError()) << "\n";
115       return;
116     }
117     convertChangesToFileReplacements(*Change, &FileToReplaces);
118   }
119 
120 private:
121   const std::vector<std::string> &NewNames, &PrevNames;
122   const std::vector<std::vector<std::string>> &USRList;
123   std::map<std::string, tooling::Replacements> &FileToReplaces;
124   bool PrintLocations;
125 };
126 
127 // A renamer to rename symbols which are identified by a give USRList to
128 // new name.
129 //
130 // FIXME: Merge with the above RenamingASTConsumer.
131 class USRSymbolRenamer : public ASTConsumer {
132 public:
133   USRSymbolRenamer(const std::vector<std::string> &NewNames,
134                    const std::vector<std::vector<std::string>> &USRList,
135                    std::map<std::string, tooling::Replacements> &FileToReplaces)
136       : NewNames(NewNames), USRList(USRList), FileToReplaces(FileToReplaces) {
137     assert(USRList.size() == NewNames.size());
138   }
139 
140   void HandleTranslationUnit(ASTContext &Context) override {
141     for (unsigned I = 0; I < NewNames.size(); ++I) {
142       // FIXME: Apply AtomicChanges directly once the refactoring APIs are
143       // ready.
144       auto AtomicChanges = tooling::createRenameAtomicChanges(
145           USRList[I], NewNames[I], Context.getTranslationUnitDecl());
146       convertChangesToFileReplacements(AtomicChanges, &FileToReplaces);
147     }
148   }
149 
150 private:
151   const std::vector<std::string> &NewNames;
152   const std::vector<std::vector<std::string>> &USRList;
153   std::map<std::string, tooling::Replacements> &FileToReplaces;
154 };
155 
156 std::unique_ptr<ASTConsumer> RenamingAction::newASTConsumer() {
157   return llvm::make_unique<RenamingASTConsumer>(NewNames, PrevNames, USRList,
158                                                 FileToReplaces, PrintLocations);
159 }
160 
161 std::unique_ptr<ASTConsumer> QualifiedRenamingAction::newASTConsumer() {
162   return llvm::make_unique<USRSymbolRenamer>(NewNames, USRList, FileToReplaces);
163 }
164 
165 } // end namespace tooling
166 } // end namespace clang
167