1*0b57cec5SDimitry Andric //===--- TextDiagnosticPrinter.cpp - Diagnostic Printer -------------------===// 2*0b57cec5SDimitry Andric // 3*0b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4*0b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information. 5*0b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6*0b57cec5SDimitry Andric // 7*0b57cec5SDimitry Andric //===----------------------------------------------------------------------===// 8*0b57cec5SDimitry Andric // 9*0b57cec5SDimitry Andric // This diagnostic client prints out their diagnostic messages. 10*0b57cec5SDimitry Andric // 11*0b57cec5SDimitry Andric //===----------------------------------------------------------------------===// 12*0b57cec5SDimitry Andric 13*0b57cec5SDimitry Andric #include "clang/Frontend/TextDiagnosticPrinter.h" 14*0b57cec5SDimitry Andric #include "clang/Basic/DiagnosticOptions.h" 15*0b57cec5SDimitry Andric #include "clang/Basic/SourceManager.h" 16*0b57cec5SDimitry Andric #include "clang/Frontend/TextDiagnostic.h" 17*0b57cec5SDimitry Andric #include "clang/Lex/Lexer.h" 18*0b57cec5SDimitry Andric #include "llvm/ADT/SmallString.h" 19*0b57cec5SDimitry Andric #include "llvm/Support/ErrorHandling.h" 20*0b57cec5SDimitry Andric #include "llvm/Support/raw_ostream.h" 21*0b57cec5SDimitry Andric #include <algorithm> 22*0b57cec5SDimitry Andric using namespace clang; 23*0b57cec5SDimitry Andric 24*0b57cec5SDimitry Andric TextDiagnosticPrinter::TextDiagnosticPrinter(raw_ostream &os, 25*0b57cec5SDimitry Andric DiagnosticOptions *diags, 26*0b57cec5SDimitry Andric bool _OwnsOutputStream) 27*0b57cec5SDimitry Andric : OS(os), DiagOpts(diags), 28*0b57cec5SDimitry Andric OwnsOutputStream(_OwnsOutputStream) { 29*0b57cec5SDimitry Andric } 30*0b57cec5SDimitry Andric 31*0b57cec5SDimitry Andric TextDiagnosticPrinter::~TextDiagnosticPrinter() { 32*0b57cec5SDimitry Andric if (OwnsOutputStream) 33*0b57cec5SDimitry Andric delete &OS; 34*0b57cec5SDimitry Andric } 35*0b57cec5SDimitry Andric 36*0b57cec5SDimitry Andric void TextDiagnosticPrinter::BeginSourceFile(const LangOptions &LO, 37*0b57cec5SDimitry Andric const Preprocessor *PP) { 38*0b57cec5SDimitry Andric // Build the TextDiagnostic utility. 39*0b57cec5SDimitry Andric TextDiag.reset(new TextDiagnostic(OS, LO, &*DiagOpts)); 40*0b57cec5SDimitry Andric } 41*0b57cec5SDimitry Andric 42*0b57cec5SDimitry Andric void TextDiagnosticPrinter::EndSourceFile() { 43*0b57cec5SDimitry Andric TextDiag.reset(); 44*0b57cec5SDimitry Andric } 45*0b57cec5SDimitry Andric 46*0b57cec5SDimitry Andric /// Print any diagnostic option information to a raw_ostream. 47*0b57cec5SDimitry Andric /// 48*0b57cec5SDimitry Andric /// This implements all of the logic for adding diagnostic options to a message 49*0b57cec5SDimitry Andric /// (via OS). Each relevant option is comma separated and all are enclosed in 50*0b57cec5SDimitry Andric /// the standard bracketing: " [...]". 51*0b57cec5SDimitry Andric static void printDiagnosticOptions(raw_ostream &OS, 52*0b57cec5SDimitry Andric DiagnosticsEngine::Level Level, 53*0b57cec5SDimitry Andric const Diagnostic &Info, 54*0b57cec5SDimitry Andric const DiagnosticOptions &DiagOpts) { 55*0b57cec5SDimitry Andric bool Started = false; 56*0b57cec5SDimitry Andric if (DiagOpts.ShowOptionNames) { 57*0b57cec5SDimitry Andric // Handle special cases for non-warnings early. 58*0b57cec5SDimitry Andric if (Info.getID() == diag::fatal_too_many_errors) { 59*0b57cec5SDimitry Andric OS << " [-ferror-limit=]"; 60*0b57cec5SDimitry Andric return; 61*0b57cec5SDimitry Andric } 62*0b57cec5SDimitry Andric 63*0b57cec5SDimitry Andric // The code below is somewhat fragile because we are essentially trying to 64*0b57cec5SDimitry Andric // report to the user what happened by inferring what the diagnostic engine 65*0b57cec5SDimitry Andric // did. Eventually it might make more sense to have the diagnostic engine 66*0b57cec5SDimitry Andric // include some "why" information in the diagnostic. 67*0b57cec5SDimitry Andric 68*0b57cec5SDimitry Andric // If this is a warning which has been mapped to an error by the user (as 69*0b57cec5SDimitry Andric // inferred by checking whether the default mapping is to an error) then 70*0b57cec5SDimitry Andric // flag it as such. Note that diagnostics could also have been mapped by a 71*0b57cec5SDimitry Andric // pragma, but we don't currently have a way to distinguish this. 72*0b57cec5SDimitry Andric if (Level == DiagnosticsEngine::Error && 73*0b57cec5SDimitry Andric DiagnosticIDs::isBuiltinWarningOrExtension(Info.getID()) && 74*0b57cec5SDimitry Andric !DiagnosticIDs::isDefaultMappingAsError(Info.getID())) { 75*0b57cec5SDimitry Andric OS << " [-Werror"; 76*0b57cec5SDimitry Andric Started = true; 77*0b57cec5SDimitry Andric } 78*0b57cec5SDimitry Andric 79*0b57cec5SDimitry Andric StringRef Opt = DiagnosticIDs::getWarningOptionForDiag(Info.getID()); 80*0b57cec5SDimitry Andric if (!Opt.empty()) { 81*0b57cec5SDimitry Andric OS << (Started ? "," : " [") 82*0b57cec5SDimitry Andric << (Level == DiagnosticsEngine::Remark ? "-R" : "-W") << Opt; 83*0b57cec5SDimitry Andric StringRef OptValue = Info.getDiags()->getFlagValue(); 84*0b57cec5SDimitry Andric if (!OptValue.empty()) 85*0b57cec5SDimitry Andric OS << "=" << OptValue; 86*0b57cec5SDimitry Andric Started = true; 87*0b57cec5SDimitry Andric } 88*0b57cec5SDimitry Andric } 89*0b57cec5SDimitry Andric 90*0b57cec5SDimitry Andric // If the user wants to see category information, include it too. 91*0b57cec5SDimitry Andric if (DiagOpts.ShowCategories) { 92*0b57cec5SDimitry Andric unsigned DiagCategory = 93*0b57cec5SDimitry Andric DiagnosticIDs::getCategoryNumberForDiag(Info.getID()); 94*0b57cec5SDimitry Andric if (DiagCategory) { 95*0b57cec5SDimitry Andric OS << (Started ? "," : " ["); 96*0b57cec5SDimitry Andric Started = true; 97*0b57cec5SDimitry Andric if (DiagOpts.ShowCategories == 1) 98*0b57cec5SDimitry Andric OS << DiagCategory; 99*0b57cec5SDimitry Andric else { 100*0b57cec5SDimitry Andric assert(DiagOpts.ShowCategories == 2 && "Invalid ShowCategories value"); 101*0b57cec5SDimitry Andric OS << DiagnosticIDs::getCategoryNameFromID(DiagCategory); 102*0b57cec5SDimitry Andric } 103*0b57cec5SDimitry Andric } 104*0b57cec5SDimitry Andric } 105*0b57cec5SDimitry Andric if (Started) 106*0b57cec5SDimitry Andric OS << ']'; 107*0b57cec5SDimitry Andric } 108*0b57cec5SDimitry Andric 109*0b57cec5SDimitry Andric void TextDiagnosticPrinter::HandleDiagnostic(DiagnosticsEngine::Level Level, 110*0b57cec5SDimitry Andric const Diagnostic &Info) { 111*0b57cec5SDimitry Andric // Default implementation (Warnings/errors count). 112*0b57cec5SDimitry Andric DiagnosticConsumer::HandleDiagnostic(Level, Info); 113*0b57cec5SDimitry Andric 114*0b57cec5SDimitry Andric // Render the diagnostic message into a temporary buffer eagerly. We'll use 115*0b57cec5SDimitry Andric // this later as we print out the diagnostic to the terminal. 116*0b57cec5SDimitry Andric SmallString<100> OutStr; 117*0b57cec5SDimitry Andric Info.FormatDiagnostic(OutStr); 118*0b57cec5SDimitry Andric 119*0b57cec5SDimitry Andric llvm::raw_svector_ostream DiagMessageStream(OutStr); 120*0b57cec5SDimitry Andric printDiagnosticOptions(DiagMessageStream, Level, Info, *DiagOpts); 121*0b57cec5SDimitry Andric 122*0b57cec5SDimitry Andric // Keeps track of the starting position of the location 123*0b57cec5SDimitry Andric // information (e.g., "foo.c:10:4:") that precedes the error 124*0b57cec5SDimitry Andric // message. We use this information to determine how long the 125*0b57cec5SDimitry Andric // file+line+column number prefix is. 126*0b57cec5SDimitry Andric uint64_t StartOfLocationInfo = OS.tell(); 127*0b57cec5SDimitry Andric 128*0b57cec5SDimitry Andric if (!Prefix.empty()) 129*0b57cec5SDimitry Andric OS << Prefix << ": "; 130*0b57cec5SDimitry Andric 131*0b57cec5SDimitry Andric // Use a dedicated, simpler path for diagnostics without a valid location. 132*0b57cec5SDimitry Andric // This is important as if the location is missing, we may be emitting 133*0b57cec5SDimitry Andric // diagnostics in a context that lacks language options, a source manager, or 134*0b57cec5SDimitry Andric // other infrastructure necessary when emitting more rich diagnostics. 135*0b57cec5SDimitry Andric if (!Info.getLocation().isValid()) { 136*0b57cec5SDimitry Andric TextDiagnostic::printDiagnosticLevel(OS, Level, DiagOpts->ShowColors, 137*0b57cec5SDimitry Andric DiagOpts->CLFallbackMode); 138*0b57cec5SDimitry Andric TextDiagnostic::printDiagnosticMessage(OS, Level, DiagMessageStream.str(), 139*0b57cec5SDimitry Andric OS.tell() - StartOfLocationInfo, 140*0b57cec5SDimitry Andric DiagOpts->MessageLength, 141*0b57cec5SDimitry Andric DiagOpts->ShowColors); 142*0b57cec5SDimitry Andric OS.flush(); 143*0b57cec5SDimitry Andric return; 144*0b57cec5SDimitry Andric } 145*0b57cec5SDimitry Andric 146*0b57cec5SDimitry Andric // Assert that the rest of our infrastructure is setup properly. 147*0b57cec5SDimitry Andric assert(DiagOpts && "Unexpected diagnostic without options set"); 148*0b57cec5SDimitry Andric assert(Info.hasSourceManager() && 149*0b57cec5SDimitry Andric "Unexpected diagnostic with no source manager"); 150*0b57cec5SDimitry Andric assert(TextDiag && "Unexpected diagnostic outside source file processing"); 151*0b57cec5SDimitry Andric 152*0b57cec5SDimitry Andric TextDiag->emitDiagnostic( 153*0b57cec5SDimitry Andric FullSourceLoc(Info.getLocation(), Info.getSourceManager()), Level, 154*0b57cec5SDimitry Andric DiagMessageStream.str(), Info.getRanges(), Info.getFixItHints()); 155*0b57cec5SDimitry Andric 156*0b57cec5SDimitry Andric OS.flush(); 157*0b57cec5SDimitry Andric } 158