clang API Documentation
00001 //===--- TextDiagnostic.cpp - Text Diagnostic Pretty-Printing -------------===// 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 #include "clang/Frontend/TextDiagnostic.h" 00011 #include "clang/Basic/FileManager.h" 00012 #include "clang/Basic/SourceManager.h" 00013 #include "clang/Basic/ConvertUTF.h" 00014 #include "clang/Frontend/DiagnosticOptions.h" 00015 #include "clang/Lex/Lexer.h" 00016 #include "llvm/Support/MemoryBuffer.h" 00017 #include "llvm/Support/raw_ostream.h" 00018 #include "llvm/Support/ErrorHandling.h" 00019 #include "llvm/Support/Locale.h" 00020 #include "llvm/ADT/SmallString.h" 00021 #include "llvm/ADT/StringExtras.h" 00022 #include <algorithm> 00023 00024 using namespace clang; 00025 00026 static const enum raw_ostream::Colors noteColor = 00027 raw_ostream::BLACK; 00028 static const enum raw_ostream::Colors fixitColor = 00029 raw_ostream::GREEN; 00030 static const enum raw_ostream::Colors caretColor = 00031 raw_ostream::GREEN; 00032 static const enum raw_ostream::Colors warningColor = 00033 raw_ostream::MAGENTA; 00034 static const enum raw_ostream::Colors errorColor = raw_ostream::RED; 00035 static const enum raw_ostream::Colors fatalColor = raw_ostream::RED; 00036 // Used for changing only the bold attribute. 00037 static const enum raw_ostream::Colors savedColor = 00038 raw_ostream::SAVEDCOLOR; 00039 00040 /// \brief Number of spaces to indent when word-wrapping. 00041 const unsigned WordWrapIndentation = 6; 00042 00043 static int bytesSincePreviousTabOrLineBegin(StringRef SourceLine, size_t i) { 00044 int bytes = 0; 00045 while (0<i) { 00046 if (SourceLine[--i]=='\t') 00047 break; 00048 ++bytes; 00049 } 00050 return bytes; 00051 } 00052 00053 /// \brief returns a printable representation of first item from input range 00054 /// 00055 /// This function returns a printable representation of the next item in a line 00056 /// of source. If the next byte begins a valid and printable character, that 00057 /// character is returned along with 'true'. 00058 /// 00059 /// Otherwise, if the next byte begins a valid, but unprintable character, a 00060 /// printable, escaped representation of the character is returned, along with 00061 /// 'false'. Otherwise a printable, escaped representation of the next byte 00062 /// is returned along with 'false'. 00063 /// 00064 /// \note The index is updated to be used with a subsequent call to 00065 /// printableTextForNextCharacter. 00066 /// 00067 /// \param SourceLine The line of source 00068 /// \param i Pointer to byte index, 00069 /// \param TabStop used to expand tabs 00070 /// \return pair(printable text, 'true' iff original text was printable) 00071 /// 00072 static std::pair<SmallString<16>, bool> 00073 printableTextForNextCharacter(StringRef SourceLine, size_t *i, 00074 unsigned TabStop) { 00075 assert(i && "i must not be null"); 00076 assert(*i<SourceLine.size() && "must point to a valid index"); 00077 00078 if (SourceLine[*i]=='\t') { 00079 assert(0 < TabStop && TabStop <= DiagnosticOptions::MaxTabStop && 00080 "Invalid -ftabstop value"); 00081 unsigned col = bytesSincePreviousTabOrLineBegin(SourceLine, *i); 00082 unsigned NumSpaces = TabStop - col%TabStop; 00083 assert(0 < NumSpaces && NumSpaces <= TabStop 00084 && "Invalid computation of space amt"); 00085 ++(*i); 00086 00087 SmallString<16> expandedTab; 00088 expandedTab.assign(NumSpaces, ' '); 00089 return std::make_pair(expandedTab, true); 00090 } 00091 00092 // FIXME: this data is copied from the private implementation of ConvertUTF.h 00093 static const char trailingBytesForUTF8[256] = { 00094 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 00095 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 00096 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 00097 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 00098 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 00099 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 00100 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 00101 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 3,3,3,3,3,3,3,3,4,4,4,4,5,5,5,5 00102 }; 00103 00104 unsigned char const *begin, *end; 00105 begin = reinterpret_cast<unsigned char const *>(&*(SourceLine.begin() + *i)); 00106 end = begin + SourceLine.size(); 00107 00108 if (isLegalUTF8Sequence(begin, end)) { 00109 UTF32 c; 00110 UTF32 *cptr = &c; 00111 unsigned char const *original_begin = begin; 00112 char trailingBytes = trailingBytesForUTF8[(unsigned char)SourceLine[*i]]; 00113 unsigned char const *cp_end = begin+trailingBytes+1; 00114 00115 ConversionResult res = ConvertUTF8toUTF32(&begin, cp_end, &cptr, cptr+1, 00116 strictConversion); 00117 (void)res; 00118 assert(conversionOK==res); 00119 assert(0 < begin-original_begin 00120 && "we must be further along in the string now"); 00121 *i += begin-original_begin; 00122 00123 if (!llvm::sys::locale::isPrint(c)) { 00124 // If next character is valid UTF-8, but not printable 00125 SmallString<16> expandedCP("<U+>"); 00126 while (c) { 00127 expandedCP.insert(expandedCP.begin()+3, llvm::hexdigit(c%16)); 00128 c/=16; 00129 } 00130 while (expandedCP.size() < 8) 00131 expandedCP.insert(expandedCP.begin()+3, llvm::hexdigit(0)); 00132 return std::make_pair(expandedCP, false); 00133 } 00134 00135 // If next character is valid UTF-8, and printable 00136 return std::make_pair(SmallString<16>(original_begin, cp_end), true); 00137 00138 } 00139 00140 // If next byte is not valid UTF-8 (and therefore not printable) 00141 SmallString<16> expandedByte("<XX>"); 00142 unsigned char byte = SourceLine[*i]; 00143 expandedByte[1] = llvm::hexdigit(byte / 16); 00144 expandedByte[2] = llvm::hexdigit(byte % 16); 00145 ++(*i); 00146 return std::make_pair(expandedByte, false); 00147 } 00148 00149 static void expandTabs(std::string &SourceLine, unsigned TabStop) { 00150 size_t i = SourceLine.size(); 00151 while (i>0) { 00152 i--; 00153 if (SourceLine[i]!='\t') 00154 continue; 00155 size_t tmp_i = i; 00156 std::pair<SmallString<16>,bool> res 00157 = printableTextForNextCharacter(SourceLine, &tmp_i, TabStop); 00158 SourceLine.replace(i, 1, res.first.c_str()); 00159 } 00160 } 00161 00162 /// This function takes a raw source line and produces a mapping from the bytes 00163 /// of the printable representation of the line to the columns those printable 00164 /// characters will appear at (numbering the first column as 0). 00165 /// 00166 /// If a byte 'i' corresponds to muliple columns (e.g. the byte contains a tab 00167 /// character) then the the array will map that byte to the first column the 00168 /// tab appears at and the next value in the map will have been incremented 00169 /// more than once. 00170 /// 00171 /// If a byte is the first in a sequence of bytes that together map to a single 00172 /// entity in the output, then the array will map that byte to the appropriate 00173 /// column while the subsequent bytes will be -1. 00174 /// 00175 /// The last element in the array does not correspond to any byte in the input 00176 /// and instead is the number of columns needed to display the source 00177 /// 00178 /// example: (given a tabstop of 8) 00179 /// 00180 /// "a \t \u3042" -> {0,1,2,8,9,-1,-1,11} 00181 /// 00182 /// (\u3042 is represented in UTF-8 by three bytes and takes two columns to 00183 /// display) 00184 static void byteToColumn(StringRef SourceLine, unsigned TabStop, 00185 SmallVectorImpl<int> &out) { 00186 out.clear(); 00187 00188 if (SourceLine.empty()) { 00189 out.resize(1u,0); 00190 return; 00191 } 00192 00193 out.resize(SourceLine.size()+1, -1); 00194 00195 int columns = 0; 00196 size_t i = 0; 00197 while (i<SourceLine.size()) { 00198 out[i] = columns; 00199 std::pair<SmallString<16>,bool> res 00200 = printableTextForNextCharacter(SourceLine, &i, TabStop); 00201 columns += llvm::sys::locale::columnWidth(res.first); 00202 } 00203 out.back() = columns; 00204 } 00205 00206 /// This function takes a raw source line and produces a mapping from columns 00207 /// to the byte of the source line that produced the character displaying at 00208 /// that column. This is the inverse of the mapping produced by byteToColumn() 00209 /// 00210 /// The last element in the array is the number of bytes in the source string 00211 /// 00212 /// example: (given a tabstop of 8) 00213 /// 00214 /// "a \t \u3042" -> {0,1,2,-1,-1,-1,-1,-1,3,4,-1,7} 00215 /// 00216 /// (\u3042 is represented in UTF-8 by three bytes and takes two columns to 00217 /// display) 00218 static void columnToByte(StringRef SourceLine, unsigned TabStop, 00219 SmallVectorImpl<int> &out) { 00220 out.clear(); 00221 00222 if (SourceLine.empty()) { 00223 out.resize(1u, 0); 00224 return; 00225 } 00226 00227 int columns = 0; 00228 size_t i = 0; 00229 while (i<SourceLine.size()) { 00230 out.resize(columns+1, -1); 00231 out.back() = i; 00232 std::pair<SmallString<16>,bool> res 00233 = printableTextForNextCharacter(SourceLine, &i, TabStop); 00234 columns += llvm::sys::locale::columnWidth(res.first); 00235 } 00236 out.resize(columns+1, -1); 00237 out.back() = i; 00238 } 00239 00240 struct SourceColumnMap { 00241 SourceColumnMap(StringRef SourceLine, unsigned TabStop) 00242 : m_SourceLine(SourceLine) { 00243 00244 ::byteToColumn(SourceLine, TabStop, m_byteToColumn); 00245 ::columnToByte(SourceLine, TabStop, m_columnToByte); 00246 00247 assert(m_byteToColumn.size()==SourceLine.size()+1); 00248 assert(0 < m_byteToColumn.size() && 0 < m_columnToByte.size()); 00249 assert(m_byteToColumn.size() 00250 == static_cast<unsigned>(m_columnToByte.back()+1)); 00251 assert(static_cast<unsigned>(m_byteToColumn.back()+1) 00252 == m_columnToByte.size()); 00253 } 00254 int columns() const { return m_byteToColumn.back(); } 00255 int bytes() const { return m_columnToByte.back(); } 00256 int byteToColumn(int n) const { 00257 assert(0<=n && n<static_cast<int>(m_byteToColumn.size())); 00258 return m_byteToColumn[n]; 00259 } 00260 int columnToByte(int n) const { 00261 assert(0<=n && n<static_cast<int>(m_columnToByte.size())); 00262 return m_columnToByte[n]; 00263 } 00264 StringRef getSourceLine() const { 00265 return m_SourceLine; 00266 } 00267 00268 private: 00269 const std::string m_SourceLine; 00270 SmallVector<int,200> m_byteToColumn; 00271 SmallVector<int,200> m_columnToByte; 00272 }; 00273 00274 // used in assert in selectInterestingSourceRegion() 00275 namespace { 00276 struct char_out_of_range { 00277 const char lower,upper; 00278 char_out_of_range(char lower, char upper) : 00279 lower(lower), upper(upper) {} 00280 bool operator()(char c) { return c < lower || upper < c; } 00281 }; 00282 } 00283 00284 /// \brief When the source code line we want to print is too long for 00285 /// the terminal, select the "interesting" region. 00286 static void selectInterestingSourceRegion(std::string &SourceLine, 00287 std::string &CaretLine, 00288 std::string &FixItInsertionLine, 00289 unsigned Columns, 00290 const SourceColumnMap &map) { 00291 unsigned MaxColumns = std::max<unsigned>(map.columns(), 00292 std::max(CaretLine.size(), 00293 FixItInsertionLine.size())); 00294 // if the number of columns is less than the desired number we're done 00295 if (MaxColumns <= Columns) 00296 return; 00297 00298 // no special characters allowed in CaretLine or FixItInsertionLine 00299 assert(CaretLine.end() == 00300 std::find_if(CaretLine.begin(), CaretLine.end(), 00301 char_out_of_range(' ','~'))); 00302 assert(FixItInsertionLine.end() == 00303 std::find_if(FixItInsertionLine.begin(), FixItInsertionLine.end(), 00304 char_out_of_range(' ','~'))); 00305 00306 // Find the slice that we need to display the full caret line 00307 // correctly. 00308 unsigned CaretStart = 0, CaretEnd = CaretLine.size(); 00309 for (; CaretStart != CaretEnd; ++CaretStart) 00310 if (!isspace(CaretLine[CaretStart])) 00311 break; 00312 00313 for (; CaretEnd != CaretStart; --CaretEnd) 00314 if (!isspace(CaretLine[CaretEnd - 1])) 00315 break; 00316 00317 // caret has already been inserted into CaretLine so the above whitespace 00318 // check is guaranteed to include the caret 00319 00320 // If we have a fix-it line, make sure the slice includes all of the 00321 // fix-it information. 00322 if (!FixItInsertionLine.empty()) { 00323 unsigned FixItStart = 0, FixItEnd = FixItInsertionLine.size(); 00324 for (; FixItStart != FixItEnd; ++FixItStart) 00325 if (!isspace(FixItInsertionLine[FixItStart])) 00326 break; 00327 00328 for (; FixItEnd != FixItStart; --FixItEnd) 00329 if (!isspace(FixItInsertionLine[FixItEnd - 1])) 00330 break; 00331 00332 CaretStart = std::min(FixItStart, CaretStart); 00333 CaretEnd = std::max(FixItEnd, CaretEnd); 00334 } 00335 00336 // CaretLine[CaretStart, CaretEnd) contains all of the interesting 00337 // parts of the caret line. While this slice is smaller than the 00338 // number of columns we have, try to grow the slice to encompass 00339 // more context. 00340 00341 unsigned SourceStart = map.columnToByte(std::min<unsigned>(CaretStart, 00342 map.columns())); 00343 unsigned SourceEnd = map.columnToByte(std::min<unsigned>(CaretEnd, 00344 map.columns())); 00345 00346 unsigned CaretColumnsOutsideSource = CaretEnd-CaretStart 00347 - (map.byteToColumn(SourceEnd)-map.byteToColumn(SourceStart)); 00348 00349 char const *front_ellipse = " ..."; 00350 char const *front_space = " "; 00351 char const *back_ellipse = "..."; 00352 unsigned ellipses_space = strlen(front_ellipse) + strlen(back_ellipse); 00353 00354 unsigned TargetColumns = Columns; 00355 // Give us extra room for the ellipses 00356 // and any of the caret line that extends past the source 00357 if (TargetColumns > ellipses_space+CaretColumnsOutsideSource) 00358 TargetColumns -= ellipses_space+CaretColumnsOutsideSource; 00359 00360 while (SourceStart>0 || SourceEnd<SourceLine.size()) { 00361 bool ExpandedRegion = false; 00362 00363 if (SourceStart>0) { 00364 unsigned NewStart = SourceStart-1; 00365 00366 // Skip over any whitespace we see here; we're looking for 00367 // another bit of interesting text. 00368 while (NewStart && 00369 (map.byteToColumn(NewStart)==-1 || isspace(SourceLine[NewStart]))) 00370 --NewStart; 00371 00372 // Skip over this bit of "interesting" text. 00373 while (NewStart && 00374 (map.byteToColumn(NewStart)!=-1 && !isspace(SourceLine[NewStart]))) 00375 --NewStart; 00376 00377 // Move up to the non-whitespace character we just saw. 00378 if (NewStart) 00379 ++NewStart; 00380 00381 unsigned NewColumns = map.byteToColumn(SourceEnd) - 00382 map.byteToColumn(NewStart); 00383 if (NewColumns <= TargetColumns) { 00384 SourceStart = NewStart; 00385 ExpandedRegion = true; 00386 } 00387 } 00388 00389 if (SourceEnd<SourceLine.size()) { 00390 unsigned NewEnd = SourceEnd+1; 00391 00392 // Skip over any whitespace we see here; we're looking for 00393 // another bit of interesting text. 00394 while (NewEnd<SourceLine.size() && 00395 (map.byteToColumn(NewEnd)==-1 || isspace(SourceLine[NewEnd]))) 00396 ++NewEnd; 00397 00398 // Skip over this bit of "interesting" text. 00399 while (NewEnd<SourceLine.size() && 00400 (map.byteToColumn(NewEnd)!=-1 && !isspace(SourceLine[NewEnd]))) 00401 ++NewEnd; 00402 00403 unsigned NewColumns = map.byteToColumn(NewEnd) - 00404 map.byteToColumn(SourceStart); 00405 if (NewColumns <= TargetColumns) { 00406 SourceEnd = NewEnd; 00407 ExpandedRegion = true; 00408 } 00409 } 00410 00411 if (!ExpandedRegion) 00412 break; 00413 } 00414 00415 CaretStart = map.byteToColumn(SourceStart); 00416 CaretEnd = map.byteToColumn(SourceEnd) + CaretColumnsOutsideSource; 00417 00418 // [CaretStart, CaretEnd) is the slice we want. Update the various 00419 // output lines to show only this slice, with two-space padding 00420 // before the lines so that it looks nicer. 00421 00422 assert(CaretStart!=(unsigned)-1 && CaretEnd!=(unsigned)-1 && 00423 SourceStart!=(unsigned)-1 && SourceEnd!=(unsigned)-1); 00424 assert(SourceStart <= SourceEnd); 00425 assert(CaretStart <= CaretEnd); 00426 00427 unsigned BackColumnsRemoved 00428 = map.byteToColumn(SourceLine.size())-map.byteToColumn(SourceEnd); 00429 unsigned FrontColumnsRemoved = CaretStart; 00430 unsigned ColumnsKept = CaretEnd-CaretStart; 00431 00432 // We checked up front that the line needed truncation 00433 assert(FrontColumnsRemoved+ColumnsKept+BackColumnsRemoved > Columns); 00434 00435 // The line needs some trunctiona, and we'd prefer to keep the front 00436 // if possible, so remove the back 00437 if (BackColumnsRemoved) 00438 SourceLine.replace(SourceEnd, std::string::npos, back_ellipse); 00439 00440 // If that's enough then we're done 00441 if (FrontColumnsRemoved+ColumnsKept <= Columns) 00442 return; 00443 00444 // Otherwise remove the front as well 00445 if (FrontColumnsRemoved) { 00446 SourceLine.replace(0, SourceStart, front_ellipse); 00447 CaretLine.replace(0, CaretStart, front_space); 00448 if (!FixItInsertionLine.empty()) 00449 FixItInsertionLine.replace(0, CaretStart, front_space); 00450 } 00451 } 00452 00453 /// \brief Skip over whitespace in the string, starting at the given 00454 /// index. 00455 /// 00456 /// \returns The index of the first non-whitespace character that is 00457 /// greater than or equal to Idx or, if no such character exists, 00458 /// returns the end of the string. 00459 static unsigned skipWhitespace(unsigned Idx, StringRef Str, unsigned Length) { 00460 while (Idx < Length && isspace(Str[Idx])) 00461 ++Idx; 00462 return Idx; 00463 } 00464 00465 /// \brief If the given character is the start of some kind of 00466 /// balanced punctuation (e.g., quotes or parentheses), return the 00467 /// character that will terminate the punctuation. 00468 /// 00469 /// \returns The ending punctuation character, if any, or the NULL 00470 /// character if the input character does not start any punctuation. 00471 static inline char findMatchingPunctuation(char c) { 00472 switch (c) { 00473 case '\'': return '\''; 00474 case '`': return '\''; 00475 case '"': return '"'; 00476 case '(': return ')'; 00477 case '[': return ']'; 00478 case '{': return '}'; 00479 default: break; 00480 } 00481 00482 return 0; 00483 } 00484 00485 /// \brief Find the end of the word starting at the given offset 00486 /// within a string. 00487 /// 00488 /// \returns the index pointing one character past the end of the 00489 /// word. 00490 static unsigned findEndOfWord(unsigned Start, StringRef Str, 00491 unsigned Length, unsigned Column, 00492 unsigned Columns) { 00493 assert(Start < Str.size() && "Invalid start position!"); 00494 unsigned End = Start + 1; 00495 00496 // If we are already at the end of the string, take that as the word. 00497 if (End == Str.size()) 00498 return End; 00499 00500 // Determine if the start of the string is actually opening 00501 // punctuation, e.g., a quote or parentheses. 00502 char EndPunct = findMatchingPunctuation(Str[Start]); 00503 if (!EndPunct) { 00504 // This is a normal word. Just find the first space character. 00505 while (End < Length && !isspace(Str[End])) 00506 ++End; 00507 return End; 00508 } 00509 00510 // We have the start of a balanced punctuation sequence (quotes, 00511 // parentheses, etc.). Determine the full sequence is. 00512 SmallString<16> PunctuationEndStack; 00513 PunctuationEndStack.push_back(EndPunct); 00514 while (End < Length && !PunctuationEndStack.empty()) { 00515 if (Str[End] == PunctuationEndStack.back()) 00516 PunctuationEndStack.pop_back(); 00517 else if (char SubEndPunct = findMatchingPunctuation(Str[End])) 00518 PunctuationEndStack.push_back(SubEndPunct); 00519 00520 ++End; 00521 } 00522 00523 // Find the first space character after the punctuation ended. 00524 while (End < Length && !isspace(Str[End])) 00525 ++End; 00526 00527 unsigned PunctWordLength = End - Start; 00528 if (// If the word fits on this line 00529 Column + PunctWordLength <= Columns || 00530 // ... or the word is "short enough" to take up the next line 00531 // without too much ugly white space 00532 PunctWordLength < Columns/3) 00533 return End; // Take the whole thing as a single "word". 00534 00535 // The whole quoted/parenthesized string is too long to print as a 00536 // single "word". Instead, find the "word" that starts just after 00537 // the punctuation and use that end-point instead. This will recurse 00538 // until it finds something small enough to consider a word. 00539 return findEndOfWord(Start + 1, Str, Length, Column + 1, Columns); 00540 } 00541 00542 /// \brief Print the given string to a stream, word-wrapping it to 00543 /// some number of columns in the process. 00544 /// 00545 /// \param OS the stream to which the word-wrapping string will be 00546 /// emitted. 00547 /// \param Str the string to word-wrap and output. 00548 /// \param Columns the number of columns to word-wrap to. 00549 /// \param Column the column number at which the first character of \p 00550 /// Str will be printed. This will be non-zero when part of the first 00551 /// line has already been printed. 00552 /// \param Indentation the number of spaces to indent any lines beyond 00553 /// the first line. 00554 /// \returns true if word-wrapping was required, or false if the 00555 /// string fit on the first line. 00556 static bool printWordWrapped(raw_ostream &OS, StringRef Str, 00557 unsigned Columns, 00558 unsigned Column = 0, 00559 unsigned Indentation = WordWrapIndentation) { 00560 const unsigned Length = std::min(Str.find('\n'), Str.size()); 00561 00562 // The string used to indent each line. 00563 SmallString<16> IndentStr; 00564 IndentStr.assign(Indentation, ' '); 00565 bool Wrapped = false; 00566 for (unsigned WordStart = 0, WordEnd; WordStart < Length; 00567 WordStart = WordEnd) { 00568 // Find the beginning of the next word. 00569 WordStart = skipWhitespace(WordStart, Str, Length); 00570 if (WordStart == Length) 00571 break; 00572 00573 // Find the end of this word. 00574 WordEnd = findEndOfWord(WordStart, Str, Length, Column, Columns); 00575 00576 // Does this word fit on the current line? 00577 unsigned WordLength = WordEnd - WordStart; 00578 if (Column + WordLength < Columns) { 00579 // This word fits on the current line; print it there. 00580 if (WordStart) { 00581 OS << ' '; 00582 Column += 1; 00583 } 00584 OS << Str.substr(WordStart, WordLength); 00585 Column += WordLength; 00586 continue; 00587 } 00588 00589 // This word does not fit on the current line, so wrap to the next 00590 // line. 00591 OS << '\n'; 00592 OS.write(&IndentStr[0], Indentation); 00593 OS << Str.substr(WordStart, WordLength); 00594 Column = Indentation + WordLength; 00595 Wrapped = true; 00596 } 00597 00598 // Append any remaning text from the message with its existing formatting. 00599 OS << Str.substr(Length); 00600 00601 return Wrapped; 00602 } 00603 00604 TextDiagnostic::TextDiagnostic(raw_ostream &OS, 00605 const LangOptions &LangOpts, 00606 const DiagnosticOptions &DiagOpts) 00607 : DiagnosticRenderer(LangOpts, DiagOpts), OS(OS) {} 00608 00609 TextDiagnostic::~TextDiagnostic() {} 00610 00611 void 00612 TextDiagnostic::emitDiagnosticMessage(SourceLocation Loc, 00613 PresumedLoc PLoc, 00614 DiagnosticsEngine::Level Level, 00615 StringRef Message, 00616 ArrayRef<clang::CharSourceRange> Ranges, 00617 const SourceManager *SM, 00618 DiagOrStoredDiag D) { 00619 uint64_t StartOfLocationInfo = OS.tell(); 00620 00621 // Emit the location of this particular diagnostic. 00622 if (Loc.isValid()) 00623 emitDiagnosticLoc(Loc, PLoc, Level, Ranges, *SM); 00624 00625 if (DiagOpts.ShowColors) 00626 OS.resetColor(); 00627 00628 printDiagnosticLevel(OS, Level, DiagOpts.ShowColors); 00629 printDiagnosticMessage(OS, Level, Message, 00630 OS.tell() - StartOfLocationInfo, 00631 DiagOpts.MessageLength, DiagOpts.ShowColors); 00632 } 00633 00634 /*static*/ void 00635 TextDiagnostic::printDiagnosticLevel(raw_ostream &OS, 00636 DiagnosticsEngine::Level Level, 00637 bool ShowColors) { 00638 if (ShowColors) { 00639 // Print diagnostic category in bold and color 00640 switch (Level) { 00641 case DiagnosticsEngine::Ignored: 00642 llvm_unreachable("Invalid diagnostic type"); 00643 case DiagnosticsEngine::Note: OS.changeColor(noteColor, true); break; 00644 case DiagnosticsEngine::Warning: OS.changeColor(warningColor, true); break; 00645 case DiagnosticsEngine::Error: OS.changeColor(errorColor, true); break; 00646 case DiagnosticsEngine::Fatal: OS.changeColor(fatalColor, true); break; 00647 } 00648 } 00649 00650 switch (Level) { 00651 case DiagnosticsEngine::Ignored: 00652 llvm_unreachable("Invalid diagnostic type"); 00653 case DiagnosticsEngine::Note: OS << "note: "; break; 00654 case DiagnosticsEngine::Warning: OS << "warning: "; break; 00655 case DiagnosticsEngine::Error: OS << "error: "; break; 00656 case DiagnosticsEngine::Fatal: OS << "fatal error: "; break; 00657 } 00658 00659 if (ShowColors) 00660 OS.resetColor(); 00661 } 00662 00663 /*static*/ void 00664 TextDiagnostic::printDiagnosticMessage(raw_ostream &OS, 00665 DiagnosticsEngine::Level Level, 00666 StringRef Message, 00667 unsigned CurrentColumn, unsigned Columns, 00668 bool ShowColors) { 00669 if (ShowColors) { 00670 // Print warnings, errors and fatal errors in bold, no color 00671 switch (Level) { 00672 case DiagnosticsEngine::Warning: OS.changeColor(savedColor, true); break; 00673 case DiagnosticsEngine::Error: OS.changeColor(savedColor, true); break; 00674 case DiagnosticsEngine::Fatal: OS.changeColor(savedColor, true); break; 00675 default: break; //don't bold notes 00676 } 00677 } 00678 00679 if (Columns) 00680 printWordWrapped(OS, Message, Columns, CurrentColumn); 00681 else 00682 OS << Message; 00683 00684 if (ShowColors) 00685 OS.resetColor(); 00686 OS << '\n'; 00687 } 00688 00689 /// \brief Print out the file/line/column information and include trace. 00690 /// 00691 /// This method handlen the emission of the diagnostic location information. 00692 /// This includes extracting as much location information as is present for 00693 /// the diagnostic and printing it, as well as any include stack or source 00694 /// ranges necessary. 00695 void TextDiagnostic::emitDiagnosticLoc(SourceLocation Loc, PresumedLoc PLoc, 00696 DiagnosticsEngine::Level Level, 00697 ArrayRef<CharSourceRange> Ranges, 00698 const SourceManager &SM) { 00699 if (PLoc.isInvalid()) { 00700 // At least print the file name if available: 00701 FileID FID = SM.getFileID(Loc); 00702 if (!FID.isInvalid()) { 00703 const FileEntry* FE = SM.getFileEntryForID(FID); 00704 if (FE && FE->getName()) { 00705 OS << FE->getName(); 00706 if (FE->getDevice() == 0 && FE->getInode() == 0 00707 && FE->getFileMode() == 0) { 00708 // in PCH is a guess, but a good one: 00709 OS << " (in PCH)"; 00710 } 00711 OS << ": "; 00712 } 00713 } 00714 return; 00715 } 00716 unsigned LineNo = PLoc.getLine(); 00717 00718 if (!DiagOpts.ShowLocation) 00719 return; 00720 00721 if (DiagOpts.ShowColors) 00722 OS.changeColor(savedColor, true); 00723 00724 OS << PLoc.getFilename(); 00725 switch (DiagOpts.Format) { 00726 case DiagnosticOptions::Clang: OS << ':' << LineNo; break; 00727 case DiagnosticOptions::Msvc: OS << '(' << LineNo; break; 00728 case DiagnosticOptions::Vi: OS << " +" << LineNo; break; 00729 } 00730 00731 if (DiagOpts.ShowColumn) 00732 // Compute the column number. 00733 if (unsigned ColNo = PLoc.getColumn()) { 00734 if (DiagOpts.Format == DiagnosticOptions::Msvc) { 00735 OS << ','; 00736 ColNo--; 00737 } else 00738 OS << ':'; 00739 OS << ColNo; 00740 } 00741 switch (DiagOpts.Format) { 00742 case DiagnosticOptions::Clang: 00743 case DiagnosticOptions::Vi: OS << ':'; break; 00744 case DiagnosticOptions::Msvc: OS << ") : "; break; 00745 } 00746 00747 if (DiagOpts.ShowSourceRanges && !Ranges.empty()) { 00748 FileID CaretFileID = 00749 SM.getFileID(SM.getExpansionLoc(Loc)); 00750 bool PrintedRange = false; 00751 00752 for (ArrayRef<CharSourceRange>::const_iterator RI = Ranges.begin(), 00753 RE = Ranges.end(); 00754 RI != RE; ++RI) { 00755 // Ignore invalid ranges. 00756 if (!RI->isValid()) continue; 00757 00758 SourceLocation B = SM.getExpansionLoc(RI->getBegin()); 00759 SourceLocation E = SM.getExpansionLoc(RI->getEnd()); 00760 00761 // If the End location and the start location are the same and are a 00762 // macro location, then the range was something that came from a 00763 // macro expansion or _Pragma. If this is an object-like macro, the 00764 // best we can do is to highlight the range. If this is a 00765 // function-like macro, we'd also like to highlight the arguments. 00766 if (B == E && RI->getEnd().isMacroID()) 00767 E = SM.getExpansionRange(RI->getEnd()).second; 00768 00769 std::pair<FileID, unsigned> BInfo = SM.getDecomposedLoc(B); 00770 std::pair<FileID, unsigned> EInfo = SM.getDecomposedLoc(E); 00771 00772 // If the start or end of the range is in another file, just discard 00773 // it. 00774 if (BInfo.first != CaretFileID || EInfo.first != CaretFileID) 00775 continue; 00776 00777 // Add in the length of the token, so that we cover multi-char 00778 // tokens. 00779 unsigned TokSize = 0; 00780 if (RI->isTokenRange()) 00781 TokSize = Lexer::MeasureTokenLength(E, SM, LangOpts); 00782 00783 OS << '{' << SM.getLineNumber(BInfo.first, BInfo.second) << ':' 00784 << SM.getColumnNumber(BInfo.first, BInfo.second) << '-' 00785 << SM.getLineNumber(EInfo.first, EInfo.second) << ':' 00786 << (SM.getColumnNumber(EInfo.first, EInfo.second)+TokSize) 00787 << '}'; 00788 PrintedRange = true; 00789 } 00790 00791 if (PrintedRange) 00792 OS << ':'; 00793 } 00794 OS << ' '; 00795 } 00796 00797 void TextDiagnostic::emitBasicNote(StringRef Message) { 00798 // FIXME: Emit this as a real note diagnostic. 00799 // FIXME: Format an actual diagnostic rather than a hard coded string. 00800 OS << "note: " << Message << "\n"; 00801 } 00802 00803 void TextDiagnostic::emitIncludeLocation(SourceLocation Loc, 00804 PresumedLoc PLoc, 00805 const SourceManager &SM) { 00806 if (DiagOpts.ShowLocation) 00807 OS << "In file included from " << PLoc.getFilename() << ':' 00808 << PLoc.getLine() << ":\n"; 00809 else 00810 OS << "In included file:\n"; 00811 } 00812 00813 /// \brief Emit a code snippet and caret line. 00814 /// 00815 /// This routine emits a single line's code snippet and caret line.. 00816 /// 00817 /// \param Loc The location for the caret. 00818 /// \param Ranges The underlined ranges for this code snippet. 00819 /// \param Hints The FixIt hints active for this diagnostic. 00820 void TextDiagnostic::emitSnippetAndCaret( 00821 SourceLocation Loc, DiagnosticsEngine::Level Level, 00822 SmallVectorImpl<CharSourceRange>& Ranges, 00823 ArrayRef<FixItHint> Hints, 00824 const SourceManager &SM) { 00825 assert(!Loc.isInvalid() && "must have a valid source location here"); 00826 assert(Loc.isFileID() && "must have a file location here"); 00827 00828 // If caret diagnostics are enabled and we have location, we want to 00829 // emit the caret. However, we only do this if the location moved 00830 // from the last diagnostic, if the last diagnostic was a note that 00831 // was part of a different warning or error diagnostic, or if the 00832 // diagnostic has ranges. We don't want to emit the same caret 00833 // multiple times if one loc has multiple diagnostics. 00834 if (!DiagOpts.ShowCarets) 00835 return; 00836 if (Loc == LastLoc && Ranges.empty() && Hints.empty() && 00837 (LastLevel != DiagnosticsEngine::Note || Level == LastLevel)) 00838 return; 00839 00840 // Decompose the location into a FID/Offset pair. 00841 std::pair<FileID, unsigned> LocInfo = SM.getDecomposedLoc(Loc); 00842 FileID FID = LocInfo.first; 00843 unsigned FileOffset = LocInfo.second; 00844 00845 // Get information about the buffer it points into. 00846 bool Invalid = false; 00847 const char *BufStart = SM.getBufferData(FID, &Invalid).data(); 00848 if (Invalid) 00849 return; 00850 00851 unsigned LineNo = SM.getLineNumber(FID, FileOffset); 00852 unsigned ColNo = SM.getColumnNumber(FID, FileOffset); 00853 unsigned CaretEndColNo 00854 = ColNo + Lexer::MeasureTokenLength(Loc, SM, LangOpts); 00855 00856 // Rewind from the current position to the start of the line. 00857 const char *TokPtr = BufStart+FileOffset; 00858 const char *LineStart = TokPtr-ColNo+1; // Column # is 1-based. 00859 00860 00861 // Compute the line end. Scan forward from the error position to the end of 00862 // the line. 00863 const char *LineEnd = TokPtr; 00864 while (*LineEnd != '\n' && *LineEnd != '\r' && *LineEnd != '\0') 00865 ++LineEnd; 00866 00867 // FIXME: This shouldn't be necessary, but the CaretEndColNo can extend past 00868 // the source line length as currently being computed. See 00869 // test/Misc/message-length.c. 00870 CaretEndColNo = std::min(CaretEndColNo, unsigned(LineEnd - LineStart)); 00871 00872 // Copy the line of code into an std::string for ease of manipulation. 00873 std::string SourceLine(LineStart, LineEnd); 00874 00875 // Create a line for the caret that is filled with spaces that is the same 00876 // length as the line of source code. 00877 std::string CaretLine(LineEnd-LineStart, ' '); 00878 00879 const SourceColumnMap sourceColMap(SourceLine, DiagOpts.TabStop); 00880 00881 // Highlight all of the characters covered by Ranges with ~ characters. 00882 for (SmallVectorImpl<CharSourceRange>::iterator I = Ranges.begin(), 00883 E = Ranges.end(); 00884 I != E; ++I) 00885 highlightRange(*I, LineNo, FID, sourceColMap, CaretLine, SM); 00886 00887 // Next, insert the caret itself. 00888 ColNo = sourceColMap.byteToColumn(ColNo-1); 00889 if (CaretLine.size()<ColNo+1) 00890 CaretLine.resize(ColNo+1, ' '); 00891 CaretLine[ColNo] = '^'; 00892 00893 std::string FixItInsertionLine = buildFixItInsertionLine(LineNo, 00894 sourceColMap, 00895 Hints, SM); 00896 00897 // If the source line is too long for our terminal, select only the 00898 // "interesting" source region within that line. 00899 unsigned Columns = DiagOpts.MessageLength; 00900 if (Columns) 00901 selectInterestingSourceRegion(SourceLine, CaretLine, FixItInsertionLine, 00902 Columns, sourceColMap); 00903 00904 // If we are in -fdiagnostics-print-source-range-info mode, we are trying 00905 // to produce easily machine parsable output. Add a space before the 00906 // source line and the caret to make it trivial to tell the main diagnostic 00907 // line from what the user is intended to see. 00908 if (DiagOpts.ShowSourceRanges) { 00909 SourceLine = ' ' + SourceLine; 00910 CaretLine = ' ' + CaretLine; 00911 } 00912 00913 // Finally, remove any blank spaces from the end of CaretLine. 00914 while (CaretLine[CaretLine.size()-1] == ' ') 00915 CaretLine.erase(CaretLine.end()-1); 00916 00917 // Emit what we have computed. 00918 emitSnippet(SourceLine); 00919 00920 if (DiagOpts.ShowColors) 00921 OS.changeColor(caretColor, true); 00922 OS << CaretLine << '\n'; 00923 if (DiagOpts.ShowColors) 00924 OS.resetColor(); 00925 00926 if (!FixItInsertionLine.empty()) { 00927 if (DiagOpts.ShowColors) 00928 // Print fixit line in color 00929 OS.changeColor(fixitColor, false); 00930 if (DiagOpts.ShowSourceRanges) 00931 OS << ' '; 00932 OS << FixItInsertionLine << '\n'; 00933 if (DiagOpts.ShowColors) 00934 OS.resetColor(); 00935 } 00936 00937 // Print out any parseable fixit information requested by the options. 00938 emitParseableFixits(Hints, SM); 00939 } 00940 00941 void TextDiagnostic::emitSnippet(StringRef line) { 00942 if (line.empty()) 00943 return; 00944 00945 size_t i = 0; 00946 00947 std::string to_print; 00948 bool print_reversed = false; 00949 00950 while (i<line.size()) { 00951 std::pair<SmallString<16>,bool> res 00952 = printableTextForNextCharacter(line, &i, DiagOpts.TabStop); 00953 bool was_printable = res.second; 00954 00955 if (DiagOpts.ShowColors && was_printable == print_reversed) { 00956 if (print_reversed) 00957 OS.reverseColor(); 00958 OS << to_print; 00959 to_print.clear(); 00960 if (DiagOpts.ShowColors) 00961 OS.resetColor(); 00962 } 00963 00964 print_reversed = !was_printable; 00965 to_print += res.first.str(); 00966 } 00967 00968 if (print_reversed && DiagOpts.ShowColors) 00969 OS.reverseColor(); 00970 OS << to_print; 00971 if (print_reversed && DiagOpts.ShowColors) 00972 OS.resetColor(); 00973 00974 OS << '\n'; 00975 } 00976 00977 /// \brief Highlight a SourceRange (with ~'s) for any characters on LineNo. 00978 void TextDiagnostic::highlightRange(const CharSourceRange &R, 00979 unsigned LineNo, FileID FID, 00980 const SourceColumnMap &map, 00981 std::string &CaretLine, 00982 const SourceManager &SM) { 00983 if (!R.isValid()) return; 00984 00985 SourceLocation Begin = SM.getExpansionLoc(R.getBegin()); 00986 SourceLocation End = SM.getExpansionLoc(R.getEnd()); 00987 00988 // If the End location and the start location are the same and are a macro 00989 // location, then the range was something that came from a macro expansion 00990 // or _Pragma. If this is an object-like macro, the best we can do is to 00991 // highlight the range. If this is a function-like macro, we'd also like to 00992 // highlight the arguments. 00993 if (Begin == End && R.getEnd().isMacroID()) 00994 End = SM.getExpansionRange(R.getEnd()).second; 00995 00996 unsigned StartLineNo = SM.getExpansionLineNumber(Begin); 00997 if (StartLineNo > LineNo || SM.getFileID(Begin) != FID) 00998 return; // No intersection. 00999 01000 unsigned EndLineNo = SM.getExpansionLineNumber(End); 01001 if (EndLineNo < LineNo || SM.getFileID(End) != FID) 01002 return; // No intersection. 01003 01004 // Compute the column number of the start. 01005 unsigned StartColNo = 0; 01006 if (StartLineNo == LineNo) { 01007 StartColNo = SM.getExpansionColumnNumber(Begin); 01008 if (StartColNo) --StartColNo; // Zero base the col #. 01009 } 01010 01011 // Compute the column number of the end. 01012 unsigned EndColNo = map.getSourceLine().size(); 01013 if (EndLineNo == LineNo) { 01014 EndColNo = SM.getExpansionColumnNumber(End); 01015 if (EndColNo) { 01016 --EndColNo; // Zero base the col #. 01017 01018 // Add in the length of the token, so that we cover multi-char tokens if 01019 // this is a token range. 01020 if (R.isTokenRange()) 01021 EndColNo += Lexer::MeasureTokenLength(End, SM, LangOpts); 01022 } else { 01023 EndColNo = CaretLine.size(); 01024 } 01025 } 01026 01027 assert(StartColNo <= EndColNo && "Invalid range!"); 01028 01029 // Check that a token range does not highlight only whitespace. 01030 if (R.isTokenRange()) { 01031 // Pick the first non-whitespace column. 01032 while (StartColNo < map.getSourceLine().size() && 01033 (map.getSourceLine()[StartColNo] == ' ' || 01034 map.getSourceLine()[StartColNo] == '\t')) 01035 ++StartColNo; 01036 01037 // Pick the last non-whitespace column. 01038 if (EndColNo > map.getSourceLine().size()) 01039 EndColNo = map.getSourceLine().size(); 01040 while (EndColNo-1 && 01041 (map.getSourceLine()[EndColNo-1] == ' ' || 01042 map.getSourceLine()[EndColNo-1] == '\t')) 01043 --EndColNo; 01044 01045 // If the start/end passed each other, then we are trying to highlight a 01046 // range that just exists in whitespace, which must be some sort of other 01047 // bug. 01048 assert(StartColNo <= EndColNo && "Trying to highlight whitespace??"); 01049 } 01050 01051 assert(StartColNo <= map.getSourceLine().size() && "Invalid range!"); 01052 assert(EndColNo <= map.getSourceLine().size() && "Invalid range!"); 01053 01054 // Fill the range with ~'s. 01055 StartColNo = map.byteToColumn(StartColNo); 01056 EndColNo = map.byteToColumn(EndColNo); 01057 01058 assert(StartColNo <= EndColNo && "Invalid range!"); 01059 if (CaretLine.size() < EndColNo) 01060 CaretLine.resize(EndColNo,' '); 01061 std::fill(CaretLine.begin()+StartColNo,CaretLine.begin()+EndColNo,'~'); 01062 } 01063 01064 std::string TextDiagnostic::buildFixItInsertionLine( 01065 unsigned LineNo, 01066 const SourceColumnMap &map, 01067 ArrayRef<FixItHint> Hints, 01068 const SourceManager &SM) { 01069 01070 std::string FixItInsertionLine; 01071 if (Hints.empty() || !DiagOpts.ShowFixits) 01072 return FixItInsertionLine; 01073 01074 for (ArrayRef<FixItHint>::iterator I = Hints.begin(), E = Hints.end(); 01075 I != E; ++I) { 01076 if (!I->CodeToInsert.empty()) { 01077 // We have an insertion hint. Determine whether the inserted 01078 // code is on the same line as the caret. 01079 std::pair<FileID, unsigned> HintLocInfo 01080 = SM.getDecomposedExpansionLoc(I->RemoveRange.getBegin()); 01081 if (LineNo == SM.getLineNumber(HintLocInfo.first, HintLocInfo.second)) { 01082 // Insert the new code into the line just below the code 01083 // that the user wrote. 01084 unsigned HintColNo 01085 = SM.getColumnNumber(HintLocInfo.first, HintLocInfo.second) - 1; 01086 // hint must start inside the source or right at the end 01087 assert(HintColNo<static_cast<unsigned>(map.bytes())+1); 01088 HintColNo = map.byteToColumn(HintColNo); 01089 01090 // FIXME: if the fixit includes tabs or other characters that do not 01091 // take up a single column per byte when displayed then 01092 // I->CodeToInsert.size() is not a column number and we're mixing 01093 // units (columns + bytes). We should get printable versions 01094 // of each fixit before using them. 01095 unsigned LastColumnModified 01096 = HintColNo + I->CodeToInsert.size(); 01097 01098 if (LastColumnModified > static_cast<unsigned>(map.bytes())) { 01099 unsigned LastExistingColumn = map.byteToColumn(map.bytes()); 01100 unsigned AddedColumns = LastColumnModified-LastExistingColumn; 01101 LastColumnModified = LastExistingColumn + AddedColumns; 01102 } else { 01103 LastColumnModified = map.byteToColumn(LastColumnModified); 01104 } 01105 01106 if (LastColumnModified > FixItInsertionLine.size()) 01107 FixItInsertionLine.resize(LastColumnModified, ' '); 01108 assert(HintColNo+I->CodeToInsert.size() <= FixItInsertionLine.size()); 01109 std::copy(I->CodeToInsert.begin(), I->CodeToInsert.end(), 01110 FixItInsertionLine.begin() + HintColNo); 01111 } else { 01112 FixItInsertionLine.clear(); 01113 break; 01114 } 01115 } 01116 } 01117 01118 expandTabs(FixItInsertionLine, DiagOpts.TabStop); 01119 01120 return FixItInsertionLine; 01121 } 01122 01123 void TextDiagnostic::emitParseableFixits(ArrayRef<FixItHint> Hints, 01124 const SourceManager &SM) { 01125 if (!DiagOpts.ShowParseableFixits) 01126 return; 01127 01128 // We follow FixItRewriter's example in not (yet) handling 01129 // fix-its in macros. 01130 for (ArrayRef<FixItHint>::iterator I = Hints.begin(), E = Hints.end(); 01131 I != E; ++I) { 01132 if (I->RemoveRange.isInvalid() || 01133 I->RemoveRange.getBegin().isMacroID() || 01134 I->RemoveRange.getEnd().isMacroID()) 01135 return; 01136 } 01137 01138 for (ArrayRef<FixItHint>::iterator I = Hints.begin(), E = Hints.end(); 01139 I != E; ++I) { 01140 SourceLocation BLoc = I->RemoveRange.getBegin(); 01141 SourceLocation ELoc = I->RemoveRange.getEnd(); 01142 01143 std::pair<FileID, unsigned> BInfo = SM.getDecomposedLoc(BLoc); 01144 std::pair<FileID, unsigned> EInfo = SM.getDecomposedLoc(ELoc); 01145 01146 // Adjust for token ranges. 01147 if (I->RemoveRange.isTokenRange()) 01148 EInfo.second += Lexer::MeasureTokenLength(ELoc, SM, LangOpts); 01149 01150 // We specifically do not do word-wrapping or tab-expansion here, 01151 // because this is supposed to be easy to parse. 01152 PresumedLoc PLoc = SM.getPresumedLoc(BLoc); 01153 if (PLoc.isInvalid()) 01154 break; 01155 01156 OS << "fix-it:\""; 01157 OS.write_escaped(PLoc.getFilename()); 01158 OS << "\":{" << SM.getLineNumber(BInfo.first, BInfo.second) 01159 << ':' << SM.getColumnNumber(BInfo.first, BInfo.second) 01160 << '-' << SM.getLineNumber(EInfo.first, EInfo.second) 01161 << ':' << SM.getColumnNumber(EInfo.first, EInfo.second) 01162 << "}:\""; 01163 OS.write_escaped(I->CodeToInsert); 01164 OS << "\"\n"; 01165 } 01166 }