yum-mirror/slang

Making it easier to work with shaders

git clone https://git.yummers.dev/yum-mirror/slang

Theresa FoleyOrganize code better by splitting some big files (#7890)8ccd495d5

master
13.3 KiB411 linesraw
1#ifndef SLANG_DIAGNOSTIC_SINK_H
2#define SLANG_DIAGNOSTIC_SINK_H
3
4#include "../core/slang-basic.h"
5#include "../core/slang-memory-arena.h"
6#include "../core/slang-writer.h"
7#include "slang-source-loc.h"
8#include "slang-token.h"
9#include "slang.h"
10
11namespace Slang
12{
13
14enum class Severity
15{
16    Disable,
17    Note,
18    Warning,
19    Error,
20    Fatal,
21    Internal
22};
23
24// Make sure that the slang.h severity constants match those defined here
25static_assert(SLANG_SEVERITY_DISABLED == int(Severity::Disable), "mismatched Severity enum values");
26static_assert(SLANG_SEVERITY_NOTE == int(Severity::Note), "mismatched Severity enum values");
27static_assert(SLANG_SEVERITY_WARNING == int(Severity::Warning), "mismatched Severity enum values");
28static_assert(SLANG_SEVERITY_ERROR == int(Severity::Error), "mismatched Severity enum values");
29static_assert(SLANG_SEVERITY_FATAL == int(Severity::Fatal), "mismatched Severity enum values");
30static_assert(
31    SLANG_SEVERITY_INTERNAL == int(Severity::Internal),
32    "mismatched Severity enum values");
33
34// TODO(tfoley): move this into a source file...
35inline const char* getSeverityName(Severity severity)
36{
37    switch (severity)
38    {
39    case Severity::Disable:
40        return "ignored";
41    case Severity::Note:
42        return "note";
43    case Severity::Warning:
44        return "warning";
45    case Severity::Error:
46        return "error";
47    case Severity::Fatal:
48        return "fatal error";
49    case Severity::Internal:
50        return "internal error";
51    default:
52        return "unknown error";
53    }
54}
55
56// A structure to be used in static data describing different
57// diagnostic messages.
58struct DiagnosticInfo
59{
60    int id;
61    Severity severity;
62    char const* name; ///< Unique name
63    char const* messageFormat;
64};
65
66class Diagnostic
67{
68public:
69    String Message;
70    SourceLoc loc;
71    int ErrorID;
72    Severity severity;
73
74    Diagnostic() { ErrorID = -1; }
75    Diagnostic(const String& msg, int id, const SourceLoc& pos, Severity severity)
76        : severity(severity)
77    {
78        Message = msg;
79        ErrorID = id;
80        loc = pos;
81    }
82};
83
84struct SourceWarningStateTrackerBase : public RefObject
85{
86    virtual Severity consumeWarningSeverity(SourceLoc loc, int id, Severity severity) = 0;
87};
88
89class Name;
90
91void printDiagnosticArg(StringBuilder& sb, char const* str);
92
93void printDiagnosticArg(StringBuilder& sb, int32_t val);
94void printDiagnosticArg(StringBuilder& sb, uint32_t val);
95
96void printDiagnosticArg(StringBuilder& sb, int64_t val);
97void printDiagnosticArg(StringBuilder& sb, uint64_t val);
98
99void printDiagnosticArg(StringBuilder& sb, double val);
100
101void printDiagnosticArg(StringBuilder& sb, Slang::String const& str);
102void printDiagnosticArg(StringBuilder& sb, Slang::UnownedStringSlice const& str);
103void printDiagnosticArg(StringBuilder& sb, Name* name);
104
105void printDiagnosticArg(StringBuilder& sb, TokenType tokenType);
106void printDiagnosticArg(StringBuilder& sb, Token const& token);
107
108struct IRInst;
109void printDiagnosticArg(StringBuilder& sb, IRInst* irObject);
110
111class Modifier;
112void printDiagnosticArg(StringBuilder& sb, Modifier* modifier);
113
114template<typename T>
115void printDiagnosticArg(StringBuilder& sb, RefPtr<T> ptr)
116{
117    printDiagnosticArg(sb, ptr.Ptr());
118}
119
120inline SourceLoc getDiagnosticPos(SourceLoc const& pos)
121{
122    return pos;
123}
124
125SourceLoc getDiagnosticPos(Token const& token);
126
127
128template<typename T>
129SourceLoc getDiagnosticPos(RefPtr<T> const& ptr)
130{
131    return getDiagnosticPos(ptr.Ptr());
132}
133
134struct DiagnosticArg
135{
136    void* data;
137    void (*printFunc)(StringBuilder&, void*);
138
139    template<typename T>
140    struct Helper
141    {
142        static void printFunc(StringBuilder& sb, void* data) { printDiagnosticArg(sb, *(T*)data); }
143    };
144
145    template<typename T>
146    DiagnosticArg(T const& arg)
147        : data((void*)&arg), printFunc(&Helper<T>::printFunc)
148    {
149    }
150};
151
152class DiagnosticSink
153{
154public:
155    /// Flags to control some aspects of Diagnostic sink behavior
156    typedef uint32_t Flags;
157    struct Flag
158    {
159        enum Enum : Flags
160        {
161            VerbosePath = 0x1, ///< Will display a more verbose path (if available) - such as a
162                               ///< canonical or absolute path
163            SourceLocationLine =
164                0x2,         ///< If set will display the location line if source is available
165            HumaneLoc = 0x4, ///< If set will display humane locs (filename/line number) information
166            TreatWarningsAsErrors = 0x8, ///< If set will turn all Warning type messages (after
167                                         ///< overrides) into Error type messages
168            LanguageServer =
169                0x10, ///< If set will format message in a way that is suitable for language server
170        };
171    };
172
173    /// Used by diagnostic sink to be able to underline tokens. If not defined on the
174    /// DiagnosticSink, will only display a caret at the SourceLoc
175    typedef UnownedStringSlice (*SourceLocationLexer)(const UnownedStringSlice& text);
176
177    /// Get the total amount of errors that have taken place on this DiagnosticSink
178    SLANG_FORCE_INLINE int getErrorCount() { return m_errorCount; }
179
180    template<typename P, typename... Args>
181    bool diagnose(P const& pos, DiagnosticInfo const& info, Args const&... args)
182    {
183        DiagnosticArg as[] = {DiagnosticArg(args)...};
184        return diagnoseImpl(getDiagnosticPos(pos), info, sizeof...(args), as);
185    }
186
187    template<typename P>
188    bool diagnose(P const& pos, DiagnosticInfo const& info)
189    {
190        // MSVC gets upset with the zero sized array above, so overload that case here
191        return diagnoseImpl(getDiagnosticPos(pos), info, 0, nullptr);
192    }
193
194    // Useful for notes on existing diagnostics, where it would be redundant to display the same
195    // line again. (Ideally we would print the error/warning and notes in one call...)
196    template<typename P, typename... Args>
197    bool diagnoseWithoutSourceView(P const& pos, DiagnosticInfo const& info, Args const&... args)
198    {
199        const auto fs = this->getFlags();
200        this->resetFlag(Flag::SourceLocationLine);
201
202        auto result = diagnose(pos, info, args...);
203
204        this->setFlags(fs);
205        return result;
206    }
207
208    // Add a diagnostic with raw text
209    // (used when we get errors from a downstream compiler)
210    void diagnoseRaw(Severity severity, char const* message);
211    void diagnoseRaw(Severity severity, const UnownedStringSlice& message);
212
213    /// During propagation of an exception for an internal
214    /// error, note that this source location was involved
215    void noteInternalErrorLoc(SourceLoc const& loc);
216
217    /// Create a blob containing diagnostics if there were any errors.
218    /// *note* only works if writer is not set, the blob is created from outputBuffer
219    SlangResult getBlobIfNeeded(ISlangBlob** outBlob);
220
221    /// Get the source manager used
222    SourceManager* getSourceManager() const { return m_sourceManager; }
223    /// Set the source manager used for lookup of source locs
224    void setSourceManager(SourceManager* inSourceManager) { m_sourceManager = inSourceManager; }
225
226    /// Set the flags
227    void setFlags(Flags flags) { m_flags = flags; }
228    /// Get the flags
229    Flags getFlags() const { return m_flags; }
230    /// Set a flag
231    void setFlag(Flag::Enum flag) { m_flags |= Flags(flag); }
232    /// Reset a flag
233    void resetFlag(Flag::Enum flag) { m_flags &= ~Flags(flag); }
234    /// Test if flag is set
235    bool isFlagSet(Flag::Enum flag) { return (m_flags & Flags(flag)) != 0; }
236
237    /// Sets an override on the severity of a specific diagnostic message (by numeric identifier)
238    /// info can be set to nullptr if only to override
239    void overrideDiagnosticSeverity(
240        int diagnosticId,
241        Severity overrideSeverity,
242        const DiagnosticInfo* info = nullptr);
243
244    /// Get the (optional) diagnostic sink lexer. This is used to
245    /// improve quality of highlighting a locations token. If not set, will just have a single
246    /// character caret at location
247    SourceLocationLexer getSourceLocationLexer() const { return m_sourceLocationLexer; }
248
249    /// Set the maximum length (in chars) of a source line displayed. Set to 0 for no limit
250    void setSourceLineMaxLength(Index length) { m_sourceLineMaxLength = length; }
251    Index getSourceLineMaxLength() const { return m_sourceLineMaxLength; }
252
253    /// The parent sink is another sink that will receive diagnostics from this sink.
254    void setParentSink(DiagnosticSink* parentSink) { m_parentSink = parentSink; }
255    DiagnosticSink* getParentSink() const { return m_parentSink; }
256
257    void setSourceWarningStateTracker(SourceWarningStateTrackerBase* ptr)
258    {
259        m_sourceWarningStateTracker = ptr;
260    }
261    RefPtr<SourceWarningStateTrackerBase> getSourceWarningStateTracker() const
262    {
263        return m_sourceWarningStateTracker;
264    }
265
266    /// Reset state.
267    /// Resets error counts. Resets the output buffer.
268    void reset();
269
270    /// Initialize state.
271    void init(SourceManager* sourceManager, SourceLocationLexer sourceLocationLexer);
272
273    /// Ctor
274    DiagnosticSink(SourceManager* sourceManager, SourceLocationLexer sourceLocationLexer)
275    {
276        init(sourceManager, sourceLocationLexer);
277    }
278    /// Default Ctor
279    DiagnosticSink()
280        : m_sourceManager(nullptr), m_sourceLocationLexer(nullptr)
281    {
282    }
283
284    // Public members
285
286    /// The outputBuffer will contain any diagnostics *iff* the writer is *not* set
287    StringBuilder outputBuffer;
288    /// If a writer is set output will *not* be written to the outputBuffer
289    ISlangWriter* writer = nullptr;
290
291protected:
292    // Returns true if a diagnostic is actually written.
293    bool diagnoseImpl(
294        SourceLoc const& pos,
295        DiagnosticInfo info,
296        int argCount,
297        DiagnosticArg const* args);
298    bool diagnoseImpl(DiagnosticInfo const& info, const UnownedStringSlice& formattedMessage);
299
300    Severity getEffectiveMessageSeverity(DiagnosticInfo const& info, SourceLoc const& location);
301
302    /// If set all diagnostics (as formatted by *this* sink, will be routed to the parent).
303    DiagnosticSink* m_parentSink = nullptr;
304
305    int m_errorCount = 0;
306    int m_internalErrorLocsNoted = 0;
307
308    /// If 0, then there is no limit, otherwise max amount of chars of the source line location
309    /// We don't know the size of a terminal in general, but for now we'll guess 120.
310    Index m_sourceLineMaxLength = 120;
311
312    Flags m_flags = 0;
313
314    // The source manager to use when mapping source locations to file+line info
315    SourceManager* m_sourceManager = nullptr;
316
317    SourceLocationLexer m_sourceLocationLexer;
318
319    // Configuration that allows the user to control the severity of certain diagnostic messages
320    Dictionary<int, Severity> m_severityOverrides;
321
322    RefPtr<SourceWarningStateTrackerBase> m_sourceWarningStateTracker = nullptr;
323};
324
325/// An `ISlangWriter` that writes directly to a diagnostic sink.
326class DiagnosticSinkWriter : public AppendBufferWriter
327{
328public:
329    typedef AppendBufferWriter Super;
330
331    DiagnosticSinkWriter(DiagnosticSink* sink)
332        : Super(WriterFlag::IsStatic), m_sink(sink)
333    {
334    }
335
336    // ISlangWriter
337    SLANG_NO_THROW virtual SlangResult SLANG_MCALL write(const char* chars, size_t numChars)
338        SLANG_OVERRIDE
339    {
340        m_sink->diagnoseRaw(Severity::Note, UnownedStringSlice(chars, chars + numChars));
341        return SLANG_OK;
342    }
343
344private:
345    DiagnosticSink* m_sink = nullptr;
346};
347
348class DiagnosticsLookup : public RefObject
349{
350public:
351    static const Index kArenaInitialSize = 65536;
352
353    /// Will take into account the slice name could be using different conventions
354    const DiagnosticInfo* findDiagnosticByName(const UnownedStringSlice& slice) const;
355    /// The name must be as defined in the diagnostics exactly, typically lower camel
356    const DiagnosticInfo* findDiagnosticByExactName(const UnownedStringSlice& slice) const;
357
358    /// Get a diagnostic by it's id.
359    /// NOTE! That it is possible for multiple diagnostics to have the same id. This will return
360    /// the first added
361    const DiagnosticInfo* getDiagnosticById(Int id) const;
362
363    /// info must stay in scope
364    Index add(const DiagnosticInfo* info);
365    /// Infos referenced must remain in scope
366    void add(const DiagnosticInfo* const* infos, Index infosCount);
367
368    /// NOTE! Name must stay in scope as long as the diagnostics lookup.
369    /// If not possible add it to the arena to keep in scope.
370    void addAlias(const char* name, const char* diagnosticName);
371
372    /// Get the diagnostics held in this lookup
373    const List<const DiagnosticInfo*>& getDiagnostics() const { return m_diagnostics; }
374
375    /// Get the associated arena
376    MemoryArena& getArena() { return m_arena; }
377
378    /// NOTE! diagnostics must stay in scope for lifetime of lookup
379    DiagnosticsLookup(const DiagnosticInfo* const* diagnostics, Index diagnosticsCount);
380    DiagnosticsLookup();
381
382protected:
383    void _addName(const char* name, Index diagnosticIndex);
384
385    Index _findDiagnosticIndexByExactName(const UnownedStringSlice& slice) const;
386
387    List<const DiagnosticInfo*> m_diagnostics;
388
389    StringBuilder m_work;
390    Dictionary<UnownedStringSlice, Index> m_nameMap;
391    Dictionary<Int, Index> m_idMap;
392
393    MemoryArena m_arena;
394};
395
396
397void outputExceptionDiagnostic(
398    const AbortCompilationException& exception,
399    DiagnosticSink& sink,
400    slang::IBlob** outDiagnostics);
401
402void outputExceptionDiagnostic(
403    const Exception& exception,
404    DiagnosticSink& sink,
405    slang::IBlob** outDiagnostics);
406
407void outputExceptionDiagnostic(DiagnosticSink& sink, slang::IBlob** outDiagnostics);
408
409} // namespace Slang
410
411#endif