[cfe-commits] r142086 - in /cfe/trunk: include/clang/Frontend/TextDiagnostic.h lib/Frontend/CMakeLists.txt lib/Frontend/TextDiagnostic.cpp lib/Frontend/TextDiagnosticPrinter.cpp

Matt Beaumont-Gay matthewbg at google.com
Tue Oct 18 15:10:22 PDT 2011


Just a few nits...

On Sat, Oct 15, 2011 at 16:43, Chandler Carruth <chandlerc at gmail.com> wrote:
> Author: chandlerc
> Date: Sat Oct 15 18:43:53 2011
> New Revision: 142086
>
> URL: http://llvm.org/viewvc/llvm-project?rev=142086&view=rev
> Log:
> Graduate the TextDiagnostic interface to its own header and source file,
> making it accessible to anyone from the Frontend library. Still a good
> bit of cleanup to do here, but its a good milestone. This ensures that
> *all* of the functionality needed to implement the DiagnosticConsumer is
> exposed via the generic interface in some form. No sneaky re-use of
> static functions.
>
> Added:
>    cfe/trunk/include/clang/Frontend/TextDiagnostic.h
>    cfe/trunk/lib/Frontend/TextDiagnostic.cpp
> Modified:
>    cfe/trunk/lib/Frontend/CMakeLists.txt
>    cfe/trunk/lib/Frontend/TextDiagnosticPrinter.cpp
>
> Added: cfe/trunk/include/clang/Frontend/TextDiagnostic.h
> URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Frontend/TextDiagnostic.h?rev=142086&view=auto
> ==============================================================================
> --- cfe/trunk/include/clang/Frontend/TextDiagnostic.h (added)
> +++ cfe/trunk/include/clang/Frontend/TextDiagnostic.h Sat Oct 15 18:43:53 2011
> @@ -0,0 +1,178 @@
> +//===--- TextDiagnostic.h - Text Diagnostic Pretty-Printing -----*- C++ -*-===//
> +//
> +//                     The LLVM Compiler Infrastructure
> +//
> +// This file is distributed under the University of Illinois Open Source
> +// License. See LICENSE.TXT for details.
> +//
> +//===----------------------------------------------------------------------===//
> +//
> +// This is a utility class that provides support for textual pretty-printing of
> +// diagnostics. It is used to implement the different code paths which require
> +// such functionality in a consistent way.
> +//
> +//===----------------------------------------------------------------------===//
> +
> +#ifndef LLVM_CLANG_FRONTEND_TEXT_DIAGNOSTIC_H_
> +#define LLVM_CLANG_FRONTEND_TEXT_DIAGNOSTIC_H_
> +
> +#include "clang/Basic/Diagnostic.h"
> +#include "clang/Basic/LLVM.h"
> +#include "clang/Basic/SourceLocation.h"
> +
> +namespace clang {
> +class DiagnosticOptions;
> +class LangOptions;
> +class SourceManager;
> +
> +/// \brief Class to encapsulate the logic for formatting and printing a textual
> +/// diagnostic message.
> +///
> +/// This class provides an interface for building and emitting a textual
> +/// diagnostic, including all of the macro backtraces, caret diagnostics, FixIt
> +/// Hints, and code snippets. In the presence of macros this involves
> +/// a recursive process, synthesizing notes for each macro expansion.
> +///
> +/// The purpose of this class is to isolate the implementation of printing
> +/// beautiful text diagnostics from any particular interfaces. The Clang
> +/// DiagnosticClient is implemented through this class as is diagnostic
> +/// printing coming out of libclang.
> +///
> +/// A brief worklist:
> +/// FIXME: Sink the recursive printing of template instantiations into this
> +/// class.
> +class TextDiagnostic {
> +  raw_ostream &OS;
> +  const SourceManager &SM;
> +  const LangOptions &LangOpts;
> +  const DiagnosticOptions &DiagOpts;

DiagnosticOpts, for consistency with LangOpts?

> +
> +  /// \brief The location of the previous diagnostic if known.
> +  ///
> +  /// This will be invalid in cases where there is no (known) previous
> +  /// diagnostic location, or that location itself is invalid or comes from
> +  /// a different source manager than SM.
> +  SourceLocation LastLoc;
> +
> +  /// \brief The location of the last include whose stack was printed if known.
> +  ///
> +  /// Same restriction as \see LastLoc essentially, but tracking include stack
> +  /// root locations rather than diagnostic locations.
> +  SourceLocation LastIncludeLoc;
> +
> +  /// \brief The level of the last diagnostic emitted.
> +  ///
> +  /// The level of the last diagnostic emitted. Used to detect level changes
> +  /// which change the amount of information displayed.
> +  DiagnosticsEngine::Level LastLevel;
> +
> +public:
> +  TextDiagnostic(raw_ostream &OS,
> +                 const SourceManager &SM,
> +                 const LangOptions &LangOpts,
> +                 const DiagnosticOptions &DiagOpts,
> +                 FullSourceLoc LastLoc = FullSourceLoc(),
> +                 FullSourceLoc LastIncludeLoc = FullSourceLoc(),
> +                 DiagnosticsEngine::Level LastLevel
> +                   = DiagnosticsEngine::Level());
> +
> +  /// \brief Get the last diagnostic location emitted.
> +  SourceLocation getLastLoc() const { return LastLoc; }
> +
> +  /// \brief Get the last emitted include stack location.
> +  SourceLocation getLastIncludeLoc() const { return LastIncludeLoc; }
> +
> +  /// \brief Get the last diagnostic level.
> +  DiagnosticsEngine::Level getLastLevel() const { return LastLevel; }
> +
> +  void Emit(SourceLocation Loc, DiagnosticsEngine::Level Level,
> +            StringRef Message, ArrayRef<CharSourceRange> Ranges,
> +            ArrayRef<FixItHint> FixItHints,
> +            bool LastCaretDiagnosticWasNote = false);
> +
> +  /// \brief Emit the caret and underlining text.
> +  ///
> +  /// Walks up the macro expansion stack printing the code snippet, caret,
> +  /// underlines and FixItHint display as appropriate at each level. Walk is
> +  /// accomplished by calling itself recursively.
> +  ///
> +  /// FIXME: Remove macro expansion from this routine, it shouldn't be tied to
> +  /// caret diagnostics.
> +  /// FIXME: Break up massive function into logical units.
> +  ///
> +  /// \param Loc The location for this caret.
> +  /// \param Ranges The underlined ranges for this code snippet.
> +  /// \param Hints The FixIt hints active for this diagnostic.
> +  /// \param MacroSkipEnd The depth to stop skipping macro expansions.
> +  /// \param OnMacroInst The current depth of the macro expansion stack.
> +  void EmitCaret(SourceLocation Loc,
> +                 SmallVectorImpl<CharSourceRange>& Ranges,
> +                 ArrayRef<FixItHint> Hints,
> +                 unsigned &MacroDepth,
> +                 unsigned OnMacroInst = 0);
> +
> +  /// \brief Emit a code snippet and caret line.
> +  ///
> +  /// This routine emits a single line's code snippet and caret line..

nit: extra '.'

> +  ///
> +  /// \param Loc The location for the caret.
> +  /// \param Ranges The underlined ranges for this code snippet.
> +  /// \param Hints The FixIt hints active for this diagnostic.
> +  void EmitSnippetAndCaret(SourceLocation Loc,
> +                           SmallVectorImpl<CharSourceRange>& Ranges,
> +                           ArrayRef<FixItHint> Hints);
> +
> +  /// \brief Print the diagonstic level to a raw_ostream.

s/diagonstic/diagnostic/

> +  ///
> +  /// This is a static helper that handles colorizing the level and formatting
> +  /// it into an arbitrary output stream. This is used internally by the
> +  /// TextDiagnostic emission code, but it can also be used directly by
> +  /// consumers that don't have a source manager or other state that the full
> +  /// TextDiagnostic logic requires.
> +  static void printDiagnosticLevel(raw_ostream &OS,
> +                                   DiagnosticsEngine::Level Level,
> +                                   bool ShowColors);
> +
> +  /// \brief Pretty-print a diagnostic message to a raw_ostream.
> +  ///
> +  /// This is a static helper to handle the line wrapping, colorizing, and
> +  /// rendering of a diagnostic message to a particular ostream. It is
> +  /// publically visible so that clients which do not have sufficient state to

"publicly" is the more common spelling.

> +  /// build a complete TextDiagnostic object can still get consistent
> +  /// formatting of their diagnostic messages.
> +  ///
> +  /// \param OS Where the message is printed
> +  /// \param Level Used to colorizing the message
> +  /// \param Message The text actually printed
> +  /// \param CurrentColumn The starting column of the first line, accounting
> +  ///                      for any prefix.
> +  /// \param Columns The number of columns to use in line-wrapping, 0 disables
> +  ///                all line-wrapping.
> +  /// \param ShowColors Enable colorizing of the message.
> +  static void printDiagnosticMessage(raw_ostream &OS,
> +                                     DiagnosticsEngine::Level Level,
> +                                     StringRef Message,
> +                                     unsigned CurrentColumn, unsigned Columns,
> +                                     bool ShowColors);
> +
> +private:
> +  void emitIncludeStack(SourceLocation Loc, DiagnosticsEngine::Level Level);
> +  void emitIncludeStackRecursively(SourceLocation Loc);
> +  void EmitDiagnosticLoc(SourceLocation Loc, PresumedLoc PLoc,
> +                         DiagnosticsEngine::Level Level,
> +                         ArrayRef<CharSourceRange> Ranges);
> +  void HighlightRange(const CharSourceRange &R,
> +                      unsigned LineNo, FileID FID,
> +                      const std::string &SourceLine,
> +                      std::string &CaretLine);
> +  std::string BuildFixItInsertionLine(unsigned LineNo,
> +                                      const char *LineStart,
> +                                      const char *LineEnd,
> +                                      ArrayRef<FixItHint> Hints);
> +  void ExpandTabs(std::string &SourceLine, std::string &CaretLine);
> +  void EmitParseableFixits(ArrayRef<FixItHint> Hints);
> +};
> +
> +} // end namespace clang
> +
> +#endif
>
> Modified: cfe/trunk/lib/Frontend/CMakeLists.txt
> URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Frontend/CMakeLists.txt?rev=142086&r1=142085&r2=142086&view=diff
> ==============================================================================
> --- cfe/trunk/lib/Frontend/CMakeLists.txt (original)
> +++ cfe/trunk/lib/Frontend/CMakeLists.txt Sat Oct 15 18:43:53 2011
> @@ -27,6 +27,7 @@
>   LogDiagnosticPrinter.cpp
>   MultiplexConsumer.cpp
>   PrintPreprocessedOutput.cpp
> +  TextDiagnostic.cpp
>   TextDiagnosticBuffer.cpp
>   TextDiagnosticPrinter.cpp
>   VerifyDiagnosticConsumer.cpp
>
> Added: cfe/trunk/lib/Frontend/TextDiagnostic.cpp
> URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Frontend/TextDiagnostic.cpp?rev=142086&view=auto
> ==============================================================================
> --- cfe/trunk/lib/Frontend/TextDiagnostic.cpp (added)
> +++ cfe/trunk/lib/Frontend/TextDiagnostic.cpp Sat Oct 15 18:43:53 2011
> @@ -0,0 +1,1098 @@
> +//===--- TextDiagnostic.cpp - Text Diagnostic Pretty-Printing -------------===//
> +//
> +//                     The LLVM Compiler Infrastructure
> +//
> +// This file is distributed under the University of Illinois Open Source
> +// License. See LICENSE.TXT for details.
> +//
> +//===----------------------------------------------------------------------===//
> +
> +#include "clang/Frontend/TextDiagnostic.h"
> +#include "clang/Basic/FileManager.h"
> +#include "clang/Basic/SourceManager.h"
> +#include "clang/Frontend/DiagnosticOptions.h"
> +#include "clang/Lex/Lexer.h"
> +#include "llvm/Support/MemoryBuffer.h"
> +#include "llvm/Support/raw_ostream.h"
> +#include "llvm/Support/ErrorHandling.h"
> +#include "llvm/ADT/SmallString.h"
> +#include <algorithm>
> +using namespace clang;
> +
> +static const enum raw_ostream::Colors noteColor =
> +  raw_ostream::BLACK;
> +static const enum raw_ostream::Colors fixitColor =
> +  raw_ostream::GREEN;
> +static const enum raw_ostream::Colors caretColor =
> +  raw_ostream::GREEN;
> +static const enum raw_ostream::Colors warningColor =
> +  raw_ostream::MAGENTA;
> +static const enum raw_ostream::Colors errorColor = raw_ostream::RED;
> +static const enum raw_ostream::Colors fatalColor = raw_ostream::RED;
> +// Used for changing only the bold attribute.
> +static const enum raw_ostream::Colors savedColor =
> +  raw_ostream::SAVEDCOLOR;
> +
> +/// \brief Number of spaces to indent when word-wrapping.
> +const unsigned WordWrapIndentation = 6;
> +
> +/// \brief When the source code line we want to print is too long for
> +/// the terminal, select the "interesting" region.
> +static void SelectInterestingSourceRegion(std::string &SourceLine,
> +                                          std::string &CaretLine,
> +                                          std::string &FixItInsertionLine,
> +                                          unsigned EndOfCaretToken,
> +                                          unsigned Columns) {
> +  unsigned MaxSize = std::max(SourceLine.size(),
> +                              std::max(CaretLine.size(),
> +                                       FixItInsertionLine.size()));
> +  if (MaxSize > SourceLine.size())
> +    SourceLine.resize(MaxSize, ' ');
> +  if (MaxSize > CaretLine.size())
> +    CaretLine.resize(MaxSize, ' ');
> +  if (!FixItInsertionLine.empty() && MaxSize > FixItInsertionLine.size())
> +    FixItInsertionLine.resize(MaxSize, ' ');
> +
> +  // Find the slice that we need to display the full caret line
> +  // correctly.
> +  unsigned CaretStart = 0, CaretEnd = CaretLine.size();
> +  for (; CaretStart != CaretEnd; ++CaretStart)
> +    if (!isspace(CaretLine[CaretStart]))
> +      break;
> +
> +  for (; CaretEnd != CaretStart; --CaretEnd)
> +    if (!isspace(CaretLine[CaretEnd - 1]))
> +      break;
> +
> +  // Make sure we don't chop the string shorter than the caret token
> +  // itself.
> +  if (CaretEnd < EndOfCaretToken)
> +    CaretEnd = EndOfCaretToken;
> +
> +  // If we have a fix-it line, make sure the slice includes all of the
> +  // fix-it information.
> +  if (!FixItInsertionLine.empty()) {
> +    unsigned FixItStart = 0, FixItEnd = FixItInsertionLine.size();
> +    for (; FixItStart != FixItEnd; ++FixItStart)
> +      if (!isspace(FixItInsertionLine[FixItStart]))
> +        break;
> +
> +    for (; FixItEnd != FixItStart; --FixItEnd)
> +      if (!isspace(FixItInsertionLine[FixItEnd - 1]))
> +        break;
> +
> +    if (FixItStart < CaretStart)
> +      CaretStart = FixItStart;
> +    if (FixItEnd > CaretEnd)
> +      CaretEnd = FixItEnd;
> +  }
> +
> +  // CaretLine[CaretStart, CaretEnd) contains all of the interesting
> +  // parts of the caret line. While this slice is smaller than the
> +  // number of columns we have, try to grow the slice to encompass
> +  // more context.
> +
> +  // If the end of the interesting region comes before we run out of
> +  // space in the terminal, start at the beginning of the line.
> +  if (Columns > 3 && CaretEnd < Columns - 3)
> +    CaretStart = 0;
> +
> +  unsigned TargetColumns = Columns;
> +  if (TargetColumns > 8)
> +    TargetColumns -= 8; // Give us extra room for the ellipses.
> +  unsigned SourceLength = SourceLine.size();
> +  while ((CaretEnd - CaretStart) < TargetColumns) {
> +    bool ExpandedRegion = false;
> +    // Move the start of the interesting region left until we've
> +    // pulled in something else interesting.
> +    if (CaretStart == 1)
> +      CaretStart = 0;
> +    else if (CaretStart > 1) {
> +      unsigned NewStart = CaretStart - 1;
> +
> +      // Skip over any whitespace we see here; we're looking for
> +      // another bit of interesting text.
> +      while (NewStart && isspace(SourceLine[NewStart]))
> +        --NewStart;
> +
> +      // Skip over this bit of "interesting" text.
> +      while (NewStart && !isspace(SourceLine[NewStart]))
> +        --NewStart;
> +
> +      // Move up to the non-whitespace character we just saw.
> +      if (NewStart)
> +        ++NewStart;
> +
> +      // If we're still within our limit, update the starting
> +      // position within the source/caret line.
> +      if (CaretEnd - NewStart <= TargetColumns) {
> +        CaretStart = NewStart;
> +        ExpandedRegion = true;
> +      }
> +    }
> +
> +    // Move the end of the interesting region right until we've
> +    // pulled in something else interesting.
> +    if (CaretEnd != SourceLength) {
> +      assert(CaretEnd < SourceLength && "Unexpected caret position!");
> +      unsigned NewEnd = CaretEnd;
> +
> +      // Skip over any whitespace we see here; we're looking for
> +      // another bit of interesting text.
> +      while (NewEnd != SourceLength && isspace(SourceLine[NewEnd - 1]))
> +        ++NewEnd;
> +
> +      // Skip over this bit of "interesting" text.
> +      while (NewEnd != SourceLength && !isspace(SourceLine[NewEnd - 1]))
> +        ++NewEnd;
> +
> +      if (NewEnd - CaretStart <= TargetColumns) {
> +        CaretEnd = NewEnd;
> +        ExpandedRegion = true;
> +      }
> +    }
> +
> +    if (!ExpandedRegion)
> +      break;
> +  }
> +
> +  // [CaretStart, CaretEnd) is the slice we want. Update the various
> +  // output lines to show only this slice, with two-space padding
> +  // before the lines so that it looks nicer.
> +  if (CaretEnd < SourceLine.size())
> +    SourceLine.replace(CaretEnd, std::string::npos, "...");
> +  if (CaretEnd < CaretLine.size())
> +    CaretLine.erase(CaretEnd, std::string::npos);
> +  if (FixItInsertionLine.size() > CaretEnd)
> +    FixItInsertionLine.erase(CaretEnd, std::string::npos);
> +
> +  if (CaretStart > 2) {
> +    SourceLine.replace(0, CaretStart, "  ...");
> +    CaretLine.replace(0, CaretStart, "     ");
> +    if (FixItInsertionLine.size() >= CaretStart)
> +      FixItInsertionLine.replace(0, CaretStart, "     ");
> +  }
> +}
> +
> +/// Look through spelling locations for a macro argument expansion, and
> +/// if found skip to it so that we can trace the argument rather than the macros
> +/// in which that argument is used. If no macro argument expansion is found,
> +/// don't skip anything and return the starting location.
> +static SourceLocation skipToMacroArgExpansion(const SourceManager &SM,
> +                                                  SourceLocation StartLoc) {
> +  for (SourceLocation L = StartLoc; L.isMacroID();
> +       L = SM.getImmediateSpellingLoc(L)) {
> +    if (SM.isMacroArgExpansion(L))
> +      return L;
> +  }
> +
> +  // Otherwise just return initial location, there's nothing to skip.
> +  return StartLoc;
> +}
> +
> +/// Gets the location of the immediate macro caller, one level up the stack
> +/// toward the initial macro typed into the source.
> +static SourceLocation getImmediateMacroCallerLoc(const SourceManager &SM,
> +                                                 SourceLocation Loc) {
> +  if (!Loc.isMacroID()) return Loc;
> +
> +  // When we have the location of (part of) an expanded parameter, its spelling
> +  // location points to the argument as typed into the macro call, and
> +  // therefore is used to locate the macro caller.
> +  if (SM.isMacroArgExpansion(Loc))
> +    return SM.getImmediateSpellingLoc(Loc);
> +
> +  // Otherwise, the caller of the macro is located where this macro is
> +  // expanded (while the spelling is part of the macro definition).
> +  return SM.getImmediateExpansionRange(Loc).first;
> +}
> +
> +/// Gets the location of the immediate macro callee, one level down the stack
> +/// toward the leaf macro.
> +static SourceLocation getImmediateMacroCalleeLoc(const SourceManager &SM,
> +                                                 SourceLocation Loc) {
> +  if (!Loc.isMacroID()) return Loc;
> +
> +  // When we have the location of (part of) an expanded parameter, its
> +  // expansion location points to the unexpanded paramater reference within
> +  // the macro definition (or callee).
> +  if (SM.isMacroArgExpansion(Loc))
> +    return SM.getImmediateExpansionRange(Loc).first;
> +
> +  // Otherwise, the callee of the macro is located where this location was
> +  // spelled inside the macro definition.

Thanks for the nice comments through here :)

