1234287Sdim//===--- TextDiagnostic.cpp - Text Diagnostic Pretty-Printing -------------===// 2234287Sdim// 3234287Sdim// The LLVM Compiler Infrastructure 4234287Sdim// 5234287Sdim// This file is distributed under the University of Illinois Open Source 6234287Sdim// License. See LICENSE.TXT for details. 7234287Sdim// 8234287Sdim//===----------------------------------------------------------------------===// 9234287Sdim 10234287Sdim#include "clang/Frontend/TextDiagnostic.h" 11249423Sdim#include "clang/Basic/CharInfo.h" 12249423Sdim#include "clang/Basic/DiagnosticOptions.h" 13234287Sdim#include "clang/Basic/FileManager.h" 14234287Sdim#include "clang/Basic/SourceManager.h" 15234287Sdim#include "clang/Lex/Lexer.h" 16249423Sdim#include "llvm/ADT/SmallString.h" 17249423Sdim#include "llvm/ADT/StringExtras.h" 18249423Sdim#include "llvm/Support/ConvertUTF.h" 19249423Sdim#include "llvm/Support/ErrorHandling.h" 20249423Sdim#include "llvm/Support/Locale.h" 21234287Sdim#include "llvm/Support/MemoryBuffer.h" 22234287Sdim#include "llvm/Support/raw_ostream.h" 23234287Sdim#include <algorithm> 24234982Sdim 25234287Sdimusing namespace clang; 26234287Sdim 27234287Sdimstatic const enum raw_ostream::Colors noteColor = 28234287Sdim raw_ostream::BLACK; 29234287Sdimstatic const enum raw_ostream::Colors fixitColor = 30234287Sdim raw_ostream::GREEN; 31234287Sdimstatic const enum raw_ostream::Colors caretColor = 32234287Sdim raw_ostream::GREEN; 33234287Sdimstatic const enum raw_ostream::Colors warningColor = 34234287Sdim raw_ostream::MAGENTA; 35239462Sdimstatic const enum raw_ostream::Colors templateColor = 36239462Sdim raw_ostream::CYAN; 37234287Sdimstatic const enum raw_ostream::Colors errorColor = raw_ostream::RED; 38234287Sdimstatic const enum raw_ostream::Colors fatalColor = raw_ostream::RED; 39234287Sdim// Used for changing only the bold attribute. 40234287Sdimstatic const enum raw_ostream::Colors savedColor = 41234287Sdim raw_ostream::SAVEDCOLOR; 42234287Sdim 43239462Sdim/// \brief Add highlights to differences in template strings. 44239462Sdimstatic void applyTemplateHighlighting(raw_ostream &OS, StringRef Str, 45239462Sdim bool &Normal, bool Bold) { 46243830Sdim while (1) { 47243830Sdim size_t Pos = Str.find(ToggleHighlight); 48243830Sdim OS << Str.slice(0, Pos); 49243830Sdim if (Pos == StringRef::npos) 50243830Sdim break; 51243830Sdim 52243830Sdim Str = Str.substr(Pos + 1); 53243830Sdim if (Normal) 54243830Sdim OS.changeColor(templateColor, true); 55243830Sdim else { 56243830Sdim OS.resetColor(); 57243830Sdim if (Bold) 58243830Sdim OS.changeColor(savedColor, true); 59239462Sdim } 60243830Sdim Normal = !Normal; 61243830Sdim } 62239462Sdim} 63239462Sdim 64234287Sdim/// \brief Number of spaces to indent when word-wrapping. 65234287Sdimconst unsigned WordWrapIndentation = 6; 66234287Sdim 67239462Sdimstatic int bytesSincePreviousTabOrLineBegin(StringRef SourceLine, size_t i) { 68234982Sdim int bytes = 0; 69234982Sdim while (0<i) { 70234982Sdim if (SourceLine[--i]=='\t') 71234982Sdim break; 72234982Sdim ++bytes; 73234982Sdim } 74234982Sdim return bytes; 75234982Sdim} 76234982Sdim 77234982Sdim/// \brief returns a printable representation of first item from input range 78234982Sdim/// 79234982Sdim/// This function returns a printable representation of the next item in a line 80234982Sdim/// of source. If the next byte begins a valid and printable character, that 81234982Sdim/// character is returned along with 'true'. 82234982Sdim/// 83234982Sdim/// Otherwise, if the next byte begins a valid, but unprintable character, a 84234982Sdim/// printable, escaped representation of the character is returned, along with 85234982Sdim/// 'false'. Otherwise a printable, escaped representation of the next byte 86234982Sdim/// is returned along with 'false'. 87234982Sdim/// 88234982Sdim/// \note The index is updated to be used with a subsequent call to 89234982Sdim/// printableTextForNextCharacter. 90234982Sdim/// 91234982Sdim/// \param SourceLine The line of source 92234982Sdim/// \param i Pointer to byte index, 93234982Sdim/// \param TabStop used to expand tabs 94234982Sdim/// \return pair(printable text, 'true' iff original text was printable) 95234982Sdim/// 96239462Sdimstatic std::pair<SmallString<16>, bool> 97234982SdimprintableTextForNextCharacter(StringRef SourceLine, size_t *i, 98234982Sdim unsigned TabStop) { 99234982Sdim assert(i && "i must not be null"); 100234982Sdim assert(*i<SourceLine.size() && "must point to a valid index"); 101234982Sdim 102234982Sdim if (SourceLine[*i]=='\t') { 103234982Sdim assert(0 < TabStop && TabStop <= DiagnosticOptions::MaxTabStop && 104234982Sdim "Invalid -ftabstop value"); 105234982Sdim unsigned col = bytesSincePreviousTabOrLineBegin(SourceLine, *i); 106234982Sdim unsigned NumSpaces = TabStop - col%TabStop; 107234982Sdim assert(0 < NumSpaces && NumSpaces <= TabStop 108234982Sdim && "Invalid computation of space amt"); 109234982Sdim ++(*i); 110234982Sdim 111234982Sdim SmallString<16> expandedTab; 112234982Sdim expandedTab.assign(NumSpaces, ' '); 113234982Sdim return std::make_pair(expandedTab, true); 114234982Sdim } 115234982Sdim 116234982Sdim unsigned char const *begin, *end; 117234982Sdim begin = reinterpret_cast<unsigned char const *>(&*(SourceLine.begin() + *i)); 118243830Sdim end = begin + (SourceLine.size() - *i); 119234982Sdim 120234982Sdim if (isLegalUTF8Sequence(begin, end)) { 121234982Sdim UTF32 c; 122234982Sdim UTF32 *cptr = &c; 123234982Sdim unsigned char const *original_begin = begin; 124243830Sdim unsigned char const *cp_end = begin+getNumBytesForUTF8(SourceLine[*i]); 125234982Sdim 126234982Sdim ConversionResult res = ConvertUTF8toUTF32(&begin, cp_end, &cptr, cptr+1, 127234982Sdim strictConversion); 128234982Sdim (void)res; 129234982Sdim assert(conversionOK==res); 130234982Sdim assert(0 < begin-original_begin 131234982Sdim && "we must be further along in the string now"); 132234982Sdim *i += begin-original_begin; 133234982Sdim 134234982Sdim if (!llvm::sys::locale::isPrint(c)) { 135234982Sdim // If next character is valid UTF-8, but not printable 136234982Sdim SmallString<16> expandedCP("<U+>"); 137234982Sdim while (c) { 138234982Sdim expandedCP.insert(expandedCP.begin()+3, llvm::hexdigit(c%16)); 139234982Sdim c/=16; 140234982Sdim } 141234982Sdim while (expandedCP.size() < 8) 142234982Sdim expandedCP.insert(expandedCP.begin()+3, llvm::hexdigit(0)); 143234982Sdim return std::make_pair(expandedCP, false); 144234982Sdim } 145234982Sdim 146234982Sdim // If next character is valid UTF-8, and printable 147234982Sdim return std::make_pair(SmallString<16>(original_begin, cp_end), true); 148234982Sdim 149234982Sdim } 150234982Sdim 151234982Sdim // If next byte is not valid UTF-8 (and therefore not printable) 152234982Sdim SmallString<16> expandedByte("<XX>"); 153234982Sdim unsigned char byte = SourceLine[*i]; 154234982Sdim expandedByte[1] = llvm::hexdigit(byte / 16); 155234982Sdim expandedByte[2] = llvm::hexdigit(byte % 16); 156234982Sdim ++(*i); 157234982Sdim return std::make_pair(expandedByte, false); 158234982Sdim} 159234982Sdim 160239462Sdimstatic void expandTabs(std::string &SourceLine, unsigned TabStop) { 161234982Sdim size_t i = SourceLine.size(); 162234982Sdim while (i>0) { 163234982Sdim i--; 164234982Sdim if (SourceLine[i]!='\t') 165234982Sdim continue; 166234982Sdim size_t tmp_i = i; 167234982Sdim std::pair<SmallString<16>,bool> res 168234982Sdim = printableTextForNextCharacter(SourceLine, &tmp_i, TabStop); 169234982Sdim SourceLine.replace(i, 1, res.first.c_str()); 170234982Sdim } 171234982Sdim} 172234982Sdim 173234982Sdim/// This function takes a raw source line and produces a mapping from the bytes 174234982Sdim/// of the printable representation of the line to the columns those printable 175234982Sdim/// characters will appear at (numbering the first column as 0). 176234982Sdim/// 177234982Sdim/// If a byte 'i' corresponds to muliple columns (e.g. the byte contains a tab 178239462Sdim/// character) then the array will map that byte to the first column the 179234982Sdim/// tab appears at and the next value in the map will have been incremented 180234982Sdim/// more than once. 181234982Sdim/// 182234982Sdim/// If a byte is the first in a sequence of bytes that together map to a single 183234982Sdim/// entity in the output, then the array will map that byte to the appropriate 184234982Sdim/// column while the subsequent bytes will be -1. 185234982Sdim/// 186234982Sdim/// The last element in the array does not correspond to any byte in the input 187234982Sdim/// and instead is the number of columns needed to display the source 188234982Sdim/// 189234982Sdim/// example: (given a tabstop of 8) 190234982Sdim/// 191234982Sdim/// "a \t \u3042" -> {0,1,2,8,9,-1,-1,11} 192234982Sdim/// 193239462Sdim/// (\\u3042 is represented in UTF-8 by three bytes and takes two columns to 194234982Sdim/// display) 195239462Sdimstatic void byteToColumn(StringRef SourceLine, unsigned TabStop, 196234982Sdim SmallVectorImpl<int> &out) { 197234982Sdim out.clear(); 198234982Sdim 199234982Sdim if (SourceLine.empty()) { 200234982Sdim out.resize(1u,0); 201234982Sdim return; 202234982Sdim } 203234982Sdim 204234982Sdim out.resize(SourceLine.size()+1, -1); 205234982Sdim 206234982Sdim int columns = 0; 207234982Sdim size_t i = 0; 208234982Sdim while (i<SourceLine.size()) { 209234982Sdim out[i] = columns; 210234982Sdim std::pair<SmallString<16>,bool> res 211234982Sdim = printableTextForNextCharacter(SourceLine, &i, TabStop); 212234982Sdim columns += llvm::sys::locale::columnWidth(res.first); 213234982Sdim } 214234982Sdim out.back() = columns; 215234982Sdim} 216234982Sdim 217234982Sdim/// This function takes a raw source line and produces a mapping from columns 218234982Sdim/// to the byte of the source line that produced the character displaying at 219234982Sdim/// that column. This is the inverse of the mapping produced by byteToColumn() 220234982Sdim/// 221234982Sdim/// The last element in the array is the number of bytes in the source string 222234982Sdim/// 223234982Sdim/// example: (given a tabstop of 8) 224234982Sdim/// 225234982Sdim/// "a \t \u3042" -> {0,1,2,-1,-1,-1,-1,-1,3,4,-1,7} 226234982Sdim/// 227239462Sdim/// (\\u3042 is represented in UTF-8 by three bytes and takes two columns to 228234982Sdim/// display) 229239462Sdimstatic void columnToByte(StringRef SourceLine, unsigned TabStop, 230234982Sdim SmallVectorImpl<int> &out) { 231234982Sdim out.clear(); 232234982Sdim 233234982Sdim if (SourceLine.empty()) { 234234982Sdim out.resize(1u, 0); 235234982Sdim return; 236234982Sdim } 237234982Sdim 238234982Sdim int columns = 0; 239234982Sdim size_t i = 0; 240234982Sdim while (i<SourceLine.size()) { 241234982Sdim out.resize(columns+1, -1); 242234982Sdim out.back() = i; 243234982Sdim std::pair<SmallString<16>,bool> res 244234982Sdim = printableTextForNextCharacter(SourceLine, &i, TabStop); 245234982Sdim columns += llvm::sys::locale::columnWidth(res.first); 246234982Sdim } 247234982Sdim out.resize(columns+1, -1); 248234982Sdim out.back() = i; 249234982Sdim} 250234982Sdim 251249423Sdimnamespace { 252234982Sdimstruct SourceColumnMap { 253234982Sdim SourceColumnMap(StringRef SourceLine, unsigned TabStop) 254234982Sdim : m_SourceLine(SourceLine) { 255234982Sdim 256234982Sdim ::byteToColumn(SourceLine, TabStop, m_byteToColumn); 257234982Sdim ::columnToByte(SourceLine, TabStop, m_columnToByte); 258234982Sdim 259234982Sdim assert(m_byteToColumn.size()==SourceLine.size()+1); 260234982Sdim assert(0 < m_byteToColumn.size() && 0 < m_columnToByte.size()); 261234982Sdim assert(m_byteToColumn.size() 262234982Sdim == static_cast<unsigned>(m_columnToByte.back()+1)); 263234982Sdim assert(static_cast<unsigned>(m_byteToColumn.back()+1) 264234982Sdim == m_columnToByte.size()); 265234982Sdim } 266234982Sdim int columns() const { return m_byteToColumn.back(); } 267234982Sdim int bytes() const { return m_columnToByte.back(); } 268243830Sdim 269243830Sdim /// \brief Map a byte to the column which it is at the start of, or return -1 270243830Sdim /// if it is not at the start of a column (for a UTF-8 trailing byte). 271234982Sdim int byteToColumn(int n) const { 272234982Sdim assert(0<=n && n<static_cast<int>(m_byteToColumn.size())); 273234982Sdim return m_byteToColumn[n]; 274234982Sdim } 275243830Sdim 276243830Sdim /// \brief Map a byte to the first column which contains it. 277243830Sdim int byteToContainingColumn(int N) const { 278243830Sdim assert(0 <= N && N < static_cast<int>(m_byteToColumn.size())); 279243830Sdim while (m_byteToColumn[N] == -1) 280243830Sdim --N; 281243830Sdim return m_byteToColumn[N]; 282243830Sdim } 283243830Sdim 284243830Sdim /// \brief Map a column to the byte which starts the column, or return -1 if 285243830Sdim /// the column the second or subsequent column of an expanded tab or similar 286243830Sdim /// multi-column entity. 287234982Sdim int columnToByte(int n) const { 288234982Sdim assert(0<=n && n<static_cast<int>(m_columnToByte.size())); 289234982Sdim return m_columnToByte[n]; 290234982Sdim } 291243830Sdim 292243830Sdim /// \brief Map from a byte index to the next byte which starts a column. 293243830Sdim int startOfNextColumn(int N) const { 294243830Sdim assert(0 <= N && N < static_cast<int>(m_columnToByte.size() - 1)); 295243830Sdim while (byteToColumn(++N) == -1) {} 296243830Sdim return N; 297243830Sdim } 298243830Sdim 299243830Sdim /// \brief Map from a byte index to the previous byte which starts a column. 300243830Sdim int startOfPreviousColumn(int N) const { 301243830Sdim assert(0 < N && N < static_cast<int>(m_columnToByte.size())); 302243830Sdim while (byteToColumn(--N) == -1) {} 303243830Sdim return N; 304243830Sdim } 305243830Sdim 306234982Sdim StringRef getSourceLine() const { 307234982Sdim return m_SourceLine; 308234982Sdim } 309234982Sdim 310234982Sdimprivate: 311234982Sdim const std::string m_SourceLine; 312234982Sdim SmallVector<int,200> m_byteToColumn; 313234982Sdim SmallVector<int,200> m_columnToByte; 314234982Sdim}; 315234982Sdim 316234982Sdim// used in assert in selectInterestingSourceRegion() 317234982Sdimstruct char_out_of_range { 318234982Sdim const char lower,upper; 319234982Sdim char_out_of_range(char lower, char upper) : 320234982Sdim lower(lower), upper(upper) {} 321234982Sdim bool operator()(char c) { return c < lower || upper < c; } 322234982Sdim}; 323249423Sdim} // end anonymous namespace 324234982Sdim 325234287Sdim/// \brief When the source code line we want to print is too long for 326234287Sdim/// the terminal, select the "interesting" region. 327234287Sdimstatic void selectInterestingSourceRegion(std::string &SourceLine, 328234287Sdim std::string &CaretLine, 329234287Sdim std::string &FixItInsertionLine, 330234982Sdim unsigned Columns, 331234982Sdim const SourceColumnMap &map) { 332234982Sdim unsigned MaxColumns = std::max<unsigned>(map.columns(), 333234982Sdim std::max(CaretLine.size(), 334234982Sdim FixItInsertionLine.size())); 335234982Sdim // if the number of columns is less than the desired number we're done 336234982Sdim if (MaxColumns <= Columns) 337234982Sdim return; 338234982Sdim 339234982Sdim // no special characters allowed in CaretLine or FixItInsertionLine 340234982Sdim assert(CaretLine.end() == 341234982Sdim std::find_if(CaretLine.begin(), CaretLine.end(), 342234982Sdim char_out_of_range(' ','~'))); 343234982Sdim assert(FixItInsertionLine.end() == 344234982Sdim std::find_if(FixItInsertionLine.begin(), FixItInsertionLine.end(), 345234982Sdim char_out_of_range(' ','~'))); 346234982Sdim 347234287Sdim // Find the slice that we need to display the full caret line 348234287Sdim // correctly. 349234287Sdim unsigned CaretStart = 0, CaretEnd = CaretLine.size(); 350234287Sdim for (; CaretStart != CaretEnd; ++CaretStart) 351249423Sdim if (!isWhitespace(CaretLine[CaretStart])) 352234287Sdim break; 353234287Sdim 354234287Sdim for (; CaretEnd != CaretStart; --CaretEnd) 355249423Sdim if (!isWhitespace(CaretLine[CaretEnd - 1])) 356234287Sdim break; 357234287Sdim 358234982Sdim // caret has already been inserted into CaretLine so the above whitespace 359234982Sdim // check is guaranteed to include the caret 360234287Sdim 361234287Sdim // If we have a fix-it line, make sure the slice includes all of the 362234287Sdim // fix-it information. 363234287Sdim if (!FixItInsertionLine.empty()) { 364234287Sdim unsigned FixItStart = 0, FixItEnd = FixItInsertionLine.size(); 365234287Sdim for (; FixItStart != FixItEnd; ++FixItStart) 366249423Sdim if (!isWhitespace(FixItInsertionLine[FixItStart])) 367234287Sdim break; 368234287Sdim 369234287Sdim for (; FixItEnd != FixItStart; --FixItEnd) 370249423Sdim if (!isWhitespace(FixItInsertionLine[FixItEnd - 1])) 371234287Sdim break; 372234287Sdim 373234982Sdim CaretStart = std::min(FixItStart, CaretStart); 374234982Sdim CaretEnd = std::max(FixItEnd, CaretEnd); 375234287Sdim } 376234287Sdim 377239462Sdim // CaretEnd may have been set at the middle of a character 378239462Sdim // If it's not at a character's first column then advance it past the current 379239462Sdim // character. 380239462Sdim while (static_cast<int>(CaretEnd) < map.columns() && 381239462Sdim -1 == map.columnToByte(CaretEnd)) 382239462Sdim ++CaretEnd; 383239462Sdim 384239462Sdim assert((static_cast<int>(CaretStart) > map.columns() || 385239462Sdim -1!=map.columnToByte(CaretStart)) && 386239462Sdim "CaretStart must not point to a column in the middle of a source" 387239462Sdim " line character"); 388239462Sdim assert((static_cast<int>(CaretEnd) > map.columns() || 389239462Sdim -1!=map.columnToByte(CaretEnd)) && 390239462Sdim "CaretEnd must not point to a column in the middle of a source line" 391239462Sdim " character"); 392239462Sdim 393234287Sdim // CaretLine[CaretStart, CaretEnd) contains all of the interesting 394234287Sdim // parts of the caret line. While this slice is smaller than the 395234287Sdim // number of columns we have, try to grow the slice to encompass 396234287Sdim // more context. 397234287Sdim 398234982Sdim unsigned SourceStart = map.columnToByte(std::min<unsigned>(CaretStart, 399234982Sdim map.columns())); 400234982Sdim unsigned SourceEnd = map.columnToByte(std::min<unsigned>(CaretEnd, 401234982Sdim map.columns())); 402234287Sdim 403234982Sdim unsigned CaretColumnsOutsideSource = CaretEnd-CaretStart 404234982Sdim - (map.byteToColumn(SourceEnd)-map.byteToColumn(SourceStart)); 405234982Sdim 406234982Sdim char const *front_ellipse = " ..."; 407234982Sdim char const *front_space = " "; 408234982Sdim char const *back_ellipse = "..."; 409234982Sdim unsigned ellipses_space = strlen(front_ellipse) + strlen(back_ellipse); 410234982Sdim 411234287Sdim unsigned TargetColumns = Columns; 412234982Sdim // Give us extra room for the ellipses 413234982Sdim // and any of the caret line that extends past the source 414234982Sdim if (TargetColumns > ellipses_space+CaretColumnsOutsideSource) 415234982Sdim TargetColumns -= ellipses_space+CaretColumnsOutsideSource; 416234982Sdim 417234982Sdim while (SourceStart>0 || SourceEnd<SourceLine.size()) { 418234287Sdim bool ExpandedRegion = false; 419234287Sdim 420234982Sdim if (SourceStart>0) { 421243830Sdim unsigned NewStart = map.startOfPreviousColumn(SourceStart); 422234982Sdim 423234287Sdim // Skip over any whitespace we see here; we're looking for 424234287Sdim // another bit of interesting text. 425243830Sdim // FIXME: Detect non-ASCII whitespace characters too. 426249423Sdim while (NewStart && isWhitespace(SourceLine[NewStart])) 427243830Sdim NewStart = map.startOfPreviousColumn(NewStart); 428234287Sdim 429234287Sdim // Skip over this bit of "interesting" text. 430243830Sdim while (NewStart) { 431243830Sdim unsigned Prev = map.startOfPreviousColumn(NewStart); 432249423Sdim if (isWhitespace(SourceLine[Prev])) 433243830Sdim break; 434243830Sdim NewStart = Prev; 435243830Sdim } 436234287Sdim 437243830Sdim assert(map.byteToColumn(NewStart) != -1); 438234982Sdim unsigned NewColumns = map.byteToColumn(SourceEnd) - 439234982Sdim map.byteToColumn(NewStart); 440234982Sdim if (NewColumns <= TargetColumns) { 441234982Sdim SourceStart = NewStart; 442234287Sdim ExpandedRegion = true; 443234287Sdim } 444234287Sdim } 445234287Sdim 446234982Sdim if (SourceEnd<SourceLine.size()) { 447243830Sdim unsigned NewEnd = map.startOfNextColumn(SourceEnd); 448234287Sdim 449234287Sdim // Skip over any whitespace we see here; we're looking for 450234287Sdim // another bit of interesting text. 451243830Sdim // FIXME: Detect non-ASCII whitespace characters too. 452249423Sdim while (NewEnd < SourceLine.size() && isWhitespace(SourceLine[NewEnd])) 453243830Sdim NewEnd = map.startOfNextColumn(NewEnd); 454234287Sdim 455234287Sdim // Skip over this bit of "interesting" text. 456249423Sdim while (NewEnd < SourceLine.size() && isWhitespace(SourceLine[NewEnd])) 457243830Sdim NewEnd = map.startOfNextColumn(NewEnd); 458234287Sdim 459243830Sdim assert(map.byteToColumn(NewEnd) != -1); 460234982Sdim unsigned NewColumns = map.byteToColumn(NewEnd) - 461234982Sdim map.byteToColumn(SourceStart); 462234982Sdim if (NewColumns <= TargetColumns) { 463234982Sdim SourceEnd = NewEnd; 464234287Sdim ExpandedRegion = true; 465234287Sdim } 466234287Sdim } 467234287Sdim 468234287Sdim if (!ExpandedRegion) 469234287Sdim break; 470234287Sdim } 471234287Sdim 472234982Sdim CaretStart = map.byteToColumn(SourceStart); 473234982Sdim CaretEnd = map.byteToColumn(SourceEnd) + CaretColumnsOutsideSource; 474234982Sdim 475234287Sdim // [CaretStart, CaretEnd) is the slice we want. Update the various 476234287Sdim // output lines to show only this slice, with two-space padding 477234287Sdim // before the lines so that it looks nicer. 478234287Sdim 479234982Sdim assert(CaretStart!=(unsigned)-1 && CaretEnd!=(unsigned)-1 && 480234982Sdim SourceStart!=(unsigned)-1 && SourceEnd!=(unsigned)-1); 481234982Sdim assert(SourceStart <= SourceEnd); 482234982Sdim assert(CaretStart <= CaretEnd); 483234982Sdim 484234982Sdim unsigned BackColumnsRemoved 485234982Sdim = map.byteToColumn(SourceLine.size())-map.byteToColumn(SourceEnd); 486234982Sdim unsigned FrontColumnsRemoved = CaretStart; 487234982Sdim unsigned ColumnsKept = CaretEnd-CaretStart; 488234982Sdim 489234982Sdim // We checked up front that the line needed truncation 490234982Sdim assert(FrontColumnsRemoved+ColumnsKept+BackColumnsRemoved > Columns); 491234982Sdim 492234982Sdim // The line needs some trunctiona, and we'd prefer to keep the front 493234982Sdim // if possible, so remove the back 494243830Sdim if (BackColumnsRemoved > strlen(back_ellipse)) 495234982Sdim SourceLine.replace(SourceEnd, std::string::npos, back_ellipse); 496234982Sdim 497234982Sdim // If that's enough then we're done 498234982Sdim if (FrontColumnsRemoved+ColumnsKept <= Columns) 499234982Sdim return; 500234982Sdim 501234982Sdim // Otherwise remove the front as well 502243830Sdim if (FrontColumnsRemoved > strlen(front_ellipse)) { 503234982Sdim SourceLine.replace(0, SourceStart, front_ellipse); 504234982Sdim CaretLine.replace(0, CaretStart, front_space); 505234982Sdim if (!FixItInsertionLine.empty()) 506234982Sdim FixItInsertionLine.replace(0, CaretStart, front_space); 507234287Sdim } 508234287Sdim} 509234287Sdim 510234287Sdim/// \brief Skip over whitespace in the string, starting at the given 511234287Sdim/// index. 512234287Sdim/// 513234287Sdim/// \returns The index of the first non-whitespace character that is 514234287Sdim/// greater than or equal to Idx or, if no such character exists, 515234287Sdim/// returns the end of the string. 516234287Sdimstatic unsigned skipWhitespace(unsigned Idx, StringRef Str, unsigned Length) { 517249423Sdim while (Idx < Length && isWhitespace(Str[Idx])) 518234287Sdim ++Idx; 519234287Sdim return Idx; 520234287Sdim} 521234287Sdim 522234287Sdim/// \brief If the given character is the start of some kind of 523234287Sdim/// balanced punctuation (e.g., quotes or parentheses), return the 524234287Sdim/// character that will terminate the punctuation. 525234287Sdim/// 526234287Sdim/// \returns The ending punctuation character, if any, or the NULL 527234287Sdim/// character if the input character does not start any punctuation. 528234287Sdimstatic inline char findMatchingPunctuation(char c) { 529234287Sdim switch (c) { 530234287Sdim case '\'': return '\''; 531234287Sdim case '`': return '\''; 532234287Sdim case '"': return '"'; 533234287Sdim case '(': return ')'; 534234287Sdim case '[': return ']'; 535234287Sdim case '{': return '}'; 536234287Sdim default: break; 537234287Sdim } 538234287Sdim 539234287Sdim return 0; 540234287Sdim} 541234287Sdim 542234287Sdim/// \brief Find the end of the word starting at the given offset 543234287Sdim/// within a string. 544234287Sdim/// 545234287Sdim/// \returns the index pointing one character past the end of the 546234287Sdim/// word. 547234287Sdimstatic unsigned findEndOfWord(unsigned Start, StringRef Str, 548234287Sdim unsigned Length, unsigned Column, 549234287Sdim unsigned Columns) { 550234287Sdim assert(Start < Str.size() && "Invalid start position!"); 551234287Sdim unsigned End = Start + 1; 552234287Sdim 553234287Sdim // If we are already at the end of the string, take that as the word. 554234287Sdim if (End == Str.size()) 555234287Sdim return End; 556234287Sdim 557234287Sdim // Determine if the start of the string is actually opening 558234287Sdim // punctuation, e.g., a quote or parentheses. 559234287Sdim char EndPunct = findMatchingPunctuation(Str[Start]); 560234287Sdim if (!EndPunct) { 561234287Sdim // This is a normal word. Just find the first space character. 562249423Sdim while (End < Length && !isWhitespace(Str[End])) 563234287Sdim ++End; 564234287Sdim return End; 565234287Sdim } 566234287Sdim 567234287Sdim // We have the start of a balanced punctuation sequence (quotes, 568234287Sdim // parentheses, etc.). Determine the full sequence is. 569234287Sdim SmallString<16> PunctuationEndStack; 570234287Sdim PunctuationEndStack.push_back(EndPunct); 571234287Sdim while (End < Length && !PunctuationEndStack.empty()) { 572234287Sdim if (Str[End] == PunctuationEndStack.back()) 573234287Sdim PunctuationEndStack.pop_back(); 574234287Sdim else if (char SubEndPunct = findMatchingPunctuation(Str[End])) 575234287Sdim PunctuationEndStack.push_back(SubEndPunct); 576234287Sdim 577234287Sdim ++End; 578234287Sdim } 579234287Sdim 580234287Sdim // Find the first space character after the punctuation ended. 581249423Sdim while (End < Length && !isWhitespace(Str[End])) 582234287Sdim ++End; 583234287Sdim 584234287Sdim unsigned PunctWordLength = End - Start; 585234287Sdim if (// If the word fits on this line 586234287Sdim Column + PunctWordLength <= Columns || 587234287Sdim // ... or the word is "short enough" to take up the next line 588234287Sdim // without too much ugly white space 589234287Sdim PunctWordLength < Columns/3) 590234287Sdim return End; // Take the whole thing as a single "word". 591234287Sdim 592234287Sdim // The whole quoted/parenthesized string is too long to print as a 593234287Sdim // single "word". Instead, find the "word" that starts just after 594234287Sdim // the punctuation and use that end-point instead. This will recurse 595234287Sdim // until it finds something small enough to consider a word. 596234287Sdim return findEndOfWord(Start + 1, Str, Length, Column + 1, Columns); 597234287Sdim} 598234287Sdim 599234287Sdim/// \brief Print the given string to a stream, word-wrapping it to 600234287Sdim/// some number of columns in the process. 601234287Sdim/// 602234287Sdim/// \param OS the stream to which the word-wrapping string will be 603234287Sdim/// emitted. 604234287Sdim/// \param Str the string to word-wrap and output. 605234287Sdim/// \param Columns the number of columns to word-wrap to. 606234287Sdim/// \param Column the column number at which the first character of \p 607234287Sdim/// Str will be printed. This will be non-zero when part of the first 608234287Sdim/// line has already been printed. 609239462Sdim/// \param Bold if the current text should be bold 610234287Sdim/// \param Indentation the number of spaces to indent any lines beyond 611234287Sdim/// the first line. 612234287Sdim/// \returns true if word-wrapping was required, or false if the 613234287Sdim/// string fit on the first line. 614234287Sdimstatic bool printWordWrapped(raw_ostream &OS, StringRef Str, 615234287Sdim unsigned Columns, 616234287Sdim unsigned Column = 0, 617239462Sdim bool Bold = false, 618234287Sdim unsigned Indentation = WordWrapIndentation) { 619234287Sdim const unsigned Length = std::min(Str.find('\n'), Str.size()); 620239462Sdim bool TextNormal = true; 621234287Sdim 622234287Sdim // The string used to indent each line. 623234287Sdim SmallString<16> IndentStr; 624234287Sdim IndentStr.assign(Indentation, ' '); 625234287Sdim bool Wrapped = false; 626234287Sdim for (unsigned WordStart = 0, WordEnd; WordStart < Length; 627234287Sdim WordStart = WordEnd) { 628234287Sdim // Find the beginning of the next word. 629234287Sdim WordStart = skipWhitespace(WordStart, Str, Length); 630234287Sdim if (WordStart == Length) 631234287Sdim break; 632234287Sdim 633234287Sdim // Find the end of this word. 634234287Sdim WordEnd = findEndOfWord(WordStart, Str, Length, Column, Columns); 635234287Sdim 636234287Sdim // Does this word fit on the current line? 637234287Sdim unsigned WordLength = WordEnd - WordStart; 638234287Sdim if (Column + WordLength < Columns) { 639234287Sdim // This word fits on the current line; print it there. 640234287Sdim if (WordStart) { 641234287Sdim OS << ' '; 642234287Sdim Column += 1; 643234287Sdim } 644239462Sdim applyTemplateHighlighting(OS, Str.substr(WordStart, WordLength), 645239462Sdim TextNormal, Bold); 646234287Sdim Column += WordLength; 647234287Sdim continue; 648234287Sdim } 649234287Sdim 650234287Sdim // This word does not fit on the current line, so wrap to the next 651234287Sdim // line. 652234287Sdim OS << '\n'; 653234287Sdim OS.write(&IndentStr[0], Indentation); 654239462Sdim applyTemplateHighlighting(OS, Str.substr(WordStart, WordLength), 655239462Sdim TextNormal, Bold); 656234287Sdim Column = Indentation + WordLength; 657234287Sdim Wrapped = true; 658234287Sdim } 659234287Sdim 660234287Sdim // Append any remaning text from the message with its existing formatting. 661239462Sdim applyTemplateHighlighting(OS, Str.substr(Length), TextNormal, Bold); 662234287Sdim 663239462Sdim assert(TextNormal && "Text highlighted at end of diagnostic message."); 664239462Sdim 665234287Sdim return Wrapped; 666234287Sdim} 667234287Sdim 668234287SdimTextDiagnostic::TextDiagnostic(raw_ostream &OS, 669234287Sdim const LangOptions &LangOpts, 670243830Sdim DiagnosticOptions *DiagOpts) 671239462Sdim : DiagnosticRenderer(LangOpts, DiagOpts), OS(OS) {} 672234287Sdim 673234287SdimTextDiagnostic::~TextDiagnostic() {} 674234287Sdim 675234287Sdimvoid 676234287SdimTextDiagnostic::emitDiagnosticMessage(SourceLocation Loc, 677234287Sdim PresumedLoc PLoc, 678234287Sdim DiagnosticsEngine::Level Level, 679234287Sdim StringRef Message, 680234287Sdim ArrayRef<clang::CharSourceRange> Ranges, 681239462Sdim const SourceManager *SM, 682234287Sdim DiagOrStoredDiag D) { 683234287Sdim uint64_t StartOfLocationInfo = OS.tell(); 684234287Sdim 685234287Sdim // Emit the location of this particular diagnostic. 686239462Sdim if (Loc.isValid()) 687239462Sdim emitDiagnosticLoc(Loc, PLoc, Level, Ranges, *SM); 688234287Sdim 689243830Sdim if (DiagOpts->ShowColors) 690234287Sdim OS.resetColor(); 691234287Sdim 692243830Sdim printDiagnosticLevel(OS, Level, DiagOpts->ShowColors); 693234287Sdim printDiagnosticMessage(OS, Level, Message, 694234287Sdim OS.tell() - StartOfLocationInfo, 695243830Sdim DiagOpts->MessageLength, DiagOpts->ShowColors); 696234287Sdim} 697234287Sdim 698234287Sdim/*static*/ void 699234287SdimTextDiagnostic::printDiagnosticLevel(raw_ostream &OS, 700234287Sdim DiagnosticsEngine::Level Level, 701234287Sdim bool ShowColors) { 702234287Sdim if (ShowColors) { 703234287Sdim // Print diagnostic category in bold and color 704234287Sdim switch (Level) { 705234287Sdim case DiagnosticsEngine::Ignored: 706234287Sdim llvm_unreachable("Invalid diagnostic type"); 707234287Sdim case DiagnosticsEngine::Note: OS.changeColor(noteColor, true); break; 708234287Sdim case DiagnosticsEngine::Warning: OS.changeColor(warningColor, true); break; 709234287Sdim case DiagnosticsEngine::Error: OS.changeColor(errorColor, true); break; 710234287Sdim case DiagnosticsEngine::Fatal: OS.changeColor(fatalColor, true); break; 711234287Sdim } 712234287Sdim } 713234287Sdim 714234287Sdim switch (Level) { 715234287Sdim case DiagnosticsEngine::Ignored: 716234287Sdim llvm_unreachable("Invalid diagnostic type"); 717234287Sdim case DiagnosticsEngine::Note: OS << "note: "; break; 718234287Sdim case DiagnosticsEngine::Warning: OS << "warning: "; break; 719234287Sdim case DiagnosticsEngine::Error: OS << "error: "; break; 720234287Sdim case DiagnosticsEngine::Fatal: OS << "fatal error: "; break; 721234287Sdim } 722234287Sdim 723234287Sdim if (ShowColors) 724234287Sdim OS.resetColor(); 725234287Sdim} 726234287Sdim 727234287Sdim/*static*/ void 728234287SdimTextDiagnostic::printDiagnosticMessage(raw_ostream &OS, 729234287Sdim DiagnosticsEngine::Level Level, 730234287Sdim StringRef Message, 731234287Sdim unsigned CurrentColumn, unsigned Columns, 732234287Sdim bool ShowColors) { 733239462Sdim bool Bold = false; 734234287Sdim if (ShowColors) { 735234287Sdim // Print warnings, errors and fatal errors in bold, no color 736234287Sdim switch (Level) { 737239462Sdim case DiagnosticsEngine::Warning: 738239462Sdim case DiagnosticsEngine::Error: 739239462Sdim case DiagnosticsEngine::Fatal: 740239462Sdim OS.changeColor(savedColor, true); 741239462Sdim Bold = true; 742239462Sdim break; 743234287Sdim default: break; //don't bold notes 744234287Sdim } 745234287Sdim } 746234287Sdim 747234287Sdim if (Columns) 748239462Sdim printWordWrapped(OS, Message, Columns, CurrentColumn, Bold); 749239462Sdim else { 750239462Sdim bool Normal = true; 751239462Sdim applyTemplateHighlighting(OS, Message, Normal, Bold); 752239462Sdim assert(Normal && "Formatting should have returned to normal"); 753239462Sdim } 754234287Sdim 755234287Sdim if (ShowColors) 756234287Sdim OS.resetColor(); 757234287Sdim OS << '\n'; 758234287Sdim} 759234287Sdim 760234287Sdim/// \brief Print out the file/line/column information and include trace. 761234287Sdim/// 762234287Sdim/// This method handlen the emission of the diagnostic location information. 763234287Sdim/// This includes extracting as much location information as is present for 764234287Sdim/// the diagnostic and printing it, as well as any include stack or source 765234287Sdim/// ranges necessary. 766234287Sdimvoid TextDiagnostic::emitDiagnosticLoc(SourceLocation Loc, PresumedLoc PLoc, 767234287Sdim DiagnosticsEngine::Level Level, 768239462Sdim ArrayRef<CharSourceRange> Ranges, 769239462Sdim const SourceManager &SM) { 770234287Sdim if (PLoc.isInvalid()) { 771234287Sdim // At least print the file name if available: 772234287Sdim FileID FID = SM.getFileID(Loc); 773234287Sdim if (!FID.isInvalid()) { 774234287Sdim const FileEntry* FE = SM.getFileEntryForID(FID); 775234287Sdim if (FE && FE->getName()) { 776234287Sdim OS << FE->getName(); 777234287Sdim if (FE->getDevice() == 0 && FE->getInode() == 0 778234287Sdim && FE->getFileMode() == 0) { 779234287Sdim // in PCH is a guess, but a good one: 780234287Sdim OS << " (in PCH)"; 781234287Sdim } 782234287Sdim OS << ": "; 783234287Sdim } 784234287Sdim } 785234287Sdim return; 786234287Sdim } 787234287Sdim unsigned LineNo = PLoc.getLine(); 788234287Sdim 789243830Sdim if (!DiagOpts->ShowLocation) 790234287Sdim return; 791234287Sdim 792243830Sdim if (DiagOpts->ShowColors) 793234287Sdim OS.changeColor(savedColor, true); 794234287Sdim 795234287Sdim OS << PLoc.getFilename(); 796243830Sdim switch (DiagOpts->getFormat()) { 797234287Sdim case DiagnosticOptions::Clang: OS << ':' << LineNo; break; 798234287Sdim case DiagnosticOptions::Msvc: OS << '(' << LineNo; break; 799234287Sdim case DiagnosticOptions::Vi: OS << " +" << LineNo; break; 800234287Sdim } 801234287Sdim 802243830Sdim if (DiagOpts->ShowColumn) 803234287Sdim // Compute the column number. 804234287Sdim if (unsigned ColNo = PLoc.getColumn()) { 805243830Sdim if (DiagOpts->getFormat() == DiagnosticOptions::Msvc) { 806234287Sdim OS << ','; 807234287Sdim ColNo--; 808234287Sdim } else 809234287Sdim OS << ':'; 810234287Sdim OS << ColNo; 811234287Sdim } 812243830Sdim switch (DiagOpts->getFormat()) { 813234287Sdim case DiagnosticOptions::Clang: 814234287Sdim case DiagnosticOptions::Vi: OS << ':'; break; 815234287Sdim case DiagnosticOptions::Msvc: OS << ") : "; break; 816234287Sdim } 817234287Sdim 818243830Sdim if (DiagOpts->ShowSourceRanges && !Ranges.empty()) { 819234287Sdim FileID CaretFileID = 820234287Sdim SM.getFileID(SM.getExpansionLoc(Loc)); 821234287Sdim bool PrintedRange = false; 822234287Sdim 823234287Sdim for (ArrayRef<CharSourceRange>::const_iterator RI = Ranges.begin(), 824234287Sdim RE = Ranges.end(); 825234287Sdim RI != RE; ++RI) { 826234287Sdim // Ignore invalid ranges. 827234287Sdim if (!RI->isValid()) continue; 828234287Sdim 829234287Sdim SourceLocation B = SM.getExpansionLoc(RI->getBegin()); 830234287Sdim SourceLocation E = SM.getExpansionLoc(RI->getEnd()); 831234287Sdim 832234287Sdim // If the End location and the start location are the same and are a 833234287Sdim // macro location, then the range was something that came from a 834234287Sdim // macro expansion or _Pragma. If this is an object-like macro, the 835234287Sdim // best we can do is to highlight the range. If this is a 836234287Sdim // function-like macro, we'd also like to highlight the arguments. 837234287Sdim if (B == E && RI->getEnd().isMacroID()) 838234287Sdim E = SM.getExpansionRange(RI->getEnd()).second; 839234287Sdim 840234287Sdim std::pair<FileID, unsigned> BInfo = SM.getDecomposedLoc(B); 841234287Sdim std::pair<FileID, unsigned> EInfo = SM.getDecomposedLoc(E); 842234287Sdim 843234287Sdim // If the start or end of the range is in another file, just discard 844234287Sdim // it. 845234287Sdim if (BInfo.first != CaretFileID || EInfo.first != CaretFileID) 846234287Sdim continue; 847234287Sdim 848234287Sdim // Add in the length of the token, so that we cover multi-char 849234287Sdim // tokens. 850234287Sdim unsigned TokSize = 0; 851234287Sdim if (RI->isTokenRange()) 852234287Sdim TokSize = Lexer::MeasureTokenLength(E, SM, LangOpts); 853234287Sdim 854234287Sdim OS << '{' << SM.getLineNumber(BInfo.first, BInfo.second) << ':' 855234287Sdim << SM.getColumnNumber(BInfo.first, BInfo.second) << '-' 856234287Sdim << SM.getLineNumber(EInfo.first, EInfo.second) << ':' 857234287Sdim << (SM.getColumnNumber(EInfo.first, EInfo.second)+TokSize) 858234287Sdim << '}'; 859234287Sdim PrintedRange = true; 860234287Sdim } 861234287Sdim 862234287Sdim if (PrintedRange) 863234287Sdim OS << ':'; 864234287Sdim } 865234287Sdim OS << ' '; 866234287Sdim} 867234287Sdim 868234287Sdimvoid TextDiagnostic::emitBasicNote(StringRef Message) { 869234287Sdim // FIXME: Emit this as a real note diagnostic. 870234287Sdim // FIXME: Format an actual diagnostic rather than a hard coded string. 871234287Sdim OS << "note: " << Message << "\n"; 872234287Sdim} 873234287Sdim 874234287Sdimvoid TextDiagnostic::emitIncludeLocation(SourceLocation Loc, 875239462Sdim PresumedLoc PLoc, 876239462Sdim const SourceManager &SM) { 877243830Sdim if (DiagOpts->ShowLocation) 878234287Sdim OS << "In file included from " << PLoc.getFilename() << ':' 879234287Sdim << PLoc.getLine() << ":\n"; 880234287Sdim else 881234287Sdim OS << "In included file:\n"; 882234287Sdim} 883234287Sdim 884249423Sdimvoid TextDiagnostic::emitImportLocation(SourceLocation Loc, PresumedLoc PLoc, 885249423Sdim StringRef ModuleName, 886249423Sdim const SourceManager &SM) { 887249423Sdim if (DiagOpts->ShowLocation) 888249423Sdim OS << "In module '" << ModuleName << "' imported from " 889249423Sdim << PLoc.getFilename() << ':' << PLoc.getLine() << ":\n"; 890249423Sdim else 891249423Sdim OS << "In module " << ModuleName << "':\n"; 892249423Sdim} 893249423Sdim 894249423Sdimvoid TextDiagnostic::emitBuildingModuleLocation(SourceLocation Loc, 895249423Sdim PresumedLoc PLoc, 896249423Sdim StringRef ModuleName, 897249423Sdim const SourceManager &SM) { 898249423Sdim if (DiagOpts->ShowLocation && PLoc.getFilename()) 899249423Sdim OS << "While building module '" << ModuleName << "' imported from " 900249423Sdim << PLoc.getFilename() << ':' << PLoc.getLine() << ":\n"; 901249423Sdim else 902249423Sdim OS << "While building module '" << ModuleName << "':\n"; 903249423Sdim} 904249423Sdim 905249423Sdim/// \brief Highlight a SourceRange (with ~'s) for any characters on LineNo. 906249423Sdimstatic void highlightRange(const CharSourceRange &R, 907249423Sdim unsigned LineNo, FileID FID, 908249423Sdim const SourceColumnMap &map, 909249423Sdim std::string &CaretLine, 910249423Sdim const SourceManager &SM, 911249423Sdim const LangOptions &LangOpts) { 912249423Sdim if (!R.isValid()) return; 913249423Sdim 914249423Sdim SourceLocation Begin = R.getBegin(); 915249423Sdim SourceLocation End = R.getEnd(); 916249423Sdim 917249423Sdim unsigned StartLineNo = SM.getExpansionLineNumber(Begin); 918249423Sdim if (StartLineNo > LineNo || SM.getFileID(Begin) != FID) 919249423Sdim return; // No intersection. 920249423Sdim 921249423Sdim unsigned EndLineNo = SM.getExpansionLineNumber(End); 922249423Sdim if (EndLineNo < LineNo || SM.getFileID(End) != FID) 923249423Sdim return; // No intersection. 924249423Sdim 925249423Sdim // Compute the column number of the start. 926249423Sdim unsigned StartColNo = 0; 927249423Sdim if (StartLineNo == LineNo) { 928249423Sdim StartColNo = SM.getExpansionColumnNumber(Begin); 929249423Sdim if (StartColNo) --StartColNo; // Zero base the col #. 930249423Sdim } 931249423Sdim 932249423Sdim // Compute the column number of the end. 933249423Sdim unsigned EndColNo = map.getSourceLine().size(); 934249423Sdim if (EndLineNo == LineNo) { 935249423Sdim EndColNo = SM.getExpansionColumnNumber(End); 936249423Sdim if (EndColNo) { 937249423Sdim --EndColNo; // Zero base the col #. 938249423Sdim 939249423Sdim // Add in the length of the token, so that we cover multi-char tokens if 940249423Sdim // this is a token range. 941249423Sdim if (R.isTokenRange()) 942249423Sdim EndColNo += Lexer::MeasureTokenLength(End, SM, LangOpts); 943249423Sdim } else { 944249423Sdim EndColNo = CaretLine.size(); 945249423Sdim } 946249423Sdim } 947249423Sdim 948249423Sdim assert(StartColNo <= EndColNo && "Invalid range!"); 949249423Sdim 950249423Sdim // Check that a token range does not highlight only whitespace. 951249423Sdim if (R.isTokenRange()) { 952249423Sdim // Pick the first non-whitespace column. 953249423Sdim while (StartColNo < map.getSourceLine().size() && 954249423Sdim (map.getSourceLine()[StartColNo] == ' ' || 955249423Sdim map.getSourceLine()[StartColNo] == '\t')) 956249423Sdim StartColNo = map.startOfNextColumn(StartColNo); 957249423Sdim 958249423Sdim // Pick the last non-whitespace column. 959249423Sdim if (EndColNo > map.getSourceLine().size()) 960249423Sdim EndColNo = map.getSourceLine().size(); 961249423Sdim while (EndColNo && 962249423Sdim (map.getSourceLine()[EndColNo-1] == ' ' || 963249423Sdim map.getSourceLine()[EndColNo-1] == '\t')) 964249423Sdim EndColNo = map.startOfPreviousColumn(EndColNo); 965249423Sdim 966249423Sdim // If the start/end passed each other, then we are trying to highlight a 967249423Sdim // range that just exists in whitespace, which must be some sort of other 968249423Sdim // bug. 969249423Sdim assert(StartColNo <= EndColNo && "Trying to highlight whitespace??"); 970249423Sdim } 971249423Sdim 972249423Sdim assert(StartColNo <= map.getSourceLine().size() && "Invalid range!"); 973249423Sdim assert(EndColNo <= map.getSourceLine().size() && "Invalid range!"); 974249423Sdim 975249423Sdim // Fill the range with ~'s. 976249423Sdim StartColNo = map.byteToContainingColumn(StartColNo); 977249423Sdim EndColNo = map.byteToContainingColumn(EndColNo); 978249423Sdim 979249423Sdim assert(StartColNo <= EndColNo && "Invalid range!"); 980249423Sdim if (CaretLine.size() < EndColNo) 981249423Sdim CaretLine.resize(EndColNo,' '); 982249423Sdim std::fill(CaretLine.begin()+StartColNo,CaretLine.begin()+EndColNo,'~'); 983249423Sdim} 984249423Sdim 985249423Sdimstatic std::string buildFixItInsertionLine(unsigned LineNo, 986249423Sdim const SourceColumnMap &map, 987249423Sdim ArrayRef<FixItHint> Hints, 988249423Sdim const SourceManager &SM, 989249423Sdim const DiagnosticOptions *DiagOpts) { 990249423Sdim std::string FixItInsertionLine; 991249423Sdim if (Hints.empty() || !DiagOpts->ShowFixits) 992249423Sdim return FixItInsertionLine; 993249423Sdim unsigned PrevHintEndCol = 0; 994249423Sdim 995249423Sdim for (ArrayRef<FixItHint>::iterator I = Hints.begin(), E = Hints.end(); 996249423Sdim I != E; ++I) { 997249423Sdim if (!I->CodeToInsert.empty()) { 998249423Sdim // We have an insertion hint. Determine whether the inserted 999249423Sdim // code contains no newlines and is on the same line as the caret. 1000249423Sdim std::pair<FileID, unsigned> HintLocInfo 1001249423Sdim = SM.getDecomposedExpansionLoc(I->RemoveRange.getBegin()); 1002249423Sdim if (LineNo == SM.getLineNumber(HintLocInfo.first, HintLocInfo.second) && 1003249423Sdim StringRef(I->CodeToInsert).find_first_of("\n\r") == StringRef::npos) { 1004249423Sdim // Insert the new code into the line just below the code 1005249423Sdim // that the user wrote. 1006249423Sdim // Note: When modifying this function, be very careful about what is a 1007249423Sdim // "column" (printed width, platform-dependent) and what is a 1008249423Sdim // "byte offset" (SourceManager "column"). 1009249423Sdim unsigned HintByteOffset 1010249423Sdim = SM.getColumnNumber(HintLocInfo.first, HintLocInfo.second) - 1; 1011249423Sdim 1012249423Sdim // The hint must start inside the source or right at the end 1013249423Sdim assert(HintByteOffset < static_cast<unsigned>(map.bytes())+1); 1014249423Sdim unsigned HintCol = map.byteToContainingColumn(HintByteOffset); 1015249423Sdim 1016249423Sdim // If we inserted a long previous hint, push this one forwards, and add 1017249423Sdim // an extra space to show that this is not part of the previous 1018249423Sdim // completion. This is sort of the best we can do when two hints appear 1019249423Sdim // to overlap. 1020249423Sdim // 1021249423Sdim // Note that if this hint is located immediately after the previous 1022249423Sdim // hint, no space will be added, since the location is more important. 1023249423Sdim if (HintCol < PrevHintEndCol) 1024249423Sdim HintCol = PrevHintEndCol + 1; 1025249423Sdim 1026249423Sdim // FIXME: This function handles multibyte characters in the source, but 1027249423Sdim // not in the fixits. This assertion is intended to catch unintended 1028249423Sdim // use of multibyte characters in fixits. If we decide to do this, we'll 1029249423Sdim // have to track separate byte widths for the source and fixit lines. 1030249423Sdim assert((size_t)llvm::sys::locale::columnWidth(I->CodeToInsert) == 1031249423Sdim I->CodeToInsert.size()); 1032249423Sdim 1033249423Sdim // This relies on one byte per column in our fixit hints. 1034249423Sdim // This should NOT use HintByteOffset, because the source might have 1035249423Sdim // Unicode characters in earlier columns. 1036249423Sdim unsigned LastColumnModified = HintCol + I->CodeToInsert.size(); 1037249423Sdim if (LastColumnModified > FixItInsertionLine.size()) 1038249423Sdim FixItInsertionLine.resize(LastColumnModified, ' '); 1039249423Sdim 1040249423Sdim std::copy(I->CodeToInsert.begin(), I->CodeToInsert.end(), 1041249423Sdim FixItInsertionLine.begin() + HintCol); 1042249423Sdim 1043249423Sdim PrevHintEndCol = LastColumnModified; 1044249423Sdim } else { 1045249423Sdim FixItInsertionLine.clear(); 1046249423Sdim break; 1047249423Sdim } 1048249423Sdim } 1049249423Sdim } 1050249423Sdim 1051249423Sdim expandTabs(FixItInsertionLine, DiagOpts->TabStop); 1052249423Sdim 1053249423Sdim return FixItInsertionLine; 1054249423Sdim} 1055249423Sdim 1056234287Sdim/// \brief Emit a code snippet and caret line. 1057234287Sdim/// 1058234287Sdim/// This routine emits a single line's code snippet and caret line.. 1059234287Sdim/// 1060234287Sdim/// \param Loc The location for the caret. 1061234287Sdim/// \param Ranges The underlined ranges for this code snippet. 1062234287Sdim/// \param Hints The FixIt hints active for this diagnostic. 1063234287Sdimvoid TextDiagnostic::emitSnippetAndCaret( 1064234287Sdim SourceLocation Loc, DiagnosticsEngine::Level Level, 1065234287Sdim SmallVectorImpl<CharSourceRange>& Ranges, 1066239462Sdim ArrayRef<FixItHint> Hints, 1067239462Sdim const SourceManager &SM) { 1068234287Sdim assert(!Loc.isInvalid() && "must have a valid source location here"); 1069234287Sdim assert(Loc.isFileID() && "must have a file location here"); 1070234287Sdim 1071234287Sdim // If caret diagnostics are enabled and we have location, we want to 1072234287Sdim // emit the caret. However, we only do this if the location moved 1073234287Sdim // from the last diagnostic, if the last diagnostic was a note that 1074234287Sdim // was part of a different warning or error diagnostic, or if the 1075234287Sdim // diagnostic has ranges. We don't want to emit the same caret 1076234287Sdim // multiple times if one loc has multiple diagnostics. 1077243830Sdim if (!DiagOpts->ShowCarets) 1078234287Sdim return; 1079234287Sdim if (Loc == LastLoc && Ranges.empty() && Hints.empty() && 1080234287Sdim (LastLevel != DiagnosticsEngine::Note || Level == LastLevel)) 1081234287Sdim return; 1082234287Sdim 1083234287Sdim // Decompose the location into a FID/Offset pair. 1084234287Sdim std::pair<FileID, unsigned> LocInfo = SM.getDecomposedLoc(Loc); 1085234287Sdim FileID FID = LocInfo.first; 1086234287Sdim unsigned FileOffset = LocInfo.second; 1087234287Sdim 1088234287Sdim // Get information about the buffer it points into. 1089234287Sdim bool Invalid = false; 1090239462Sdim const char *BufStart = SM.getBufferData(FID, &Invalid).data(); 1091234287Sdim if (Invalid) 1092234287Sdim return; 1093234287Sdim 1094234287Sdim unsigned LineNo = SM.getLineNumber(FID, FileOffset); 1095234287Sdim unsigned ColNo = SM.getColumnNumber(FID, FileOffset); 1096249423Sdim 1097249423Sdim // Arbitrarily stop showing snippets when the line is too long. 1098251662Sdim static const size_t MaxLineLengthToPrint = 4096; 1099249423Sdim if (ColNo > MaxLineLengthToPrint) 1100249423Sdim return; 1101234287Sdim 1102234287Sdim // Rewind from the current position to the start of the line. 1103234287Sdim const char *TokPtr = BufStart+FileOffset; 1104234287Sdim const char *LineStart = TokPtr-ColNo+1; // Column # is 1-based. 1105234287Sdim 1106234287Sdim // Compute the line end. Scan forward from the error position to the end of 1107234287Sdim // the line. 1108234287Sdim const char *LineEnd = TokPtr; 1109239462Sdim while (*LineEnd != '\n' && *LineEnd != '\r' && *LineEnd != '\0') 1110234287Sdim ++LineEnd; 1111234287Sdim 1112249423Sdim // Arbitrarily stop showing snippets when the line is too long. 1113251662Sdim if (size_t(LineEnd - LineStart) > MaxLineLengthToPrint) 1114249423Sdim return; 1115249423Sdim 1116234287Sdim // Copy the line of code into an std::string for ease of manipulation. 1117234287Sdim std::string SourceLine(LineStart, LineEnd); 1118234287Sdim 1119234287Sdim // Create a line for the caret that is filled with spaces that is the same 1120234287Sdim // length as the line of source code. 1121234287Sdim std::string CaretLine(LineEnd-LineStart, ' '); 1122234287Sdim 1123243830Sdim const SourceColumnMap sourceColMap(SourceLine, DiagOpts->TabStop); 1124234982Sdim 1125234287Sdim // Highlight all of the characters covered by Ranges with ~ characters. 1126234287Sdim for (SmallVectorImpl<CharSourceRange>::iterator I = Ranges.begin(), 1127234287Sdim E = Ranges.end(); 1128234287Sdim I != E; ++I) 1129249423Sdim highlightRange(*I, LineNo, FID, sourceColMap, CaretLine, SM, LangOpts); 1130234287Sdim 1131234287Sdim // Next, insert the caret itself. 1132243830Sdim ColNo = sourceColMap.byteToContainingColumn(ColNo-1); 1133234982Sdim if (CaretLine.size()<ColNo+1) 1134234982Sdim CaretLine.resize(ColNo+1, ' '); 1135234982Sdim CaretLine[ColNo] = '^'; 1136234287Sdim 1137234982Sdim std::string FixItInsertionLine = buildFixItInsertionLine(LineNo, 1138234982Sdim sourceColMap, 1139249423Sdim Hints, SM, 1140249423Sdim DiagOpts.getPtr()); 1141234287Sdim 1142234982Sdim // If the source line is too long for our terminal, select only the 1143234982Sdim // "interesting" source region within that line. 1144243830Sdim unsigned Columns = DiagOpts->MessageLength; 1145234982Sdim if (Columns) 1146234982Sdim selectInterestingSourceRegion(SourceLine, CaretLine, FixItInsertionLine, 1147234982Sdim Columns, sourceColMap); 1148234982Sdim 1149234287Sdim // If we are in -fdiagnostics-print-source-range-info mode, we are trying 1150234287Sdim // to produce easily machine parsable output. Add a space before the 1151234287Sdim // source line and the caret to make it trivial to tell the main diagnostic 1152234287Sdim // line from what the user is intended to see. 1153243830Sdim if (DiagOpts->ShowSourceRanges) { 1154234287Sdim SourceLine = ' ' + SourceLine; 1155234287Sdim CaretLine = ' ' + CaretLine; 1156234287Sdim } 1157234287Sdim 1158234287Sdim // Finally, remove any blank spaces from the end of CaretLine. 1159234287Sdim while (CaretLine[CaretLine.size()-1] == ' ') 1160234287Sdim CaretLine.erase(CaretLine.end()-1); 1161234287Sdim 1162234287Sdim // Emit what we have computed. 1163234982Sdim emitSnippet(SourceLine); 1164234287Sdim 1165243830Sdim if (DiagOpts->ShowColors) 1166234287Sdim OS.changeColor(caretColor, true); 1167234287Sdim OS << CaretLine << '\n'; 1168243830Sdim if (DiagOpts->ShowColors) 1169234287Sdim OS.resetColor(); 1170234287Sdim 1171234287Sdim if (!FixItInsertionLine.empty()) { 1172243830Sdim if (DiagOpts->ShowColors) 1173234287Sdim // Print fixit line in color 1174234287Sdim OS.changeColor(fixitColor, false); 1175243830Sdim if (DiagOpts->ShowSourceRanges) 1176234287Sdim OS << ' '; 1177234287Sdim OS << FixItInsertionLine << '\n'; 1178243830Sdim if (DiagOpts->ShowColors) 1179234287Sdim OS.resetColor(); 1180234287Sdim } 1181234287Sdim 1182234287Sdim // Print out any parseable fixit information requested by the options. 1183239462Sdim emitParseableFixits(Hints, SM); 1184234287Sdim} 1185234287Sdim 1186239462Sdimvoid TextDiagnostic::emitSnippet(StringRef line) { 1187234982Sdim if (line.empty()) 1188234982Sdim return; 1189234982Sdim 1190234982Sdim size_t i = 0; 1191234982Sdim 1192234982Sdim std::string to_print; 1193234982Sdim bool print_reversed = false; 1194234982Sdim 1195234982Sdim while (i<line.size()) { 1196234982Sdim std::pair<SmallString<16>,bool> res 1197243830Sdim = printableTextForNextCharacter(line, &i, DiagOpts->TabStop); 1198234982Sdim bool was_printable = res.second; 1199234982Sdim 1200243830Sdim if (DiagOpts->ShowColors && was_printable == print_reversed) { 1201234982Sdim if (print_reversed) 1202234982Sdim OS.reverseColor(); 1203234982Sdim OS << to_print; 1204234982Sdim to_print.clear(); 1205243830Sdim if (DiagOpts->ShowColors) 1206234982Sdim OS.resetColor(); 1207234982Sdim } 1208234982Sdim 1209234982Sdim print_reversed = !was_printable; 1210234982Sdim to_print += res.first.str(); 1211234982Sdim } 1212234982Sdim 1213243830Sdim if (print_reversed && DiagOpts->ShowColors) 1214234982Sdim OS.reverseColor(); 1215234982Sdim OS << to_print; 1216243830Sdim if (print_reversed && DiagOpts->ShowColors) 1217234982Sdim OS.resetColor(); 1218234982Sdim 1219234982Sdim OS << '\n'; 1220234982Sdim} 1221234982Sdim 1222239462Sdimvoid TextDiagnostic::emitParseableFixits(ArrayRef<FixItHint> Hints, 1223239462Sdim const SourceManager &SM) { 1224243830Sdim if (!DiagOpts->ShowParseableFixits) 1225234287Sdim return; 1226234287Sdim 1227234287Sdim // We follow FixItRewriter's example in not (yet) handling 1228234287Sdim // fix-its in macros. 1229234287Sdim for (ArrayRef<FixItHint>::iterator I = Hints.begin(), E = Hints.end(); 1230234287Sdim I != E; ++I) { 1231234287Sdim if (I->RemoveRange.isInvalid() || 1232234287Sdim I->RemoveRange.getBegin().isMacroID() || 1233234287Sdim I->RemoveRange.getEnd().isMacroID()) 1234234287Sdim return; 1235234287Sdim } 1236234287Sdim 1237234287Sdim for (ArrayRef<FixItHint>::iterator I = Hints.begin(), E = Hints.end(); 1238234287Sdim I != E; ++I) { 1239234287Sdim SourceLocation BLoc = I->RemoveRange.getBegin(); 1240234287Sdim SourceLocation ELoc = I->RemoveRange.getEnd(); 1241234287Sdim 1242234287Sdim std::pair<FileID, unsigned> BInfo = SM.getDecomposedLoc(BLoc); 1243234287Sdim std::pair<FileID, unsigned> EInfo = SM.getDecomposedLoc(ELoc); 1244234287Sdim 1245234287Sdim // Adjust for token ranges. 1246234287Sdim if (I->RemoveRange.isTokenRange()) 1247234287Sdim EInfo.second += Lexer::MeasureTokenLength(ELoc, SM, LangOpts); 1248234287Sdim 1249234287Sdim // We specifically do not do word-wrapping or tab-expansion here, 1250234287Sdim // because this is supposed to be easy to parse. 1251234287Sdim PresumedLoc PLoc = SM.getPresumedLoc(BLoc); 1252234287Sdim if (PLoc.isInvalid()) 1253234287Sdim break; 1254234287Sdim 1255234287Sdim OS << "fix-it:\""; 1256234287Sdim OS.write_escaped(PLoc.getFilename()); 1257234287Sdim OS << "\":{" << SM.getLineNumber(BInfo.first, BInfo.second) 1258234287Sdim << ':' << SM.getColumnNumber(BInfo.first, BInfo.second) 1259234287Sdim << '-' << SM.getLineNumber(EInfo.first, EInfo.second) 1260234287Sdim << ':' << SM.getColumnNumber(EInfo.first, EInfo.second) 1261234287Sdim << "}:\""; 1262234287Sdim OS.write_escaped(I->CodeToInsert); 1263234287Sdim OS << "\"\n"; 1264234287Sdim } 1265234287Sdim} 1266