1 //===--- Refactoring.cpp - Framework for clang refactoring tools ----------===// 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 // Implements tools to support refactorings. 11 // 12 //===----------------------------------------------------------------------===// 13 14 #include "clang/Basic/DiagnosticOptions.h" 15 #include "clang/Basic/FileManager.h" 16 #include "clang/Basic/SourceManager.h" 17 #include "clang/Frontend/TextDiagnosticPrinter.h" 18 #include "clang/Lex/Lexer.h" 19 #include "clang/Rewrite/Core/Rewriter.h" 20 #include "clang/Tooling/Refactoring.h" 21 #include "llvm/Support/raw_os_ostream.h" 22 23 namespace clang { 24 namespace tooling { 25 26 static const char * const InvalidLocation = ""; 27 28 Replacement::Replacement() 29 : FilePath(InvalidLocation) {} 30 31 Replacement::Replacement(StringRef FilePath, unsigned Offset, unsigned Length, 32 StringRef ReplacementText) 33 : FilePath(FilePath), ReplacementRange(Offset, Length), 34 ReplacementText(ReplacementText) {} 35 36 Replacement::Replacement(SourceManager &Sources, SourceLocation Start, 37 unsigned Length, StringRef ReplacementText) { 38 setFromSourceLocation(Sources, Start, Length, ReplacementText); 39 } 40 41 Replacement::Replacement(SourceManager &Sources, const CharSourceRange &Range, 42 StringRef ReplacementText) { 43 setFromSourceRange(Sources, Range, ReplacementText); 44 } 45 46 bool Replacement::isApplicable() const { 47 return FilePath != InvalidLocation; 48 } 49 50 bool Replacement::apply(Rewriter &Rewrite) const { 51 SourceManager &SM = Rewrite.getSourceMgr(); 52 const FileEntry *Entry = SM.getFileManager().getFile(FilePath); 53 if (Entry == NULL) 54 return false; 55 FileID ID; 56 // FIXME: Use SM.translateFile directly. 57 SourceLocation Location = SM.translateFileLineCol(Entry, 1, 1); 58 ID = Location.isValid() ? 59 SM.getFileID(Location) : 60 SM.createFileID(Entry, SourceLocation(), SrcMgr::C_User); 61 // FIXME: We cannot check whether Offset + Length is in the file, as 62 // the remapping API is not public in the RewriteBuffer. 63 const SourceLocation Start = 64 SM.getLocForStartOfFile(ID). 65 getLocWithOffset(ReplacementRange.getOffset()); 66 // ReplaceText returns false on success. 67 // ReplaceText only fails if the source location is not a file location, in 68 // which case we already returned false earlier. 69 bool RewriteSucceeded = !Rewrite.ReplaceText( 70 Start, ReplacementRange.getLength(), ReplacementText); 71 assert(RewriteSucceeded); 72 return RewriteSucceeded; 73 } 74 75 std::string Replacement::toString() const { 76 std::string result; 77 llvm::raw_string_ostream stream(result); 78 stream << FilePath << ": " << ReplacementRange.getOffset() << ":+" 79 << ReplacementRange.getLength() << ":\"" << ReplacementText << "\""; 80 return result; 81 } 82 83 bool Replacement::Less::operator()(const Replacement &R1, 84 const Replacement &R2) const { 85 if (R1.FilePath != R2.FilePath) return R1.FilePath < R2.FilePath; 86 if (R1.ReplacementRange.getOffset() != R2.ReplacementRange.getOffset()) 87 return R1.ReplacementRange.getOffset() < R2.ReplacementRange.getOffset(); 88 if (R1.ReplacementRange.getLength() != R2.ReplacementRange.getLength()) 89 return R1.ReplacementRange.getLength() < R2.ReplacementRange.getLength(); 90 return R1.ReplacementText < R2.ReplacementText; 91 } 92 93 bool Replacement::operator==(const Replacement &Other) const { 94 return ReplacementRange.getOffset() == Other.ReplacementRange.getOffset() && 95 ReplacementRange.getLength() == Other.ReplacementRange.getLength() && 96 FilePath == Other.FilePath && ReplacementText == Other.ReplacementText; 97 } 98 99 void Replacement::setFromSourceLocation(SourceManager &Sources, 100 SourceLocation Start, unsigned Length, 101 StringRef ReplacementText) { 102 const std::pair<FileID, unsigned> DecomposedLocation = 103 Sources.getDecomposedLoc(Start); 104 const FileEntry *Entry = Sources.getFileEntryForID(DecomposedLocation.first); 105 this->FilePath = Entry != NULL ? Entry->getName() : InvalidLocation; 106 this->ReplacementRange = Range(DecomposedLocation.second, Length); 107 this->ReplacementText = ReplacementText; 108 } 109 110 // FIXME: This should go into the Lexer, but we need to figure out how 111 // to handle ranges for refactoring in general first - there is no obvious 112 // good way how to integrate this into the Lexer yet. 113 static int getRangeSize(SourceManager &Sources, const CharSourceRange &Range) { 114 SourceLocation SpellingBegin = Sources.getSpellingLoc(Range.getBegin()); 115 SourceLocation SpellingEnd = Sources.getSpellingLoc(Range.getEnd()); 116 std::pair<FileID, unsigned> Start = Sources.getDecomposedLoc(SpellingBegin); 117 std::pair<FileID, unsigned> End = Sources.getDecomposedLoc(SpellingEnd); 118 if (Start.first != End.first) return -1; 119 if (Range.isTokenRange()) 120 End.second += Lexer::MeasureTokenLength(SpellingEnd, Sources, 121 LangOptions()); 122 return End.second - Start.second; 123 } 124 125 void Replacement::setFromSourceRange(SourceManager &Sources, 126 const CharSourceRange &Range, 127 StringRef ReplacementText) { 128 setFromSourceLocation(Sources, Sources.getSpellingLoc(Range.getBegin()), 129 getRangeSize(Sources, Range), ReplacementText); 130 } 131 132 bool applyAllReplacements(const Replacements &Replaces, Rewriter &Rewrite) { 133 bool Result = true; 134 for (Replacements::const_iterator I = Replaces.begin(), 135 E = Replaces.end(); 136 I != E; ++I) { 137 if (I->isApplicable()) { 138 Result = I->apply(Rewrite) && Result; 139 } else { 140 Result = false; 141 } 142 } 143 return Result; 144 } 145 146 // FIXME: Remove this function when Replacements is implemented as std::vector 147 // instead of std::set. 148 bool applyAllReplacements(const std::vector<Replacement> &Replaces, 149 Rewriter &Rewrite) { 150 bool Result = true; 151 for (std::vector<Replacement>::const_iterator I = Replaces.begin(), 152 E = Replaces.end(); 153 I != E; ++I) { 154 if (I->isApplicable()) { 155 Result = I->apply(Rewrite) && Result; 156 } else { 157 Result = false; 158 } 159 } 160 return Result; 161 } 162 163 std::string applyAllReplacements(StringRef Code, const Replacements &Replaces) { 164 FileManager Files((FileSystemOptions())); 165 DiagnosticsEngine Diagnostics( 166 IntrusiveRefCntPtr<DiagnosticIDs>(new DiagnosticIDs), 167 new DiagnosticOptions); 168 Diagnostics.setClient(new TextDiagnosticPrinter( 169 llvm::outs(), &Diagnostics.getDiagnosticOptions())); 170 SourceManager SourceMgr(Diagnostics, Files); 171 Rewriter Rewrite(SourceMgr, LangOptions()); 172 llvm::MemoryBuffer *Buf = llvm::MemoryBuffer::getMemBuffer(Code, "<stdin>"); 173 const clang::FileEntry *Entry = 174 Files.getVirtualFile("<stdin>", Buf->getBufferSize(), 0); 175 SourceMgr.overrideFileContents(Entry, Buf); 176 FileID ID = 177 SourceMgr.createFileID(Entry, SourceLocation(), clang::SrcMgr::C_User); 178 for (Replacements::const_iterator I = Replaces.begin(), E = Replaces.end(); 179 I != E; ++I) { 180 Replacement Replace("<stdin>", I->getOffset(), I->getLength(), 181 I->getReplacementText()); 182 if (!Replace.apply(Rewrite)) 183 return ""; 184 } 185 std::string Result; 186 llvm::raw_string_ostream OS(Result); 187 Rewrite.getEditBuffer(ID).write(OS); 188 OS.flush(); 189 return Result; 190 } 191 192 unsigned shiftedCodePosition(const Replacements &Replaces, unsigned Position) { 193 unsigned NewPosition = Position; 194 for (Replacements::iterator I = Replaces.begin(), E = Replaces.end(); I != E; 195 ++I) { 196 if (I->getOffset() >= Position) 197 break; 198 if (I->getOffset() + I->getLength() > Position) 199 NewPosition += I->getOffset() + I->getLength() - Position; 200 NewPosition += I->getReplacementText().size() - I->getLength(); 201 } 202 return NewPosition; 203 } 204 205 void deduplicate(std::vector<Replacement> &Replaces, 206 std::vector<Range> &Conflicts) { 207 if (Replaces.empty()) 208 return; 209 210 // Deduplicate 211 std::sort(Replaces.begin(), Replaces.end(), Replacement::Less()); 212 std::vector<Replacement>::iterator End = 213 std::unique(Replaces.begin(), Replaces.end()); 214 Replaces.erase(End, Replaces.end()); 215 216 // Detect conflicts 217 Range ConflictRange(Replaces.front().getOffset(), 218 Replaces.front().getLength()); 219 unsigned ConflictStart = 0; 220 unsigned ConflictLength = 1; 221 for (unsigned i = 1; i < Replaces.size(); ++i) { 222 Range Current(Replaces[i].getOffset(), Replaces[i].getLength()); 223 if (ConflictRange.overlapsWith(Current)) { 224 // Extend conflicted range 225 ConflictRange = Range(ConflictRange.getOffset(), 226 std::max(ConflictRange.getLength(), 227 Current.getOffset() + Current.getLength() - 228 ConflictRange.getOffset())); 229 ++ConflictLength; 230 } else { 231 if (ConflictLength > 1) 232 Conflicts.push_back(Range(ConflictStart, ConflictLength)); 233 ConflictRange = Current; 234 ConflictStart = i; 235 ConflictLength = 1; 236 } 237 } 238 239 if (ConflictLength > 1) 240 Conflicts.push_back(Range(ConflictStart, ConflictLength)); 241 } 242 243 244 RefactoringTool::RefactoringTool(const CompilationDatabase &Compilations, 245 ArrayRef<std::string> SourcePaths) 246 : ClangTool(Compilations, SourcePaths) {} 247 248 Replacements &RefactoringTool::getReplacements() { return Replace; } 249 250 int RefactoringTool::runAndSave(FrontendActionFactory *ActionFactory) { 251 if (int Result = run(ActionFactory)) { 252 return Result; 253 } 254 255 LangOptions DefaultLangOptions; 256 IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts = new DiagnosticOptions(); 257 TextDiagnosticPrinter DiagnosticPrinter(llvm::errs(), &*DiagOpts); 258 DiagnosticsEngine Diagnostics( 259 IntrusiveRefCntPtr<DiagnosticIDs>(new DiagnosticIDs()), 260 &*DiagOpts, &DiagnosticPrinter, false); 261 SourceManager Sources(Diagnostics, getFiles()); 262 Rewriter Rewrite(Sources, DefaultLangOptions); 263 264 if (!applyAllReplacements(Rewrite)) { 265 llvm::errs() << "Skipped some replacements.\n"; 266 } 267 268 return saveRewrittenFiles(Rewrite); 269 } 270 271 bool RefactoringTool::applyAllReplacements(Rewriter &Rewrite) { 272 return tooling::applyAllReplacements(Replace, Rewrite); 273 } 274 275 int RefactoringTool::saveRewrittenFiles(Rewriter &Rewrite) { 276 for (Rewriter::buffer_iterator I = Rewrite.buffer_begin(), 277 E = Rewrite.buffer_end(); 278 I != E; ++I) { 279 // FIXME: This code is copied from the FixItRewriter.cpp - I think it should 280 // go into directly into Rewriter (there we also have the Diagnostics to 281 // handle the error cases better). 282 const FileEntry *Entry = 283 Rewrite.getSourceMgr().getFileEntryForID(I->first); 284 std::string ErrorInfo; 285 llvm::raw_fd_ostream FileStream(Entry->getName(), ErrorInfo, 286 llvm::sys::fs::F_Binary); 287 if (!ErrorInfo.empty()) 288 return 1; 289 I->second.write(FileStream); 290 FileStream.flush(); 291 } 292 return 0; 293 } 294 295 } // end namespace tooling 296 } // end namespace clang 297