TextDiagnosticPrinter.cpp revision cc2b653c319599f502425d2c3de29865d47bb9e4
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/DiagnosticOptions.h"
16#include "clang/Basic/FileManager.h"
17#include "clang/Basic/SourceManager.h"
18#include "clang/Frontend/TextDiagnostic.h"
19#include "clang/Lex/Lexer.h"
20#include "llvm/ADT/SmallString.h"
21#include "llvm/Support/ErrorHandling.h"
22#include "llvm/Support/MemoryBuffer.h"
23#include "llvm/Support/raw_ostream.h"
24#include <algorithm>
25using namespace clang;
26
27TextDiagnosticPrinter::TextDiagnosticPrinter(raw_ostream &os,
28                                             DiagnosticOptions *diags,
29                                             bool _OwnsOutputStream)
30  : OS(os), DiagOpts(diags),
31    OwnsOutputStream(_OwnsOutputStream) {
32}
33
34TextDiagnosticPrinter::~TextDiagnosticPrinter() {
35  if (OwnsOutputStream)
36    delete &OS;
37}
38
39void TextDiagnosticPrinter::BeginSourceFile(const LangOptions &LO,
40                                            const Preprocessor *PP) {
41  // Build the TextDiagnostic utility.
42  TextDiag.reset(new TextDiagnostic(OS, LO, &*DiagOpts));
43}
44
45void TextDiagnosticPrinter::EndSourceFile() {
46  TextDiag.reset(0);
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    StringRef Opt = DiagnosticIDs::getWarningOptionForDiag(Info.getID());
83    if (!Opt.empty()) {
84      OS << (Started ? "," : " [") << "-W" << Opt;
85      Started = true;
86    }
87  }
88
89  // If the user wants to see category information, include it too.
90  if (DiagOpts.ShowCategories) {
91    unsigned DiagCategory =
92      DiagnosticIDs::getCategoryNumberForDiag(Info.getID());
93    if (DiagCategory) {
94      OS << (Started ? "," : " [");
95      Started = true;
96      if (DiagOpts.ShowCategories == 1)
97        OS << DiagCategory;
98      else {
99        assert(DiagOpts.ShowCategories == 2 && "Invalid ShowCategories value");
100        OS << DiagnosticIDs::getCategoryNameFromID(DiagCategory);
101      }
102    }
103  }
104  if (Started)
105    OS << ']';
106}
107
108void TextDiagnosticPrinter::HandleDiagnostic(DiagnosticsEngine::Level Level,
109                                             const Diagnostic &Info) {
110  // Default implementation (Warnings/errors count).
111  DiagnosticConsumer::HandleDiagnostic(Level, Info);
112
113  // Render the diagnostic message into a temporary buffer eagerly. We'll use
114  // this later as we print out the diagnostic to the terminal.
115  SmallString<100> OutStr;
116  Info.FormatDiagnostic(OutStr);
117
118  llvm::raw_svector_ostream DiagMessageStream(OutStr);
119  printDiagnosticOptions(DiagMessageStream, Level, Info, *DiagOpts);
120
121  // Keeps track of the starting position of the location
122  // information (e.g., "foo.c:10:4:") that precedes the error
123  // message. We use this information to determine how long the
124  // file+line+column number prefix is.
125  uint64_t StartOfLocationInfo = OS.tell();
126
127  if (!Prefix.empty())
128    OS << Prefix << ": ";
129
130  // Use a dedicated, simpler path for diagnostics without a valid location.
131  // This is important as if the location is missing, we may be emitting
132  // diagnostics in a context that lacks language options, a source manager, or
133  // other infrastructure necessary when emitting more rich diagnostics.
134  if (!Info.getLocation().isValid()) {
135    TextDiagnostic::printDiagnosticLevel(OS, Level, DiagOpts->ShowColors);
136    TextDiagnostic::printDiagnosticMessage(OS, Level, DiagMessageStream.str(),
137                                           OS.tell() - StartOfLocationInfo,
138                                           DiagOpts->MessageLength,
139                                           DiagOpts->ShowColors);
140    OS.flush();
141    return;
142  }
143
144  // Assert that the rest of our infrastructure is setup properly.
145  assert(DiagOpts && "Unexpected diagnostic without options set");
146  assert(Info.hasSourceManager() &&
147         "Unexpected diagnostic with no source manager");
148  assert(TextDiag && "Unexpected diagnostic outside source file processing");
149
150  TextDiag->emitDiagnostic(Info.getLocation(), Level, DiagMessageStream.str(),
151                           Info.getRanges(),
152                           llvm::makeArrayRef(Info.getFixItHints(),
153                                              Info.getNumFixItHints()),
154                           &Info.getSourceManager());
155
156  OS.flush();
157}
158