1239313Sdim//===--- Refactoring.cpp - Framework for clang refactoring tools ----------===// 2239313Sdim// 3239313Sdim// The LLVM Compiler Infrastructure 4239313Sdim// 5239313Sdim// This file is distributed under the University of Illinois Open Source 6239313Sdim// License. See LICENSE.TXT for details. 7239313Sdim// 8239313Sdim//===----------------------------------------------------------------------===// 9239313Sdim// 10239313Sdim// Implements tools to support refactorings. 11239313Sdim// 12239313Sdim//===----------------------------------------------------------------------===// 13239313Sdim 14243830Sdim#include "clang/Basic/DiagnosticOptions.h" 15239313Sdim#include "clang/Basic/FileManager.h" 16239313Sdim#include "clang/Basic/SourceManager.h" 17239313Sdim#include "clang/Frontend/TextDiagnosticPrinter.h" 18239313Sdim#include "clang/Lex/Lexer.h" 19243830Sdim#include "clang/Rewrite/Core/Rewriter.h" 20239313Sdim#include "clang/Tooling/Refactoring.h" 21239313Sdim#include "llvm/Support/raw_os_ostream.h" 22263508Sdim#include "llvm/Support/FileSystem.h" 23263508Sdim#include "llvm/Support/Path.h" 24239313Sdim 25239313Sdimnamespace clang { 26239313Sdimnamespace tooling { 27239313Sdim 28239313Sdimstatic const char * const InvalidLocation = ""; 29239313Sdim 30239313SdimReplacement::Replacement() 31263508Sdim : FilePath(InvalidLocation) {} 32239313Sdim 33263508SdimReplacement::Replacement(StringRef FilePath, unsigned Offset, unsigned Length, 34263508Sdim StringRef ReplacementText) 35263508Sdim : FilePath(FilePath), ReplacementRange(Offset, Length), 36263508Sdim ReplacementText(ReplacementText) {} 37239313Sdim 38239313SdimReplacement::Replacement(SourceManager &Sources, SourceLocation Start, 39249423Sdim unsigned Length, StringRef ReplacementText) { 40239313Sdim setFromSourceLocation(Sources, Start, Length, ReplacementText); 41239313Sdim} 42239313Sdim 43239313SdimReplacement::Replacement(SourceManager &Sources, const CharSourceRange &Range, 44249423Sdim StringRef ReplacementText) { 45239313Sdim setFromSourceRange(Sources, Range, ReplacementText); 46239313Sdim} 47239313Sdim 48239313Sdimbool Replacement::isApplicable() const { 49239313Sdim return FilePath != InvalidLocation; 50239313Sdim} 51239313Sdim 52239313Sdimbool Replacement::apply(Rewriter &Rewrite) const { 53239313Sdim SourceManager &SM = Rewrite.getSourceMgr(); 54239313Sdim const FileEntry *Entry = SM.getFileManager().getFile(FilePath); 55239313Sdim if (Entry == NULL) 56239313Sdim return false; 57239313Sdim FileID ID; 58239313Sdim // FIXME: Use SM.translateFile directly. 59239313Sdim SourceLocation Location = SM.translateFileLineCol(Entry, 1, 1); 60239313Sdim ID = Location.isValid() ? 61239313Sdim SM.getFileID(Location) : 62239313Sdim SM.createFileID(Entry, SourceLocation(), SrcMgr::C_User); 63239313Sdim // FIXME: We cannot check whether Offset + Length is in the file, as 64239313Sdim // the remapping API is not public in the RewriteBuffer. 65239313Sdim const SourceLocation Start = 66239313Sdim SM.getLocForStartOfFile(ID). 67263508Sdim getLocWithOffset(ReplacementRange.getOffset()); 68239313Sdim // ReplaceText returns false on success. 69239313Sdim // ReplaceText only fails if the source location is not a file location, in 70239313Sdim // which case we already returned false earlier. 71263508Sdim bool RewriteSucceeded = !Rewrite.ReplaceText( 72263508Sdim Start, ReplacementRange.getLength(), ReplacementText); 73239313Sdim assert(RewriteSucceeded); 74239313Sdim return RewriteSucceeded; 75239313Sdim} 76239313Sdim 77239313Sdimstd::string Replacement::toString() const { 78239313Sdim std::string result; 79239313Sdim llvm::raw_string_ostream stream(result); 80263508Sdim stream << FilePath << ": " << ReplacementRange.getOffset() << ":+" 81263508Sdim << ReplacementRange.getLength() << ":\"" << ReplacementText << "\""; 82239313Sdim return result; 83239313Sdim} 84239313Sdim 85263508Sdimbool operator<(const Replacement &LHS, const Replacement &RHS) { 86263508Sdim if (LHS.getOffset() != RHS.getOffset()) 87263508Sdim return LHS.getOffset() < RHS.getOffset(); 88263508Sdim if (LHS.getLength() != RHS.getLength()) 89263508Sdim return LHS.getLength() < RHS.getLength(); 90263508Sdim if (LHS.getFilePath() != RHS.getFilePath()) 91263508Sdim return LHS.getFilePath() < RHS.getFilePath(); 92263508Sdim return LHS.getReplacementText() < RHS.getReplacementText(); 93239313Sdim} 94239313Sdim 95263508Sdimbool operator==(const Replacement &LHS, const Replacement &RHS) { 96263508Sdim return LHS.getOffset() == RHS.getOffset() && 97263508Sdim LHS.getLength() == RHS.getLength() && 98263508Sdim LHS.getFilePath() == RHS.getFilePath() && 99263508Sdim LHS.getReplacementText() == RHS.getReplacementText(); 100263508Sdim} 101263508Sdim 102239313Sdimvoid Replacement::setFromSourceLocation(SourceManager &Sources, 103239313Sdim SourceLocation Start, unsigned Length, 104249423Sdim StringRef ReplacementText) { 105239313Sdim const std::pair<FileID, unsigned> DecomposedLocation = 106239313Sdim Sources.getDecomposedLoc(Start); 107239313Sdim const FileEntry *Entry = Sources.getFileEntryForID(DecomposedLocation.first); 108263508Sdim if (Entry != NULL) { 109263508Sdim // Make FilePath absolute so replacements can be applied correctly when 110263508Sdim // relative paths for files are used. 111263508Sdim llvm::SmallString<256> FilePath(Entry->getName()); 112263508Sdim llvm::error_code EC = llvm::sys::fs::make_absolute(FilePath); 113263508Sdim this->FilePath = EC ? FilePath.c_str() : Entry->getName(); 114263508Sdim } else { 115263508Sdim this->FilePath = InvalidLocation; 116263508Sdim } 117263508Sdim this->ReplacementRange = Range(DecomposedLocation.second, Length); 118239313Sdim this->ReplacementText = ReplacementText; 119239313Sdim} 120239313Sdim 121239313Sdim// FIXME: This should go into the Lexer, but we need to figure out how 122239313Sdim// to handle ranges for refactoring in general first - there is no obvious 123239313Sdim// good way how to integrate this into the Lexer yet. 124239313Sdimstatic int getRangeSize(SourceManager &Sources, const CharSourceRange &Range) { 125239313Sdim SourceLocation SpellingBegin = Sources.getSpellingLoc(Range.getBegin()); 126239313Sdim SourceLocation SpellingEnd = Sources.getSpellingLoc(Range.getEnd()); 127239313Sdim std::pair<FileID, unsigned> Start = Sources.getDecomposedLoc(SpellingBegin); 128239313Sdim std::pair<FileID, unsigned> End = Sources.getDecomposedLoc(SpellingEnd); 129239313Sdim if (Start.first != End.first) return -1; 130239313Sdim if (Range.isTokenRange()) 131239313Sdim End.second += Lexer::MeasureTokenLength(SpellingEnd, Sources, 132239313Sdim LangOptions()); 133239313Sdim return End.second - Start.second; 134239313Sdim} 135239313Sdim 136239313Sdimvoid Replacement::setFromSourceRange(SourceManager &Sources, 137239313Sdim const CharSourceRange &Range, 138249423Sdim StringRef ReplacementText) { 139239313Sdim setFromSourceLocation(Sources, Sources.getSpellingLoc(Range.getBegin()), 140239313Sdim getRangeSize(Sources, Range), ReplacementText); 141239313Sdim} 142239313Sdim 143263508Sdimbool applyAllReplacements(const Replacements &Replaces, Rewriter &Rewrite) { 144239313Sdim bool Result = true; 145239313Sdim for (Replacements::const_iterator I = Replaces.begin(), 146239313Sdim E = Replaces.end(); 147239313Sdim I != E; ++I) { 148239313Sdim if (I->isApplicable()) { 149239313Sdim Result = I->apply(Rewrite) && Result; 150239313Sdim } else { 151239313Sdim Result = false; 152239313Sdim } 153239313Sdim } 154239313Sdim return Result; 155239313Sdim} 156239313Sdim 157263508Sdim// FIXME: Remove this function when Replacements is implemented as std::vector 158263508Sdim// instead of std::set. 159263508Sdimbool applyAllReplacements(const std::vector<Replacement> &Replaces, 160263508Sdim Rewriter &Rewrite) { 161263508Sdim bool Result = true; 162263508Sdim for (std::vector<Replacement>::const_iterator I = Replaces.begin(), 163263508Sdim E = Replaces.end(); 164263508Sdim I != E; ++I) { 165263508Sdim if (I->isApplicable()) { 166263508Sdim Result = I->apply(Rewrite) && Result; 167263508Sdim } else { 168263508Sdim Result = false; 169263508Sdim } 170263508Sdim } 171263508Sdim return Result; 172263508Sdim} 173263508Sdim 174263508Sdimstd::string applyAllReplacements(StringRef Code, const Replacements &Replaces) { 175263508Sdim FileManager Files((FileSystemOptions())); 176263508Sdim DiagnosticsEngine Diagnostics( 177263508Sdim IntrusiveRefCntPtr<DiagnosticIDs>(new DiagnosticIDs), 178263508Sdim new DiagnosticOptions); 179263508Sdim Diagnostics.setClient(new TextDiagnosticPrinter( 180263508Sdim llvm::outs(), &Diagnostics.getDiagnosticOptions())); 181263508Sdim SourceManager SourceMgr(Diagnostics, Files); 182263508Sdim Rewriter Rewrite(SourceMgr, LangOptions()); 183263508Sdim llvm::MemoryBuffer *Buf = llvm::MemoryBuffer::getMemBuffer(Code, "<stdin>"); 184263508Sdim const clang::FileEntry *Entry = 185263508Sdim Files.getVirtualFile("<stdin>", Buf->getBufferSize(), 0); 186263508Sdim SourceMgr.overrideFileContents(Entry, Buf); 187263508Sdim FileID ID = 188263508Sdim SourceMgr.createFileID(Entry, SourceLocation(), clang::SrcMgr::C_User); 189263508Sdim for (Replacements::const_iterator I = Replaces.begin(), E = Replaces.end(); 190263508Sdim I != E; ++I) { 191263508Sdim Replacement Replace("<stdin>", I->getOffset(), I->getLength(), 192263508Sdim I->getReplacementText()); 193263508Sdim if (!Replace.apply(Rewrite)) 194263508Sdim return ""; 195263508Sdim } 196263508Sdim std::string Result; 197263508Sdim llvm::raw_string_ostream OS(Result); 198263508Sdim Rewrite.getEditBuffer(ID).write(OS); 199263508Sdim OS.flush(); 200263508Sdim return Result; 201263508Sdim} 202263508Sdim 203263508Sdimunsigned shiftedCodePosition(const Replacements &Replaces, unsigned Position) { 204263508Sdim unsigned NewPosition = Position; 205263508Sdim for (Replacements::iterator I = Replaces.begin(), E = Replaces.end(); I != E; 206263508Sdim ++I) { 207263508Sdim if (I->getOffset() >= Position) 208263508Sdim break; 209263508Sdim if (I->getOffset() + I->getLength() > Position) 210263508Sdim NewPosition += I->getOffset() + I->getLength() - Position; 211263508Sdim NewPosition += I->getReplacementText().size() - I->getLength(); 212263508Sdim } 213263508Sdim return NewPosition; 214263508Sdim} 215263508Sdim 216263508Sdim// FIXME: Remove this function when Replacements is implemented as std::vector 217263508Sdim// instead of std::set. 218263508Sdimunsigned shiftedCodePosition(const std::vector<Replacement> &Replaces, 219263508Sdim unsigned Position) { 220263508Sdim unsigned NewPosition = Position; 221263508Sdim for (std::vector<Replacement>::const_iterator I = Replaces.begin(), 222263508Sdim E = Replaces.end(); 223263508Sdim I != E; ++I) { 224263508Sdim if (I->getOffset() >= Position) 225263508Sdim break; 226263508Sdim if (I->getOffset() + I->getLength() > Position) 227263508Sdim NewPosition += I->getOffset() + I->getLength() - Position; 228263508Sdim NewPosition += I->getReplacementText().size() - I->getLength(); 229263508Sdim } 230263508Sdim return NewPosition; 231263508Sdim} 232263508Sdim 233263508Sdimvoid deduplicate(std::vector<Replacement> &Replaces, 234263508Sdim std::vector<Range> &Conflicts) { 235263508Sdim if (Replaces.empty()) 236263508Sdim return; 237263508Sdim 238263508Sdim // Deduplicate 239263508Sdim std::sort(Replaces.begin(), Replaces.end()); 240263508Sdim std::vector<Replacement>::iterator End = 241263508Sdim std::unique(Replaces.begin(), Replaces.end()); 242263508Sdim Replaces.erase(End, Replaces.end()); 243263508Sdim 244263508Sdim // Detect conflicts 245263508Sdim Range ConflictRange(Replaces.front().getOffset(), 246263508Sdim Replaces.front().getLength()); 247263508Sdim unsigned ConflictStart = 0; 248263508Sdim unsigned ConflictLength = 1; 249263508Sdim for (unsigned i = 1; i < Replaces.size(); ++i) { 250263508Sdim Range Current(Replaces[i].getOffset(), Replaces[i].getLength()); 251263508Sdim if (ConflictRange.overlapsWith(Current)) { 252263508Sdim // Extend conflicted range 253263508Sdim ConflictRange = Range(ConflictRange.getOffset(), 254263508Sdim std::max(ConflictRange.getLength(), 255263508Sdim Current.getOffset() + Current.getLength() - 256263508Sdim ConflictRange.getOffset())); 257263508Sdim ++ConflictLength; 258263508Sdim } else { 259263508Sdim if (ConflictLength > 1) 260263508Sdim Conflicts.push_back(Range(ConflictStart, ConflictLength)); 261263508Sdim ConflictRange = Current; 262263508Sdim ConflictStart = i; 263263508Sdim ConflictLength = 1; 264263508Sdim } 265263508Sdim } 266263508Sdim 267263508Sdim if (ConflictLength > 1) 268263508Sdim Conflicts.push_back(Range(ConflictStart, ConflictLength)); 269263508Sdim} 270263508Sdim 271263508Sdim 272249423SdimRefactoringTool::RefactoringTool(const CompilationDatabase &Compilations, 273249423Sdim ArrayRef<std::string> SourcePaths) 274249423Sdim : ClangTool(Compilations, SourcePaths) {} 275249423Sdim 276249423SdimReplacements &RefactoringTool::getReplacements() { return Replace; } 277249423Sdim 278249423Sdimint RefactoringTool::runAndSave(FrontendActionFactory *ActionFactory) { 279249423Sdim if (int Result = run(ActionFactory)) { 280249423Sdim return Result; 281249423Sdim } 282249423Sdim 283249423Sdim LangOptions DefaultLangOptions; 284249423Sdim IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts = new DiagnosticOptions(); 285249423Sdim TextDiagnosticPrinter DiagnosticPrinter(llvm::errs(), &*DiagOpts); 286249423Sdim DiagnosticsEngine Diagnostics( 287249423Sdim IntrusiveRefCntPtr<DiagnosticIDs>(new DiagnosticIDs()), 288249423Sdim &*DiagOpts, &DiagnosticPrinter, false); 289249423Sdim SourceManager Sources(Diagnostics, getFiles()); 290249423Sdim Rewriter Rewrite(Sources, DefaultLangOptions); 291249423Sdim 292249423Sdim if (!applyAllReplacements(Rewrite)) { 293249423Sdim llvm::errs() << "Skipped some replacements.\n"; 294249423Sdim } 295249423Sdim 296249423Sdim return saveRewrittenFiles(Rewrite); 297249423Sdim} 298249423Sdim 299249423Sdimbool RefactoringTool::applyAllReplacements(Rewriter &Rewrite) { 300249423Sdim return tooling::applyAllReplacements(Replace, Rewrite); 301249423Sdim} 302249423Sdim 303249423Sdimint RefactoringTool::saveRewrittenFiles(Rewriter &Rewrite) { 304263508Sdim return Rewrite.overwriteChangedFiles() ? 1 : 0; 305239313Sdim} 306239313Sdim 307239313Sdim} // end namespace tooling 308239313Sdim} // end namespace clang 309