yum-mirror/slang
Making it easier to work with shaders
git clone https://git.yummers.dev/yum-mirror/slang
8ccd495d5
master
1// slang-diagnostic-sink.cpp 2#include "slang-diagnostic-sink.h" 3 4#include "../core/slang-char-util.h" 5#include "../core/slang-dictionary.h" 6#include "../core/slang-memory-arena.h" 7#include "../core/slang-string-util.h" 8#include "slang-core-diagnostics.h" 9#include "slang-name-convention-util.h" 10#include "slang-name.h" 11 12namespace Slang 13{ 14 15void printDiagnosticArg (StringBuilder & sb ,char const * str ) 16{ 17sb <<str ; 18} 19 20void printDiagnosticArg (StringBuilder & sb ,int32_t val ) 21{ 22sb <<val ; 23} 24 25void printDiagnosticArg (StringBuilder & sb ,uint32_t val ) 26{ 27sb <<val ; 28} 29 30void printDiagnosticArg (StringBuilder & sb ,int64_t val ) 31{ 32sb <<val ; 33} 34 35void printDiagnosticArg (StringBuilder & sb ,uint64_t val ) 36{ 37sb <<val ; 38} 39 40void printDiagnosticArg (StringBuilder & sb ,double val ) 41{ 42sb <<val ; 43} 44 45void printDiagnosticArg (StringBuilder & sb ,Slang ::String const & str ) 46{ 47sb <<str ; 48} 49 50void printDiagnosticArg (StringBuilder & sb ,Slang ::UnownedStringSlice const & str ) 51{ 52sb .append (str ); 53} 54 55 56void printDiagnosticArg (StringBuilder & sb ,Name * name ) 57{ 58sb <<getText (name ); 59} 60 61 62void printDiagnosticArg (StringBuilder & sb ,TokenType tokenType ) 63{ 64sb <<TokenTypeToString (tokenType ); 65} 66 67void printDiagnosticArg (StringBuilder & sb ,Token const & token ) 68{ 69sb <<token .getContent (); 70} 71 72SourceLoc getDiagnosticPos (Token const & token ) 73{ 74return token .loc ; 75} 76 77// Take the format string for a diagnostic message, along with its arguments, and turn it into a 78static void formatDiagnosticMessage ( 79StringBuilder & sb , 80char const * format , 81int argCount , 82DiagnosticArg const * args ) 83{ 84char const * spanBegin = format ; 85for (;;) 86 { 87char const * spanEnd = spanBegin ; 88while (int c = * spanEnd ) 89 { 90if (c == '$' ) 91break ; 92spanEnd ++ ; 93 } 94 95sb .append (spanBegin ,int (spanEnd - spanBegin )); 96if (!* spanEnd ) 97return ; 98 99SLANG_ASSERT (* spanEnd == '$' ); 100spanEnd ++ ; 101int d = * spanEnd ++ ; 102switch (d ) 103 { 104// A double dollar sign `$$` is used to emit a single `$` 105case '$' : 106sb .append ('$' ); 107break ; 108 109// A single digit means to emit the corresponding argument. 110// TODO: support more than 10 arguments, and add options 111// to control formatting, etc. 112case '0' : 113case '1' : 114case '2' : 115case '3' : 116case '4' : 117case '5' : 118case '6' : 119case '7' : 120case '8' : 121case '9' : 122 { 123int index = d - '0' ; 124if (index >=argCount ) 125 { 126// TODO(tfoley): figure out what a good policy will be for "panic" situations 127// like this 128SLANG_INVALID_OPERATION ("too few arguments for diagnostic message" ); 129 } 130else 131 { 132DiagnosticArg const & arg = args [index ]; 133arg .printFunc (sb ,arg .data ); 134 } 135 } 136break ; 137 138default : 139SLANG_INVALID_OPERATION ("invalid diagnostic message format" ); 140break ; 141 } 142 143spanBegin = spanEnd ; 144 } 145} 146 147static void formatDiagnostic ( 148const HumaneSourceLoc & humaneLoc , 149Diagnostic const & diagnostic , 150DiagnosticSink ::Flags flags , 151StringBuilder & outBuilder ) 152{ 153if (flags & DiagnosticSink ::Flag ::HumaneLoc ) 154 { 155outBuilder <<humaneLoc .pathInfo .foundPath ; 156outBuilder <<"(" ; 157outBuilder <<Int32 (humaneLoc .line ); 158if (flags & DiagnosticSink ::Flag ::LanguageServer ) 159 { 160outBuilder <<", " <<humaneLoc .column ; 161 } 162outBuilder <<"): " ; 163 } 164 165outBuilder <<getSeverityName (diagnostic .severity ); 166 167if ((flags & DiagnosticSink ::Flag ::LanguageServer )|| diagnostic .ErrorID >=0 ) 168 { 169outBuilder <<" " ; 170outBuilder <<diagnostic .ErrorID ; 171 } 172 173outBuilder <<": " ; 174outBuilder <<diagnostic .Message ; 175outBuilder <<"\n" ; 176} 177 178static void _replaceTabWithSpaces (const UnownedStringSlice & slice ,Int tabSize ,StringBuilder & out ) 179{ 180const char * start = slice .begin (); 181const char * const end = slice .end (); 182 183const Index startLength = out .getLength (); 184 185for (const char * cur = start ;cur < end ;cur ++ ) 186 { 187if (* cur == '\t' ) 188 { 189if (start < cur ) 190 { 191out .append (start ,cur ); 192 } 193 194// The amount of spaces we add depends on the current position. 195const Index lastPosition = out .getLength ()- startLength ; 196Index tabPosition = lastPosition ; 197 198// Strip the tabPosition so it's back to the tab stop 199// Special case if tabSize is a power of 2 200if ((tabSize & (tabSize - 1 ))== 0 ) 201 { 202tabPosition = tabPosition & ~Index (tabSize - 1 ); 203 } 204else 205 { 206tabPosition -= tabPosition %tabSize ; 207 } 208 209// Move to next tab 210tabPosition += tabSize ; 211 212// The amount of spaces to simulate the tab 213const Index spacesCount = tabPosition - lastPosition ; 214 215// Add the spaces 216out .appendRepeatedChar (' ' ,spacesCount ); 217 218// Set the start at the first character past 219start = cur + 1 ; 220 } 221 } 222 223if (start < end ) 224 { 225out .append (start ,end ); 226 } 227} 228 229// Given multi-line text, and a position within the text (as a pointer into the memory of text) 230// extract the line that contains pos 231static UnownedStringSlice _extractLineContainingPosition ( 232const UnownedStringSlice & text , 233const char * pos ) 234{ 235SLANG_ASSERT (text .isMemoryContained (pos )); 236 237const char * const contentStart = text .begin (); 238const char * const contentEnd = text .end (); 239 240// We want to determine the start of the line, and the end of the line 241const char * start = pos ; 242for (;start > contentStart ;-- start ) 243 { 244const char c = * start ; 245if (c == '\n' || c == '\r' ) 246 { 247// We want the character after, but we can only do this if not already at pos 248start += int (start < pos ); 249break ; 250 } 251 } 252const char * end = pos ; 253for (;end < contentEnd ;++ end ) 254 { 255const char c = * end ; 256if (c == '\n' || c == '\r' ) 257 { 258break ; 259 } 260 } 261 262return UnownedStringSlice (start ,end ); 263} 264 265static void _reduceLength (Index startIndex ,const UnownedStringSlice & prefix ,StringBuilder & ioBuf ) 266{ 267StringBuilder buf ; 268buf <<prefix ; 269buf .append (ioBuf .getUnownedSlice ().tail (startIndex )); 270ioBuf = buf ; 271} 272 273static void _sourceLocationNoteDiagnostic ( 274DiagnosticSink * sink , 275SourceView * sourceView , 276SourceLoc sourceLoc , 277StringBuilder & sb ) 278{ 279SourceFile * sourceFile = sourceView -> getSourceFile (); 280if (!sourceFile ) 281 { 282return ; 283 } 284 285UnownedStringSlice content = sourceFile -> getContent (); 286 287// Make sure the offset is within content. 288// This is important because it's possible to have a 'SourceFile' that doesn't contain any 289// content (for example when reconstructed via serialization with just line offsets, the actual 290// source text 'content' isn't available). 291const int offset = sourceView -> getRange ().getOffset (sourceLoc ); 292if (offset < 0 || offset >=content .getLength ()) 293 { 294return ; 295 } 296 297// Work out the position of the SourceLoc in the source 298const char * const pos = content .begin ()+ offset ; 299 300UnownedStringSlice line = _extractLineContainingPosition (content ,pos ); 301 302// Trim any trailing white space 303line = UnownedStringSlice (line .begin (),line .trim ().end ()); 304 305// TODO(JS): The tab size should ideally be configurable from command line. 306// For now just go with 4. 307const Index tabSize = 4 ; 308 309StringBuilder sourceLine ; 310StringBuilder caretLine ; 311 312// First work out the sourceLine 313_replaceTabWithSpaces (line ,tabSize ,sourceLine ); 314 315// Now the caretLine which appears underneath the sourceLine 316 { 317// Produce the text up to the caret position (at pos), taking into account tabs 318_replaceTabWithSpaces (UnownedStringSlice (line .begin (),pos ),tabSize ,caretLine ); 319 320// Now make all spaces 321const Index length = caretLine .getLength (); 322caretLine .clear (); 323caretLine .appendRepeatedChar (' ' ,length ); 324 325Index caretIndex = caretLine .getLength (); 326 327// Add caret 328caretLine <<"^" ; 329 330auto lexer = sink -> getSourceLocationLexer (); 331if (lexer ) 332 { 333UnownedStringSlice token = lexer (UnownedStringSlice (pos ,line .end ())); 334 335if (token .getLength ()> 1 ) 336 { 337caretLine .appendRepeatedChar ('~' ,token .getLength ()- 1 ); 338 } 339 } 340 341const Index maxLength = sink -> getSourceLineMaxLength (); 342if (maxLength > 0 ) 343 { 344const UnownedStringSlice ellipsis = UnownedStringSlice ::fromLiteral ("..." ); 345const UnownedStringSlice spaces = UnownedStringSlice ::fromLiteral (" " ); 346SLANG_ASSERT (ellipsis .getLength ()== spaces .getLength ()); 347 348// We use the caretLine length if we have a lexer, because it will have underscores such 349// that it's end is the end of the item at issue. If we don't have the lexer, we 350// guesstimate using 1/4 of the maximum length 351const Index endIndex = lexer ?caretLine .getLength () : (caretIndex + (maxLength /4 )); 352 353if (endIndex > maxLength ) 354 { 355const Index startIndex = endIndex - (maxLength - ellipsis .getLength ()); 356 357_reduceLength (startIndex ,ellipsis ,sourceLine ); 358_reduceLength (startIndex ,spaces ,caretLine ); 359 } 360 361if (sourceLine .getLength ()> maxLength ) 362 { 363StringBuilder buf ; 364buf .append (sourceLine .getUnownedSlice ().head (maxLength - ellipsis .getLength ())); 365buf <<ellipsis ; 366sourceLine = buf ; 367 } 368 } 369 } 370 371// We could have handling here for if the line is too long, that we surround the important 372// section will ellipsis for example. For now we just output. 373 374sb <<sourceLine <<"\n" ; 375sb <<caretLine <<"\n" ; 376} 377 378// Output the length of the token at `sourceLoc`. This is used by language server. 379static void _tokenLengthNoteDiagnostic ( 380DiagnosticSink * sink , 381SourceView * sourceView , 382SourceLoc sourceLoc , 383StringBuilder & sb ) 384{ 385SourceFile * sourceFile = sourceView -> getSourceFile (); 386if (!sourceFile ) 387 { 388return ; 389 } 390 391UnownedStringSlice content = sourceFile -> getContent (); 392 393// Make sure the offset is within content. 394// This is important because it's possible to have a 'SourceFile' that doesn't contain any 395// content (for example when reconstructed via serialization with just line offsets, the actual 396// source text 'content' isn't available). 397const int offset = sourceView -> getRange ().getOffset (sourceLoc ); 398if (offset < 0 || offset >=content .getLength ()) 399 { 400return ; 401 } 402 403// Work out the position of the SourceLoc in the source 404const char * const pos = content .begin ()+ offset ; 405 406UnownedStringSlice line = _extractLineContainingPosition (content ,pos ); 407 408// Trim any trailing white space 409line = UnownedStringSlice (line .begin (),line .trim ().end ()); 410 411auto lexer = sink -> getSourceLocationLexer (); 412if (lexer ) 413 { 414UnownedStringSlice token = lexer (UnownedStringSlice (pos ,line .end ())); 415 416if (token .getLength ()> 1 ) 417 { 418sb <<"^+" <<token .getLength () <<"\n" ; 419 } 420 } 421} 422 423static void formatDiagnostic (DiagnosticSink * sink ,Diagnostic const & diagnostic ,StringBuilder & sb ) 424{ 425auto sourceManager = sink -> getSourceManager (); 426 427SourceView * sourceView = nullptr ; 428HumaneSourceLoc humaneLoc ; 429const auto sourceLoc = diagnostic .loc ; 430 { 431if (sourceManager ) 432 { 433sourceView = sourceManager -> findSourceViewRecursively (sourceLoc ); 434if (sourceView ) 435 { 436humaneLoc = sourceView -> getHumaneLoc (sourceLoc ); 437 } 438 } 439 440formatDiagnostic (humaneLoc ,diagnostic ,sink -> getFlags (),sb ); 441 442 { 443SourceView * currentView = sourceView ; 444 445while (currentView && currentView -> getInitiatingSourceLoc ().isValid ()&& 446currentView -> getSourceFile ()-> getPathInfo ().type == PathInfo ::Type ::TokenPaste ) 447 { 448SourceView * initiatingView = 449sourceManager 450 ?sourceManager -> findSourceView (currentView -> getInitiatingSourceLoc ()) 451 :nullptr ; 452if (initiatingView == nullptr ) 453 { 454break ; 455 } 456 457const DiagnosticInfo & diagnosticInfo = MiscDiagnostics ::seeTokenPasteLocation ; 458 459// Turn the message format into a message. For the moment it assumes no parameters. 460StringBuilder msg ; 461formatDiagnosticMessage (msg ,diagnosticInfo .messageFormat ,0 ,nullptr ); 462 463// Set up the diagnostic. 464Diagnostic initiationDiagnostic ; 465initiationDiagnostic .ErrorID = diagnosticInfo .id ; 466initiationDiagnostic .Message = msg .produceString (); 467initiationDiagnostic .loc = sourceView -> getInitiatingSourceLoc (); 468initiationDiagnostic .severity = diagnosticInfo .severity ; 469 470// TODO(JS): 471// Not 100% clear what the best sourceLoc type is most useful here - we will go 472// with default for now 473HumaneSourceLoc pasteHumaneLoc = 474initiatingView -> getHumaneLoc (sourceView -> getInitiatingSourceLoc ()); 475 476// Okay we should output where the token paste took place 477formatDiagnostic (pasteHumaneLoc ,initiationDiagnostic ,sink -> getFlags (),sb ); 478 479// Make the initiatingView the current view 480currentView = initiatingView ; 481 } 482 } 483 } 484 485// If we are a language server, output additional token length info. 486if (sourceView && sink -> isFlagSet (DiagnosticSink ::Flag ::LanguageServer )) 487 { 488_tokenLengthNoteDiagnostic (sink ,sourceView ,sourceLoc ,sb ); 489 } 490 491if (sourceView && sink -> isFlagSet (DiagnosticSink ::Flag ::SourceLocationLine )&& 492diagnostic .loc .isValid ()) 493 { 494_sourceLocationNoteDiagnostic (sink ,sourceView ,sourceLoc ,sb ); 495 } 496 497if (sourceView && sink -> isFlagSet (DiagnosticSink ::Flag ::VerbosePath )) 498 { 499auto actualHumaneLoc = sourceView -> getHumaneLoc (diagnostic .loc ,SourceLocType ::Actual ); 500 501// Look up the path verbosely (will get the canonical path if necessary) 502actualHumaneLoc .pathInfo .foundPath = sourceView -> getSourceFile ()-> calcVerbosePath (); 503 504// Only output if it's actually different 505if (actualHumaneLoc .pathInfo .foundPath != humaneLoc .pathInfo .foundPath || 506actualHumaneLoc .line != humaneLoc .line || actualHumaneLoc .column != humaneLoc .column ) 507 { 508formatDiagnostic (actualHumaneLoc ,diagnostic ,sink -> getFlags (),sb ); 509 } 510 } 511} 512 513void DiagnosticSink ::init (SourceManager * sourceManager ,SourceLocationLexer sourceLocationLexer ) 514{ 515m_errorCount = 0 ; 516m_internalErrorLocsNoted = 0 ; 517 518m_sourceManager = sourceManager ; 519m_sourceLocationLexer = sourceLocationLexer ; 520m_sourceLineMaxLength = 0 ; 521 522m_flags = Flag ::HumaneLoc ; 523 524// If we have a source location lexer, we'll by default enable source location output 525if (sourceLocationLexer ) 526 { 527setFlag (Flag ::SourceLocationLine ); 528 } 529} 530 531void DiagnosticSink ::reset () 532{ 533m_errorCount = 0 ; 534m_internalErrorLocsNoted = 0 ; 535 536outputBuffer .clear (); 537} 538 539 540void DiagnosticSink ::noteInternalErrorLoc (SourceLoc const & loc ) 541{ 542// Don't consider invalid source locations. 543if (!loc .isValid ()) 544return ; 545 546if (m_parentSink ) 547 { 548m_parentSink -> noteInternalErrorLoc (loc ); 549 } 550 551// If this is the first source location being noted, 552// then emit a message to help the user isolate what 553// code might have confused the compiler. 554if (m_internalErrorLocsNoted == 0 ) 555 { 556diagnose (loc ,MiscDiagnostics ::noteLocationOfInternalError ); 557 } 558m_internalErrorLocsNoted ++ ; 559} 560 561SlangResult DiagnosticSink ::getBlobIfNeeded (ISlangBlob ** outBlob ) 562{ 563// If the client doesn't want an output blob, there is nothing to do. 564// 565if (!outBlob ) 566return SLANG_OK ; 567 568// For outputBuffer to be valid and hold diagnostics, writer must not be set 569SLANG_ASSERT (writer == nullptr ); 570 571// If there were no errors, and there was no diagnostic output, there is nothing to do. 572if (getErrorCount ()== 0 && outputBuffer .getLength ()== 0 ) 573 { 574return SLANG_OK ; 575 } 576 577Slang ::ComPtr < ISlangBlob > blob = Slang ::StringUtil ::createStringBlob (outputBuffer ); 578* outBlob = blob .detach (); 579 580return SLANG_OK ; 581} 582 583bool DiagnosticSink ::diagnoseImpl ( 584DiagnosticInfo const & info , 585const UnownedStringSlice & formattedMessage ) 586{ 587if (info .severity >=Severity ::Error ) 588 { 589m_errorCount ++ ; 590 } 591 592if (writer ) 593 { 594writer -> write (formattedMessage .begin (),formattedMessage .getLength ()); 595 } 596else 597 { 598outputBuffer .append (formattedMessage ); 599 } 600 601if (m_parentSink ) 602 { 603m_parentSink -> diagnoseImpl (info ,formattedMessage ); 604 } 605 606if (info .severity >=Severity ::Fatal ) 607 { 608// TODO: figure out a better policy for aborting compilation 609 std::string message (formattedMessage .begin (),formattedMessage .end ()); 610SLANG_ABORT_COMPILATION (message .c_str ()); 611 } 612return true; 613} 614 615Severity DiagnosticSink ::getEffectiveMessageSeverity ( 616DiagnosticInfo const & info , 617SourceLoc const & location ) 618{ 619Severity effectiveSeverity = info .severity ; 620 621if (effectiveSeverity <=Severity ::Warning && m_sourceWarningStateTracker ) 622 { 623effectiveSeverity = m_sourceWarningStateTracker -> consumeWarningSeverity ( 624location , 625info .id , 626effectiveSeverity ); 627 } 628 629Severity * pSeverityOverride = m_severityOverrides .tryGetValue (info .id ); 630 631// See if there is an override 632if (pSeverityOverride ) 633 { 634// Override the current severity, but don't allow lowering it if it's Error or Fatal 635if (effectiveSeverity < Severity ::Error || * pSeverityOverride >=effectiveSeverity ) 636effectiveSeverity = * pSeverityOverride ; 637 } 638 639if (isFlagSet (Flag ::TreatWarningsAsErrors )&& effectiveSeverity == Severity ::Warning ) 640effectiveSeverity = Severity ::Error ; 641 642return effectiveSeverity ; 643} 644 645bool DiagnosticSink ::diagnoseImpl ( 646SourceLoc const & pos , 647DiagnosticInfo info , 648int argCount , 649DiagnosticArg const * args ) 650{ 651// Override the severity in the 'info' structure to pass it further into formatDiagnostics 652info .severity = getEffectiveMessageSeverity (info ,pos ); 653 654if (info .severity == Severity ::Disable ) 655return false; 656 657StringBuilder messageBuilder ; 658 { 659StringBuilder sb ; 660formatDiagnosticMessage (sb ,info .messageFormat ,argCount ,args ); 661 662Diagnostic diagnostic ; 663diagnostic .ErrorID = info .id ; 664diagnostic .Message = sb .produceString (); 665diagnostic .loc = pos ; 666diagnostic .severity = info .severity ; 667 668// If so, pass the error string along to them 669formatDiagnostic (this ,diagnostic ,messageBuilder ); 670 } 671 672return diagnoseImpl (info ,messageBuilder .getUnownedSlice ()); 673} 674 675void DiagnosticSink ::diagnoseRaw (Severity severity ,char const * message ) 676{ 677return diagnoseRaw (severity ,UnownedStringSlice (message )); 678} 679 680void DiagnosticSink ::diagnoseRaw (Severity severity ,const UnownedStringSlice & message ) 681{ 682if (severity >=Severity ::Error ) 683 { 684m_errorCount ++ ; 685 } 686 687// Did the client supply a callback for us to use? 688if (writer ) 689 { 690// If so, pass the error string along to them. 691writer -> write (message .begin (),message .getLength ()); 692 } 693else 694 { 695// If the user doesn't have a callback, then just 696// collect our diagnostic messages into a buffer. 697outputBuffer .append (message ); 698 } 699 700if (m_parentSink ) 701 { 702m_parentSink -> diagnoseRaw (severity ,message ); 703 } 704 705if (severity >=Severity ::Fatal ) 706 { 707// TODO: figure out a better policy for aborting compilation 708SLANG_ABORT_COMPILATION ("" ); 709 } 710} 711 712void DiagnosticSink ::overrideDiagnosticSeverity ( 713int diagnosticId , 714Severity overrideSeverity , 715const DiagnosticInfo * info ) 716{ 717if (info ) 718 { 719SLANG_ASSERT (info -> id == diagnosticId ); 720 721// If the override is the same as the default, we can just remove the override 722if (info -> severity == overrideSeverity ) 723 { 724m_severityOverrides .remove (diagnosticId ); 725return ; 726 } 727 } 728 729// Set the override 730m_severityOverrides [diagnosticId ]= overrideSeverity ; 731} 732 733/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! DiagnosticLookup 734* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ 735 736Index DiagnosticsLookup ::_findDiagnosticIndexByExactName (const UnownedStringSlice & slice )const 737{ 738const Index * indexPtr = m_nameMap .tryGetValue (slice ); 739return indexPtr ?* indexPtr :-1 ; 740} 741 742void DiagnosticsLookup ::_addName (const char * name ,Index diagnosticIndex ) 743{ 744UnownedStringSlice nameSlice (name ); 745m_nameMap .add (nameSlice ,diagnosticIndex ); 746} 747 748void DiagnosticsLookup ::addAlias (const char * name ,const char * diagnosticName ) 749{ 750const Index index = _findDiagnosticIndexByExactName (UnownedStringSlice (diagnosticName )); 751SLANG_ASSERT (index >=0 ); 752if (index >=0 ) 753 { 754_addName (name ,index ); 755 } 756} 757 758const DiagnosticInfo * DiagnosticsLookup ::getDiagnosticById (Int id )const 759{ 760const auto indexPtr = m_idMap .tryGetValue (id ); 761return indexPtr ?m_diagnostics [* indexPtr ] :nullptr ; 762} 763 764const DiagnosticInfo * DiagnosticsLookup ::findDiagnosticByExactName ( 765const UnownedStringSlice & slice )const 766{ 767const Index * indexPtr = m_nameMap .tryGetValue (slice ); 768return indexPtr ?m_diagnostics [* indexPtr ] :nullptr ; 769} 770 771const DiagnosticInfo * DiagnosticsLookup ::findDiagnosticByName (const UnownedStringSlice & slice )const 772{ 773const auto convention = NameConventionUtil ::inferConventionFromText (slice ); 774switch (convention ) 775 { 776case NameConvention ::Invalid : 777return nullptr ; 778case NameConvention ::LowerCamel : 779return findDiagnosticByExactName (slice ); 780default : 781break ; 782 } 783 784StringBuilder buf ; 785NameConventionUtil ::convert (getNameStyle (convention ),slice ,NameConvention ::LowerCamel ,buf ); 786 787return findDiagnosticByExactName (buf .getUnownedSlice ()); 788} 789 790Index DiagnosticsLookup ::add (const DiagnosticInfo * info ) 791{ 792// Check it's not already added 793SLANG_ASSERT (m_diagnostics .indexOf (info )< 0 ); 794 795const Index diagnosticIndex = m_diagnostics .getCount (); 796m_diagnostics .add (info ); 797 798_addName (info -> name ,diagnosticIndex ); 799m_idMap .addIfNotExists (info -> id ,diagnosticIndex ); 800 801return diagnosticIndex ; 802} 803 804void DiagnosticsLookup ::add (const DiagnosticInfo * const * infos ,Index infosCount ) 805{ 806for (Index i = 0 ;i < infosCount ;++ i ) 807 { 808add (infos [i ]); 809 } 810} 811 812DiagnosticsLookup ::DiagnosticsLookup () 813 :m_arena (kArenaInitialSize ) 814{ 815} 816 817DiagnosticsLookup ::DiagnosticsLookup ( 818const DiagnosticInfo * const * diagnostics , 819Index diagnosticsCount ) 820 :m_arena (kArenaInitialSize ) 821{ 822// TODO: We should eventually have a more formal system for associating individual 823// diagnostics, or groups of diagnostics, with user-exposed names for use when 824// enabling/disabling warnings (or turning warnings into errors, etc.). 825// 826// For now we build a map from diagnostic name to it's entry. 827 828add (diagnostics ,diagnosticsCount ); 829} 830 831void outputExceptionDiagnostic ( 832const AbortCompilationException & exception , 833DiagnosticSink & sink , 834 slang::IBlob ** outDiagnostics ) 835{ 836sink .diagnoseRaw (Severity ::Error ,exception .Message .getUnownedSlice ()); 837sink .getBlobIfNeeded (outDiagnostics ); 838} 839 840void outputExceptionDiagnostic ( 841const Exception & exception , 842DiagnosticSink & sink , 843 slang::IBlob ** outDiagnostics ) 844{ 845try 846 { 847sink .diagnoseRaw (Severity ::Internal ,exception .Message .getUnownedSlice ()); 848 } 849catch (const AbortCompilationException & ) 850 { 851// Catch and ignore the AbortCompilationException that diagnoseRaw throws 852// for Internal severity to prevent exception leak from loadModule 853 } 854sink .getBlobIfNeeded (outDiagnostics ); 855} 856 857void outputExceptionDiagnostic (DiagnosticSink & sink , slang::IBlob ** outDiagnostics ) 858{ 859try 860 { 861sink .diagnoseRaw (Severity ::Fatal ,"An unknown exception occurred" ); 862 } 863catch (const AbortCompilationException & ) 864 { 865// Catch and ignore the AbortCompilationException that diagnoseRaw throws 866// for Fatal severity to prevent exception leak from loadModule 867 } 868sink .getBlobIfNeeded (outDiagnostics ); 869} 870 871 872}// namespace Slang