TextDiagnosticPrinter.cpp revision 7531f571808201d44002fa38b67ee0a81e5ae936
1//===--- TextDiagnosticPrinter.cpp - Diagnostic Printer -------------------===//
2//
3//                     The LLVM Compiler Infrastructure
4//
5// This file is distributed under the University of Illinois Open Source
6// License. See LICENSE.TXT for details.
7//
8//===----------------------------------------------------------------------===//
9//
10// This diagnostic client prints out their diagnostic messages.
11//
12//===----------------------------------------------------------------------===//
13
14#include "clang/Frontend/TextDiagnosticPrinter.h"
15#include "clang/Basic/FileManager.h"
16#include "clang/Basic/SourceManager.h"
17#include "clang/Frontend/DiagnosticOptions.h"
18#include "clang/Frontend/TextDiagnostic.h"
19#include "clang/Lex/Lexer.h"
20#include "llvm/Support/MemoryBuffer.h"
21#include "llvm/Support/raw_ostream.h"
22#include "llvm/Support/ErrorHandling.h"
23#include "llvm/ADT/SmallString.h"
24#include <algorithm>
25using namespace clang;
26
27TextDiagnosticPrinter::TextDiagnosticPrinter(raw_ostream &os,
28                                             const DiagnosticOptions &diags,
29                                             bool _OwnsOutputStream)
30  : OS(os), LangOpts(0), DiagOpts(&diags), LastLevel(),
31    OwnsOutputStream(_OwnsOutputStream) {
32}
33
34TextDiagnosticPrinter::~TextDiagnosticPrinter() {
35  if (OwnsOutputStream)
36    delete &OS;
37}
38
39/// \brief Print the diagnostic name to a raw_ostream.
40///
41/// This prints the diagnostic name to a raw_ostream if it has one. It formats
42/// the name according to the expected diagnostic message formatting:
43///   " [diagnostic_name_here]"
44static void printDiagnosticName(raw_ostream &OS, const Diagnostic &Info) {
45  if (!DiagnosticIDs::isBuiltinNote(Info.getID()))
46    OS << " [" << DiagnosticIDs::getName(Info.getID()) << "]";
47}
48
49/// \brief Print any diagnostic option information to a raw_ostream.
50///
51/// This implements all of the logic for adding diagnostic options to a message
52/// (via OS). Each relevant option is comma separated and all are enclosed in
53/// the standard bracketing: " [...]".
54static void printDiagnosticOptions(raw_ostream &OS,
55                                   DiagnosticsEngine::Level Level,
56                                   const Diagnostic &Info,
57                                   const DiagnosticOptions &DiagOpts) {
58  bool Started = false;
59  if (DiagOpts.ShowOptionNames) {
60    // Handle special cases for non-warnings early.
61    if (Info.getID() == diag::fatal_too_many_errors) {
62      OS << " [-ferror-limit=]";
63      return;
64    }
65
66    // The code below is somewhat fragile because we are essentially trying to
67    // report to the user what happened by inferring what the diagnostic engine
68    // did. Eventually it might make more sense to have the diagnostic engine
69    // include some "why" information in the diagnostic.
70
71    // If this is a warning which has been mapped to an error by the user (as
72    // inferred by checking whether the default mapping is to an error) then
73    // flag it as such. Note that diagnostics could also have been mapped by a
74    // pragma, but we don't currently have a way to distinguish this.
75    if (Level == DiagnosticsEngine::Error &&
76        DiagnosticIDs::isBuiltinWarningOrExtension(Info.getID()) &&
77        !DiagnosticIDs::isDefaultMappingAsError(Info.getID())) {
78      OS << " [-Werror";
79      Started = true;
80    }
81
82    // If the diagnostic is an extension diagnostic and not enabled by default
83    // then it must have been turned on with -pedantic.
84    bool EnabledByDefault;
85    if (DiagnosticIDs::isBuiltinExtensionDiag(Info.getID(),
86                                              EnabledByDefault) &&
87        !EnabledByDefault) {
88      OS << (Started ? "," : " [") << "-pedantic";
89      Started = true;
90    }
91
92    StringRef Opt = DiagnosticIDs::getWarningOptionForDiag(Info.getID());
93    if (!Opt.empty()) {
94      OS << (Started ? "," : " [") << "-W" << Opt;
95      Started = true;
96    }
97  }
98
99  // If the user wants to see category information, include it too.
100  if (DiagOpts.ShowCategories) {
101    unsigned DiagCategory =
102      DiagnosticIDs::getCategoryNumberForDiag(Info.getID());
103    if (DiagCategory) {
104      OS << (Started ? "," : " [");
105      Started = true;
106      if (DiagOpts.ShowCategories == 1)
107        OS << DiagCategory;
108      else {
109        assert(DiagOpts.ShowCategories == 2 && "Invalid ShowCategories value");
110        OS << DiagnosticIDs::getCategoryNameFromID(DiagCategory);
111      }
112    }
113  }
114  if (Started)
115    OS << ']';
116}
117
118void TextDiagnosticPrinter::HandleDiagnostic(DiagnosticsEngine::Level Level,
119                                             const Diagnostic &Info) {
120  // Default implementation (Warnings/errors count).
121  DiagnosticConsumer::HandleDiagnostic(Level, Info);
122
123  // Render the diagnostic message into a temporary buffer eagerly. We'll use
124  // this later as we print out the diagnostic to the terminal.
125  llvm::SmallString<100> OutStr;
126  Info.FormatDiagnostic(OutStr);
127
128  llvm::raw_svector_ostream DiagMessageStream(OutStr);
129  if (DiagOpts->ShowNames)
130    printDiagnosticName(DiagMessageStream, Info);
131  printDiagnosticOptions(DiagMessageStream, Level, Info, *DiagOpts);
132
133  // Keeps track of the the starting position of the location
134  // information (e.g., "foo.c:10:4:") that precedes the error
135  // message. We use this information to determine how long the
136  // file+line+column number prefix is.
137  uint64_t StartOfLocationInfo = OS.tell();
138
139  if (!Prefix.empty())
140    OS << Prefix << ": ";
141
142  // Use a dedicated, simpler path for diagnostics without a valid location.
143  // This is important as if the location is missing, we may be emitting
144  // diagnostics in a context that lacks language options, a source manager, or
145  // other infrastructure necessary when emitting more rich diagnostics.
146  if (!Info.getLocation().isValid()) {
147    TextDiagnostic::printDiagnosticLevel(OS, Level, DiagOpts->ShowColors);
148    TextDiagnostic::printDiagnosticMessage(OS, Level, DiagMessageStream.str(),
149                                           OS.tell() - StartOfLocationInfo,
150                                           DiagOpts->MessageLength,
151                                           DiagOpts->ShowColors);
152    OS.flush();
153    return;
154  }
155
156  // Assert that the rest of our infrastructure is setup properly.
157  assert(LangOpts && "Unexpected diagnostic outside source file processing");
158  assert(DiagOpts && "Unexpected diagnostic without options set");
159  assert(Info.hasSourceManager() &&
160         "Unexpected diagnostic with no source manager");
161  const SourceManager &SM = Info.getSourceManager();
162  TextDiagnostic TextDiag(OS, SM, *LangOpts, *DiagOpts,
163                          LastLoc, LastIncludeLoc, LastLevel);
164
165  TextDiag.emitDiagnostic(Info.getLocation(), Level, DiagMessageStream.str(),
166                          Info.getRanges(),
167                          llvm::makeArrayRef(Info.getFixItHints(),
168                                             Info.getNumFixItHints()));
169
170  // Cache the LastLoc from the TextDiagnostic printing.
171  // FIXME: Rather than this, we should persist a TextDiagnostic object across
172  // diagnostics until the SourceManager changes. That will allow the
173  // TextDiagnostic object to form a 'session' of output where we can
174  // reasonably collapse redundant information.
175  LastLoc = FullSourceLoc(TextDiag.getLastLoc(), SM);
176  LastIncludeLoc = FullSourceLoc(TextDiag.getLastIncludeLoc(), SM);
177  LastLevel = TextDiag.getLastLevel();
178
179  OS.flush();
180}
181
182DiagnosticConsumer *
183TextDiagnosticPrinter::clone(DiagnosticsEngine &Diags) const {
184  return new TextDiagnosticPrinter(OS, *DiagOpts, /*OwnsOutputStream=*/false);
185}
186