clang API Documentation
00001 //===--- Replacement.cpp - Framework for clang refactoring tools ----------===// 00002 // 00003 // The LLVM Compiler Infrastructure 00004 // 00005 // This file is distributed under the University of Illinois Open Source 00006 // License. See LICENSE.TXT for details. 00007 // 00008 //===----------------------------------------------------------------------===// 00009 // 00010 // Implements classes to support/store refactorings. 00011 // 00012 //===----------------------------------------------------------------------===// 00013 00014 #include "clang/Basic/Diagnostic.h" 00015 #include "clang/Basic/DiagnosticIDs.h" 00016 #include "clang/Basic/DiagnosticOptions.h" 00017 #include "clang/Basic/FileManager.h" 00018 #include "clang/Basic/SourceManager.h" 00019 #include "clang/Lex/Lexer.h" 00020 #include "clang/Rewrite/Core/Rewriter.h" 00021 #include "clang/Tooling/Core/Replacement.h" 00022 #include "llvm/Support/FileSystem.h" 00023 #include "llvm/Support/Path.h" 00024 #include "llvm/Support/raw_os_ostream.h" 00025 00026 namespace clang { 00027 namespace tooling { 00028 00029 static const char * const InvalidLocation = ""; 00030 00031 Replacement::Replacement() 00032 : FilePath(InvalidLocation) {} 00033 00034 Replacement::Replacement(StringRef FilePath, unsigned Offset, unsigned Length, 00035 StringRef ReplacementText) 00036 : FilePath(FilePath), ReplacementRange(Offset, Length), 00037 ReplacementText(ReplacementText) {} 00038 00039 Replacement::Replacement(const SourceManager &Sources, SourceLocation Start, 00040 unsigned Length, StringRef ReplacementText) { 00041 setFromSourceLocation(Sources, Start, Length, ReplacementText); 00042 } 00043 00044 Replacement::Replacement(const SourceManager &Sources, 00045 const CharSourceRange &Range, 00046 StringRef ReplacementText) { 00047 setFromSourceRange(Sources, Range, ReplacementText); 00048 } 00049 00050 bool Replacement::isApplicable() const { 00051 return FilePath != InvalidLocation; 00052 } 00053 00054 bool Replacement::apply(Rewriter &Rewrite) const { 00055 SourceManager &SM = Rewrite.getSourceMgr(); 00056 const FileEntry *Entry = SM.getFileManager().getFile(FilePath); 00057 if (!Entry) 00058 return false; 00059 FileID ID; 00060 // FIXME: Use SM.translateFile directly. 00061 SourceLocation Location = SM.translateFileLineCol(Entry, 1, 1); 00062 ID = Location.isValid() ? 00063 SM.getFileID(Location) : 00064 SM.createFileID(Entry, SourceLocation(), SrcMgr::C_User); 00065 // FIXME: We cannot check whether Offset + Length is in the file, as 00066 // the remapping API is not public in the RewriteBuffer. 00067 const SourceLocation Start = 00068 SM.getLocForStartOfFile(ID). 00069 getLocWithOffset(ReplacementRange.getOffset()); 00070 // ReplaceText returns false on success. 00071 // ReplaceText only fails if the source location is not a file location, in 00072 // which case we already returned false earlier. 00073 bool RewriteSucceeded = !Rewrite.ReplaceText( 00074 Start, ReplacementRange.getLength(), ReplacementText); 00075 assert(RewriteSucceeded); 00076 return RewriteSucceeded; 00077 } 00078 00079 std::string Replacement::toString() const { 00080 std::string result; 00081 llvm::raw_string_ostream stream(result); 00082 stream << FilePath << ": " << ReplacementRange.getOffset() << ":+" 00083 << ReplacementRange.getLength() << ":\"" << ReplacementText << "\""; 00084 return result; 00085 } 00086 00087 bool operator<(const Replacement &LHS, const Replacement &RHS) { 00088 if (LHS.getOffset() != RHS.getOffset()) 00089 return LHS.getOffset() < RHS.getOffset(); 00090 if (LHS.getLength() != RHS.getLength()) 00091 return LHS.getLength() < RHS.getLength(); 00092 if (LHS.getFilePath() != RHS.getFilePath()) 00093 return LHS.getFilePath() < RHS.getFilePath(); 00094 return LHS.getReplacementText() < RHS.getReplacementText(); 00095 } 00096 00097 bool operator==(const Replacement &LHS, const Replacement &RHS) { 00098 return LHS.getOffset() == RHS.getOffset() && 00099 LHS.getLength() == RHS.getLength() && 00100 LHS.getFilePath() == RHS.getFilePath() && 00101 LHS.getReplacementText() == RHS.getReplacementText(); 00102 } 00103 00104 void Replacement::setFromSourceLocation(const SourceManager &Sources, 00105 SourceLocation Start, unsigned Length, 00106 StringRef ReplacementText) { 00107 const std::pair<FileID, unsigned> DecomposedLocation = 00108 Sources.getDecomposedLoc(Start); 00109 const FileEntry *Entry = Sources.getFileEntryForID(DecomposedLocation.first); 00110 if (Entry) { 00111 // Make FilePath absolute so replacements can be applied correctly when 00112 // relative paths for files are used. 00113 llvm::SmallString<256> FilePath(Entry->getName()); 00114 std::error_code EC = llvm::sys::fs::make_absolute(FilePath); 00115 this->FilePath = EC ? FilePath.c_str() : Entry->getName(); 00116 } else { 00117 this->FilePath = InvalidLocation; 00118 } 00119 this->ReplacementRange = Range(DecomposedLocation.second, Length); 00120 this->ReplacementText = ReplacementText; 00121 } 00122 00123 // FIXME: This should go into the Lexer, but we need to figure out how 00124 // to handle ranges for refactoring in general first - there is no obvious 00125 // good way how to integrate this into the Lexer yet. 00126 static int getRangeSize(const SourceManager &Sources, 00127 const CharSourceRange &Range) { 00128 SourceLocation SpellingBegin = Sources.getSpellingLoc(Range.getBegin()); 00129 SourceLocation SpellingEnd = Sources.getSpellingLoc(Range.getEnd()); 00130 std::pair<FileID, unsigned> Start = Sources.getDecomposedLoc(SpellingBegin); 00131 std::pair<FileID, unsigned> End = Sources.getDecomposedLoc(SpellingEnd); 00132 if (Start.first != End.first) return -1; 00133 if (Range.isTokenRange()) 00134 End.second += Lexer::MeasureTokenLength(SpellingEnd, Sources, 00135 LangOptions()); 00136 return End.second - Start.second; 00137 } 00138 00139 void Replacement::setFromSourceRange(const SourceManager &Sources, 00140 const CharSourceRange &Range, 00141 StringRef ReplacementText) { 00142 setFromSourceLocation(Sources, Sources.getSpellingLoc(Range.getBegin()), 00143 getRangeSize(Sources, Range), ReplacementText); 00144 } 00145 00146 unsigned shiftedCodePosition(const Replacements &Replaces, unsigned Position) { 00147 unsigned NewPosition = Position; 00148 for (Replacements::iterator I = Replaces.begin(), E = Replaces.end(); I != E; 00149 ++I) { 00150 if (I->getOffset() >= Position) 00151 break; 00152 if (I->getOffset() + I->getLength() > Position) 00153 NewPosition += I->getOffset() + I->getLength() - Position; 00154 NewPosition += I->getReplacementText().size() - I->getLength(); 00155 } 00156 return NewPosition; 00157 } 00158 00159 // FIXME: Remove this function when Replacements is implemented as std::vector 00160 // instead of std::set. 00161 unsigned shiftedCodePosition(const std::vector<Replacement> &Replaces, 00162 unsigned Position) { 00163 unsigned NewPosition = Position; 00164 for (std::vector<Replacement>::const_iterator I = Replaces.begin(), 00165 E = Replaces.end(); 00166 I != E; ++I) { 00167 if (I->getOffset() >= Position) 00168 break; 00169 if (I->getOffset() + I->getLength() > Position) 00170 NewPosition += I->getOffset() + I->getLength() - Position; 00171 NewPosition += I->getReplacementText().size() - I->getLength(); 00172 } 00173 return NewPosition; 00174 } 00175 00176 void deduplicate(std::vector<Replacement> &Replaces, 00177 std::vector<Range> &Conflicts) { 00178 if (Replaces.empty()) 00179 return; 00180 00181 auto LessNoPath = [](const Replacement &LHS, const Replacement &RHS) { 00182 if (LHS.getOffset() != RHS.getOffset()) 00183 return LHS.getOffset() < RHS.getOffset(); 00184 if (LHS.getLength() != RHS.getLength()) 00185 return LHS.getLength() < RHS.getLength(); 00186 return LHS.getReplacementText() < RHS.getReplacementText(); 00187 }; 00188 00189 auto EqualNoPath = [](const Replacement &LHS, const Replacement &RHS) { 00190 return LHS.getOffset() == RHS.getOffset() && 00191 LHS.getLength() == RHS.getLength() && 00192 LHS.getReplacementText() == RHS.getReplacementText(); 00193 }; 00194 00195 // Deduplicate. We don't want to deduplicate based on the path as we assume 00196 // that all replacements refer to the same file (or are symlinks). 00197 std::sort(Replaces.begin(), Replaces.end(), LessNoPath); 00198 Replaces.erase(std::unique(Replaces.begin(), Replaces.end(), EqualNoPath), 00199 Replaces.end()); 00200 00201 // Detect conflicts 00202 Range ConflictRange(Replaces.front().getOffset(), 00203 Replaces.front().getLength()); 00204 unsigned ConflictStart = 0; 00205 unsigned ConflictLength = 1; 00206 for (unsigned i = 1; i < Replaces.size(); ++i) { 00207 Range Current(Replaces[i].getOffset(), Replaces[i].getLength()); 00208 if (ConflictRange.overlapsWith(Current)) { 00209 // Extend conflicted range 00210 ConflictRange = Range(ConflictRange.getOffset(), 00211 std::max(ConflictRange.getLength(), 00212 Current.getOffset() + Current.getLength() - 00213 ConflictRange.getOffset())); 00214 ++ConflictLength; 00215 } else { 00216 if (ConflictLength > 1) 00217 Conflicts.push_back(Range(ConflictStart, ConflictLength)); 00218 ConflictRange = Current; 00219 ConflictStart = i; 00220 ConflictLength = 1; 00221 } 00222 } 00223 00224 if (ConflictLength > 1) 00225 Conflicts.push_back(Range(ConflictStart, ConflictLength)); 00226 } 00227 00228 bool applyAllReplacements(const Replacements &Replaces, Rewriter &Rewrite) { 00229 bool Result = true; 00230 for (Replacements::const_iterator I = Replaces.begin(), 00231 E = Replaces.end(); 00232 I != E; ++I) { 00233 if (I->isApplicable()) { 00234 Result = I->apply(Rewrite) && Result; 00235 } else { 00236 Result = false; 00237 } 00238 } 00239 return Result; 00240 } 00241 00242 // FIXME: Remove this function when Replacements is implemented as std::vector 00243 // instead of std::set. 00244 bool applyAllReplacements(const std::vector<Replacement> &Replaces, 00245 Rewriter &Rewrite) { 00246 bool Result = true; 00247 for (std::vector<Replacement>::const_iterator I = Replaces.begin(), 00248 E = Replaces.end(); 00249 I != E; ++I) { 00250 if (I->isApplicable()) { 00251 Result = I->apply(Rewrite) && Result; 00252 } else { 00253 Result = false; 00254 } 00255 } 00256 return Result; 00257 } 00258 00259 std::string applyAllReplacements(StringRef Code, const Replacements &Replaces) { 00260 FileManager Files((FileSystemOptions())); 00261 DiagnosticsEngine Diagnostics( 00262 IntrusiveRefCntPtr<DiagnosticIDs>(new DiagnosticIDs), 00263 new DiagnosticOptions); 00264 SourceManager SourceMgr(Diagnostics, Files); 00265 Rewriter Rewrite(SourceMgr, LangOptions()); 00266 std::unique_ptr<llvm::MemoryBuffer> Buf = 00267 llvm::MemoryBuffer::getMemBuffer(Code, "<stdin>"); 00268 const clang::FileEntry *Entry = 00269 Files.getVirtualFile("<stdin>", Buf->getBufferSize(), 0); 00270 SourceMgr.overrideFileContents(Entry, std::move(Buf)); 00271 FileID ID = 00272 SourceMgr.createFileID(Entry, SourceLocation(), clang::SrcMgr::C_User); 00273 for (Replacements::const_iterator I = Replaces.begin(), E = Replaces.end(); 00274 I != E; ++I) { 00275 Replacement Replace("<stdin>", I->getOffset(), I->getLength(), 00276 I->getReplacementText()); 00277 if (!Replace.apply(Rewrite)) 00278 return ""; 00279 } 00280 std::string Result; 00281 llvm::raw_string_ostream OS(Result); 00282 Rewrite.getEditBuffer(ID).write(OS); 00283 OS.flush(); 00284 return Result; 00285 } 00286 00287 } // end namespace tooling 00288 } // end namespace clang 00289