aboutsummaryrefslogtreecommitdiff
path: root/include/cps/Diagnostics.h
blob: 9c9ea375d7f406e9e180039ef187f8fd8aabde28 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69

#pragma once
#include <cstdarg>
#include <cstdio>
#include <set>
#include <string>

namespace cps {
enum class Severity { Error, Warning, Note, Fatal };

// Path of the unit being diagnosed, as given on the command line.
inline std::string DiagnosticSource;

// Set by the driver from -fdiagnostics-color.
inline bool DiagnosticColors = false;

// gcc layout: "<locus>: <label>: "; an empty locus prints the label alone.
// With colors, the bytes match gcc's default GCC_COLORS: bold locus
// (including its colon), then the label in bold red/magenta/cyan.
inline std::string diagnosticPrefix(Severity S, const std::string &Locus = "cpsc") {
    const char *Label = S == Severity::Error   ? "error: "
                      : S == Severity::Warning ? "warning: "
                      : S == Severity::Note    ? "note: "
                                               : "fatal error: ";
    if (!DiagnosticColors) return (Locus.empty() ? "" : Locus + ": ") + Label;
    const char *Color = S == Severity::Warning ? "01;35"
                      : S == Severity::Note    ? "01;36"
                                               : "01;31";
    auto Span = [](const char *SGR, const std::string &Text) {
        return std::string("\33[") + SGR + "m\33[K" + Text + "\33[m\33[K";
    };
    return (Locus.empty() ? "" : Span("01", Locus + ":") + " ") + Span(Color, Label);
}

// Every diagnostic line printed so far, to suppress exact repeats.
inline std::set<std::string> PrintedDiagnostics;

// Prints "<path>:<line>: <label>: <message>\n" (just "<path>:" when the line is
// unknown, "cpsc:" when no source is set).
inline void vsourceDiagnostic(Severity S, int Line, const char *Format, va_list Args) {
    std::string Locus = DiagnosticSource.empty() ? "cpsc" : DiagnosticSource;
    if (!DiagnosticSource.empty() && Line > 0) Locus += ":" + std::to_string(Line);
    va_list Copy;
    va_copy(Copy, Args);
    int Length = std::vsnprintf(nullptr, 0, Format, Copy);
    va_end(Copy);
    std::string Message(Length > 0 ? Length : 0, '\0');
    if (Length > 0) std::vsnprintf(Message.data(), Message.size() + 1, Format, Args);
    std::string Text = diagnosticPrefix(S, Locus) + Message + '\n';
    // The same node can be diagnosed more than once; print each line once.
    if (!PrintedDiagnostics.insert(Text).second) return;
    std::fputs(Text.c_str(), stderr);
}

__attribute__((format(printf, 2, 3)))
inline void sourceError(int Line, const char *Format, ...) {
    va_list Args;
    va_start(Args, Format);
    vsourceDiagnostic(Severity::Error, Line, Format, Args);
    va_end(Args);
}

__attribute__((format(printf, 2, 3)))
inline void sourceWarning(int Line, const char *Format, ...) {
    va_list Args;
    va_start(Args, Format);
    vsourceDiagnostic(Severity::Warning, Line, Format, Args);
    va_end(Args);
}
}