> +  return SM.getImmediateSpellingLoc(Loc);
> +}
> +
> +/// Get the presumed location of a diagnostic message. This computes the
> +/// presumed location for the top of any macro backtrace when present.
> +static PresumedLoc getDiagnosticPresumedLoc(const SourceManager &SM,
> +                                            SourceLocation Loc) {
> +  // This is a condensed form of the algorithm used by EmitCaretDiagnostic to
> +  // walk to the top of the macro call stack.
> +  while (Loc.isMacroID()) {
> +    Loc = skipToMacroArgExpansion(SM, Loc);
> +    Loc = getImmediateMacroCallerLoc(SM, Loc);
> +  }
> +
> +  return SM.getPresumedLoc(Loc);
> +}
> +
> +/// \brief Skip over whitespace in the string, starting at the given
> +/// index.
> +///
> +/// \returns The index of the first non-whitespace character that is
> +/// greater than or equal to Idx or, if no such character exists,
> +/// returns the end of the string.
> +static unsigned skipWhitespace(unsigned Idx, StringRef Str, unsigned Length) {
> +  while (Idx < Length && isspace(Str[Idx]))
> +    ++Idx;
> +  return Idx;
> +}
> +
> +/// \brief If the given character is the start of some kind of
> +/// balanced punctuation (e.g., quotes or parentheses), return the
> +/// character that will terminate the punctuation.
> +///
> +/// \returns The ending punctuation character, if any, or the NULL
> +/// character if the input character does not start any punctuation.
> +static inline char findMatchingPunctuation(char c) {
> +  switch (c) {
> +  case '\'': return '\'';
> +  case '`': return '\'';
> +  case '"':  return '"';
> +  case '(':  return ')';
> +  case '[': return ']';
> +  case '{': return '}';
> +  default: break;
> +  }
> +
> +  return 0;
> +}
> +
> +/// \brief Find the end of the word starting at the given offset
> +/// within a string.
> +///
> +/// \returns the index pointing one character past the end of the
> +/// word.
> +static unsigned findEndOfWord(unsigned Start, StringRef Str,
> +                              unsigned Length, unsigned Column,
> +                              unsigned Columns) {
> +  assert(Start < Str.size() && "Invalid start position!");
> +  unsigned End = Start + 1;
> +
> +  // If we are already at the end of the string, take that as the word.
> +  if (End == Str.size())
> +    return End;
> +
> +  // Determine if the start of the string is actually opening
> +  // punctuation, e.g., a quote or parentheses.
> +  char EndPunct = findMatchingPunctuation(Str[Start]);
> +  if (!EndPunct) {
> +    // This is a normal word. Just find the first space character.
> +    while (End < Length && !isspace(Str[End]))
> +      ++End;
> +    return End;
> +  }
> +
> +  // We have the start of a balanced punctuation sequence (quotes,
> +  // parentheses, etc.). Determine the full sequence is.
> +  llvm::SmallString<16> PunctuationEndStack;
> +  PunctuationEndStack.push_back(EndPunct);
> +  while (End < Length && !PunctuationEndStack.empty()) {
> +    if (Str[End] == PunctuationEndStack.back())
> +      PunctuationEndStack.pop_back();
> +    else if (char SubEndPunct = findMatchingPunctuation(Str[End]))
> +      PunctuationEndStack.push_back(SubEndPunct);
> +
> +    ++End;
> +  }
> +
> +  // Find the first space character after the punctuation ended.
> +  while (End < Length && !isspace(Str[End]))
> +    ++End;
> +
> +  unsigned PunctWordLength = End - Start;
> +  if (// If the word fits on this line
> +      Column + PunctWordLength <= Columns ||
> +      // ... or the word is "short enough" to take up the next line
> +      // without too much ugly white space
> +      PunctWordLength < Columns/3)
> +    return End; // Take the whole thing as a single "word".
> +
> +  // The whole quoted/parenthesized string is too long to print as a
> +  // single "word". Instead, find the "word" that starts just after
> +  // the punctuation and use that end-point instead. This will recurse
> +  // until it finds something small enough to consider a word.
> +  return findEndOfWord(Start + 1, Str, Length, Column + 1, Columns);
> +}
> +
> +/// \brief Print the given string to a stream, word-wrapping it to
> +/// some number of columns in the process.
> +///
> +/// \param OS the stream to which the word-wrapping string will be
> +/// emitted.
> +/// \param Str the string to word-wrap and output.
> +/// \param Columns the number of columns to word-wrap to.
> +/// \param Column the column number at which the first character of \p
> +/// Str will be printed. This will be non-zero when part of the first
> +/// line has already been printed.
> +/// \param Indentation the number of spaces to indent any lines beyond
> +/// the first line.
> +/// \returns true if word-wrapping was required, or false if the
> +/// string fit on the first line.
> +static bool printWordWrapped(raw_ostream &OS, StringRef Str,
> +                             unsigned Columns,
> +                             unsigned Column = 0,
> +                             unsigned Indentation = WordWrapIndentation) {
> +  const unsigned Length = std::min(Str.find('\n'), Str.size());
> +
> +  // The string used to indent each line.
> +  llvm::SmallString<16> IndentStr;
> +  IndentStr.assign(Indentation, ' ');
> +  bool Wrapped = false;
> +  for (unsigned WordStart = 0, WordEnd; WordStart < Length;
> +       WordStart = WordEnd) {
> +    // Find the beginning of the next word.
> +    WordStart = skipWhitespace(WordStart, Str, Length);
> +    if (WordStart == Length)
> +      break;
> +
> +    // Find the end of this word.
> +    WordEnd = findEndOfWord(WordStart, Str, Length, Column, Columns);
> +
> +    // Does this word fit on the current line?
> +    unsigned WordLength = WordEnd - WordStart;
> +    if (Column + WordLength < Columns) {
> +      // This word fits on the current line; print it there.
> +      if (WordStart) {
> +        OS << ' ';
> +        Column += 1;
> +      }
> +      OS << Str.substr(WordStart, WordLength);
> +      Column += WordLength;
> +      continue;
> +    }
> +
> +    // This word does not fit on the current line, so wrap to the next
> +    // line.
> +    OS << '\n';
> +    OS.write(&IndentStr[0], Indentation);
> +    OS << Str.substr(WordStart, WordLength);
> +    Column = Indentation + WordLength;
> +    Wrapped = true;
> +  }
> +
> +  // Append any remaning text from the message with its existing formatting.
> +  OS << Str.substr(Length);
> +
> +  return Wrapped;
> +}
> +
> +TextDiagnostic::TextDiagnostic(raw_ostream &OS,
> +                               const SourceManager &SM,
> +                               const LangOptions &LangOpts,
> +                               const DiagnosticOptions &DiagOpts,
> +                               FullSourceLoc LastLoc,
> +                               FullSourceLoc LastIncludeLoc,
> +                               DiagnosticsEngine::Level LastLevel)
> +  : OS(OS), SM(SM), LangOpts(LangOpts), DiagOpts(DiagOpts),
> +    LastLoc(LastLoc), LastIncludeLoc(LastIncludeLoc), LastLevel(LastLevel) {
> +  if (LastLoc.isValid() && &SM != &LastLoc.getManager())
> +    this->LastLoc = SourceLocation();
> +  if (LastIncludeLoc.isValid() && &SM != &LastIncludeLoc.getManager())
> +    this->LastIncludeLoc = SourceLocation();
> +    }
> +
> +void TextDiagnostic::Emit(SourceLocation Loc, DiagnosticsEngine::Level Level,
> +                          StringRef Message, ArrayRef<CharSourceRange> Ranges,
> +                          ArrayRef<FixItHint> FixItHints,
> +                          bool LastCaretDiagnosticWasNote) {
> +  PresumedLoc PLoc = getDiagnosticPresumedLoc(SM, Loc);
> +
> +  // First, if this diagnostic is not in the main file, print out the
> +  // "included from" lines.
> +  emitIncludeStack(PLoc.getIncludeLoc(), Level);
> +
> +  uint64_t StartOfLocationInfo = OS.tell();
> +
> +  // Next emit the location of this particular diagnostic.
> +  EmitDiagnosticLoc(Loc, PLoc, Level, Ranges);
> +
> +  if (DiagOpts.ShowColors)
> +    OS.resetColor();
> +
> +  printDiagnosticLevel(OS, Level, DiagOpts.ShowColors);
> +  printDiagnosticMessage(OS, Level, Message,
> +                         OS.tell() - StartOfLocationInfo,
> +                         DiagOpts.MessageLength, DiagOpts.ShowColors);
> +
> +  // If caret diagnostics are enabled and we have location, we want to
> +  // emit the caret.  However, we only do this if the location moved
> +  // from the last diagnostic, if the last diagnostic was a note that
> +  // was part of a different warning or error diagnostic, or if the
> +  // diagnostic has ranges.  We don't want to emit the same caret
> +  // multiple times if one loc has multiple diagnostics.
> +  if (DiagOpts.ShowCarets &&
> +      (Loc != LastLoc || !Ranges.empty() || !FixItHints.empty() ||
> +       (LastLevel == DiagnosticsEngine::Note && Level != LastLevel))) {
> +    // Get the ranges into a local array we can hack on.
> +    SmallVector<CharSourceRange, 20> MutableRanges(Ranges.begin(),
> +                                                   Ranges.end());
> +
> +    for (ArrayRef<FixItHint>::const_iterator I = FixItHints.begin(),
> +                                             E = FixItHints.end();
> +         I != E; ++I)
> +      if (I->RemoveRange.isValid())
> +        MutableRanges.push_back(I->RemoveRange);
> +
> +    unsigned MacroDepth = 0;
> +    EmitCaret(Loc, MutableRanges, FixItHints, MacroDepth);
> +  }
> +
> +  LastLoc = Loc;
> +  LastLevel = Level;
> +}
> +
> +/// \brief Emit the caret and underlining text.
> +///
> +/// Walks up the macro expansion stack printing the code snippet, caret,
> +/// underlines and FixItHint display as appropriate at each level. Walk is
> +/// accomplished by calling itself recursively.
> +///
> +/// FIXME: Remove macro expansion from this routine, it shouldn't be tied to
> +/// caret diagnostics.
> +/// FIXME: Break up massive function into logical units.
> +///
> +/// \param Loc The location for this caret.
> +/// \param Ranges The underlined ranges for this code snippet.
> +/// \param Hints The FixIt hints active for this diagnostic.
> +/// \param MacroSkipEnd The depth to stop skipping macro expansions.

