yum-mirror/slang
Making it easier to work with shaders
git clone https://git.yummers.dev/yum-mirror/slang
8ccd495d5
master
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{ 16Disable , 17Note , 18Warning , 19Error , 20Fatal , 21Internal 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 ( 31SLANG_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{ 37switch (severity ) 38 { 39case Severity ::Disable : 40return "ignored" ; 41case Severity ::Note : 42return "note" ; 43case Severity ::Warning : 44return "warning" ; 45case Severity ::Error : 46return "error" ; 47case Severity ::Fatal : 48return "fatal error" ; 49case Severity ::Internal : 50return "internal error" ; 51default : 52return "unknown error" ; 53 } 54} 55 56// A structure to be used in static data describing different 57// diagnostic messages. 58struct DiagnosticInfo 59{ 60int id ; 61Severity severity ; 62char const * name ;///< Unique name 63char const * messageFormat ; 64}; 65 66class Diagnostic 67{ 68public : 69String Message ; 70SourceLoc loc ; 71int ErrorID ; 72Severity severity ; 73 74Diagnostic () {ErrorID = -1 ; } 75Diagnostic (const String & msg ,int id ,const SourceLoc & pos ,Severity severity ) 76 :severity (severity ) 77 { 78Message = msg ; 79ErrorID = id ; 80loc = pos ; 81 } 82}; 83 84struct SourceWarningStateTrackerBase :public RefObject 85{ 86virtual 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{ 117printDiagnosticArg (sb ,ptr .Ptr ()); 118} 119 120inline SourceLoc getDiagnosticPos (SourceLoc const & pos ) 121{ 122return pos ; 123} 124 125SourceLoc getDiagnosticPos (Token const & token ); 126 127 128template < typename T > 129SourceLoc getDiagnosticPos (RefPtr < T > const & ptr ) 130{ 131return getDiagnosticPos (ptr .Ptr ()); 132} 133 134struct DiagnosticArg 135{ 136void * data ; 137void (* printFunc )(StringBuilder & ,void * ); 138 139template < typename T > 140struct Helper 141 { 142static void printFunc (StringBuilder & sb ,void * data ) {printDiagnosticArg (sb ,* (T * ) data ); } 143}; 144 145template < typename T > 146DiagnosticArg ( 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 156typedef uint32_t Flags ; 157struct Flag 158{ 159enum Enum : Flags 160{ 161VerbosePath = 0x1 , ///< Will display a more verbose path (if available) - such as a 162///< canonical or absolute path 163SourceLocationLine = 1640x2 , ///< If set will display the location line if source is available 165HumaneLoc = 0x4 , ///< If set will display humane locs (filename/line number) information 166TreatWarningsAsErrors = 0x8 , ///< If set will turn all Warning type messages (after 167///< overrides) into Error type messages 168LanguageServer = 1690x10 , ///< 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 175typedef UnownedStringSlice ( * SourceLocationLexer)( const UnownedStringSlice & text ); 176 177/// Get the total amount of errors that have taken place on this DiagnosticSink 178SLANG_FORCE_INLINE int getErrorCount () { return m_errorCount ; } 179 180template < typename P , typename ... Args > 181bool diagnose (P const & pos , DiagnosticInfo const & info , Args const & ... args ) 182{ 183DiagnosticArg as [] = { DiagnosticArg ( args )...}; 184return diagnoseImpl ( getDiagnosticPos ( pos ), info , sizeof ...( args ), as ); 185} 186 187template < typename P > 188bool diagnose (P const & pos , DiagnosticInfo const & info ) 189{ 190// MSVC gets upset with the zero sized array above, so overload that case here 191return 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...) 196template < typename P , typename ... Args > 197bool diagnoseWithoutSourceView (P const & pos , DiagnosticInfo const & info , Args const & ... args ) 198{ 199const auto fs = this -> getFlags (); 200this -> resetFlag (Flag::SourceLocationLine); 201 202auto result = diagnose (pos, info, args...); 203 204this -> setFlags (fs); 205return result; 206} 207 208// Add a diagnostic with raw text 209// (used when we get errors from a downstream compiler) 210void diagnoseRaw ( Severity severity, char const * message); 211void 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 215void 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 219SlangResult getBlobIfNeeded ( ISlangBlob ** outBlob); 220 221/// Get the source manager used 222SourceManager * getSourceManager () const { return m_sourceManager; } 223/// Set the source manager used for lookup of source locs 224void setSourceManager ( SourceManager * inSourceManager) { m_sourceManager = inSourceManager; } 225 226/// Set the flags 227void setFlags ( Flags flags) { m_flags = flags; } 228/// Get the flags 229Flags getFlags () const { return m_flags; } 230/// Set a flag 231void setFlag ( Flag ::Enum flag) { m_flags |= Flags (flag); } 232/// Reset a flag 233void resetFlag ( Flag ::Enum flag) { m_flags &= ~ Flags (flag); } 234/// Test if flag is set 235bool 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 239void overrideDiagnosticSeverity ( 240int diagnosticId, 241Severity overrideSeverity, 242const 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 247SourceLocationLexer getSourceLocationLexer () const { return m_sourceLocationLexer; } 248 249/// Set the maximum length (in chars) of a source line displayed. Set to 0 for no limit 250void setSourceLineMaxLength ( Index length) { m_sourceLineMaxLength = length; } 251Index getSourceLineMaxLength () const { return m_sourceLineMaxLength; } 252 253/// The parent sink is another sink that will receive diagnostics from this sink. 254void setParentSink ( DiagnosticSink * parentSink) { m_parentSink = parentSink; } 255DiagnosticSink * getParentSink () const { return m_parentSink; } 256 257void setSourceWarningStateTracker ( SourceWarningStateTrackerBase * ptr) 258{ 259m_sourceWarningStateTracker = ptr; 260} 261RefPtr < SourceWarningStateTrackerBase > getSourceWarningStateTracker () const 262{ 263return m_sourceWarningStateTracker; 264} 265 266/// Reset state. 267/// Resets error counts. Resets the output buffer. 268void reset (); 269 270/// Initialize state. 271void init ( SourceManager * sourceManager, SourceLocationLexer sourceLocationLexer); 272 273/// Ctor 274DiagnosticSink (SourceManager * sourceManager, SourceLocationLexer sourceLocationLexer) 275{ 276init (sourceManager, sourceLocationLexer); 277} 278/// Default Ctor 279DiagnosticSink () 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 287StringBuilder outputBuffer; 288/// If a writer is set output will *not* be written to the outputBuffer 289ISlangWriter * writer = nullptr ; 290 291protected : 292// Returns true if a diagnostic is actually written. 293bool diagnoseImpl ( 294SourceLoc const & pos, 295DiagnosticInfo info, 296int argCount, 297DiagnosticArg const * args); 298bool diagnoseImpl ( DiagnosticInfo const & info, const UnownedStringSlice & formattedMessage); 299 300Severity getEffectiveMessageSeverity ( DiagnosticInfo const & info, SourceLoc const & location); 301 302/// If set all diagnostics (as formatted by *this* sink, will be routed to the parent). 303DiagnosticSink * m_parentSink = nullptr ; 304 305int m_errorCount = 0 ; 306int 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. 310Index m_sourceLineMaxLength = 120 ; 311 312Flags m_flags = 0 ; 313 314// The source manager to use when mapping source locations to file+line info 315SourceManager * m_sourceManager = nullptr ; 316 317SourceLocationLexer m_sourceLocationLexer; 318 319// Configuration that allows the user to control the severity of certain diagnostic messages 320Dictionary < int, Severity > m_severityOverrides; 321 322RefPtr < SourceWarningStateTrackerBase > m_sourceWarningStateTracker = nullptr ; 323}; 324 325/// An `ISlangWriter` that writes directly to a diagnostic sink. 326class DiagnosticSinkWriter : public AppendBufferWriter 327{ 328public : 329typedef AppendBufferWriter Super; 330 331DiagnosticSinkWriter (DiagnosticSink * sink) 332: Super( WriterFlag ::IsStatic), m_sink ( sink ) 333{ 334} 335 336// ISlangWriter 337SLANG_NO_THROW virtual SlangResult SLANG_MCALL write ( const char * chars, size_t numChars) 338SLANG_OVERRIDE 339{ 340m_sink -> diagnoseRaw (Severity::Note, UnownedStringSlice (chars, chars + numChars)); 341return SLANG_OK ; 342} 343 344private : 345DiagnosticSink * m_sink = nullptr ; 346}; 347 348class DiagnosticsLookup : public RefObject 349{ 350public : 351static const Index kArenaInitialSize = 65536 ; 352 353/// Will take into account the slice name could be using different conventions 354const DiagnosticInfo * findDiagnosticByName ( const UnownedStringSlice & slice) const; 355/// The name must be as defined in the diagnostics exactly, typically lower camel 356const 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 361const DiagnosticInfo * getDiagnosticById ( Int id) const; 362 363/// info must stay in scope 364Index add ( const DiagnosticInfo * info); 365/// Infos referenced must remain in scope 366void 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. 370void addAlias ( const char * name, const char * diagnosticName); 371 372/// Get the diagnostics held in this lookup 373const List < const DiagnosticInfo *>& getDiagnostics () const { return m_diagnostics ; } 374 375/// Get the associated arena 376MemoryArena & getArena () { return m_arena; } 377 378/// NOTE! diagnostics must stay in scope for lifetime of lookup 379DiagnosticsLookup( const DiagnosticInfo * const * diagnostics, Index diagnosticsCount); 380DiagnosticsLookup(); 381 382protected : 383void _addName ( const char * name, Index diagnosticIndex); 384 385Index _findDiagnosticIndexByExactName ( const UnownedStringSlice & slice) const; 386 387List < const DiagnosticInfo *> m_diagnostics; 388 389StringBuilder m_work; 390Dictionary < UnownedStringSlice, Index > m_nameMap; 391Dictionary < Int, Index > m_idMap; 392 393MemoryArena m_arena; 394}; 395 396 397void outputExceptionDiagnostic ( 398const AbortCompilationException & exception, 399DiagnosticSink & sink, 400slang ::IBlob ** outDiagnostics); 401 402void outputExceptionDiagnostic ( 403const Exception & exception, 404DiagnosticSink & sink, 405slang ::IBlob ** outDiagnostics); 406 407void outputExceptionDiagnostic ( DiagnosticSink & sink, slang ::IBlob ** outDiagnostics); 408 409} // namespace Slang 410 411#endif