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(nullptr);
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 ? "," : " [")
85         << (Level == DiagnosticsEngine::Remark ? "-R" : "-W") << Opt;
86      StringRef OptValue = Info.getDiags()->getFlagValue();
87      if (!OptValue.empty())
88        OS << "=" << OptValue;
89      Started = true;
90    }
91  }
92
93  // If the user wants to see category information, include it too.
94  if (DiagOpts.ShowCategories) {
95    unsigned DiagCategory =
96      DiagnosticIDs::getCategoryNumberForDiag(Info.getID());
97    if (DiagCategory) {
98      OS << (Started ? "," : " [");
99      Started = true;
100      if (DiagOpts.ShowCategories == 1)
101        OS << DiagCategory;
102      else {
103        assert(DiagOpts.ShowCategories == 2 && "Invalid ShowCategories value");
104        OS << DiagnosticIDs::getCategoryNameFromID(DiagCategory);
105      }
106    }
107  }
108  if (Started)
109    OS << ']';
110}
111
112void TextDiagnosticPrinter::HandleDiagnostic(DiagnosticsEngine::Level Level,
113                                             const Diagnostic &Info) {
114  // Default implementation (Warnings/errors count).
115  DiagnosticConsumer::HandleDiagnostic(Level, Info);
116
117  // Render the diagnostic message into a temporary buffer eagerly. We'll use
118  // this later as we print out the diagnostic to the terminal.
119  SmallString<100> OutStr;
120  Info.FormatDiagnostic(OutStr);
121
122  llvm::raw_svector_ostream DiagMessageStream(OutStr);
123  printDiagnosticOptions(DiagMessageStream, Level, Info, *DiagOpts);
124
125  // Keeps track of the starting position of the location
126  // information (e.g., "foo.c:10:4:") that precedes the error
127  // message. We use this information to determine how long the
128  // file+line+column number prefix is.
129  uint64_t StartOfLocationInfo = OS.tell();
130
131  if (!Prefix.empty())
132    OS << Prefix << ": ";
133
134  // Use a dedicated, simpler path for diagnostics without a valid location.
135  // This is important as if the location is missing, we may be emitting
136  // diagnostics in a context that lacks language options, a source manager, or
137  // other infrastructure necessary when emitting more rich diagnostics.
138  if (!Info.getLocation().isValid()) {
139    TextDiagnostic::printDiagnosticLevel(OS, Level, DiagOpts->ShowColors,
140                                         DiagOpts->CLFallbackMode);
141    TextDiagnostic::printDiagnosticMessage(OS, Level, DiagMessageStream.str(),
142                                           OS.tell() - StartOfLocationInfo,
143                                           DiagOpts->MessageLength,
144                                           DiagOpts->ShowColors);
145    OS.flush();
146    return;
147  }
148
149  // Assert that the rest of our infrastructure is setup properly.
150  assert(DiagOpts && "Unexpected diagnostic without options set");
151  assert(Info.hasSourceManager() &&
152         "Unexpected diagnostic with no source manager");
153  assert(TextDiag && "Unexpected diagnostic outside source file processing");
154
155  TextDiag->emitDiagnostic(Info.getLocation(), Level, DiagMessageStream.str(),
156                           Info.getRanges(),
157                           Info.getFixItHints(),
158                           &Info.getSourceManager());
159
160  OS.flush();
161}
162