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/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 }