TextDiagnosticPrinter.cpp revision f6ac97b101c8840efa92bf29166077ce4049e293
1//===--- TextDiagnosticPrinter.cpp - Diagnostic Printer -------------------===//
2//
3//                     The LLVM Compiler Infrastructure
4//
5// This file is distributed under the University of Illinois Open Source
6// License. See LICENSE.TXT for details.
7//
8//===----------------------------------------------------------------------===//
9//
10// This diagnostic client prints out their diagnostic messages.
11//
12//===----------------------------------------------------------------------===//
13
14#include "clang/Frontend/TextDiagnosticPrinter.h"
15#include "clang/Basic/SourceManager.h"
16#include "clang/Frontend/DiagnosticOptions.h"
17#include "clang/Lex/Lexer.h"
18#include "llvm/Support/MemoryBuffer.h"
19#include "llvm/Support/raw_ostream.h"
20#include "llvm/ADT/SmallString.h"
21#include <algorithm>
22using namespace clang;
23
24static const enum llvm::raw_ostream::Colors noteColor =
25  llvm::raw_ostream::BLACK;
26static const enum llvm::raw_ostream::Colors fixitColor =
27  llvm::raw_ostream::GREEN;
28static const enum llvm::raw_ostream::Colors caretColor =
29  llvm::raw_ostream::GREEN;
30static const enum llvm::raw_ostream::Colors warningColor =
31  llvm::raw_ostream::MAGENTA;
32static const enum llvm::raw_ostream::Colors errorColor = llvm::raw_ostream::RED;
33static const enum llvm::raw_ostream::Colors fatalColor = llvm::raw_ostream::RED;
34// Used for changing only the bold attribute.
35static const enum llvm::raw_ostream::Colors savedColor =
36  llvm::raw_ostream::SAVEDCOLOR;
37
38/// \brief Number of spaces to indent when word-wrapping.
39const unsigned WordWrapIndentation = 6;
40
41TextDiagnosticPrinter::TextDiagnosticPrinter(llvm::raw_ostream &os,
42                                             const DiagnosticOptions &diags,
43                                             bool _OwnsOutputStream)
44  : OS(os), LangOpts(0), DiagOpts(&diags),
45    LastCaretDiagnosticWasNote(0),
46    OwnsOutputStream(_OwnsOutputStream) {
47}
48
49TextDiagnosticPrinter::~TextDiagnosticPrinter() {
50  if (OwnsOutputStream)
51    delete &OS;
52}
53
54void TextDiagnosticPrinter::
55PrintIncludeStack(SourceLocation Loc, const SourceManager &SM) {
56  if (Loc.isInvalid()) return;
57
58  PresumedLoc PLoc = SM.getPresumedLoc(Loc);
59
60  // Print out the other include frames first.
61  PrintIncludeStack(PLoc.getIncludeLoc(), SM);
62
63  if (DiagOpts->ShowLocation)
64    OS << "In file included from " << PLoc.getFilename()
65       << ':' << PLoc.getLine() << ":\n";
66  else
67    OS << "In included file:\n";
68}
69
70/// HighlightRange - Given a SourceRange and a line number, highlight (with ~'s)
71/// any characters in LineNo that intersect the SourceRange.
72void TextDiagnosticPrinter::HighlightRange(const SourceRange &R,
73                                           const SourceManager &SM,
74                                           unsigned LineNo, FileID FID,
75                                           std::string &CaretLine,
76                                           const std::string &SourceLine) {
77  assert(CaretLine.size() == SourceLine.size() &&
78         "Expect a correspondence between source and caret line!");
79  if (!R.isValid()) return;
80
81  SourceLocation Begin = SM.getInstantiationLoc(R.getBegin());
82  SourceLocation End = SM.getInstantiationLoc(R.getEnd());
83
84  // If the End location and the start location are the same and are a macro
85  // location, then the range was something that came from a macro expansion
86  // or _Pragma.  If this is an object-like macro, the best we can do is to
87  // highlight the range.  If this is a function-like macro, we'd also like to
88  // highlight the arguments.
89  if (Begin == End && R.getEnd().isMacroID())
90    End = SM.getInstantiationRange(R.getEnd()).second;
91
92  unsigned StartLineNo = SM.getInstantiationLineNumber(Begin);
93  if (StartLineNo > LineNo || SM.getFileID(Begin) != FID)
94    return;  // No intersection.
95
96  unsigned EndLineNo = SM.getInstantiationLineNumber(End);
97  if (EndLineNo < LineNo || SM.getFileID(End) != FID)
98    return;  // No intersection.
99
100  // Compute the column number of the start.
101  unsigned StartColNo = 0;
102  if (StartLineNo == LineNo) {
103    StartColNo = SM.getInstantiationColumnNumber(Begin);
104    if (StartColNo) --StartColNo;  // Zero base the col #.
105  }
106
107  // Compute the column number of the end.
108  unsigned EndColNo = CaretLine.size();
109  if (EndLineNo == LineNo) {
110    EndColNo = SM.getInstantiationColumnNumber(End);
111    if (EndColNo) {
112      --EndColNo;  // Zero base the col #.
113
114      // Add in the length of the token, so that we cover multi-char tokens.
115      EndColNo += Lexer::MeasureTokenLength(End, SM, *LangOpts);
116    } else {
117      EndColNo = CaretLine.size();
118    }
119  }
120
121  assert(StartColNo <= EndColNo && "Invalid range!");
122
123  // Pick the first non-whitespace column.
124  while (StartColNo < SourceLine.size() &&
125         (SourceLine[StartColNo] == ' ' || SourceLine[StartColNo] == '\t'))
126    ++StartColNo;
127
128  // Pick the last non-whitespace column.
129  if (EndColNo > SourceLine.size())
130    EndColNo = SourceLine.size();
131  while (EndColNo-1 &&
132         (SourceLine[EndColNo-1] == ' ' || SourceLine[EndColNo-1] == '\t'))
133    --EndColNo;
134
135  // If the start/end passed each other, then we are trying to highlight a range
136  // that just exists in whitespace, which must be some sort of other bug.
137  assert(StartColNo <= EndColNo && "Trying to highlight whitespace??");
138
139  // Fill the range with ~'s.
140  for (unsigned i = StartColNo; i < EndColNo; ++i)
141    CaretLine[i] = '~';
142}
143
144/// \brief When the source code line we want to print is too long for
145/// the terminal, select the "interesting" region.
146static void SelectInterestingSourceRegion(std::string &SourceLine,
147                                          std::string &CaretLine,
148                                          std::string &FixItInsertionLine,
149                                          unsigned EndOfCaretToken,
150                                          unsigned Columns) {
151  if (CaretLine.size() > SourceLine.size())
152    SourceLine.resize(CaretLine.size(), ' ');
153
154  // Find the slice that we need to display the full caret line
155  // correctly.
156  unsigned CaretStart = 0, CaretEnd = CaretLine.size();
157  for (; CaretStart != CaretEnd; ++CaretStart)
158    if (!isspace(CaretLine[CaretStart]))
159      break;
160
161  for (; CaretEnd != CaretStart; --CaretEnd)
162    if (!isspace(CaretLine[CaretEnd - 1]))
163      break;
164
165  // Make sure we don't chop the string shorter than the caret token
166  // itself.
167  if (CaretEnd < EndOfCaretToken)
168    CaretEnd = EndOfCaretToken;
169
170  // If we have a fix-it line, make sure the slice includes all of the
171  // fix-it information.
172  if (!FixItInsertionLine.empty()) {
173    unsigned FixItStart = 0, FixItEnd = FixItInsertionLine.size();
174    for (; FixItStart != FixItEnd; ++FixItStart)
175      if (!isspace(FixItInsertionLine[FixItStart]))
176        break;
177
178    for (; FixItEnd != FixItStart; --FixItEnd)
179      if (!isspace(FixItInsertionLine[FixItEnd - 1]))
180        break;
181
182    if (FixItStart < CaretStart)
183      CaretStart = FixItStart;
184    if (FixItEnd > CaretEnd)
185      CaretEnd = FixItEnd;
186  }
187
188  // CaretLine[CaretStart, CaretEnd) contains all of the interesting
189  // parts of the caret line. While this slice is smaller than the
190  // number of columns we have, try to grow the slice to encompass
191  // more context.
192
193  // If the end of the interesting region comes before we run out of
194  // space in the terminal, start at the beginning of the line.
195  if (Columns > 3 && CaretEnd < Columns - 3)
196    CaretStart = 0;
197
198  unsigned TargetColumns = Columns;
199  if (TargetColumns > 8)
200    TargetColumns -= 8; // Give us extra room for the ellipses.
201  unsigned SourceLength = SourceLine.size();
202  while ((CaretEnd - CaretStart) < TargetColumns) {
203    bool ExpandedRegion = false;
204    // Move the start of the interesting region left until we've
205    // pulled in something else interesting.
206    if (CaretStart == 1)
207      CaretStart = 0;
208    else if (CaretStart > 1) {
209      unsigned NewStart = CaretStart - 1;
210
211      // Skip over any whitespace we see here; we're looking for
212      // another bit of interesting text.
213      while (NewStart && isspace(SourceLine[NewStart]))
214        --NewStart;
215
216      // Skip over this bit of "interesting" text.
217      while (NewStart && !isspace(SourceLine[NewStart]))
218        --NewStart;
219
220      // Move up to the non-whitespace character we just saw.
221      if (NewStart)
222        ++NewStart;
223
224      // If we're still within our limit, update the starting
225      // position within the source/caret line.
226      if (CaretEnd - NewStart <= TargetColumns) {
227        CaretStart = NewStart;
228        ExpandedRegion = true;
229      }
230    }
231
232    // Move the end of the interesting region right until we've
233    // pulled in something else interesting.
234    if (CaretEnd != SourceLength) {
235      assert(CaretEnd < SourceLength && "Unexpected caret position!");
236      unsigned NewEnd = CaretEnd;
237
238      // Skip over any whitespace we see here; we're looking for
239      // another bit of interesting text.
240      while (NewEnd != SourceLength && isspace(SourceLine[NewEnd - 1]))
241        ++NewEnd;
242
243      // Skip over this bit of "interesting" text.
244      while (NewEnd != SourceLength && !isspace(SourceLine[NewEnd - 1]))
245        ++NewEnd;
246
247      if (NewEnd - CaretStart <= TargetColumns) {
248        CaretEnd = NewEnd;
249        ExpandedRegion = true;
250      }
251    }
252
253    if (!ExpandedRegion)
254      break;
255  }
256
257  // [CaretStart, CaretEnd) is the slice we want. Update the various
258  // output lines to show only this slice, with two-space padding
259  // before the lines so that it looks nicer.
260  if (CaretEnd < SourceLine.size())
261    SourceLine.replace(CaretEnd, std::string::npos, "...");
262  if (CaretEnd < CaretLine.size())
263    CaretLine.erase(CaretEnd, std::string::npos);
264  if (FixItInsertionLine.size() > CaretEnd)
265    FixItInsertionLine.erase(CaretEnd, std::string::npos);
266
267  if (CaretStart > 2) {
268    SourceLine.replace(0, CaretStart, "  ...");
269    CaretLine.replace(0, CaretStart, "     ");
270    if (FixItInsertionLine.size() >= CaretStart)
271      FixItInsertionLine.replace(0, CaretStart, "     ");
272  }
273}
274
275void TextDiagnosticPrinter::EmitCaretDiagnostic(SourceLocation Loc,
276                                                SourceRange *Ranges,
277                                                unsigned NumRanges,
278                                                SourceManager &SM,
279                                          const CodeModificationHint *Hints,
280                                                unsigned NumHints,
281                                                unsigned Columns) {
282  assert(LangOpts && "Unexpected diagnostic outside source file processing");
283  assert(!Loc.isInvalid() && "must have a valid source location here");
284
285  // If this is a macro ID, first emit information about where this was
286  // instantiated (recursively) then emit information about where the token was
287  // spelled from.
288  if (!Loc.isFileID()) {
289    SourceLocation OneLevelUp = SM.getImmediateInstantiationRange(Loc).first;
290    // FIXME: Map ranges?
291    EmitCaretDiagnostic(OneLevelUp, Ranges, NumRanges, SM, 0, 0, Columns);
292
293    // Map the location.
294    Loc = SM.getImmediateSpellingLoc(Loc);
295
296    // Map the ranges.
297    for (unsigned i = 0; i != NumRanges; ++i) {
298      SourceLocation S = Ranges[i].getBegin(), E = Ranges[i].getEnd();
299      if (S.isMacroID()) S = SM.getImmediateSpellingLoc(S);
300      if (E.isMacroID()) E = SM.getImmediateSpellingLoc(E);
301      Ranges[i] = SourceRange(S, E);
302    }
303
304    // Get the pretty name, according to #line directives etc.
305    PresumedLoc PLoc = SM.getPresumedLoc(Loc);
306
307    // If this diagnostic is not in the main file, print out the "included from"
308    // lines.
309    if (LastWarningLoc != PLoc.getIncludeLoc()) {
310      LastWarningLoc = PLoc.getIncludeLoc();
311      PrintIncludeStack(LastWarningLoc, SM);
312    }
313
314    if (DiagOpts->ShowLocation) {
315      // Emit the file/line/column that this expansion came from.
316      OS << PLoc.getFilename() << ':' << PLoc.getLine() << ':';
317      if (DiagOpts->ShowColumn)
318        OS << PLoc.getColumn() << ':';
319      OS << ' ';
320    }
321    OS << "note: instantiated from:\n";
322
323    EmitCaretDiagnostic(Loc, Ranges, NumRanges, SM, Hints, NumHints, Columns);
324    return;
325  }
326
327  // Decompose the location into a FID/Offset pair.
328  std::pair<FileID, unsigned> LocInfo = SM.getDecomposedLoc(Loc);
329  FileID FID = LocInfo.first;
330  unsigned FileOffset = LocInfo.second;
331
332  // Get information about the buffer it points into.
333  bool Invalid = false;
334  const char *BufStart = SM.getBufferData(FID, &Invalid).data();
335  if (Invalid)
336    return;
337
338  unsigned ColNo = SM.getColumnNumber(FID, FileOffset);
339  unsigned CaretEndColNo
340    = ColNo + Lexer::MeasureTokenLength(Loc, SM, *LangOpts);
341
342  // Rewind from the current position to the start of the line.
343  const char *TokPtr = BufStart+FileOffset;
344  const char *LineStart = TokPtr-ColNo+1; // Column # is 1-based.
345
346
347  // Compute the line end.  Scan forward from the error position to the end of
348  // the line.
349  const char *LineEnd = TokPtr;
350  while (*LineEnd != '\n' && *LineEnd != '\r' && *LineEnd != '\0')
351    ++LineEnd;
352
353  // FIXME: This shouldn't be necessary, but the CaretEndColNo can extend past
354  // the source line length as currently being computed. See
355  // test/Misc/message-length.c.
356  CaretEndColNo = std::min(CaretEndColNo, unsigned(LineEnd - LineStart));
357
358  // Copy the line of code into an std::string for ease of manipulation.
359  std::string SourceLine(LineStart, LineEnd);
360
361  // Create a line for the caret that is filled with spaces that is the same
362  // length as the line of source code.
363  std::string CaretLine(LineEnd-LineStart, ' ');
364
365  // Highlight all of the characters covered by Ranges with ~ characters.
366  if (NumRanges) {
367    unsigned LineNo = SM.getLineNumber(FID, FileOffset);
368
369    for (unsigned i = 0, e = NumRanges; i != e; ++i)
370      HighlightRange(Ranges[i], SM, LineNo, FID, CaretLine, SourceLine);
371  }
372
373  // Next, insert the caret itself.
374  if (ColNo-1 < CaretLine.size())
375    CaretLine[ColNo-1] = '^';
376  else
377    CaretLine.push_back('^');
378
379  // Scan the source line, looking for tabs.  If we find any, manually expand
380  // them to spaces and update the CaretLine to match.
381  for (unsigned i = 0; i != SourceLine.size(); ++i) {
382    if (SourceLine[i] != '\t') continue;
383
384    // Replace this tab with at least one space.
385    SourceLine[i] = ' ';
386
387    // Compute the number of spaces we need to insert.
388    unsigned TabStop = DiagOpts->TabStop;
389    assert(0 < TabStop && TabStop <= DiagnosticOptions::MaxTabStop &&
390           "Invalid -ftabstop value");
391    unsigned NumSpaces = ((i+TabStop)/TabStop * TabStop) - (i+1);
392    assert(NumSpaces < TabStop && "Invalid computation of space amt");
393
394    // Insert spaces into the SourceLine.
395    SourceLine.insert(i+1, NumSpaces, ' ');
396
397    // Insert spaces or ~'s into CaretLine.
398    CaretLine.insert(i+1, NumSpaces, CaretLine[i] == '~' ? '~' : ' ');
399  }
400
401  // If we are in -fdiagnostics-print-source-range-info mode, we are trying to
402  // produce easily machine parsable output.  Add a space before the source line
403  // and the caret to make it trivial to tell the main diagnostic line from what
404  // the user is intended to see.
405  if (DiagOpts->ShowSourceRanges) {
406    SourceLine = ' ' + SourceLine;
407    CaretLine = ' ' + CaretLine;
408  }
409
410  std::string FixItInsertionLine;
411  if (NumHints && DiagOpts->ShowFixits) {
412    for (const CodeModificationHint *Hint = Hints, *LastHint = Hints + NumHints;
413         Hint != LastHint; ++Hint) {
414      if (Hint->InsertionLoc.isValid()) {
415        // We have an insertion hint. Determine whether the inserted
416        // code is on the same line as the caret.
417        std::pair<FileID, unsigned> HintLocInfo
418          = SM.getDecomposedInstantiationLoc(Hint->InsertionLoc);
419        if (SM.getLineNumber(HintLocInfo.first, HintLocInfo.second) ==
420              SM.getLineNumber(FID, FileOffset)) {
421          // Insert the new code into the line just below the code
422          // that the user wrote.
423          unsigned HintColNo
424            = SM.getColumnNumber(HintLocInfo.first, HintLocInfo.second);
425          unsigned LastColumnModified
426            = HintColNo - 1 + Hint->CodeToInsert.size();
427          if (LastColumnModified > FixItInsertionLine.size())
428            FixItInsertionLine.resize(LastColumnModified, ' ');
429          std::copy(Hint->CodeToInsert.begin(), Hint->CodeToInsert.end(),
430                    FixItInsertionLine.begin() + HintColNo - 1);
431        } else {
432          FixItInsertionLine.clear();
433          break;
434        }
435      }
436    }
437    // Now that we have the entire fixit line, expand the tabs in it.
438    // Since we don't want to insert spaces in the middle of a word,
439    // find each word and the column it should line up with and insert
440    // spaces until they match.
441    if (!FixItInsertionLine.empty()) {
442      unsigned FixItPos = 0;
443      unsigned LinePos = 0;
444      unsigned TabExpandedCol = 0;
445      unsigned LineLength = LineEnd - LineStart;
446
447      while (FixItPos < FixItInsertionLine.size() && LinePos < LineLength) {
448        // Find the next word in the FixIt line.
449        while (FixItPos < FixItInsertionLine.size() &&
450               FixItInsertionLine[FixItPos] == ' ')
451          ++FixItPos;
452        unsigned CharDistance = FixItPos - TabExpandedCol;
453
454        // Walk forward in the source line, keeping track of
455        // the tab-expanded column.
456        for (unsigned I = 0; I < CharDistance; ++I, ++LinePos)
457          if (LinePos >= LineLength || LineStart[LinePos] != '\t')
458            ++TabExpandedCol;
459          else
460            TabExpandedCol =
461              (TabExpandedCol/DiagOpts->TabStop + 1) * DiagOpts->TabStop;
462
463        // Adjust the fixit line to match this column.
464        FixItInsertionLine.insert(FixItPos, TabExpandedCol-FixItPos, ' ');
465        FixItPos = TabExpandedCol;
466
467        // Walk to the end of the word.
468        while (FixItPos < FixItInsertionLine.size() &&
469               FixItInsertionLine[FixItPos] != ' ')
470          ++FixItPos;
471      }
472    }
473  }
474
475  // If the source line is too long for our terminal, select only the
476  // "interesting" source region within that line.
477  if (Columns && SourceLine.size() > Columns)
478    SelectInterestingSourceRegion(SourceLine, CaretLine, FixItInsertionLine,
479                                  CaretEndColNo, Columns);
480
481  // Finally, remove any blank spaces from the end of CaretLine.
482  while (CaretLine[CaretLine.size()-1] == ' ')
483    CaretLine.erase(CaretLine.end()-1);
484
485  // Emit what we have computed.
486  OS << SourceLine << '\n';
487
488  if (DiagOpts->ShowColors)
489    OS.changeColor(caretColor, true);
490  OS << CaretLine << '\n';
491  if (DiagOpts->ShowColors)
492    OS.resetColor();
493
494  if (!FixItInsertionLine.empty()) {
495    if (DiagOpts->ShowColors)
496      // Print fixit line in color
497      OS.changeColor(fixitColor, false);
498    if (DiagOpts->ShowSourceRanges)
499      OS << ' ';
500    OS << FixItInsertionLine << '\n';
501    if (DiagOpts->ShowColors)
502      OS.resetColor();
503  }
504}
505
506/// \brief Skip over whitespace in the string, starting at the given
507/// index.
508///
509/// \returns The index of the first non-whitespace character that is
510/// greater than or equal to Idx or, if no such character exists,
511/// returns the end of the string.
512static unsigned skipWhitespace(unsigned Idx,
513                               const llvm::SmallVectorImpl<char> &Str,
514                               unsigned Length) {
515  while (Idx < Length && isspace(Str[Idx]))
516    ++Idx;
517  return Idx;
518}
519
520/// \brief If the given character is the start of some kind of
521/// balanced punctuation (e.g., quotes or parentheses), return the
522/// character that will terminate the punctuation.
523///
524/// \returns The ending punctuation character, if any, or the NULL
525/// character if the input character does not start any punctuation.
526static inline char findMatchingPunctuation(char c) {
527  switch (c) {
528  case '\'': return '\'';
529  case '`': return '\'';
530  case '"':  return '"';
531  case '(':  return ')';
532  case '[': return ']';
533  case '{': return '}';
534  default: break;
535  }
536
537  return 0;
538}
539
540/// \brief Find the end of the word starting at the given offset
541/// within a string.
542///
543/// \returns the index pointing one character past the end of the
544/// word.
545static unsigned findEndOfWord(unsigned Start,
546                              const llvm::SmallVectorImpl<char> &Str,
547                              unsigned Length, unsigned Column,
548                              unsigned Columns) {
549  assert(Start < Str.size() && "Invalid start position!");
550  unsigned End = Start + 1;
551
552  // If we are already at the end of the string, take that as the word.
553  if (End == Str.size())
554    return End;
555
556  // Determine if the start of the string is actually opening
557  // punctuation, e.g., a quote or parentheses.
558  char EndPunct = findMatchingPunctuation(Str[Start]);
559  if (!EndPunct) {
560    // This is a normal word. Just find the first space character.
561    while (End < Length && !isspace(Str[End]))
562      ++End;
563    return End;
564  }
565
566  // We have the start of a balanced punctuation sequence (quotes,
567  // parentheses, etc.). Determine the full sequence is.
568  llvm::SmallString<16> PunctuationEndStack;
569  PunctuationEndStack.push_back(EndPunct);
570  while (End < Length && !PunctuationEndStack.empty()) {
571    if (Str[End] == PunctuationEndStack.back())
572      PunctuationEndStack.pop_back();
573    else if (char SubEndPunct = findMatchingPunctuation(Str[End]))
574      PunctuationEndStack.push_back(SubEndPunct);
575
576    ++End;
577  }
578
579  // Find the first space character after the punctuation ended.
580  while (End < Length && !isspace(Str[End]))
581    ++End;
582
583  unsigned PunctWordLength = End - Start;
584  if (// If the word fits on this line
585      Column + PunctWordLength <= Columns ||
586      // ... or the word is "short enough" to take up the next line
587      // without too much ugly white space
588      PunctWordLength < Columns/3)
589    return End; // Take the whole thing as a single "word".
590
591  // The whole quoted/parenthesized string is too long to print as a
592  // single "word". Instead, find the "word" that starts just after
593  // the punctuation and use that end-point instead. This will recurse
594  // until it finds something small enough to consider a word.
595  return findEndOfWord(Start + 1, Str, Length, Column + 1, Columns);
596}
597
598/// \brief Print the given string to a stream, word-wrapping it to
599/// some number of columns in the process.
600///
601/// \brief OS the stream to which the word-wrapping string will be
602/// emitted.
603///
604/// \brief Str the string to word-wrap and output.
605///
606/// \brief Columns the number of columns to word-wrap to.
607///
608/// \brief Column the column number at which the first character of \p
609/// Str will be printed. This will be non-zero when part of the first
610/// line has already been printed.
611///
612/// \brief Indentation the number of spaces to indent any lines beyond
613/// the first line.
614///
615/// \returns true if word-wrapping was required, or false if the
616/// string fit on the first line.
617static bool PrintWordWrapped(llvm::raw_ostream &OS,
618                             const llvm::SmallVectorImpl<char> &Str,
619                             unsigned Columns,
620                             unsigned Column = 0,
621                             unsigned Indentation = WordWrapIndentation) {
622  unsigned Length = Str.size();
623
624  // If there is a newline in this message somewhere, find that
625  // newline and split the message into the part before the newline
626  // (which will be word-wrapped) and the part from the newline one
627  // (which will be emitted unchanged).
628  for (unsigned I = 0; I != Length; ++I)
629    if (Str[I] == '\n') {
630      Length = I;
631      break;
632    }
633
634  // The string used to indent each line.
635  llvm::SmallString<16> IndentStr;
636  IndentStr.assign(Indentation, ' ');
637  bool Wrapped = false;
638  for (unsigned WordStart = 0, WordEnd; WordStart < Length;
639       WordStart = WordEnd) {
640    // Find the beginning of the next word.
641    WordStart = skipWhitespace(WordStart, Str, Length);
642    if (WordStart == Length)
643      break;
644
645    // Find the end of this word.
646    WordEnd = findEndOfWord(WordStart, Str, Length, Column, Columns);
647
648    // Does this word fit on the current line?
649    unsigned WordLength = WordEnd - WordStart;
650    if (Column + WordLength < Columns) {
651      // This word fits on the current line; print it there.
652      if (WordStart) {
653        OS << ' ';
654        Column += 1;
655      }
656      OS.write(&Str[WordStart], WordLength);
657      Column += WordLength;
658      continue;
659    }
660
661    // This word does not fit on the current line, so wrap to the next
662    // line.
663    OS << '\n';
664    OS.write(&IndentStr[0], Indentation);
665    OS.write(&Str[WordStart], WordLength);
666    Column = Indentation + WordLength;
667    Wrapped = true;
668  }
669
670  if (Length == Str.size())
671    return Wrapped; // We're done.
672
673  // There is a newline in the message, followed by something that
674  // will not be word-wrapped. Print that.
675  OS.write(&Str[Length], Str.size() - Length);
676  return true;
677}
678
679void TextDiagnosticPrinter::HandleDiagnostic(Diagnostic::Level Level,
680                                             const DiagnosticInfo &Info) {
681  // Keeps track of the the starting position of the location
682  // information (e.g., "foo.c:10:4:") that precedes the error
683  // message. We use this information to determine how long the
684  // file+line+column number prefix is.
685  uint64_t StartOfLocationInfo = OS.tell();
686
687  if (!Prefix.empty())
688    OS << Prefix << ": ";
689
690  // If the location is specified, print out a file/line/col and include trace
691  // if enabled.
692  if (Info.getLocation().isValid()) {
693    const SourceManager &SM = Info.getLocation().getManager();
694    PresumedLoc PLoc = SM.getPresumedLoc(Info.getLocation());
695    unsigned LineNo = PLoc.getLine();
696
697    // First, if this diagnostic is not in the main file, print out the
698    // "included from" lines.
699    if (LastWarningLoc != PLoc.getIncludeLoc()) {
700      LastWarningLoc = PLoc.getIncludeLoc();
701      PrintIncludeStack(LastWarningLoc, SM);
702      StartOfLocationInfo = OS.tell();
703    }
704
705    // Compute the column number.
706    if (DiagOpts->ShowLocation) {
707      if (DiagOpts->ShowColors)
708        OS.changeColor(savedColor, true);
709
710      // Emit a Visual Studio compatible line number syntax.
711      if (LangOpts && LangOpts->Microsoft) {
712        OS << PLoc.getFilename() << '(' << LineNo << ')';
713        OS << " : ";
714      } else {
715        OS << PLoc.getFilename() << ':' << LineNo << ':';
716        if (DiagOpts->ShowColumn)
717          if (unsigned ColNo = PLoc.getColumn())
718            OS << ColNo << ':';
719      }
720      if (DiagOpts->ShowSourceRanges && Info.getNumRanges()) {
721        FileID CaretFileID =
722          SM.getFileID(SM.getInstantiationLoc(Info.getLocation()));
723        bool PrintedRange = false;
724
725        for (unsigned i = 0, e = Info.getNumRanges(); i != e; ++i) {
726          // Ignore invalid ranges.
727          if (!Info.getRange(i).isValid()) continue;
728
729          SourceLocation B = Info.getRange(i).getBegin();
730          SourceLocation E = Info.getRange(i).getEnd();
731          B = SM.getInstantiationLoc(B);
732          E = SM.getInstantiationLoc(E);
733
734          // If the End location and the start location are the same and are a
735          // macro location, then the range was something that came from a macro
736          // expansion or _Pragma.  If this is an object-like macro, the best we
737          // can do is to highlight the range.  If this is a function-like
738          // macro, we'd also like to highlight the arguments.
739          if (B == E && Info.getRange(i).getEnd().isMacroID())
740            E = SM.getInstantiationRange(Info.getRange(i).getEnd()).second;
741
742          std::pair<FileID, unsigned> BInfo = SM.getDecomposedLoc(B);
743          std::pair<FileID, unsigned> EInfo = SM.getDecomposedLoc(E);
744
745          // If the start or end of the range is in another file, just discard
746          // it.
747          if (BInfo.first != CaretFileID || EInfo.first != CaretFileID)
748            continue;
749
750          // Add in the length of the token, so that we cover multi-char tokens.
751          unsigned TokSize = Lexer::MeasureTokenLength(E, SM, *LangOpts);
752
753          OS << '{' << SM.getLineNumber(BInfo.first, BInfo.second) << ':'
754             << SM.getColumnNumber(BInfo.first, BInfo.second) << '-'
755             << SM.getLineNumber(EInfo.first, EInfo.second) << ':'
756             << (SM.getColumnNumber(EInfo.first, EInfo.second)+TokSize) << '}';
757          PrintedRange = true;
758        }
759
760        if (PrintedRange)
761          OS << ':';
762      }
763      OS << ' ';
764      if (DiagOpts->ShowColors)
765        OS.resetColor();
766    }
767  }
768
769  if (DiagOpts->ShowColors) {
770    // Print diagnostic category in bold and color
771    switch (Level) {
772    case Diagnostic::Ignored: assert(0 && "Invalid diagnostic type");
773    case Diagnostic::Note:    OS.changeColor(noteColor, true); break;
774    case Diagnostic::Warning: OS.changeColor(warningColor, true); break;
775    case Diagnostic::Error:   OS.changeColor(errorColor, true); break;
776    case Diagnostic::Fatal:   OS.changeColor(fatalColor, true); break;
777    }
778  }
779
780  switch (Level) {
781  case Diagnostic::Ignored: assert(0 && "Invalid diagnostic type");
782  case Diagnostic::Note:    OS << "note: "; break;
783  case Diagnostic::Warning: OS << "warning: "; break;
784  case Diagnostic::Error:   OS << "error: "; break;
785  case Diagnostic::Fatal:   OS << "fatal error: "; break;
786  }
787
788  if (DiagOpts->ShowColors)
789    OS.resetColor();
790
791  llvm::SmallString<100> OutStr;
792  Info.FormatDiagnostic(OutStr);
793
794  if (DiagOpts->ShowOptionNames) {
795    if (const char *Opt = Diagnostic::getWarningOptionForDiag(Info.getID())) {
796      OutStr += " [-W";
797      OutStr += Opt;
798      OutStr += ']';
799    } else if (Diagnostic::isBuiltinExtensionDiag(Info.getID())) {
800      OutStr += " [-pedantic]";
801    }
802  }
803
804  if (DiagOpts->ShowColors) {
805    // Print warnings, errors and fatal errors in bold, no color
806    switch (Level) {
807    case Diagnostic::Warning: OS.changeColor(savedColor, true); break;
808    case Diagnostic::Error:   OS.changeColor(savedColor, true); break;
809    case Diagnostic::Fatal:   OS.changeColor(savedColor, true); break;
810    default: break; //don't bold notes
811    }
812  }
813
814  if (DiagOpts->MessageLength) {
815    // We will be word-wrapping the error message, so compute the
816    // column number where we currently are (after printing the
817    // location information).
818    unsigned Column = OS.tell() - StartOfLocationInfo;
819    PrintWordWrapped(OS, OutStr, DiagOpts->MessageLength, Column);
820  } else {
821    OS.write(OutStr.begin(), OutStr.size());
822  }
823  OS << '\n';
824  if (DiagOpts->ShowColors)
825    OS.resetColor();
826
827  // If caret diagnostics are enabled and we have location, we want to
828  // emit the caret.  However, we only do this if the location moved
829  // from the last diagnostic, if the last diagnostic was a note that
830  // was part of a different warning or error diagnostic, or if the
831  // diagnostic has ranges.  We don't want to emit the same caret
832  // multiple times if one loc has multiple diagnostics.
833  if (DiagOpts->ShowCarets && Info.getLocation().isValid() &&
834      ((LastLoc != Info.getLocation()) || Info.getNumRanges() ||
835       (LastCaretDiagnosticWasNote && Level != Diagnostic::Note) ||
836       Info.getNumCodeModificationHints())) {
837    // Cache the LastLoc, it allows us to omit duplicate source/caret spewage.
838    LastLoc = Info.getLocation();
839    LastCaretDiagnosticWasNote = (Level == Diagnostic::Note);
840
841    // Get the ranges into a local array we can hack on.
842    SourceRange Ranges[20];
843    unsigned NumRanges = Info.getNumRanges();
844    assert(NumRanges < 20 && "Out of space");
845    for (unsigned i = 0; i != NumRanges; ++i)
846      Ranges[i] = Info.getRange(i);
847
848    unsigned NumHints = Info.getNumCodeModificationHints();
849    for (unsigned idx = 0; idx < NumHints; ++idx) {
850      const CodeModificationHint &Hint = Info.getCodeModificationHint(idx);
851      if (Hint.RemoveRange.isValid()) {
852        assert(NumRanges < 20 && "Out of space");
853        Ranges[NumRanges++] = Hint.RemoveRange;
854      }
855    }
856
857    EmitCaretDiagnostic(LastLoc, Ranges, NumRanges, LastLoc.getManager(),
858                        Info.getCodeModificationHints(),
859                        Info.getNumCodeModificationHints(),
860                        DiagOpts->MessageLength);
861  }
862
863  OS.flush();
864}
865