clang API Documentation

TextDiagnosticPrinter.cpp
Go to the documentation of this file.
00001 //===--- TextDiagnosticPrinter.cpp - Diagnostic Printer -------------------===//
00002 //
00003 //                     The LLVM Compiler Infrastructure
00004 //
00005 // This file is distributed under the University of Illinois Open Source
00006 // License. See LICENSE.TXT for details.
00007 //
00008 //===----------------------------------------------------------------------===//
00009 //
00010 // This diagnostic client prints out their diagnostic messages.
00011 //
00012 //===----------------------------------------------------------------------===//
00013 
00014 #include "clang/Frontend/TextDiagnosticPrinter.h"
00015 #include "clang/Basic/DiagnosticOptions.h"
00016 #include "clang/Basic/FileManager.h"
00017 #include "clang/Basic/SourceManager.h"
00018 #include "clang/Frontend/TextDiagnostic.h"
00019 #include "clang/Lex/Lexer.h"
00020 #include "llvm/ADT/SmallString.h"
00021 #include "llvm/Support/ErrorHandling.h"
00022 #include "llvm/Support/MemoryBuffer.h"
00023 #include "llvm/Support/raw_ostream.h"
00024 #include <algorithm>
00025 using namespace clang;
00026 
00027 TextDiagnosticPrinter::TextDiagnosticPrinter(raw_ostream &os,
00028                                              DiagnosticOptions *diags,
00029                                              bool _OwnsOutputStream)
00030   : OS(os), DiagOpts(diags),
00031     OwnsOutputStream(_OwnsOutputStream) {
00032 }
00033 
00034 TextDiagnosticPrinter::~TextDiagnosticPrinter() {
00035   if (OwnsOutputStream)
00036     delete &OS;
00037 }
00038 
00039 void TextDiagnosticPrinter::BeginSourceFile(const LangOptions &LO,
00040                                             const Preprocessor *PP) {
00041   // Build the TextDiagnostic utility.
00042   TextDiag.reset(new TextDiagnostic(OS, LO, &*DiagOpts));
00043 }
00044 
00045 void TextDiagnosticPrinter::EndSourceFile() {
00046   TextDiag.reset();
00047 }
00048 
00049 /// \brief Print any diagnostic option information to a raw_ostream.
00050 ///
00051 /// This implements all of the logic for adding diagnostic options to a message
00052 /// (via OS). Each relevant option is comma separated and all are enclosed in
00053 /// the standard bracketing: " [...]".
00054 static void printDiagnosticOptions(raw_ostream &OS,
00055                                    DiagnosticsEngine::Level Level,
00056                                    const Diagnostic &Info,
00057                                    const DiagnosticOptions &DiagOpts) {
00058   bool Started = false;
00059   if (DiagOpts.ShowOptionNames) {
00060     // Handle special cases for non-warnings early.
00061     if (Info.getID() == diag::fatal_too_many_errors) {
00062       OS << " [-ferror-limit=]";
00063       return;
00064     }
00065 
00066     // The code below is somewhat fragile because we are essentially trying to
00067     // report to the user what happened by inferring what the diagnostic engine
00068     // did. Eventually it might make more sense to have the diagnostic engine
00069     // include some "why" information in the diagnostic.
00070 
00071     // If this is a warning which has been mapped to an error by the user (as
00072     // inferred by checking whether the default mapping is to an error) then
00073     // flag it as such. Note that diagnostics could also have been mapped by a
00074     // pragma, but we don't currently have a way to distinguish this.
00075     if (Level == DiagnosticsEngine::Error &&
00076         DiagnosticIDs::isBuiltinWarningOrExtension(Info.getID()) &&
00077         !DiagnosticIDs::isDefaultMappingAsError(Info.getID())) {
00078       OS << " [-Werror";
00079       Started = true;
00080     }
00081 
00082     StringRef Opt = DiagnosticIDs::getWarningOptionForDiag(Info.getID());
00083     if (!Opt.empty()) {
00084       OS << (Started ? "," : " [")
00085          << (Level == DiagnosticsEngine::Remark ? "-R" : "-W") << Opt;
00086       StringRef OptValue = Info.getDiags()->getFlagValue();
00087       if (!OptValue.empty())
00088         OS << "=" << OptValue;
00089       Started = true;
00090     }
00091   }
00092 
00093   // If the user wants to see category information, include it too.
00094   if (DiagOpts.ShowCategories) {
00095     unsigned DiagCategory =
00096       DiagnosticIDs::getCategoryNumberForDiag(Info.getID());
00097     if (DiagCategory) {
00098       OS << (Started ? "," : " [");
00099       Started = true;
00100       if (DiagOpts.ShowCategories == 1)
00101         OS << DiagCategory;
00102       else {
00103         assert(DiagOpts.ShowCategories == 2 && "Invalid ShowCategories value");
00104         OS << DiagnosticIDs::getCategoryNameFromID(DiagCategory);
00105       }
00106     }
00107   }
00108   if (Started)
00109     OS << ']';
00110 }
00111 
00112 void TextDiagnosticPrinter::HandleDiagnostic(DiagnosticsEngine::Level Level,
00113                                              const Diagnostic &Info) {
00114   // Default implementation (Warnings/errors count).
00115   DiagnosticConsumer::HandleDiagnostic(Level, Info);
00116 
00117   // Render the diagnostic message into a temporary buffer eagerly. We'll use
00118   // this later as we print out the diagnostic to the terminal.
00119   SmallString<100> OutStr;
00120   Info.FormatDiagnostic(OutStr);
00121 
00122   llvm::raw_svector_ostream DiagMessageStream(OutStr);
00123   printDiagnosticOptions(DiagMessageStream, Level, Info, *DiagOpts);
00124 
00125   // Keeps track of the starting position of the location
00126   // information (e.g., "foo.c:10:4:") that precedes the error
00127   // message. We use this information to determine how long the
00128   // file+line+column number prefix is.
00129   uint64_t StartOfLocationInfo = OS.tell();
00130 
00131   if (!Prefix.empty())
00132     OS << Prefix << ": ";
00133 
00134   // Use a dedicated, simpler path for diagnostics without a valid location.
00135   // This is important as if the location is missing, we may be emitting
00136   // diagnostics in a context that lacks language options, a source manager, or
00137   // other infrastructure necessary when emitting more rich diagnostics.
00138   if (!Info.getLocation().isValid()) {
00139     TextDiagnostic::printDiagnosticLevel(OS, Level, DiagOpts->ShowColors,
00140                                          DiagOpts->CLFallbackMode);
00141     TextDiagnostic::printDiagnosticMessage(OS, Level, DiagMessageStream.str(),
00142                                            OS.tell() - StartOfLocationInfo,
00143                                            DiagOpts->MessageLength,
00144                                            DiagOpts->ShowColors);
00145     OS.flush();
00146     return;
00147   }
00148 
00149   // Assert that the rest of our infrastructure is setup properly.
00150   assert(DiagOpts && "Unexpected diagnostic without options set");
00151   assert(Info.hasSourceManager() &&
00152          "Unexpected diagnostic with no source manager");
00153   assert(TextDiag && "Unexpected diagnostic outside source file processing");
00154 
00155   TextDiag->emitDiagnostic(Info.getLocation(), Level, DiagMessageStream.str(),
00156                            Info.getRanges(),
00157                            Info.getFixItHints(),
00158                            &Info.getSourceManager());
00159 
00160   OS.flush();
00161 }