Refactor 'TextDiagnostic' to have a parent class 'DiagnosticRenderer' which handles
the policy of how diagnostics are lowered/rendered, while TextDiagnostic handles
the actual pretty-printing.

This is a first part of reworking SerializedDiagnosticPrinter to use the same
inclusion-stack/macro-expansion logic as TextDiagnostic.

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@146819 91177308-0d34-0410-b5e6-96231b3b80d8
diff --git a/lib/Frontend/DiagnosticRenderer.cpp b/lib/Frontend/DiagnosticRenderer.cpp
new file mode 100644
index 0000000..293ffb7
--- /dev/null
+++ b/lib/Frontend/DiagnosticRenderer.cpp
@@ -0,0 +1,297 @@
+//===--- DiagnosticRenderer.cpp - 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/DiagnosticRenderer.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;
+
+/// 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);
+}
+
+/// \brief Retrieve the name of the immediate macro expansion.
+///
+/// This routine starts from a source location, and finds the name of the macro
+/// responsible for its immediate expansion. It looks through any intervening
+/// macro argument expansions to compute this. It returns a StringRef which
+/// refers to the SourceManager-owned buffer of the source where that macro
+/// name is spelled. Thus, the result shouldn't out-live that SourceManager.
+///
+static StringRef getImmediateMacroName(SourceLocation Loc,
+                                       const SourceManager &SM,
+                                       const LangOptions &LangOpts) {
+  assert(Loc.isMacroID() && "Only reasonble to call this on macros");
+  // Walk past macro argument expanions.
+  while (SM.isMacroArgExpansion(Loc))
+    Loc = SM.getImmediateExpansionRange(Loc).first;
+  
+  // Find the spelling location of the start of the non-argument expansion
+  // range. This is where the macro name was spelled in order to begin
+  // expanding this macro.
+  Loc = SM.getSpellingLoc(SM.getImmediateExpansionRange(Loc).first);
+  
+  // Dig out the buffer where the macro name was spelled and the extents of the
+  // name so that we can render it into the expansion note.
+  std::pair<FileID, unsigned> ExpansionInfo = SM.getDecomposedLoc(Loc);
+  unsigned MacroTokenLength = Lexer::MeasureTokenLength(Loc, SM, LangOpts);
+  StringRef ExpansionBuffer = SM.getBufferData(ExpansionInfo.first);
+  return ExpansionBuffer.substr(ExpansionInfo.second, MacroTokenLength);
+}
+
+/// 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);
+}
+
+DiagnosticRenderer::DiagnosticRenderer(const SourceManager &SM,
+                                       const LangOptions &LangOpts,
+                                       const DiagnosticOptions &DiagOpts)
+: SM(SM), LangOpts(LangOpts), DiagOpts(DiagOpts), LastLevel() {}
+
+DiagnosticRenderer::~DiagnosticRenderer() {}
+
+
+void DiagnosticRenderer::emitDiagnostic(SourceLocation Loc,
+                                        DiagnosticsEngine::Level Level,
+                                        StringRef Message,
+                                        ArrayRef<CharSourceRange> Ranges,
+                                        ArrayRef<FixItHint> FixItHints,
+                                        const Diagnostic *Info) {
+  
+  beginDiagnostic(Info, Level);
+  
+  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);
+  
+  // Next, emit the actual diagnostic message.
+  emitDiagnosticMessage(Loc, PLoc, Level, Message, Ranges, Info);
+  
+  // Only recurse if we have a valid location.
+  if (Loc.isValid()) {
+    // 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;
+    emitMacroExpansionsAndCarets(Loc, Level, MutableRanges, FixItHints,
+                                 MacroDepth);
+  }
+  
+  LastLoc = Loc;
+  LastLevel = Level;
+  
+  endDiagnostic(Info, Level);
+}
+
+/// \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 DiagnosticRenderer::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 DiagnosticRenderer::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());
+  
+  // Emit the inclusion text/note.
+  emitIncludeLocation(Loc, PLoc);
+}
+
+/// \brief Recursively emit notes for each macro expansion and caret
+/// diagnostics where appropriate.
+///
+/// Walks up the macro expansion stack printing expansion notes, the code
+/// snippet, caret, underlines and FixItHint display as appropriate at each
+/// level.
+///
+/// \param Loc The location for this caret.
+/// \param Level The diagnostic level currently being emitted.
+/// \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 DiagnosticRenderer::emitMacroExpansionsAndCarets(
+       SourceLocation Loc,
+       DiagnosticsEngine::Level Level,
+       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;
+    emitCodeContext(Loc, Level, 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?
+  emitMacroExpansionsAndCarets(OneLevelUp, Level, Ranges, Hints, MacroDepth,
+                               OnMacroInst + 1);
+  
+  // Save the original location so we can find the spelling of the macro call.
+  SourceLocation MacroLoc = Loc;
+  
+  // 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) {
+    // Tell the user that we've skipped contexts.
+    if (OnMacroInst == MacroSkipStart) {
+      llvm::SmallString<200> MessageStorage;
+      llvm::raw_svector_ostream Message(MessageStorage);
+      Message << "(skipping " << (MacroSkipEnd - MacroSkipStart)
+              << " expansions in backtrace; use -fmacro-backtrace-limit=0 to "
+                 "see all)";
+      emitBasicNote(Message.str());      
+    }
+    return;
+  }
+  
+  llvm::SmallString<100> MessageStorage;
+  llvm::raw_svector_ostream Message(MessageStorage);
+  Message << "expanded from macro '"
+          << getImmediateMacroName(MacroLoc, SM, LangOpts) << "'";
+  emitDiagnostic(SM.getSpellingLoc(Loc), DiagnosticsEngine::Note,
+                 Message.str(),
+                 Ranges, ArrayRef<FixItHint>());
+}
+