That's not the actual param name...

> +/// \param OnMacroInst The current depth of the macro expansion stack.

I'm not a big fan of "OnMacroInst." Maybe "CurrentMacroDepth"?

> +void TextDiagnostic::EmitCaret(SourceLocation Loc,
> +                               SmallVectorImpl<CharSourceRange>& Ranges,
> +                               ArrayRef<FixItHint> Hints,
> +                               unsigned &MacroDepth,
> +                               unsigned OnMacroInst) {
> +  assert(!Loc.isInvalid() && "must have a valid source location here");
> +
> +  // If this is a file source location, directly emit the source snippet and
> +  // caret line. Also record the macro depth reached.
> +  if (Loc.isFileID()) {
> +    assert(MacroDepth == 0 && "We shouldn't hit a leaf node twice!");
> +    MacroDepth = OnMacroInst;
> +    EmitSnippetAndCaret(Loc, Ranges, Hints);
> +    return;
> +  }
> +  // Otherwise recurse through each macro expansion layer.
> +
> +  // When processing macros, skip over the expansions leading up to
> +  // a macro argument, and trace the argument's expansion stack instead.
> +  Loc = skipToMacroArgExpansion(SM, Loc);
> +
> +  SourceLocation OneLevelUp = getImmediateMacroCallerLoc(SM, Loc);
> +
> +  // FIXME: Map ranges?
> +  EmitCaret(OneLevelUp, Ranges, Hints, MacroDepth, OnMacroInst + 1);
> +
> +  // Map the location.
> +  Loc = getImmediateMacroCalleeLoc(SM, Loc);
> +
> +  unsigned MacroSkipStart = 0, MacroSkipEnd = 0;
> +  if (MacroDepth > DiagOpts.MacroBacktraceLimit) {
> +    MacroSkipStart = DiagOpts.MacroBacktraceLimit / 2 +
> +      DiagOpts.MacroBacktraceLimit % 2;
> +    MacroSkipEnd = MacroDepth - DiagOpts.MacroBacktraceLimit / 2;
> +  }
> +
> +  // Whether to suppress printing this macro expansion.
> +  bool Suppressed = (OnMacroInst >= MacroSkipStart &&
> +                     OnMacroInst < MacroSkipEnd);
> +
> +  // Map the ranges.
> +  for (SmallVectorImpl<CharSourceRange>::iterator I = Ranges.begin(),
> +                                                  E = Ranges.end();
> +       I != E; ++I) {
> +    SourceLocation Start = I->getBegin(), End = I->getEnd();
> +    if (Start.isMacroID())
> +      I->setBegin(getImmediateMacroCalleeLoc(SM, Start));
> +    if (End.isMacroID())
> +      I->setEnd(getImmediateMacroCalleeLoc(SM, End));
> +  }
> +
> +  if (!Suppressed) {
> +    // Don't print recursive expansion notes from an expansion note.
> +    Loc = SM.getSpellingLoc(Loc);
> +
> +    // Get the pretty name, according to #line directives etc.
> +    PresumedLoc PLoc = SM.getPresumedLoc(Loc);
> +    if (PLoc.isInvalid())
> +      return;
> +
> +    // If this diagnostic is not in the main file, print out the
> +    // "included from" lines.
> +    emitIncludeStack(PLoc.getIncludeLoc(), DiagnosticsEngine::Note);
> +
> +    if (DiagOpts.ShowLocation) {
> +      // Emit the file/line/column that this expansion came from.
> +      OS << PLoc.getFilename() << ':' << PLoc.getLine() << ':';
> +      if (DiagOpts.ShowColumn)
> +        OS << PLoc.getColumn() << ':';
> +      OS << ' ';
> +    }
> +    OS << "note: expanded from:\n";
> +
> +    EmitSnippetAndCaret(Loc, Ranges, ArrayRef<FixItHint>());
> +    return;
> +  }
> +
> +  if (OnMacroInst == MacroSkipStart) {
> +    // Tell the user that we've skipped contexts.
> +    OS << "note: (skipping " << (MacroSkipEnd - MacroSkipStart)
> +    << " expansions in backtrace; use -fmacro-backtrace-limit=0 to see "
> +    "all)\n";
> +  }
> +}
> +
> +/// \brief Emit a code snippet and caret line.
> +///
> +/// This routine emits a single line's code snippet and caret line..
> +///
> +/// \param Loc The location for the caret.
> +/// \param Ranges The underlined ranges for this code snippet.
> +/// \param Hints The FixIt hints active for this diagnostic.
> +void TextDiagnostic::EmitSnippetAndCaret(
> +    SourceLocation Loc,
> +    SmallVectorImpl<CharSourceRange>& Ranges,
> +    ArrayRef<FixItHint> Hints) {
> +  assert(!Loc.isInvalid() && "must have a valid source location here");
> +  assert(Loc.isFileID() && "must have a file location here");
> +
> +  // Decompose the location into a FID/Offset pair.
> +  std::pair<FileID, unsigned> LocInfo = SM.getDecomposedLoc(Loc);
> +  FileID FID = LocInfo.first;
> +  unsigned FileOffset = LocInfo.second;
> +
> +  // Get information about the buffer it points into.
> +  bool Invalid = false;
> +  const char *BufStart = SM.getBufferData(FID, &Invalid).data();
> +  if (Invalid)
> +    return;
> +
> +  unsigned LineNo = SM.getLineNumber(FID, FileOffset);
> +  unsigned ColNo = SM.getColumnNumber(FID, FileOffset);
> +  unsigned CaretEndColNo
> +    = ColNo + Lexer::MeasureTokenLength(Loc, SM, LangOpts);
> +
> +  // Rewind from the current position to the start of the line.
> +  const char *TokPtr = BufStart+FileOffset;
> +  const char *LineStart = TokPtr-ColNo+1; // Column # is 1-based.
> +
> +
> +  // Compute the line end.  Scan forward from the error position to the end of
> +  // the line.
> +  const char *LineEnd = TokPtr;
> +  while (*LineEnd != '\n' && *LineEnd != '\r' && *LineEnd != '\0')
> +    ++LineEnd;
> +
> +  // FIXME: This shouldn't be necessary, but the CaretEndColNo can extend past
> +  // the source line length as currently being computed. See
> +  // test/Misc/message-length.c.
> +  CaretEndColNo = std::min(CaretEndColNo, unsigned(LineEnd - LineStart));
> +
> +  // Copy the line of code into an std::string for ease of manipulation.
> +  std::string SourceLine(LineStart, LineEnd);
> +
> +  // Create a line for the caret that is filled with spaces that is the same
> +  // length as the line of source code.
> +  std::string CaretLine(LineEnd-LineStart, ' ');
> +
> +  // Highlight all of the characters covered by Ranges with ~ characters.
> +  for (SmallVectorImpl<CharSourceRange>::iterator I = Ranges.begin(),
> +                                                  E = Ranges.end();
> +       I != E; ++I)
> +    HighlightRange(*I, LineNo, FID, SourceLine, CaretLine);
> +
> +  // Next, insert the caret itself.
> +  if (ColNo-1 < CaretLine.size())
> +    CaretLine[ColNo-1] = '^';
> +  else
> +    CaretLine.push_back('^');
> +
> +  ExpandTabs(SourceLine, CaretLine);
> +
> +  // If we are in -fdiagnostics-print-source-range-info mode, we are trying
> +  // to produce easily machine parsable output.  Add a space before the
> +  // source line and the caret to make it trivial to tell the main diagnostic
> +  // line from what the user is intended to see.
> +  if (DiagOpts.ShowSourceRanges) {
> +    SourceLine = ' ' + SourceLine;
> +    CaretLine = ' ' + CaretLine;
> +  }
> +
> +  std::string FixItInsertionLine = BuildFixItInsertionLine(LineNo,
> +                                                           LineStart, LineEnd,
> +                                                           Hints);
> +
> +  // If the source line is too long for our terminal, select only the
> +  // "interesting" source region within that line.
> +  unsigned Columns = DiagOpts.MessageLength;
> +  if (Columns && SourceLine.size() > Columns)
> +    SelectInterestingSourceRegion(SourceLine, CaretLine, FixItInsertionLine,
> +                                  CaretEndColNo, Columns);
> +
> +  // Finally, remove any blank spaces from the end of CaretLine.
> +  while (CaretLine[CaretLine.size()-1] == ' ')
> +    CaretLine.erase(CaretLine.end()-1);
> +
> +  // Emit what we have computed.
> +  OS << SourceLine << '\n';
> +
> +  if (DiagOpts.ShowColors)
> +    OS.changeColor(caretColor, true);
> +  OS << CaretLine << '\n';
> +  if (DiagOpts.ShowColors)
> +    OS.resetColor();
> +
> +  if (!FixItInsertionLine.empty()) {
> +    if (DiagOpts.ShowColors)
> +      // Print fixit line in color
> +      OS.changeColor(fixitColor, false);
> +    if (DiagOpts.ShowSourceRanges)
> +      OS << ' ';
> +    OS << FixItInsertionLine << '\n';
> +    if (DiagOpts.ShowColors)
> +      OS.resetColor();
> +  }
> +
> +  // Print out any parseable fixit information requested by the options.
> +  EmitParseableFixits(Hints);
> +}
> +
> +/*static*/ void
> +TextDiagnostic::printDiagnosticLevel(raw_ostream &OS,
> +                                     DiagnosticsEngine::Level Level,
> +                                     bool ShowColors) {
> +  if (ShowColors) {
> +    // Print diagnostic category in bold and color
> +    switch (Level) {
> +    case DiagnosticsEngine::Ignored:
> +      llvm_unreachable("Invalid diagnostic type");
> +    case DiagnosticsEngine::Note:    OS.changeColor(noteColor, true); break;
> +    case DiagnosticsEngine::Warning: OS.changeColor(warningColor, true); break;
> +    case DiagnosticsEngine::Error:   OS.changeColor(errorColor, true); break;
> +    case DiagnosticsEngine::Fatal:   OS.changeColor(fatalColor, true); break;
> +    }
> +  }
> +
> +  switch (Level) {
> +  case DiagnosticsEngine::Ignored:
> +    llvm_unreachable("Invalid diagnostic type");
> +  case DiagnosticsEngine::Note:    OS << "note: "; break;
> +  case DiagnosticsEngine::Warning: OS << "warning: "; break;
> +  case DiagnosticsEngine::Error:   OS << "error: "; break;
> +  case DiagnosticsEngine::Fatal:   OS << "fatal error: "; break;
> +  }
> +
> +  if (ShowColors)
> +    OS.resetColor();
> +}
> +
> +/*static*/ void
> +TextDiagnostic::printDiagnosticMessage(raw_ostream &OS,
> +                                       DiagnosticsEngine::Level Level,
> +                                       StringRef Message,
> +                                       unsigned CurrentColumn, unsigned Columns,
> +                                       bool ShowColors) {
> +  if (ShowColors) {
> +    // Print warnings, errors and fatal errors in bold, no color
> +    switch (Level) {
> +    case DiagnosticsEngine::Warning: OS.changeColor(savedColor, true); break;
> +    case DiagnosticsEngine::Error:   OS.changeColor(savedColor, true); break;
> +    case DiagnosticsEngine::Fatal:   OS.changeColor(savedColor, true); break;
> +    default: break; //don't bold notes
> +    }
> +  }
> +
> +  if (Columns)
> +    printWordWrapped(OS, Message, Columns, CurrentColumn);
> +  else
> +    OS << Message;
> +
> +  if (ShowColors)
> +    OS.resetColor();
> +  OS << '\n';
> +}
> +
> +/// \brief Prints an include stack when appropriate for a particular
> +/// diagnostic level and location.
> +///
> +/// This routine handles all the logic of suppressing particular include
> +/// stacks (such as those for notes) and duplicate include stacks when
> +/// repeated warnings occur within the same file. It also handles the logic
> +/// of customizing the formatting and display of the include stack.
> +///
> +/// \param Level The diagnostic level of the message this stack pertains to.
> +/// \param Loc   The include location of the current file (not the diagnostic
> +///              location).
> +void TextDiagnostic::emitIncludeStack(SourceLocation Loc,
> +                                      DiagnosticsEngine::Level Level) {
> +  // Skip redundant include stacks altogether.
> +  if (LastIncludeLoc == Loc)
> +    return;
> +  LastIncludeLoc = Loc;
> +
> +  if (!DiagOpts.ShowNoteIncludeStack && Level == DiagnosticsEngine::Note)
> +    return;
> +
> +  emitIncludeStackRecursively(Loc);
> +}
> +
> +/// \brief Helper to recursivly walk up the include stack and print each layer
> +/// on the way back down.
> +void TextDiagnostic::emitIncludeStackRecursively(SourceLocation Loc) {
> +  if (Loc.isInvalid())
> +    return;
> +
> +  PresumedLoc PLoc = SM.getPresumedLoc(Loc);
> +  if (PLoc.isInvalid())
> +    return;
> +
> +  // Emit the other include frames first.
> +  emitIncludeStackRecursively(PLoc.getIncludeLoc());
> +
> +  if (DiagOpts.ShowLocation)
> +    OS << "In file included from " << PLoc.getFilename()
> +       << ':' << PLoc.getLine() << ":\n";
> +  else
> +    OS << "In included file:\n";
> +}
> +
> +/// \brief Print out the file/line/column information and include trace.
> +///
> +/// This method handlen the emission of the diagnostic location information.

