clang API Documentation
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/FileManager.h" 00016 #include "clang/Basic/SourceManager.h" 00017 #include "clang/Frontend/DiagnosticOptions.h" 00018 #include "clang/Frontend/TextDiagnostic.h" 00019 #include "clang/Lex/Lexer.h" 00020 #include "llvm/Support/MemoryBuffer.h" 00021 #include "llvm/Support/raw_ostream.h" 00022 #include "llvm/Support/ErrorHandling.h" 00023 #include "llvm/ADT/SmallString.h" 00024 #include <algorithm> 00025 using namespace clang; 00026 00027 TextDiagnosticPrinter::TextDiagnosticPrinter(raw_ostream &os, 00028 const 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(0); 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 // If the diagnostic is an extension diagnostic and not enabled by default 00083 // then it must have been turned on with -pedantic. 00084 bool EnabledByDefault; 00085 if (DiagnosticIDs::isBuiltinExtensionDiag(Info.getID(), 00086 EnabledByDefault) && 00087 !EnabledByDefault) { 00088 OS << (Started ? "," : " [") << "-pedantic"; 00089 Started = true; 00090 } 00091 00092 StringRef Opt = DiagnosticIDs::getWarningOptionForDiag(Info.getID()); 00093 if (!Opt.empty()) { 00094 OS << (Started ? "," : " [") << "-W" << Opt; 00095 Started = true; 00096 } 00097 } 00098 00099 // If the user wants to see category information, include it too. 00100 if (DiagOpts.ShowCategories) { 00101 unsigned DiagCategory = 00102 DiagnosticIDs::getCategoryNumberForDiag(Info.getID()); 00103 if (DiagCategory) { 00104 OS << (Started ? "," : " ["); 00105 Started = true; 00106 if (DiagOpts.ShowCategories == 1) 00107 OS << DiagCategory; 00108 else { 00109 assert(DiagOpts.ShowCategories == 2 && "Invalid ShowCategories value"); 00110 OS << DiagnosticIDs::getCategoryNameFromID(DiagCategory); 00111 } 00112 } 00113 } 00114 if (Started) 00115 OS << ']'; 00116 } 00117 00118 void TextDiagnosticPrinter::HandleDiagnostic(DiagnosticsEngine::Level Level, 00119 const Diagnostic &Info) { 00120 // Default implementation (Warnings/errors count). 00121 DiagnosticConsumer::HandleDiagnostic(Level, Info); 00122 00123 // Render the diagnostic message into a temporary buffer eagerly. We'll use 00124 // this later as we print out the diagnostic to the terminal. 00125 SmallString<100> OutStr; 00126 Info.FormatDiagnostic(OutStr); 00127 00128 llvm::raw_svector_ostream DiagMessageStream(OutStr); 00129 printDiagnosticOptions(DiagMessageStream, Level, Info, *DiagOpts); 00130 00131 // Keeps track of the the starting position of the location 00132 // information (e.g., "foo.c:10:4:") that precedes the error 00133 // message. We use this information to determine how long the 00134 // file+line+column number prefix is. 00135 uint64_t StartOfLocationInfo = OS.tell(); 00136 00137 if (!Prefix.empty()) 00138 OS << Prefix << ": "; 00139 00140 // Use a dedicated, simpler path for diagnostics without a valid location. 00141 // This is important as if the location is missing, we may be emitting 00142 // diagnostics in a context that lacks language options, a source manager, or 00143 // other infrastructure necessary when emitting more rich diagnostics. 00144 if (!Info.getLocation().isValid()) { 00145 TextDiagnostic::printDiagnosticLevel(OS, Level, DiagOpts->ShowColors); 00146 TextDiagnostic::printDiagnosticMessage(OS, Level, DiagMessageStream.str(), 00147 OS.tell() - StartOfLocationInfo, 00148 DiagOpts->MessageLength, 00149 DiagOpts->ShowColors); 00150 OS.flush(); 00151 return; 00152 } 00153 00154 // Assert that the rest of our infrastructure is setup properly. 00155 assert(DiagOpts && "Unexpected diagnostic without options set"); 00156 assert(Info.hasSourceManager() && 00157 "Unexpected diagnostic with no source manager"); 00158 assert(TextDiag && "Unexpected diagnostic outside source file processing"); 00159 00160 TextDiag->emitDiagnostic(Info.getLocation(), Level, DiagMessageStream.str(), 00161 Info.getRanges(), 00162 llvm::makeArrayRef(Info.getFixItHints(), 00163 Info.getNumFixItHints()), 00164 &Info.getSourceManager()); 00165 00166 OS.flush(); 00167 } 00168 00169 DiagnosticConsumer * 00170 TextDiagnosticPrinter::clone(DiagnosticsEngine &Diags) const { 00171 return new TextDiagnosticPrinter(OS, *DiagOpts, /*OwnsOutputStream=*/false); 00172 }