@@ -134,6 +134,7 @@ struct DiagnosticRenderer
134134 Int64 number = 0 ;
135135 UnownedStringSlice content;
136136 List<LineHighlight> spans;
137+ bool sourceAvailable = false ;
137138 };
138139
139140 // A collection of nearby HighlightedLines
@@ -303,12 +304,13 @@ struct DiagnosticRenderer
303304 {
304305 HighlightedLine& line = grouped[span.line ];
305306 line.number = span.line ;
306- if (line.content .getLength () == 0 )
307+ if (line.content .getLength () == 0 && !line. sourceAvailable )
307308 {
308309 SourceView* view =
309310 m_sourceManager ? m_sourceManager->findSourceView (span.startLoc ) : nullptr ;
310311 if (view)
311312 {
313+ line.sourceAvailable = true ;
312314 // Get the line content and trim end-of-line characters and trailing whitespace
313315 UnownedStringSlice rawLine = StringUtil::trimEndOfLine (
314316 view->getSourceFile ()->getLineAtIndex (span.line - 1 ));
@@ -347,6 +349,18 @@ struct DiagnosticRenderer
347349 return section;
348350 }
349351
352+ // Returns true if any line in the section has source text available.
353+ // When source is unavailable (e.g. built-in modules), we skip rendering
354+ // the section body to avoid showing empty source lines with underlines.
355+ bool sectionHasSourceAvailable (const SectionLayout& section) const
356+ {
357+ for (const auto & block : section.blocks )
358+ for (const auto & line : block.lines )
359+ if (line.sourceAvailable )
360+ return true ;
361+ return false ;
362+ }
363+
350364 Int64 findCommonIndent (const List<LayoutBlock>& blocks)
351365 {
352366 Index minIndent = std::numeric_limits<Index>::max ();
@@ -504,6 +518,24 @@ struct DiagnosticRenderer
504518 return rows;
505519 }
506520
521+ // When source text is unavailable (e.g. built-in modules), render span
522+ // labels with a compact gutter structure (pipe + closing corner), using a
523+ // minimal gutter width since there are no line numbers to display.
524+ void renderOrphanedLabels (StringBuilder& ss, const SectionLayout& section, Int64 gutterWidth)
525+ {
526+ String gutter =
527+ repeat (' ' , gutterWidth + 1 ) + color (TerminalColor::Cyan, m_glyphs.vertical );
528+ for (const auto & block : section.blocks )
529+ for (const auto & line : block.lines )
530+ for (const auto & span : line.spans )
531+ if (span.label .getLength () > 0 )
532+ ss << gutter << " " << color (TerminalColor::BoldRed, span.label ) << " \n " ;
533+ ss << color (
534+ TerminalColor::Cyan,
535+ repeat (m_glyphs.secondaryUnderline , gutterWidth + 1 ) + m_glyphs.gutterCorner )
536+ << " \n " ;
537+ }
538+
507539 void renderSectionBody (StringBuilder& ss, const SectionLayout& section)
508540 {
509541 for (const auto & block : section.blocks )
@@ -643,36 +675,61 @@ struct DiagnosticRenderer
643675 layout.primaryLoc .line > 0 || layout.primaryLoc .fileName .getLength () > 0 ;
644676 if (hasValidLocation)
645677 {
646- renderLocation (ss, layout.primaryLoc );
647-
648- if (layout.primarySection .blocks .getCount () > 0 )
678+ bool primaryHasSource = sectionHasSourceAvailable (layout.primarySection );
679+ if (primaryHasSource)
680+ {
681+ renderLocation (ss, layout.primaryLoc );
682+ if (layout.primarySection .blocks .getCount () > 0 )
683+ {
684+ ss << repeat (' ' , layout.primarySection .maxGutterWidth + 1 )
685+ << color (TerminalColor::Cyan, m_glyphs.vertical ) << " \n " ;
686+ renderSectionBody (ss, layout.primarySection );
687+ ss << color (
688+ TerminalColor::Cyan,
689+ repeat (
690+ m_glyphs.secondaryUnderline ,
691+ layout.primarySection .maxGutterWidth + 1 ) +
692+ m_glyphs.gutterCorner )
693+ << " \n " ;
694+ }
695+ }
696+ else
649697 {
650- ss << repeat (' ' , layout.primarySection .maxGutterWidth + 1 )
651- << color (TerminalColor::Cyan, m_glyphs.vertical ) << " \n " ;
652- renderSectionBody (ss, layout.primarySection );
653- ss << color (
654- TerminalColor::Cyan,
655- repeat (
656- m_glyphs.secondaryUnderline ,
657- layout.primarySection .maxGutterWidth + 1 ) +
658- m_glyphs.gutterCorner )
659- << " \n " ;
698+ constexpr Int64 kOrphanGutterWidth = 0 ;
699+ DiagnosticLayout::Location loc = layout.primaryLoc ;
700+ loc.gutterIndent = kOrphanGutterWidth ;
701+ renderLocation (ss, loc);
702+ if (layout.primarySection .blocks .getCount () > 0 )
703+ renderOrphanedLabels (ss, layout.primarySection , kOrphanGutterWidth );
660704 }
661705 }
662706 for (const auto & note : layout.notes )
663707 {
664708 ss << " \n " << color (TerminalColor::Cyan, " note" ) << " : " << note.message << " \n " ;
665- renderNoteLocation (ss, note.loc );
666- if (note.section .blocks .getCount () > 0 )
709+ bool noteHasSource = sectionHasSourceAvailable (note.section );
710+ if (noteHasSource)
711+ {
712+ renderNoteLocation (ss, note.loc );
713+ if (note.section .blocks .getCount () > 0 )
714+ {
715+ ss << repeat (' ' , note.section .maxGutterWidth + 1 )
716+ << color (TerminalColor::Cyan, m_glyphs.vertical ) << " \n " ;
717+ renderSectionBody (ss, note.section );
718+ ss << color (
719+ TerminalColor::Cyan,
720+ repeat (m_glyphs.secondaryUnderline , note.section .maxGutterWidth + 1 ) +
721+ m_glyphs.gutterCorner )
722+ << " \n " ;
723+ }
724+ }
725+ else
667726 {
668- ss << repeat (' ' , note.section .maxGutterWidth + 1 )
669- << color (TerminalColor::Cyan, m_glyphs.vertical ) << " \n " ;
670- renderSectionBody (ss, note.section );
671- ss << color (
672- TerminalColor::Cyan,
673- repeat (m_glyphs.secondaryUnderline , note.section .maxGutterWidth + 1 ) +
674- m_glyphs.gutterCorner )
675- << " \n " ;
727+ constexpr Int64 kOrphanGutterWidth = 0 ;
728+ DiagnosticLayout::Location noteLoc = note.loc ;
729+ noteLoc.gutterIndent = kOrphanGutterWidth ;
730+ renderNoteLocation (ss, noteLoc);
731+ if (note.section .blocks .getCount () > 0 )
732+ renderOrphanedLabels (ss, note.section , kOrphanGutterWidth );
676733 }
677734 }
678735 return ss.produceString ();
0 commit comments