s/handlen/handles/

> +/// This includes extracting as much location information as is present for
> +/// the diagnostic and printing it, as well as any include stack or source
> +/// ranges necessary.
> +void TextDiagnostic::EmitDiagnosticLoc(SourceLocation Loc, PresumedLoc PLoc,
> +                                       DiagnosticsEngine::Level Level,
> +                                       ArrayRef<CharSourceRange> Ranges) {
> +  if (PLoc.isInvalid()) {
> +    // At least print the file name if available:
> +    FileID FID = SM.getFileID(Loc);
> +    if (!FID.isInvalid()) {
> +      const FileEntry* FE = SM.getFileEntryForID(FID);
> +      if (FE && FE->getName()) {
> +        OS << FE->getName();
> +        if (FE->getDevice() == 0 && FE->getInode() == 0
> +            && FE->getFileMode() == 0) {
> +          // in PCH is a guess, but a good one:
> +          OS << " (in PCH)";
> +        }
> +        OS << ": ";
> +      }
> +    }
> +    return;
> +  }
> +  unsigned LineNo = PLoc.getLine();
> +
> +  if (!DiagOpts.ShowLocation)
> +    return;
> +
> +  if (DiagOpts.ShowColors)
> +    OS.changeColor(savedColor, true);
> +
> +  OS << PLoc.getFilename();
> +  switch (DiagOpts.Format) {
> +  case DiagnosticOptions::Clang: OS << ':'  << LineNo; break;
> +  case DiagnosticOptions::Msvc:  OS << '('  << LineNo; break;
> +  case DiagnosticOptions::Vi:    OS << " +" << LineNo; break;
> +  }
> +
> +  if (DiagOpts.ShowColumn)
> +    // Compute the column number.
> +    if (unsigned ColNo = PLoc.getColumn()) {
> +      if (DiagOpts.Format == DiagnosticOptions::Msvc) {
> +        OS << ',';
> +        ColNo--;
> +      } else
> +        OS << ':';
> +      OS << ColNo;
> +    }
> +  switch (DiagOpts.Format) {
> +  case DiagnosticOptions::Clang:
> +  case DiagnosticOptions::Vi:    OS << ':';    break;
> +  case DiagnosticOptions::Msvc:  OS << ") : "; break;
> +  }
> +
> +  if (DiagOpts.ShowSourceRanges && !Ranges.empty()) {
> +    FileID CaretFileID =
> +      SM.getFileID(SM.getExpansionLoc(Loc));
> +    bool PrintedRange = false;
> +
> +    for (ArrayRef<CharSourceRange>::const_iterator RI = Ranges.begin(),
> +         RE = Ranges.end();
> +         RI != RE; ++RI) {
> +      // Ignore invalid ranges.
> +      if (!RI->isValid()) continue;
> +
> +      SourceLocation B = SM.getExpansionLoc(RI->getBegin());
> +      SourceLocation E = SM.getExpansionLoc(RI->getEnd());
> +
> +      // If the End location and the start location are the same and are a
> +      // macro location, then the range was something that came from a
> +      // macro expansion or _Pragma.  If this is an object-like macro, the
> +      // best we can do is to highlight the range.  If this is a
> +      // function-like macro, we'd also like to highlight the arguments.
> +      if (B == E && RI->getEnd().isMacroID())
> +        E = SM.getExpansionRange(RI->getEnd()).second;
> +
> +      std::pair<FileID, unsigned> BInfo = SM.getDecomposedLoc(B);
> +      std::pair<FileID, unsigned> EInfo = SM.getDecomposedLoc(E);
> +
> +      // If the start or end of the range is in another file, just discard
> +      // it.
> +      if (BInfo.first != CaretFileID || EInfo.first != CaretFileID)
> +        continue;
> +
> +      // Add in the length of the token, so that we cover multi-char
> +      // tokens.
> +      unsigned TokSize = 0;
> +      if (RI->isTokenRange())
> +        TokSize = Lexer::MeasureTokenLength(E, SM, LangOpts);
> +
> +      OS << '{' << SM.getLineNumber(BInfo.first, BInfo.second) << ':'
> +        << SM.getColumnNumber(BInfo.first, BInfo.second) << '-'
> +        << SM.getLineNumber(EInfo.first, EInfo.second) << ':'
> +        << (SM.getColumnNumber(EInfo.first, EInfo.second)+TokSize)
> +        << '}';
> +      PrintedRange = true;
> +    }
> +
> +    if (PrintedRange)
> +      OS << ':';
> +  }
> +  OS << ' ';
> +}
> +
> +/// \brief Highlight a SourceRange (with ~'s) for any characters on LineNo.
> +void TextDiagnostic::HighlightRange(const CharSourceRange &R,
> +                                    unsigned LineNo, FileID FID,
> +                                    const std::string &SourceLine,
> +                                    std::string &CaretLine) {
> +  assert(CaretLine.size() == SourceLine.size() &&
> +         "Expect a correspondence between source and caret line!");
> +  if (!R.isValid()) return;
> +
> +  SourceLocation Begin = SM.getExpansionLoc(R.getBegin());
> +  SourceLocation End = SM.getExpansionLoc(R.getEnd());
> +
> +  // If the End location and the start location are the same and are a macro
> +  // location, then the range was something that came from a macro expansion
> +  // or _Pragma.  If this is an object-like macro, the best we can do is to
> +  // highlight the range.  If this is a function-like macro, we'd also like to
> +  // highlight the arguments.
> +  if (Begin == End && R.getEnd().isMacroID())
> +    End = SM.getExpansionRange(R.getEnd()).second;
> +
> +  unsigned StartLineNo = SM.getExpansionLineNumber(Begin);
> +  if (StartLineNo > LineNo || SM.getFileID(Begin) != FID)
> +    return;  // No intersection.
> +
> +  unsigned EndLineNo = SM.getExpansionLineNumber(End);
> +  if (EndLineNo < LineNo || SM.getFileID(End) != FID)
> +    return;  // No intersection.
> +
> +  // Compute the column number of the start.
> +  unsigned StartColNo = 0;
> +  if (StartLineNo == LineNo) {
> +    StartColNo = SM.getExpansionColumnNumber(Begin);
> +    if (StartColNo) --StartColNo;  // Zero base the col #.
> +  }
> +
> +  // Compute the column number of the end.
> +  unsigned EndColNo = CaretLine.size();
> +  if (EndLineNo == LineNo) {
> +    EndColNo = SM.getExpansionColumnNumber(End);
> +    if (EndColNo) {
> +      --EndColNo;  // Zero base the col #.
> +
> +      // Add in the length of the token, so that we cover multi-char tokens if
> +      // this is a token range.
> +      if (R.isTokenRange())
> +        EndColNo += Lexer::MeasureTokenLength(End, SM, LangOpts);
> +    } else {
> +      EndColNo = CaretLine.size();
> +    }
> +  }
> +
> +  assert(StartColNo <= EndColNo && "Invalid range!");
> +
> +  // Check that a token range does not highlight only whitespace.
> +  if (R.isTokenRange()) {
> +    // Pick the first non-whitespace column.
> +    while (StartColNo < SourceLine.size() &&
> +           (SourceLine[StartColNo] == ' ' || SourceLine[StartColNo] == '\t'))
> +      ++StartColNo;
> +
> +    // Pick the last non-whitespace column.
> +    if (EndColNo > SourceLine.size())
> +      EndColNo = SourceLine.size();
> +    while (EndColNo-1 &&
> +           (SourceLine[EndColNo-1] == ' ' || SourceLine[EndColNo-1] == '\t'))
> +      --EndColNo;
> +
> +    // If the start/end passed each other, then we are trying to highlight a
> +    // range that just exists in whitespace, which must be some sort of other
> +    // bug.
> +    assert(StartColNo <= EndColNo && "Trying to highlight whitespace??");
> +  }
> +
> +  // Fill the range with ~'s.
> +  for (unsigned i = StartColNo; i < EndColNo; ++i)
> +    CaretLine[i] = '~';
> +}
> +
> +std::string TextDiagnostic::BuildFixItInsertionLine(unsigned LineNo,
> +                                                    const char *LineStart,
> +                                                    const char *LineEnd,
> +                                                    ArrayRef<FixItHint> Hints) {
> +  std::string FixItInsertionLine;
> +  if (Hints.empty() || !DiagOpts.ShowFixits)
> +    return FixItInsertionLine;
> +
> +  for (ArrayRef<FixItHint>::iterator I = Hints.begin(), E = Hints.end();
> +       I != E; ++I) {
> +    if (!I->CodeToInsert.empty()) {
> +      // We have an insertion hint. Determine whether the inserted
> +      // code is on the same line as the caret.
> +      std::pair<FileID, unsigned> HintLocInfo
> +        = SM.getDecomposedExpansionLoc(I->RemoveRange.getBegin());
> +      if (LineNo == SM.getLineNumber(HintLocInfo.first, HintLocInfo.second)) {
> +        // Insert the new code into the line just below the code
> +        // that the user wrote.
> +        unsigned HintColNo
> +          = SM.getColumnNumber(HintLocInfo.first, HintLocInfo.second);
> +        unsigned LastColumnModified
> +          = HintColNo - 1 + I->CodeToInsert.size();
> +        if (LastColumnModified > FixItInsertionLine.size())
> +          FixItInsertionLine.resize(LastColumnModified, ' ');
> +        std::copy(I->CodeToInsert.begin(), I->CodeToInsert.end(),
> +                  FixItInsertionLine.begin() + HintColNo - 1);
> +      } else {
> +        FixItInsertionLine.clear();
> +        break;
> +      }
> +    }
> +  }
> +
> +  if (FixItInsertionLine.empty())
> +    return FixItInsertionLine;
> +
> +  // Now that we have the entire fixit line, expand the tabs in it.
> +  // Since we don't want to insert spaces in the middle of a word,
> +  // find each word and the column it should line up with and insert
> +  // spaces until they match.
> +  unsigned FixItPos = 0;
> +  unsigned LinePos = 0;
> +  unsigned TabExpandedCol = 0;
> +  unsigned LineLength = LineEnd - LineStart;
> +
> +  while (FixItPos < FixItInsertionLine.size() && LinePos < LineLength) {
> +    // Find the next word in the FixIt line.
> +    while (FixItPos < FixItInsertionLine.size() &&
> +           FixItInsertionLine[FixItPos] == ' ')
> +      ++FixItPos;
> +    unsigned CharDistance = FixItPos - TabExpandedCol;
> +
> +    // Walk forward in the source line, keeping track of
> +    // the tab-expanded column.
> +    for (unsigned I = 0; I < CharDistance; ++I, ++LinePos)
> +      if (LinePos >= LineLength || LineStart[LinePos] != '\t')
> +        ++TabExpandedCol;
> +      else
> +        TabExpandedCol =
> +          (TabExpandedCol/DiagOpts.TabStop + 1) * DiagOpts.TabStop;
> +
> +    // Adjust the fixit line to match this column.
> +    FixItInsertionLine.insert(FixItPos, TabExpandedCol-FixItPos, ' ');
> +    FixItPos = TabExpandedCol;
> +
> +    // Walk to the end of the word.
> +    while (FixItPos < FixItInsertionLine.size() &&
> +           FixItInsertionLine[FixItPos] != ' ')
> +      ++FixItPos;
> +  }
> +
> +  return FixItInsertionLine;
> +}
> +
> +void TextDiagnostic::ExpandTabs(std::string &SourceLine,
> +                                std::string &CaretLine) {
> +  // Scan the source line, looking for tabs.  If we find any, manually expand
> +  // them to spaces and update the CaretLine to match.
> +  for (unsigned i = 0; i != SourceLine.size(); ++i) {
> +    if (SourceLine[i] != '\t') continue;
> +
> +    // Replace this tab with at least one space.
> +    SourceLine[i] = ' ';
> +
> +    // Compute the number of spaces we need to insert.
> +    unsigned TabStop = DiagOpts.TabStop;
> +    assert(0 < TabStop && TabStop <= DiagnosticOptions::MaxTabStop &&
> +           "Invalid -ftabstop value");
> +    unsigned NumSpaces = ((i+TabStop)/TabStop * TabStop) - (i+1);
> +    assert(NumSpaces < TabStop && "Invalid computation of space amt");
> +
> +    // Insert spaces into the SourceLine.
> +    SourceLine.insert(i+1, NumSpaces, ' ');
> +
> +    // Insert spaces or ~'s into CaretLine.
> +    CaretLine.insert(i+1, NumSpaces, CaretLine[i] == '~' ? '~' : ' ');
> +  }
> +}
> +
> +void TextDiagnostic::EmitParseableFixits(ArrayRef<FixItHint> Hints) {
> +  if (!DiagOpts.ShowParseableFixits)
> +    return;
> +
> +  // We follow FixItRewriter's example in not (yet) handling
> +  // fix-its in macros.
> +  for (ArrayRef<FixItHint>::iterator I = Hints.begin(), E = Hints.end();
> +       I != E; ++I) {
> +    if (I->RemoveRange.isInvalid() ||
> +        I->RemoveRange.getBegin().isMacroID() ||
> +        I->RemoveRange.getEnd().isMacroID())
> +      return;
> +  }
> +
> +  for (ArrayRef<FixItHint>::iterator I = Hints.begin(), E = Hints.end();
> +       I != E; ++I) {
> +    SourceLocation BLoc = I->RemoveRange.getBegin();
> +    SourceLocation ELoc = I->RemoveRange.getEnd();
> +
> +    std::pair<FileID, unsigned> BInfo = SM.getDecomposedLoc(BLoc);
> +    std::pair<FileID, unsigned> EInfo = SM.getDecomposedLoc(ELoc);
> +
> +    // Adjust for token ranges.
> +    if (I->RemoveRange.isTokenRange())
> +      EInfo.second += Lexer::MeasureTokenLength(ELoc, SM, LangOpts);
> +
> +    // We specifically do not do word-wrapping or tab-expansion here,
> +    // because this is supposed to be easy to parse.
> +    PresumedLoc PLoc = SM.getPresumedLoc(BLoc);
> +    if (PLoc.isInvalid())
> +      break;
> +
> +    OS << "fix-it:\"";
> +    OS.write_escaped(PLoc.getFilename());
> +    OS << "\":{" << SM.getLineNumber(BInfo.first, BInfo.second)
> +      << ':' << SM.getColumnNumber(BInfo.first, BInfo.second)
> +      << '-' << SM.getLineNumber(EInfo.first, EInfo.second)
> +      << ':' << SM.getColumnNumber(EInfo.first, EInfo.second)
> +      << "}:\"";
> +    OS.write_escaped(I->CodeToInsert);
> +    OS << "\"\n";
> +  }
> +}
> +
>
> Modified: cfe/trunk/lib/Frontend/TextDiagnosticPrinter.cpp
> URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Frontend/TextDiagnosticPrinter.cpp?rev=142086&r1=142085&r2=142086&view=diff
> ==============================================================================
> --- cfe/trunk/lib/Frontend/TextDiagnosticPrinter.cpp (original)
> +++ cfe/trunk/lib/Frontend/TextDiagnosticPrinter.cpp Sat Oct 15 18:43:53 2011
> @@ -15,6 +15,7 @@
>  #include "clang/Basic/FileManager.h"
>  #include "clang/Basic/SourceManager.h"
>  #include "clang/Frontend/DiagnosticOptions.h"
> +#include "clang/Frontend/TextDiagnostic.h"
>  #include "clang/Lex/Lexer.h"
>  #include "llvm/Support/MemoryBuffer.h"
>  #include "llvm/Support/raw_ostream.h"
> @@ -23,23 +24,6 @@
>  #include <algorithm>
>  using namespace clang;
>
> -static const enum raw_ostream::Colors noteColor =
> -  raw_ostream::BLACK;
> -static const enum raw_ostream::Colors fixitColor =
> -  raw_ostream::GREEN;
> -static const enum raw_ostream::Colors caretColor =
> -  raw_ostream::GREEN;
> -static const enum raw_ostream::Colors warningColor =
> -  raw_ostream::MAGENTA;
> -static const enum raw_ostream::Colors errorColor = raw_ostream::RED;
> -static const enum raw_ostream::Colors fatalColor = raw_ostream::RED;
> -// Used for changing only the bold attribute.
> -static const enum raw_ostream::Colors savedColor =
> -  raw_ostream::SAVEDCOLOR;
> -
> -/// \brief Number of spaces to indent when word-wrapping.
> -const unsigned WordWrapIndentation = 6;
> -
>  TextDiagnosticPrinter::TextDiagnosticPrinter(raw_ostream &os,
>                                              const DiagnosticOptions &diags,
>                                              bool _OwnsOutputStream)
> @@ -52,1144 +36,6 @@
>     delete &OS;
>  }
>
> -/// \brief When the source code line we want to print is too long for
> -/// the terminal, select the "interesting" region.
> -static void SelectInterestingSourceRegion(std::string &SourceLine,
> -                                          std::string &CaretLine,
> -                                          std::string &FixItInsertionLine,
> -                                          unsigned EndOfCaretToken,
> -                                          unsigned Columns) {
> -  unsigned MaxSize = std::max(SourceLine.size(),
> -                              std::max(CaretLine.size(),
> -                                       FixItInsertionLine.size()));
> -  if (MaxSize > SourceLine.size())
> -    SourceLine.resize(MaxSize, ' ');
> -  if (MaxSize > CaretLine.size())
> -    CaretLine.resize(MaxSize, ' ');
> -  if (!FixItInsertionLine.empty() && MaxSize > FixItInsertionLine.size())
> -    FixItInsertionLine.resize(MaxSize, ' ');
> -
> -  // Find the slice that we need to display the full caret line
> -  // correctly.
> -  unsigned CaretStart = 0, CaretEnd = CaretLine.size();
> -  for (; CaretStart != CaretEnd; ++CaretStart)
> -    if (!isspace(CaretLine[CaretStart]))
> -      break;
> -
> -  for (; CaretEnd != CaretStart; --CaretEnd)
> -    if (!isspace(CaretLine[CaretEnd - 1]))
> -      break;
> -
> -  // Make sure we don't chop the string shorter than the caret token
> -  // itself.
> -  if (CaretEnd < EndOfCaretToken)
> -    CaretEnd = EndOfCaretToken;
> -
> -  // If we have a fix-it line, make sure the slice includes all of the
> -  // fix-it information.
> -  if (!FixItInsertionLine.empty()) {
> -    unsigned FixItStart = 0, FixItEnd = FixItInsertionLine.size();
> -    for (; FixItStart != FixItEnd; ++FixItStart)
> -      if (!isspace(FixItInsertionLine[FixItStart]))
> -        break;
> -
> -    for (; FixItEnd != FixItStart; --FixItEnd)
> -      if (!isspace(FixItInsertionLine[FixItEnd - 1]))
> -        break;
> -
> -    if (FixItStart < CaretStart)
> -      CaretStart = FixItStart;
> -    if (FixItEnd > CaretEnd)
> -      CaretEnd = FixItEnd;
> -  }
> -
> -  // CaretLine[CaretStart, CaretEnd) contains all of the interesting
> -  // parts of the caret line. While this slice is smaller than the
> -  // number of columns we have, try to grow the slice to encompass
> -  // more context.
> -
> -  // If the end of the interesting region comes before we run out of
> -  // space in the terminal, start at the beginning of the line.
> -  if (Columns > 3 && CaretEnd < Columns - 3)
> -    CaretStart = 0;
> -
> -  unsigned TargetColumns = Columns;
> -  if (TargetColumns > 8)
> -    TargetColumns -= 8; // Give us extra room for the ellipses.
> -  unsigned SourceLength = SourceLine.size();
> -  while ((CaretEnd - CaretStart) < TargetColumns) {
> -    bool ExpandedRegion = false;
> -    // Move the start of the interesting region left until we've
> -    // pulled in something else interesting.
> -    if (CaretStart == 1)
> -      CaretStart = 0;
> -    else if (CaretStart > 1) {
> -      unsigned NewStart = CaretStart - 1;
> -
> -      // Skip over any whitespace we see here; we're looking for
> -      // another bit of interesting text.
> -      while (NewStart && isspace(SourceLine[NewStart]))
> -        --NewStart;
> -
> -      // Skip over this bit of "interesting" text.
> -      while (NewStart && !isspace(SourceLine[NewStart]))
> -        --NewStart;
> -
> -      // Move up to the non-whitespace character we just saw.
> -      if (NewStart)
> -        ++NewStart;
> -
> -      // If we're still within our limit, update the starting
> -      // position within the source/caret line.
> -      if (CaretEnd - NewStart <= TargetColumns) {
> -        CaretStart = NewStart;
> -        ExpandedRegion = true;
> -      }
> -    }
> -
> -    // Move the end of the interesting region right until we've
> -    // pulled in something else interesting.
> -    if (CaretEnd != SourceLength) {
> -      assert(CaretEnd < SourceLength && "Unexpected caret position!");
> -      unsigned NewEnd = CaretEnd;
> -
> -      // Skip over any whitespace we see here; we're looking for
> -      // another bit of interesting text.
> -      while (NewEnd != SourceLength && isspace(SourceLine[NewEnd - 1]))
> -        ++NewEnd;
> -
> -      // Skip over this bit of "interesting" text.
> -      while (NewEnd != SourceLength && !isspace(SourceLine[NewEnd - 1]))
> -        ++NewEnd;
> -
> -      if (NewEnd - CaretStart <= TargetColumns) {
> -        CaretEnd = NewEnd;
> -        ExpandedRegion = true;
> -      }
> -    }
> -
> -    if (!ExpandedRegion)
> -      break;
> -  }
> -
> -  // [CaretStart, CaretEnd) is the slice we want. Update the various
> -  // output lines to show only this slice, with two-space padding
> -  // before the lines so that it looks nicer.
> -  if (CaretEnd < SourceLine.size())
> -    SourceLine.replace(CaretEnd, std::string::npos, "...");
> -  if (CaretEnd < CaretLine.size())
> -    CaretLine.erase(CaretEnd, std::string::npos);
> -  if (FixItInsertionLine.size() > CaretEnd)
> -    FixItInsertionLine.erase(CaretEnd, std::string::npos);
> -
> -  if (CaretStart > 2) {
> -    SourceLine.replace(0, CaretStart, "  ...");
> -    CaretLine.replace(0, CaretStart, "     ");
> -    if (FixItInsertionLine.size() >= CaretStart)
> -      FixItInsertionLine.replace(0, CaretStart, "     ");
> -  }
> -}
> -
> -/// Look through spelling locations for a macro argument expansion, and
> -/// if found skip to it so that we can trace the argument rather than the macros
> -/// in which that argument is used. If no macro argument expansion is found,
> -/// don't skip anything and return the starting location.
> -static SourceLocation skipToMacroArgExpansion(const SourceManager &SM,
> -                                                  SourceLocation StartLoc) {
> -  for (SourceLocation L = StartLoc; L.isMacroID();
> -       L = SM.getImmediateSpellingLoc(L)) {
> -    if (SM.isMacroArgExpansion(L))
> -      return L;
> -  }
> -
> -  // Otherwise just return initial location, there's nothing to skip.
> -  return StartLoc;
> -}
> -
> -/// Gets the location of the immediate macro caller, one level up the stack
> -/// toward the initial macro typed into the source.
> -static SourceLocation getImmediateMacroCallerLoc(const SourceManager &SM,
> -                                                 SourceLocation Loc) {
> -  if (!Loc.isMacroID()) return Loc;
> -
> -  // When we have the location of (part of) an expanded parameter, its spelling
> -  // location points to the argument as typed into the macro call, and
> -  // therefore is used to locate the macro caller.
> -  if (SM.isMacroArgExpansion(Loc))
> -    return SM.getImmediateSpellingLoc(Loc);
> -
> -  // Otherwise, the caller of the macro is located where this macro is
> -  // expanded (while the spelling is part of the macro definition).
> -  return SM.getImmediateExpansionRange(Loc).first;
> -}
> -
> -/// Gets the location of the immediate macro callee, one level down the stack
> -/// toward the leaf macro.
> -static SourceLocation getImmediateMacroCalleeLoc(const SourceManager &SM,
> -                                                 SourceLocation Loc) {
> -  if (!Loc.isMacroID()) return Loc;
> -
> -  // When we have the location of (part of) an expanded parameter, its
> -  // expansion location points to the unexpanded paramater reference within
> -  // the macro definition (or callee).
> -  if (SM.isMacroArgExpansion(Loc))
> -    return SM.getImmediateExpansionRange(Loc).first;
> -
> -  // Otherwise, the callee of the macro is located where this location was
> -  // spelled inside the macro definition.
> -  return SM.getImmediateSpellingLoc(Loc);
> -}
> -
> -/// Get the presumed location of a diagnostic message. This computes the
> -/// presumed location for the top of any macro backtrace when present.
> -static PresumedLoc getDiagnosticPresumedLoc(const SourceManager &SM,
> -                                            SourceLocation Loc) {
> -  // This is a condensed form of the algorithm used by EmitCaretDiagnostic to
> -  // walk to the top of the macro call stack.
> -  while (Loc.isMacroID()) {
> -    Loc = skipToMacroArgExpansion(SM, Loc);
> -    Loc = getImmediateMacroCallerLoc(SM, Loc);
> -  }
> -
> -  return SM.getPresumedLoc(Loc);
> -}
> -
> -/// \brief Skip over whitespace in the string, starting at the given
> -/// index.
> -///
> -/// \returns The index of the first non-whitespace character that is
> -/// greater than or equal to Idx or, if no such character exists,
> -/// returns the end of the string.
> -static unsigned skipWhitespace(unsigned Idx, StringRef Str, unsigned Length) {
> -  while (Idx < Length && isspace(Str[Idx]))
> -    ++Idx;
> -  return Idx;
> -}
> -
> -/// \brief If the given character is the start of some kind of
> -/// balanced punctuation (e.g., quotes or parentheses), return the
> -/// character that will terminate the punctuation.
> -///
> -/// \returns The ending punctuation character, if any, or the NULL
> -/// character if the input character does not start any punctuation.
> -static inline char findMatchingPunctuation(char c) {
> -  switch (c) {
> -  case '\'': return '\'';
> -  case '`': return '\'';
> -  case '"':  return '"';
> -  case '(':  return ')';
> -  case '[': return ']';
> -  case '{': return '}';
> -  default: break;
> -  }
> -
> -  return 0;
> -}
> -
> -/// \brief Find the end of the word starting at the given offset
> -/// within a string.
> -///
> -/// \returns the index pointing one character past the end of the
> -/// word.
> -static unsigned findEndOfWord(unsigned Start, StringRef Str,
> -                              unsigned Length, unsigned Column,
> -                              unsigned Columns) {
> -  assert(Start < Str.size() && "Invalid start position!");
> -  unsigned End = Start + 1;
> -
> -  // If we are already at the end of the string, take that as the word.
> -  if (End == Str.size())
> -    return End;
> -
> -  // Determine if the start of the string is actually opening
> -  // punctuation, e.g., a quote or parentheses.
> -  char EndPunct = findMatchingPunctuation(Str[Start]);
> -  if (!EndPunct) {
> -    // This is a normal word. Just find the first space character.
> -    while (End < Length && !isspace(Str[End]))
> -      ++End;
> -    return End;
> -  }
> -
> -  // We have the start of a balanced punctuation sequence (quotes,
> -  // parentheses, etc.). Determine the full sequence is.
> -  llvm::SmallString<16> PunctuationEndStack;
> -  PunctuationEndStack.push_back(EndPunct);
> -  while (End < Length && !PunctuationEndStack.empty()) {
> -    if (Str[End] == PunctuationEndStack.back())
> -      PunctuationEndStack.pop_back();
> -    else if (char SubEndPunct = findMatchingPunctuation(Str[End]))
> -      PunctuationEndStack.push_back(SubEndPunct);
> -
> -    ++End;
> -  }
> -
> -  // Find the first space character after the punctuation ended.
> -  while (End < Length && !isspace(Str[End]))
> -    ++End;
> -
> -  unsigned PunctWordLength = End - Start;
> -  if (// If the word fits on this line
> -      Column + PunctWordLength <= Columns ||
> -      // ... or the word is "short enough" to take up the next line
> -      // without too much ugly white space
> -      PunctWordLength < Columns/3)
> -    return End; // Take the whole thing as a single "word".
> -
> -  // The whole quoted/parenthesized string is too long to print as a
> -  // single "word". Instead, find the "word" that starts just after
> -  // the punctuation and use that end-point instead. This will recurse
> -  // until it finds something small enough to consider a word.
> -  return findEndOfWord(Start + 1, Str, Length, Column + 1, Columns);
> -}
> -
> -/// \brief Print the given string to a stream, word-wrapping it to
> -/// some number of columns in the process.
> -///
> -/// \param OS the stream to which the word-wrapping string will be
> -/// emitted.
> -/// \param Str the string to word-wrap and output.
> -/// \param Columns the number of columns to word-wrap to.
> -/// \param Column the column number at which the first character of \p
> -/// Str will be printed. This will be non-zero when part of the first
> -/// line has already been printed.
> -/// \param Indentation the number of spaces to indent any lines beyond
> -/// the first line.
> -/// \returns true if word-wrapping was required, or false if the
> -/// string fit on the first line.
> -static bool printWordWrapped(raw_ostream &OS, StringRef Str,
> -                             unsigned Columns,
> -                             unsigned Column = 0,
> -                             unsigned Indentation = WordWrapIndentation) {
> -  const unsigned Length = std::min(Str.find('\n'), Str.size());
> -
> -  // The string used to indent each line.
> -  llvm::SmallString<16> IndentStr;
> -  IndentStr.assign(Indentation, ' ');
> -  bool Wrapped = false;
> -  for (unsigned WordStart = 0, WordEnd; WordStart < Length;
> -       WordStart = WordEnd) {
> -    // Find the beginning of the next word.
> -    WordStart = skipWhitespace(WordStart, Str, Length);
> -    if (WordStart == Length)
> -      break;
> -
> -    // Find the end of this word.
> -    WordEnd = findEndOfWord(WordStart, Str, Length, Column, Columns);
> -
> -    // Does this word fit on the current line?
> -    unsigned WordLength = WordEnd - WordStart;
> -    if (Column + WordLength < Columns) {
> -      // This word fits on the current line; print it there.
> -      if (WordStart) {
> -        OS << ' ';
> -        Column += 1;
> -      }
> -      OS << Str.substr(WordStart, WordLength);
> -      Column += WordLength;
> -      continue;
> -    }
> -
> -    // This word does not fit on the current line, so wrap to the next
> -    // line.
> -    OS << '\n';
> -    OS.write(&IndentStr[0], Indentation);
> -    OS << Str.substr(WordStart, WordLength);
> -    Column = Indentation + WordLength;
> -    Wrapped = true;
> -  }
> -
> -  // Append any remaning text from the message with its existing formatting.
> -  OS << Str.substr(Length);
> -
> -  return Wrapped;
> -}
> -
> -namespace {
> -
> -/// \brief Class to encapsulate the logic for formatting and printing a textual
> -/// diagnostic message.
> -///
> -/// This class provides an interface for building and emitting a textual
> -/// diagnostic, including all of the macro backtraces, caret diagnostics, FixIt
> -/// Hints, and code snippets. In the presence of macros this involves
> -/// a recursive process, synthesizing notes for each macro expansion.
> -///
> -/// The purpose of this class is to isolate the implementation of printing
> -/// beautiful text diagnostics from any particular interfaces. The Clang
> -/// DiagnosticClient is implemented through this class as is diagnostic
> -/// printing coming out of libclang.
> -///
> -/// A brief worklist:
> -/// FIXME: Sink the recursive printing of template instantiations into this
> -/// class.
> -class TextDiagnostic {
> -  raw_ostream &OS;
> -  const SourceManager &SM;
> -  const LangOptions &LangOpts;
> -  const DiagnosticOptions &DiagOpts;
> -
> -  /// \brief The location of the previous diagnostic if known.
> -  ///
> -  /// This will be invalid in cases where there is no (known) previous
> -  /// diagnostic location, or that location itself is invalid or comes from
> -  /// a different source manager than SM.
> -  SourceLocation LastLoc;
> -
> -  /// \brief The location of the last include whose stack was printed if known.
> -  ///
> -  /// Same restriction as \see LastLoc essentially, but tracking include stack
> -  /// root locations rather than diagnostic locations.
> -  SourceLocation LastIncludeLoc;
> -
> -  /// \brief The level of the last diagnostic emitted.
> -  ///
> -  /// The level of the last diagnostic emitted. Used to detect level changes
> -  /// which change the amount of information displayed.
> -  DiagnosticsEngine::Level LastLevel;
> -
> -public:
> -  TextDiagnostic(raw_ostream &OS,
> -                 const SourceManager &SM,
> -                 const LangOptions &LangOpts,
> -                 const DiagnosticOptions &DiagOpts,
> -                 FullSourceLoc LastLoc = FullSourceLoc(),
> -                 FullSourceLoc LastIncludeLoc = FullSourceLoc(),
> -                 DiagnosticsEngine::Level LastLevel
> -                   = DiagnosticsEngine::Level())
> -    : OS(OS), SM(SM), LangOpts(LangOpts), DiagOpts(DiagOpts),
> -      LastLoc(LastLoc), LastIncludeLoc(LastIncludeLoc), LastLevel(LastLevel) {
> -    if (LastLoc.isValid() && &SM != &LastLoc.getManager())
> -      this->LastLoc = SourceLocation();
> -    if (LastIncludeLoc.isValid() && &SM != &LastIncludeLoc.getManager())
> -      this->LastIncludeLoc = SourceLocation();
> -  }
> -
> -  /// \brief Get the last diagnostic location emitted.
> -  SourceLocation getLastLoc() const { return LastLoc; }
> -
> -  /// \brief Get the last emitted include stack location.
> -  SourceLocation getLastIncludeLoc() const { return LastIncludeLoc; }
> -
> -  /// \brief Get the last diagnostic level.
> -  DiagnosticsEngine::Level getLastLevel() const { return LastLevel; }
> -
> -  void Emit(SourceLocation Loc, DiagnosticsEngine::Level Level,
> -            StringRef Message, ArrayRef<CharSourceRange> Ranges,
> -            ArrayRef<FixItHint> FixItHints,
> -            bool LastCaretDiagnosticWasNote = false) {
> -    PresumedLoc PLoc = getDiagnosticPresumedLoc(SM, Loc);
> -
> -    // First, if this diagnostic is not in the main file, print out the
> -    // "included from" lines.
> -    emitIncludeStack(PLoc.getIncludeLoc(), Level);
> -
> -    uint64_t StartOfLocationInfo = OS.tell();
> -
> -    // Next emit the location of this particular diagnostic.
> -    EmitDiagnosticLoc(Loc, PLoc, Level, Ranges);
> -
> -    if (DiagOpts.ShowColors)
> -      OS.resetColor();
> -
> -    printDiagnosticLevel(OS, Level, DiagOpts.ShowColors);
> -    printDiagnosticMessage(OS, Level, Message,
> -                           OS.tell() - StartOfLocationInfo,
> -                           DiagOpts.MessageLength, DiagOpts.ShowColors);
> -
> -    // If caret diagnostics are enabled and we have location, we want to
> -    // emit the caret.  However, we only do this if the location moved
> -    // from the last diagnostic, if the last diagnostic was a note that
> -    // was part of a different warning or error diagnostic, or if the
> -    // diagnostic has ranges.  We don't want to emit the same caret
> -    // multiple times if one loc has multiple diagnostics.
> -    if (DiagOpts.ShowCarets &&
> -        (Loc != LastLoc || !Ranges.empty() || !FixItHints.empty() ||
> -         (LastLevel == DiagnosticsEngine::Note && Level != LastLevel))) {
> -      // Get the ranges into a local array we can hack on.
> -      SmallVector<CharSourceRange, 20> MutableRanges(Ranges.begin(),
> -                                                     Ranges.end());
> -
> -      for (ArrayRef<FixItHint>::const_iterator I = FixItHints.begin(),
> -                                               E = FixItHints.end();
> -           I != E; ++I)
> -        if (I->RemoveRange.isValid())
> -          MutableRanges.push_back(I->RemoveRange);
> -
> -      unsigned MacroDepth = 0;
> -      EmitCaret(Loc, MutableRanges, FixItHints, MacroDepth);
> -    }
> -
> -    LastLoc = Loc;
> -    LastLevel = Level;
> -  }
> -
> -  /// \brief Emit the caret and underlining text.
> -  ///
> -  /// Walks up the macro expansion stack printing the code snippet, caret,
> -  /// underlines and FixItHint display as appropriate at each level. Walk is
> -  /// accomplished by calling itself recursively.
> -  ///
> -  /// FIXME: Remove macro expansion from this routine, it shouldn't be tied to
> -  /// caret diagnostics.
> -  /// FIXME: Break up massive function into logical units.
> -  ///
> -  /// \param Loc The location for this caret.
> -  /// \param Ranges The underlined ranges for this code snippet.
> -  /// \param Hints The FixIt hints active for this diagnostic.
> -  /// \param MacroSkipEnd The depth to stop skipping macro expansions.
> -  /// \param OnMacroInst The current depth of the macro expansion stack.
> -  void EmitCaret(SourceLocation Loc,
> -            SmallVectorImpl<CharSourceRange>& Ranges,
> -            ArrayRef<FixItHint> Hints,
> -            unsigned &MacroDepth,
> -            unsigned OnMacroInst = 0) {
> -    assert(!Loc.isInvalid() && "must have a valid source location here");
> -
> -    // If this is a file source location, directly emit the source snippet and
> -    // caret line. Also record the macro depth reached.
> -    if (Loc.isFileID()) {
> -      assert(MacroDepth == 0 && "We shouldn't hit a leaf node twice!");
> -      MacroDepth = OnMacroInst;
> -      EmitSnippetAndCaret(Loc, Ranges, Hints);
> -      return;
> -    }
> -    // Otherwise recurse through each macro expansion layer.
> -
> -    // When processing macros, skip over the expansions leading up to
> -    // a macro argument, and trace the argument's expansion stack instead.
> -    Loc = skipToMacroArgExpansion(SM, Loc);
> -
> -    SourceLocation OneLevelUp = getImmediateMacroCallerLoc(SM, Loc);
> -
> -    // FIXME: Map ranges?
> -    EmitCaret(OneLevelUp, Ranges, Hints, MacroDepth, OnMacroInst + 1);
> -
> -    // Map the location.
> -    Loc = getImmediateMacroCalleeLoc(SM, Loc);
> -
> -    unsigned MacroSkipStart = 0, MacroSkipEnd = 0;
> -    if (MacroDepth > DiagOpts.MacroBacktraceLimit) {
> -      MacroSkipStart = DiagOpts.MacroBacktraceLimit / 2 +
> -        DiagOpts.MacroBacktraceLimit % 2;
> -      MacroSkipEnd = MacroDepth - DiagOpts.MacroBacktraceLimit / 2;
> -    }
> -
> -    // Whether to suppress printing this macro expansion.
> -    bool Suppressed = (OnMacroInst >= MacroSkipStart &&
> -                       OnMacroInst < MacroSkipEnd);
> -
> -    // Map the ranges.
> -    for (SmallVectorImpl<CharSourceRange>::iterator I = Ranges.begin(),
> -                                                    E = Ranges.end();
> -         I != E; ++I) {
> -      SourceLocation Start = I->getBegin(), End = I->getEnd();
> -      if (Start.isMacroID())
> -        I->setBegin(getImmediateMacroCalleeLoc(SM, Start));
> -      if (End.isMacroID())
> -        I->setEnd(getImmediateMacroCalleeLoc(SM, End));
> -    }
> -
> -    if (!Suppressed) {
> -      // Don't print recursive expansion notes from an expansion note.
> -      Loc = SM.getSpellingLoc(Loc);
> -
> -      // Get the pretty name, according to #line directives etc.
> -      PresumedLoc PLoc = SM.getPresumedLoc(Loc);
> -      if (PLoc.isInvalid())
> -        return;
> -
> -      // If this diagnostic is not in the main file, print out the
> -      // "included from" lines.
> -      emitIncludeStack(PLoc.getIncludeLoc(), DiagnosticsEngine::Note);
> -
> -      if (DiagOpts.ShowLocation) {
> -        // Emit the file/line/column that this expansion came from.
> -        OS << PLoc.getFilename() << ':' << PLoc.getLine() << ':';
> -        if (DiagOpts.ShowColumn)
> -          OS << PLoc.getColumn() << ':';
> -        OS << ' ';
> -      }
> -      OS << "note: expanded from:\n";
> -
> -      EmitSnippetAndCaret(Loc, Ranges, ArrayRef<FixItHint>());
> -      return;
> -    }
> -
> -    if (OnMacroInst == MacroSkipStart) {
> -      // Tell the user that we've skipped contexts.
> -      OS << "note: (skipping " << (MacroSkipEnd - MacroSkipStart)
> -      << " expansions in backtrace; use -fmacro-backtrace-limit=0 to see "
> -      "all)\n";
> -    }
> -  }
> -
> -  /// \brief Emit a code snippet and caret line.
> -  ///
> -  /// This routine emits a single line's code snippet and caret line..
> -  ///
> -  /// \param Loc The location for the caret.
> -  /// \param Ranges The underlined ranges for this code snippet.
> -  /// \param Hints The FixIt hints active for this diagnostic.
> -  void EmitSnippetAndCaret(SourceLocation Loc,
> -                           SmallVectorImpl<CharSourceRange>& Ranges,
> -                           ArrayRef<FixItHint> Hints) {
> -    assert(!Loc.isInvalid() && "must have a valid source location here");
> -    assert(Loc.isFileID() && "must have a file location here");
> -
> -    // Decompose the location into a FID/Offset pair.
> -    std::pair<FileID, unsigned> LocInfo = SM.getDecomposedLoc(Loc);
> -    FileID FID = LocInfo.first;
> -    unsigned FileOffset = LocInfo.second;
> -
> -    // Get information about the buffer it points into.
> -    bool Invalid = false;
> -    const char *BufStart = SM.getBufferData(FID, &Invalid).data();
> -    if (Invalid)
> -      return;
> -
> -    unsigned LineNo = SM.getLineNumber(FID, FileOffset);
> -    unsigned ColNo = SM.getColumnNumber(FID, FileOffset);
> -    unsigned CaretEndColNo
> -      = ColNo + Lexer::MeasureTokenLength(Loc, SM, LangOpts);
> -
> -    // Rewind from the current position to the start of the line.
> -    const char *TokPtr = BufStart+FileOffset;
> -    const char *LineStart = TokPtr-ColNo+1; // Column # is 1-based.
> -
> -
> -    // Compute the line end.  Scan forward from the error position to the end of
> -    // the line.
> -    const char *LineEnd = TokPtr;
> -    while (*LineEnd != '\n' && *LineEnd != '\r' && *LineEnd != '\0')
> -      ++LineEnd;
> -
> -    // FIXME: This shouldn't be necessary, but the CaretEndColNo can extend past
> -    // the source line length as currently being computed. See
> -    // test/Misc/message-length.c.
> -    CaretEndColNo = std::min(CaretEndColNo, unsigned(LineEnd - LineStart));
> -
> -    // Copy the line of code into an std::string for ease of manipulation.
> -    std::string SourceLine(LineStart, LineEnd);
> -
> -    // Create a line for the caret that is filled with spaces that is the same
> -    // length as the line of source code.
> -    std::string CaretLine(LineEnd-LineStart, ' ');
> -
> -    // Highlight all of the characters covered by Ranges with ~ characters.
> -    for (SmallVectorImpl<CharSourceRange>::iterator I = Ranges.begin(),
> -                                                    E = Ranges.end();
> -         I != E; ++I)
> -      HighlightRange(*I, LineNo, FID, SourceLine, CaretLine);
> -
> -    // Next, insert the caret itself.
> -    if (ColNo-1 < CaretLine.size())
> -      CaretLine[ColNo-1] = '^';
> -    else
> -      CaretLine.push_back('^');
> -
> -    ExpandTabs(SourceLine, CaretLine);
> -
> -    // If we are in -fdiagnostics-print-source-range-info mode, we are trying
> -    // to produce easily machine parsable output.  Add a space before the
> -    // source line and the caret to make it trivial to tell the main diagnostic
> -    // line from what the user is intended to see.
> -    if (DiagOpts.ShowSourceRanges) {
> -      SourceLine = ' ' + SourceLine;
> -      CaretLine = ' ' + CaretLine;
> -    }
> -
> -    std::string FixItInsertionLine = BuildFixItInsertionLine(LineNo,
> -                                                             LineStart, LineEnd,
> -                                                             Hints);
> -
> -    // If the source line is too long for our terminal, select only the
> -    // "interesting" source region within that line.
> -    unsigned Columns = DiagOpts.MessageLength;
> -    if (Columns && SourceLine.size() > Columns)
> -      SelectInterestingSourceRegion(SourceLine, CaretLine, FixItInsertionLine,
> -                                    CaretEndColNo, Columns);
> -
> -    // Finally, remove any blank spaces from the end of CaretLine.
> -    while (CaretLine[CaretLine.size()-1] == ' ')
> -      CaretLine.erase(CaretLine.end()-1);
> -
> -    // Emit what we have computed.
> -    OS << SourceLine << '\n';
> -
> -    if (DiagOpts.ShowColors)
> -      OS.changeColor(caretColor, true);
> -    OS << CaretLine << '\n';
> -    if (DiagOpts.ShowColors)
> -      OS.resetColor();
> -
> -    if (!FixItInsertionLine.empty()) {
> -      if (DiagOpts.ShowColors)
> -        // Print fixit line in color
> -        OS.changeColor(fixitColor, false);
> -      if (DiagOpts.ShowSourceRanges)
> -        OS << ' ';
> -      OS << FixItInsertionLine << '\n';
> -      if (DiagOpts.ShowColors)
> -        OS.resetColor();
> -    }
> -
> -    // Print out any parseable fixit information requested by the options.
> -    EmitParseableFixits(Hints);
> -  }
> -
> -  /// \brief Print the diagonstic level to a raw_ostream.
> -  ///
> -  /// This is a static helper that handles colorizing the level and formatting
> -  /// it into an arbitrary output stream. This is used internally by the
> -  /// TextDiagnostic emission code, but it can also be used directly by
> -  /// consumers that don't have a source manager or other state that the full
> -  /// TextDiagnostic logic requires.
> -  static void printDiagnosticLevel(raw_ostream &OS,
> -                                   DiagnosticsEngine::Level Level,
> -                                   bool ShowColors) {
> -    if (ShowColors) {
> -      // Print diagnostic category in bold and color
> -      switch (Level) {
> -      case DiagnosticsEngine::Ignored:
> -        llvm_unreachable("Invalid diagnostic type");
> -      case DiagnosticsEngine::Note:    OS.changeColor(noteColor, true); break;
> -      case DiagnosticsEngine::Warning:
> -        OS.changeColor(warningColor, true);
> -        break;
> -      case DiagnosticsEngine::Error:   OS.changeColor(errorColor, true); break;
> -      case DiagnosticsEngine::Fatal:   OS.changeColor(fatalColor, true); break;
> -      }
> -    }
> -
> -    switch (Level) {
> -    case DiagnosticsEngine::Ignored:
> -      llvm_unreachable("Invalid diagnostic type");
> -    case DiagnosticsEngine::Note:    OS << "note: "; break;
> -    case DiagnosticsEngine::Warning: OS << "warning: "; break;
> -    case DiagnosticsEngine::Error:   OS << "error: "; break;
> -    case DiagnosticsEngine::Fatal:   OS << "fatal error: "; break;
> -    }
> -
> -    if (ShowColors)
> -      OS.resetColor();
> -  }
> -
> -  /// \brief Pretty-print a diagnostic message to a raw_ostream.
> -  ///
> -  /// This is a static helper to handle the line wrapping, colorizing, and
> -  /// rendering of a diagnostic message to a particular ostream. It is
> -  /// publically visible so that clients which do not have sufficient state to
> -  /// build a complete TextDiagnostic object can still get consistent
> -  /// formatting of their diagnostic messages.
> -  ///
> -  /// \param OS Where the message is printed
> -  /// \param Level Used to colorizing the message
> -  /// \param Message The text actually printed
> -  /// \param CurrentColumn The starting column of the first line, accounting
> -  ///                      for any prefix.
> -  /// \param Columns The number of columns to use in line-wrapping, 0 disables
> -  ///                all line-wrapping.
> -  /// \param ShowColors Enable colorizing of the message.
> -  static void printDiagnosticMessage(raw_ostream &OS,
> -                                     DiagnosticsEngine::Level Level,
> -                                     StringRef Message,
> -                                     unsigned CurrentColumn, unsigned Columns,
> -                                     bool ShowColors) {
> -    if (ShowColors) {
> -      // Print warnings, errors and fatal errors in bold, no color
> -      switch (Level) {
> -      case DiagnosticsEngine::Warning: OS.changeColor(savedColor, true); break;
> -      case DiagnosticsEngine::Error:   OS.changeColor(savedColor, true); break;
> -      case DiagnosticsEngine::Fatal:   OS.changeColor(savedColor, true); break;
> -      default: break; //don't bold notes
> -      }
> -    }
> -
> -    if (Columns)
> -      printWordWrapped(OS, Message, Columns, CurrentColumn);
> -    else
> -      OS << Message;
> -
> -    if (ShowColors)
> -      OS.resetColor();
> -    OS << '\n';
> -  }
> -
> -private:
> -  /// \brief Prints an include stack when appropriate for a particular
> -  /// diagnostic level and location.
> -  ///
> -  /// This routine handles all the logic of suppressing particular include
> -  /// stacks (such as those for notes) and duplicate include stacks when
> -  /// repeated warnings occur within the same file. It also handles the logic
> -  /// of customizing the formatting and display of the include stack.
> -  ///
> -  /// \param Level The diagnostic level of the message this stack pertains to.
> -  /// \param Loc   The include location of the current file (not the diagnostic
> -  ///              location).
> -  void emitIncludeStack(SourceLocation Loc, DiagnosticsEngine::Level Level) {
> -    // Skip redundant include stacks altogether.
> -    if (LastIncludeLoc == Loc)
> -      return;
> -    LastIncludeLoc = Loc;
> -
> -    if (!DiagOpts.ShowNoteIncludeStack && Level == DiagnosticsEngine::Note)
> -      return;
> -
> -    emitIncludeStackRecursively(Loc);
> -  }
> -
> -  /// \brief Helper to recursivly walk up the include stack and print each layer
> -  /// on the way back down.
> -  void emitIncludeStackRecursively(SourceLocation Loc) {
> -    if (Loc.isInvalid())
> -      return;
> -
> -    PresumedLoc PLoc = SM.getPresumedLoc(Loc);
> -    if (PLoc.isInvalid())
> -      return;
> -
> -    // Emit the other include frames first.
> -    emitIncludeStackRecursively(PLoc.getIncludeLoc());
> -
> -    if (DiagOpts.ShowLocation)
> -      OS << "In file included from " << PLoc.getFilename()
> -         << ':' << PLoc.getLine() << ":\n";
> -    else
> -      OS << "In included file:\n";
> -  }
> -
> -  /// \brief Print out the file/line/column information and include trace.
> -  ///
> -  /// This method handlen the emission of the diagnostic location information.
> -  /// This includes extracting as much location information as is present for
> -  /// the diagnostic and printing it, as well as any include stack or source
> -  /// ranges necessary.
> -  void EmitDiagnosticLoc(SourceLocation Loc, PresumedLoc PLoc,
> -                         DiagnosticsEngine::Level Level,
> -                         ArrayRef<CharSourceRange> Ranges) {
> -    if (PLoc.isInvalid()) {
> -      // At least print the file name if available:
> -      FileID FID = SM.getFileID(Loc);
> -      if (!FID.isInvalid()) {
> -        const FileEntry* FE = SM.getFileEntryForID(FID);
> -        if (FE && FE->getName()) {
> -          OS << FE->getName();
> -          if (FE->getDevice() == 0 && FE->getInode() == 0
> -              && FE->getFileMode() == 0) {
> -            // in PCH is a guess, but a good one:
> -            OS << " (in PCH)";
> -          }
> -          OS << ": ";
> -        }
> -      }
> -      return;
> -    }
> -    unsigned LineNo = PLoc.getLine();
> -
> -    if (!DiagOpts.ShowLocation)
> -      return;
> -
> -    if (DiagOpts.ShowColors)
> -      OS.changeColor(savedColor, true);
> -
> -    OS << PLoc.getFilename();
> -    switch (DiagOpts.Format) {
> -    case DiagnosticOptions::Clang: OS << ':'  << LineNo; break;
> -    case DiagnosticOptions::Msvc:  OS << '('  << LineNo; break;
> -    case DiagnosticOptions::Vi:    OS << " +" << LineNo; break;
> -    }
> -
> -    if (DiagOpts.ShowColumn)
> -      // Compute the column number.
> -      if (unsigned ColNo = PLoc.getColumn()) {
> -        if (DiagOpts.Format == DiagnosticOptions::Msvc) {
> -          OS << ',';
> -          ColNo--;
> -        } else
> -          OS << ':';
> -        OS << ColNo;
> -      }
> -    switch (DiagOpts.Format) {
> -    case DiagnosticOptions::Clang:
> -    case DiagnosticOptions::Vi:    OS << ':';    break;
> -    case DiagnosticOptions::Msvc:  OS << ") : "; break;
> -    }
> -
> -    if (DiagOpts.ShowSourceRanges && !Ranges.empty()) {
> -      FileID CaretFileID =
> -        SM.getFileID(SM.getExpansionLoc(Loc));
> -      bool PrintedRange = false;
> -
> -      for (ArrayRef<CharSourceRange>::const_iterator RI = Ranges.begin(),
> -           RE = Ranges.end();
> -           RI != RE; ++RI) {
> -        // Ignore invalid ranges.
> -        if (!RI->isValid()) continue;
> -
> -        SourceLocation B = SM.getExpansionLoc(RI->getBegin());
> -        SourceLocation E = SM.getExpansionLoc(RI->getEnd());
> -
> -        // If the End location and the start location are the same and are a
> -        // macro location, then the range was something that came from a
> -        // macro expansion or _Pragma.  If this is an object-like macro, the
> -        // best we can do is to highlight the range.  If this is a
> -        // function-like macro, we'd also like to highlight the arguments.
> -        if (B == E && RI->getEnd().isMacroID())
> -          E = SM.getExpansionRange(RI->getEnd()).second;
> -
> -        std::pair<FileID, unsigned> BInfo = SM.getDecomposedLoc(B);
> -        std::pair<FileID, unsigned> EInfo = SM.getDecomposedLoc(E);
> -
> -        // If the start or end of the range is in another file, just discard
> -        // it.
> -        if (BInfo.first != CaretFileID || EInfo.first != CaretFileID)
> -          continue;
> -
> -        // Add in the length of the token, so that we cover multi-char
> -        // tokens.
> -        unsigned TokSize = 0;
> -        if (RI->isTokenRange())
> -          TokSize = Lexer::MeasureTokenLength(E, SM, LangOpts);
> -
> -        OS << '{' << SM.getLineNumber(BInfo.first, BInfo.second) << ':'
> -          << SM.getColumnNumber(BInfo.first, BInfo.second) << '-'
> -          << SM.getLineNumber(EInfo.first, EInfo.second) << ':'
> -          << (SM.getColumnNumber(EInfo.first, EInfo.second)+TokSize)
> -          << '}';
> -        PrintedRange = true;
> -      }
> -
> -      if (PrintedRange)
> -        OS << ':';
> -    }
> -    OS << ' ';
> -  }
> -
> -  /// \brief Highlight a SourceRange (with ~'s) for any characters on LineNo.
> -  void HighlightRange(const CharSourceRange &R,
> -                      unsigned LineNo, FileID FID,
> -                      const std::string &SourceLine,
> -                      std::string &CaretLine) {
> -    assert(CaretLine.size() == SourceLine.size() &&
> -           "Expect a correspondence between source and caret line!");
> -    if (!R.isValid()) return;
> -
> -    SourceLocation Begin = SM.getExpansionLoc(R.getBegin());
> -    SourceLocation End = SM.getExpansionLoc(R.getEnd());
> -
> -    // If the End location and the start location are the same and are a macro
> -    // location, then the range was something that came from a macro expansion
> -    // or _Pragma.  If this is an object-like macro, the best we can do is to
> -    // highlight the range.  If this is a function-like macro, we'd also like to
> -    // highlight the arguments.
> -    if (Begin == End && R.getEnd().isMacroID())
> -      End = SM.getExpansionRange(R.getEnd()).second;
> -
> -    unsigned StartLineNo = SM.getExpansionLineNumber(Begin);
> -    if (StartLineNo > LineNo || SM.getFileID(Begin) != FID)
> -      return;  // No intersection.
> -
> -    unsigned EndLineNo = SM.getExpansionLineNumber(End);
> -    if (EndLineNo < LineNo || SM.getFileID(End) != FID)
> -      return;  // No intersection.
> -
> -    // Compute the column number of the start.
> -    unsigned StartColNo = 0;
> -    if (StartLineNo == LineNo) {
> -      StartColNo = SM.getExpansionColumnNumber(Begin);
> -      if (StartColNo) --StartColNo;  // Zero base the col #.
> -    }
> -
> -    // Compute the column number of the end.
> -    unsigned EndColNo = CaretLine.size();
> -    if (EndLineNo == LineNo) {
> -      EndColNo = SM.getExpansionColumnNumber(End);
> -      if (EndColNo) {
> -        --EndColNo;  // Zero base the col #.
> -
> -        // Add in the length of the token, so that we cover multi-char tokens if
> -        // this is a token range.
> -        if (R.isTokenRange())
> -          EndColNo += Lexer::MeasureTokenLength(End, SM, LangOpts);
> -      } else {
> -        EndColNo = CaretLine.size();
> -      }
> -    }
> -
> -    assert(StartColNo <= EndColNo && "Invalid range!");
> -
> -    // Check that a token range does not highlight only whitespace.
> -    if (R.isTokenRange()) {
> -      // Pick the first non-whitespace column.
> -      while (StartColNo < SourceLine.size() &&
> -             (SourceLine[StartColNo] == ' ' || SourceLine[StartColNo] == '\t'))
> -        ++StartColNo;
> -
> -      // Pick the last non-whitespace column.
> -      if (EndColNo > SourceLine.size())
> -        EndColNo = SourceLine.size();
> -      while (EndColNo-1 &&
> -             (SourceLine[EndColNo-1] == ' ' || SourceLine[EndColNo-1] == '\t'))
> -        --EndColNo;
> -
> -      // If the start/end passed each other, then we are trying to highlight a
> -      // range that just exists in whitespace, which must be some sort of other
> -      // bug.
> -      assert(StartColNo <= EndColNo && "Trying to highlight whitespace??");
> -    }
> -
> -    // Fill the range with ~'s.
> -    for (unsigned i = StartColNo; i < EndColNo; ++i)
> -      CaretLine[i] = '~';
> -  }
> -
> -  std::string BuildFixItInsertionLine(unsigned LineNo,
> -                                      const char *LineStart,
> -                                      const char *LineEnd,
> -                                      ArrayRef<FixItHint> Hints) {
> -    std::string FixItInsertionLine;
> -    if (Hints.empty() || !DiagOpts.ShowFixits)
> -      return FixItInsertionLine;
> -
> -    for (ArrayRef<FixItHint>::iterator I = Hints.begin(), E = Hints.end();
> -         I != E; ++I) {
> -      if (!I->CodeToInsert.empty()) {
> -        // We have an insertion hint. Determine whether the inserted
> -        // code is on the same line as the caret.
> -        std::pair<FileID, unsigned> HintLocInfo
> -          = SM.getDecomposedExpansionLoc(I->RemoveRange.getBegin());
> -        if (LineNo == SM.getLineNumber(HintLocInfo.first, HintLocInfo.second)) {
> -          // Insert the new code into the line just below the code
> -          // that the user wrote.
> -          unsigned HintColNo
> -            = SM.getColumnNumber(HintLocInfo.first, HintLocInfo.second);
> -          unsigned LastColumnModified
> -            = HintColNo - 1 + I->CodeToInsert.size();
> -          if (LastColumnModified > FixItInsertionLine.size())
> -            FixItInsertionLine.resize(LastColumnModified, ' ');
> -          std::copy(I->CodeToInsert.begin(), I->CodeToInsert.end(),
> -                    FixItInsertionLine.begin() + HintColNo - 1);
> -        } else {
> -          FixItInsertionLine.clear();
> -          break;
> -        }
> -      }
> -    }
> -
> -    if (FixItInsertionLine.empty())
> -      return FixItInsertionLine;
> -
> -    // Now that we have the entire fixit line, expand the tabs in it.
> -    // Since we don't want to insert spaces in the middle of a word,
> -    // find each word and the column it should line up with and insert
> -    // spaces until they match.
> -    unsigned FixItPos = 0;
> -    unsigned LinePos = 0;
> -    unsigned TabExpandedCol = 0;
> -    unsigned LineLength = LineEnd - LineStart;
> -
> -    while (FixItPos < FixItInsertionLine.size() && LinePos < LineLength) {
> -      // Find the next word in the FixIt line.
> -      while (FixItPos < FixItInsertionLine.size() &&
> -             FixItInsertionLine[FixItPos] == ' ')
> -        ++FixItPos;
> -      unsigned CharDistance = FixItPos - TabExpandedCol;
> -
> -      // Walk forward in the source line, keeping track of
> -      // the tab-expanded column.
> -      for (unsigned I = 0; I < CharDistance; ++I, ++LinePos)
> -        if (LinePos >= LineLength || LineStart[LinePos] != '\t')
> -          ++TabExpandedCol;
> -        else
> -          TabExpandedCol =
> -            (TabExpandedCol/DiagOpts.TabStop + 1) * DiagOpts.TabStop;
> -
> -      // Adjust the fixit line to match this column.
> -      FixItInsertionLine.insert(FixItPos, TabExpandedCol-FixItPos, ' ');
> -      FixItPos = TabExpandedCol;
> -
> -      // Walk to the end of the word.
> -      while (FixItPos < FixItInsertionLine.size() &&
> -             FixItInsertionLine[FixItPos] != ' ')
> -        ++FixItPos;
> -    }
> -
> -    return FixItInsertionLine;
> -  }
> -
> -  void ExpandTabs(std::string &SourceLine, std::string &CaretLine) {
> -    // Scan the source line, looking for tabs.  If we find any, manually expand
> -    // them to spaces and update the CaretLine to match.
> -    for (unsigned i = 0; i != SourceLine.size(); ++i) {
> -      if (SourceLine[i] != '\t') continue;
> -
> -      // Replace this tab with at least one space.
> -      SourceLine[i] = ' ';
> -
> -      // Compute the number of spaces we need to insert.
> -      unsigned TabStop = DiagOpts.TabStop;
> -      assert(0 < TabStop && TabStop <= DiagnosticOptions::MaxTabStop &&
> -             "Invalid -ftabstop value");
> -      unsigned NumSpaces = ((i+TabStop)/TabStop * TabStop) - (i+1);
> -      assert(NumSpaces < TabStop && "Invalid computation of space amt");
> -
> -      // Insert spaces into the SourceLine.
> -      SourceLine.insert(i+1, NumSpaces, ' ');
> -
> -      // Insert spaces or ~'s into CaretLine.
> -      CaretLine.insert(i+1, NumSpaces, CaretLine[i] == '~' ? '~' : ' ');
> -    }
> -  }
> -
> -  void EmitParseableFixits(ArrayRef<FixItHint> Hints) {
> -    if (!DiagOpts.ShowParseableFixits)
> -      return;
> -
> -    // We follow FixItRewriter's example in not (yet) handling
> -    // fix-its in macros.
> -    for (ArrayRef<FixItHint>::iterator I = Hints.begin(), E = Hints.end();
> -         I != E; ++I) {
> -      if (I->RemoveRange.isInvalid() ||
> -          I->RemoveRange.getBegin().isMacroID() ||
> -          I->RemoveRange.getEnd().isMacroID())
> -        return;
> -    }
> -
> -    for (ArrayRef<FixItHint>::iterator I = Hints.begin(), E = Hints.end();
> -         I != E; ++I) {
> -      SourceLocation BLoc = I->RemoveRange.getBegin();
> -      SourceLocation ELoc = I->RemoveRange.getEnd();
> -
> -      std::pair<FileID, unsigned> BInfo = SM.getDecomposedLoc(BLoc);
> -      std::pair<FileID, unsigned> EInfo = SM.getDecomposedLoc(ELoc);
> -
> -      // Adjust for token ranges.
> -      if (I->RemoveRange.isTokenRange())
> -        EInfo.second += Lexer::MeasureTokenLength(ELoc, SM, LangOpts);
> -
> -      // We specifically do not do word-wrapping or tab-expansion here,
> -      // because this is supposed to be easy to parse.
> -      PresumedLoc PLoc = SM.getPresumedLoc(BLoc);
> -      if (PLoc.isInvalid())
> -        break;
> -
> -      OS << "fix-it:\"";
> -      OS.write_escaped(PLoc.getFilename());
> -      OS << "\":{" << SM.getLineNumber(BInfo.first, BInfo.second)
> -        << ':' << SM.getColumnNumber(BInfo.first, BInfo.second)
> -        << '-' << SM.getLineNumber(EInfo.first, EInfo.second)
> -        << ':' << SM.getColumnNumber(EInfo.first, EInfo.second)
> -        << "}:\"";
> -      OS.write_escaped(I->CodeToInsert);
> -      OS << "\"\n";
> -    }
> -  }
> -};
> -
> -} // end namespace
> -
>  /// \brief Print the diagnostic name to a raw_ostream.
>  ///
>  /// This prints the diagnostic name to a raw_ostream if it has one. It formats
>
>
> _______________________________________________
> cfe-commits mailing list
> cfe-commits at cs.uiuc.edu
> http://lists.cs.uiuc.edu/mailman/listinfo/cfe-commits
>




More information about the cfe-commits mailing list