TextDiagnosticPrinter.cpp revision f7ccbad5d9949e7ddd1cbef43d482553b811e026
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), SM(0),
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  LangOpts = &LO;
42}
43
44void TextDiagnosticPrinter::EndSourceFile() {
45  LangOpts = 0;
46  TextDiag.reset(0);
47}
48
49/// \brief Print the diagnostic name to a raw_ostream.
50///
51/// This prints the diagnostic name to a raw_ostream if it has one. It formats
52/// the name according to the expected diagnostic message formatting:
53///   " [diagnostic_name_here]"
54static void printDiagnosticName(raw_ostream &OS, const Diagnostic &Info) {
55  if (!DiagnosticIDs::isBuiltinNote(Info.getID()))
56    OS << " [" << DiagnosticIDs::getName(Info.getID()) << "]";
57}
58
59/// \brief Print any diagnostic option information to a raw_ostream.
60///
61/// This implements all of the logic for adding diagnostic options to a message
62/// (via OS). Each relevant option is comma separated and all are enclosed in
63/// the standard bracketing: " [...]".
64static void printDiagnosticOptions(raw_ostream &OS,
65                                   DiagnosticsEngine::Level Level,
66                                   const Diagnostic &Info,
67                                   const DiagnosticOptions &DiagOpts) {
68  bool Started = false;
69  if (DiagOpts.ShowOptionNames) {
70    // Handle special cases for non-warnings early.
71    if (Info.getID() == diag::fatal_too_many_errors) {
72      OS << " [-ferror-limit=]";
73      return;
74    }
75
76    // The code below is somewhat fragile because we are essentially trying to
77    // report to the user what happened by inferring what the diagnostic engine
78    // did. Eventually it might make more sense to have the diagnostic engine
79    // include some "why" information in the diagnostic.
80
81    // If this is a warning which has been mapped to an error by the user (as
82    // inferred by checking whether the default mapping is to an error) then
83    // flag it as such. Note that diagnostics could also have been mapped by a
84    // pragma, but we don't currently have a way to distinguish this.
85    if (Level == DiagnosticsEngine::Error &&
86        DiagnosticIDs::isBuiltinWarningOrExtension(Info.getID()) &&
87        !DiagnosticIDs::isDefaultMappingAsError(Info.getID())) {
88      OS << " [-Werror";
89      Started = true;
90    }
91
92    // If the diagnostic is an extension diagnostic and not enabled by default
93    // then it must have been turned on with -pedantic.
94    bool EnabledByDefault;
95    if (DiagnosticIDs::isBuiltinExtensionDiag(Info.getID(),
96                                              EnabledByDefault) &&
97        !EnabledByDefault) {
98      OS << (Started ? "," : " [") << "-pedantic";
99      Started = true;
100    }
101
102    StringRef Opt = DiagnosticIDs::getWarningOptionForDiag(Info.getID());
103    if (!Opt.empty()) {
104      OS << (Started ? "," : " [") << "-W" << Opt;
105      Started = true;
106    }
107  }
108
109  // If the user wants to see category information, include it too.
110  if (DiagOpts.ShowCategories) {
111    unsigned DiagCategory =
112      DiagnosticIDs::getCategoryNumberForDiag(Info.getID());
113    if (DiagCategory) {
114      OS << (Started ? "," : " [");
115      Started = true;
116      if (DiagOpts.ShowCategories == 1)
117        OS << DiagCategory;
118      else {
119        assert(DiagOpts.ShowCategories == 2 && "Invalid ShowCategories value");
120        OS << DiagnosticIDs::getCategoryNameFromID(DiagCategory);
121      }
122    }
123  }
124  if (Started)
125    OS << ']';
126}
127
128void TextDiagnosticPrinter::HandleDiagnostic(DiagnosticsEngine::Level Level,
129                                             const Diagnostic &Info) {
130  // Default implementation (Warnings/errors count).
131  DiagnosticConsumer::HandleDiagnostic(Level, Info);
132
133  // Render the diagnostic message into a temporary buffer eagerly. We'll use
134  // this later as we print out the diagnostic to the terminal.
135  SmallString<100> OutStr;
136  Info.FormatDiagnostic(OutStr);
137
138  llvm::raw_svector_ostream DiagMessageStream(OutStr);
139  if (DiagOpts->ShowNames)
140    printDiagnosticName(DiagMessageStream, Info);
141  printDiagnosticOptions(DiagMessageStream, Level, Info, *DiagOpts);
142
143  // Keeps track of the the starting position of the location
144  // information (e.g., "foo.c:10:4:") that precedes the error
145  // message. We use this information to determine how long the
146  // file+line+column number prefix is.
147  uint64_t StartOfLocationInfo = OS.tell();
148
149  if (!Prefix.empty())
150    OS << Prefix << ": ";
151
152  // Use a dedicated, simpler path for diagnostics without a valid location.
153  // This is important as if the location is missing, we may be emitting
154  // diagnostics in a context that lacks language options, a source manager, or
155  // other infrastructure necessary when emitting more rich diagnostics.
156  if (!Info.getLocation().isValid()) {
157    TextDiagnostic::printDiagnosticLevel(OS, Level, DiagOpts->ShowColors);
158    TextDiagnostic::printDiagnosticMessage(OS, Level, DiagMessageStream.str(),
159                                           OS.tell() - StartOfLocationInfo,
160                                           DiagOpts->MessageLength,
161                                           DiagOpts->ShowColors);
162    OS.flush();
163    return;
164  }
165
166  // Assert that the rest of our infrastructure is setup properly.
167  assert(LangOpts && "Unexpected diagnostic outside source file processing");
168  assert(DiagOpts && "Unexpected diagnostic without options set");
169  assert(Info.hasSourceManager() &&
170         "Unexpected diagnostic with no source manager");
171
172  // Rebuild the TextDiagnostic utility if missing or the source manager has
173  // changed.
174  if (!TextDiag || SM != &Info.getSourceManager()) {
175    SM = &Info.getSourceManager();
176    TextDiag.reset(new TextDiagnostic(OS, *SM, *LangOpts, *DiagOpts));
177  }
178
179  TextDiag->emitDiagnostic(Info.getLocation(), Level, DiagMessageStream.str(),
180                           Info.getRanges(),
181                           llvm::makeArrayRef(Info.getFixItHints(),
182                                              Info.getNumFixItHints()));
183
184  OS.flush();
185}
186
187DiagnosticConsumer *
188TextDiagnosticPrinter::clone(DiagnosticsEngine &Diags) const {
189  return new TextDiagnosticPrinter(OS, *DiagOpts, /*OwnsOutputStream=*/false);
190}
191