Path: blob/main/contrib/llvm-project/clang/lib/Frontend/TextDiagnosticPrinter.cpp
35233 views
//===--- TextDiagnosticPrinter.cpp - Diagnostic Printer -------------------===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//7//8// This diagnostic client prints out their diagnostic messages.9//10//===----------------------------------------------------------------------===//1112#include "clang/Frontend/TextDiagnosticPrinter.h"13#include "clang/Basic/DiagnosticOptions.h"14#include "clang/Basic/SourceManager.h"15#include "clang/Frontend/TextDiagnostic.h"16#include "clang/Lex/Lexer.h"17#include "llvm/ADT/SmallString.h"18#include "llvm/Support/ErrorHandling.h"19#include "llvm/Support/raw_ostream.h"20#include <algorithm>21using namespace clang;2223TextDiagnosticPrinter::TextDiagnosticPrinter(raw_ostream &os,24DiagnosticOptions *diags,25bool _OwnsOutputStream)26: OS(os), DiagOpts(diags),27OwnsOutputStream(_OwnsOutputStream) {28}2930TextDiagnosticPrinter::~TextDiagnosticPrinter() {31if (OwnsOutputStream)32delete &OS;33}3435void TextDiagnosticPrinter::BeginSourceFile(const LangOptions &LO,36const Preprocessor *PP) {37// Build the TextDiagnostic utility.38TextDiag.reset(new TextDiagnostic(OS, LO, &*DiagOpts, PP));39}4041void TextDiagnosticPrinter::EndSourceFile() {42TextDiag.reset();43}4445/// Print any diagnostic option information to a raw_ostream.46///47/// This implements all of the logic for adding diagnostic options to a message48/// (via OS). Each relevant option is comma separated and all are enclosed in49/// the standard bracketing: " [...]".50static void printDiagnosticOptions(raw_ostream &OS,51DiagnosticsEngine::Level Level,52const Diagnostic &Info,53const DiagnosticOptions &DiagOpts) {54bool Started = false;55if (DiagOpts.ShowOptionNames) {56// Handle special cases for non-warnings early.57if (Info.getID() == diag::fatal_too_many_errors) {58OS << " [-ferror-limit=]";59return;60}6162// The code below is somewhat fragile because we are essentially trying to63// report to the user what happened by inferring what the diagnostic engine64// did. Eventually it might make more sense to have the diagnostic engine65// include some "why" information in the diagnostic.6667// If this is a warning which has been mapped to an error by the user (as68// inferred by checking whether the default mapping is to an error) then69// flag it as such. Note that diagnostics could also have been mapped by a70// pragma, but we don't currently have a way to distinguish this.71if (Level == DiagnosticsEngine::Error &&72DiagnosticIDs::isBuiltinWarningOrExtension(Info.getID()) &&73!DiagnosticIDs::isDefaultMappingAsError(Info.getID())) {74OS << " [-Werror";75Started = true;76}7778StringRef Opt = DiagnosticIDs::getWarningOptionForDiag(Info.getID());79if (!Opt.empty()) {80OS << (Started ? "," : " [")81<< (Level == DiagnosticsEngine::Remark ? "-R" : "-W") << Opt;82StringRef OptValue = Info.getDiags()->getFlagValue();83if (!OptValue.empty())84OS << "=" << OptValue;85Started = true;86}87}8889// If the user wants to see category information, include it too.90if (DiagOpts.ShowCategories) {91unsigned DiagCategory =92DiagnosticIDs::getCategoryNumberForDiag(Info.getID());93if (DiagCategory) {94OS << (Started ? "," : " [");95Started = true;96if (DiagOpts.ShowCategories == 1)97OS << DiagCategory;98else {99assert(DiagOpts.ShowCategories == 2 && "Invalid ShowCategories value");100OS << DiagnosticIDs::getCategoryNameFromID(DiagCategory);101}102}103}104if (Started)105OS << ']';106}107108void TextDiagnosticPrinter::HandleDiagnostic(DiagnosticsEngine::Level Level,109const Diagnostic &Info) {110// Default implementation (Warnings/errors count).111DiagnosticConsumer::HandleDiagnostic(Level, Info);112113// Render the diagnostic message into a temporary buffer eagerly. We'll use114// this later as we print out the diagnostic to the terminal.115SmallString<100> OutStr;116Info.FormatDiagnostic(OutStr);117118llvm::raw_svector_ostream DiagMessageStream(OutStr);119printDiagnosticOptions(DiagMessageStream, Level, Info, *DiagOpts);120121// Keeps track of the starting position of the location122// information (e.g., "foo.c:10:4:") that precedes the error123// message. We use this information to determine how long the124// file+line+column number prefix is.125uint64_t StartOfLocationInfo = OS.tell();126127if (!Prefix.empty())128OS << Prefix << ": ";129130// Use a dedicated, simpler path for diagnostics without a valid location.131// This is important as if the location is missing, we may be emitting132// diagnostics in a context that lacks language options, a source manager, or133// other infrastructure necessary when emitting more rich diagnostics.134if (!Info.getLocation().isValid()) {135TextDiagnostic::printDiagnosticLevel(OS, Level, DiagOpts->ShowColors);136TextDiagnostic::printDiagnosticMessage(137OS, /*IsSupplemental=*/Level == DiagnosticsEngine::Note,138DiagMessageStream.str(), OS.tell() - StartOfLocationInfo,139DiagOpts->MessageLength, DiagOpts->ShowColors);140OS.flush();141return;142}143144// Assert that the rest of our infrastructure is setup properly.145assert(DiagOpts && "Unexpected diagnostic without options set");146assert(Info.hasSourceManager() &&147"Unexpected diagnostic with no source manager");148assert(TextDiag && "Unexpected diagnostic outside source file processing");149150TextDiag->emitDiagnostic(151FullSourceLoc(Info.getLocation(), Info.getSourceManager()), Level,152DiagMessageStream.str(), Info.getRanges(), Info.getFixItHints());153154OS.flush();155}156157158