tamer: diagnose: Additional documentation
I had waited to provide more documentation until I was sure that the abstraction was not going to change significantly; there was a lot of refactoring in prior commits. DEV-12151main
parent
fc1dad8483
commit
75b966c577
|
@ -22,6 +22,58 @@
|
|||
//! This system is heavily motivated by Rust's.
|
||||
//! While the data structures and organization may differ,
|
||||
//! the diagnostic output is visually similar.
|
||||
//!
|
||||
//! Visual Report
|
||||
//! -------------
|
||||
//! The primary output of the system is a [`Report`](report::Report).
|
||||
//! A report consists not only of the diagnostic information provided by the
|
||||
//! system
|
||||
//! (such as error messages),
|
||||
//! but also annotated source code.
|
||||
//! Reports attempt to create a narrative that walks the user through why
|
||||
//! and how problems arose,
|
||||
//! and hopefully provides information on how to resolve the problem.
|
||||
//!
|
||||
//! Here is an example report:
|
||||
//!
|
||||
//! ```text
|
||||
//! error: expected closing tag for `classify`
|
||||
//! --> /home/user/program/foo.xml:16:5
|
||||
//! |
|
||||
//! 16 | <classify as="foo" desc="Example classification">
|
||||
//! | --------- note: element `classify` is opened here
|
||||
//!
|
||||
//! --> /home/user/program/foo.xml:24:5
|
||||
//! |
|
||||
//! 24 | </wrong>
|
||||
//! | ^^^^^^^^ error: expected `</classify>`
|
||||
//! ```
|
||||
//!
|
||||
//! A single report is produced for each error
|
||||
//! (or other suitable diagnostic event).
|
||||
//! The system may produce multiple reports at a time if it discovers
|
||||
//! multiple issues and is able to recover enough to continue to discover
|
||||
//! others.
|
||||
//!
|
||||
//! Each report is separated into a number of sections,
|
||||
//! with each section delimited by a header.
|
||||
//! Each section describes one or more spans related to a range of related
|
||||
//! source lines.
|
||||
//! Those source lines are annotated using the [`Span`]s associated with the
|
||||
//! emitted diagnostic data,
|
||||
//! such as an error.
|
||||
//!
|
||||
//! Each section has a _gutter_ containing the line number of source lines.
|
||||
//! The area below the section header and to the right of the gutter is
|
||||
//! called the _body_ of the section.
|
||||
//! The gutter will expand as needed to fit the line number.
|
||||
//!
|
||||
//! _Warning: Reports do not yet strip terminal escape sequences,
|
||||
//! which may interfere with the diagnostic output.
|
||||
//! This may represent a security risk depending on your threat model,
|
||||
//! but does require access to the source code being compiled._
|
||||
//!
|
||||
//! See the [`report`] module for more information.
|
||||
|
||||
mod report;
|
||||
mod resolver;
|
||||
|
|
|
@ -17,7 +17,13 @@
|
|||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Rendering of diagnostic information.
|
||||
//! Rendering of diagnostic report.
|
||||
//!
|
||||
//! This module is responsible for [resolving](super::resolver) and
|
||||
//! rendering spans into a formatted [`Report`],
|
||||
//! which in turn can be rendered into a string with [`Display::fmt`].
|
||||
//!
|
||||
//! See the [parent module](super) for more summary information.
|
||||
|
||||
// NB: `write!` together with `\n` is preferred to `writeln!` so that there
|
||||
// is only a single sequence of characters to search for while tracking
|
||||
|
@ -38,6 +44,10 @@ use std::{
|
|||
ops::Add,
|
||||
};
|
||||
|
||||
/// Render a [`Report`] with detailed diagnostic information.
|
||||
///
|
||||
/// Rendering of reports is layered---this
|
||||
/// report can be further rendered into a string using [`Display::fmt`].
|
||||
pub trait Reporter {
|
||||
/// Render diagnostic report.
|
||||
///
|
||||
|
@ -199,15 +209,49 @@ impl<'d, S: ResolvedSpanData> MaybeResolvedSpan<'d, S> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Diagnostic report.
|
||||
///
|
||||
/// This contains the raw data that,
|
||||
/// when rendered with [`Display::fmt`],
|
||||
/// will produce a robust report to help guide the user through diagnosing
|
||||
/// and resolving problems.
|
||||
#[derive(Debug)]
|
||||
pub struct Report<'d, D: Diagnostic> {
|
||||
/// Summary message of the contents of the report.
|
||||
///
|
||||
/// This message should be suitable on its own,
|
||||
/// e.g. a typical one-line error message.
|
||||
msg: Message<'d, D>,
|
||||
secs: Vec<Section<'d>>,
|
||||
|
||||
/// The largest severity level found within all of the [`Section`]s of
|
||||
/// the report.
|
||||
///
|
||||
/// This level should be used alongside the summary message to describe
|
||||
/// how severe of a problem this report represents.
|
||||
level: Level,
|
||||
|
||||
/// Sections of the report.
|
||||
///
|
||||
/// A section contains a header describing a location in the source
|
||||
/// code (line and column numbers),
|
||||
/// followed by annotated source code and descriptive labels.
|
||||
secs: Vec<Section<'d>>,
|
||||
|
||||
/// The maximum line number encountered in each of the [`Section`]s of
|
||||
/// the report.
|
||||
///
|
||||
/// This number is used to determine the gutter width,
|
||||
/// which contains the line numbers of the annotated source lines.
|
||||
/// It can be propagated to all [`Section`]s using
|
||||
/// [`normalize_gutters`](Report::normalize_gutters).
|
||||
line_max: NonZeroU32,
|
||||
}
|
||||
|
||||
impl<'d, D: Diagnostic> Report<'d, D> {
|
||||
/// Create an empty report.
|
||||
///
|
||||
/// To add to the body of the report,
|
||||
/// use [`Extend::extend`].
|
||||
fn empty(msg: Message<'d, D>) -> Self {
|
||||
Self {
|
||||
msg,
|
||||
|
@ -229,6 +273,12 @@ impl<'d, D: Diagnostic> Report<'d, D> {
|
|||
}
|
||||
|
||||
impl<'d, D: Diagnostic> Extend<Section<'d>> for Report<'d, D> {
|
||||
/// Extend the body of the report.
|
||||
///
|
||||
/// This tracks the most severe [`Level`] and highest line number seen.
|
||||
/// Further,
|
||||
/// adjacent sections may be squashed if they meet certain criteria
|
||||
/// (see [`Section::maybe_squash_into`]).
|
||||
fn extend<T: IntoIterator<Item = Section<'d>>>(&mut self, secs: T) {
|
||||
for sec in secs {
|
||||
self.level = self.level.min(sec.level());
|
||||
|
@ -258,6 +308,7 @@ impl<'d, D: Diagnostic> Display for Report<'d, D> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Summary diagnostic message.
|
||||
#[derive(Debug)]
|
||||
struct Message<'d, D: Diagnostic>(&'d D);
|
||||
|
||||
|
@ -281,10 +332,27 @@ impl<'d, D: Diagnostic> Display for Message<'d, D> {
|
|||
/// messages quickly.
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
struct Section<'d> {
|
||||
/// Heading that delimits the beginning of each section.
|
||||
///
|
||||
/// The heading describes the location of its principal [`Span`].
|
||||
heading: SpanHeading,
|
||||
|
||||
/// The most severe [`Level`] encountered in this section body.
|
||||
level: Level,
|
||||
|
||||
/// The principal [`Span`] that this section describes.
|
||||
///
|
||||
/// If a section contains information about multiple spans,
|
||||
/// this represents the one that the user should focus on.
|
||||
span: Span,
|
||||
|
||||
/// Annotated source lines and labels.
|
||||
body: Vec<SectionLine<'d>>,
|
||||
|
||||
/// The largest line number encountered in this section.
|
||||
///
|
||||
/// This is used to determine how wide to render the gutter,
|
||||
/// which contain the line numbers for source lines.
|
||||
line_max: NonZeroU32,
|
||||
}
|
||||
|
||||
|
@ -598,9 +666,25 @@ impl Display for HeadingColNum {
|
|||
/// Line of output in a [`Section`] body.
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
enum SectionLine<'d> {
|
||||
/// Padding for a possibly annotated source line.
|
||||
///
|
||||
/// A padding line is intended to add extra space above or below a
|
||||
/// source line to make it easier to read.
|
||||
/// Padding lines contain a gutter,
|
||||
/// but no line number.
|
||||
SourceLinePadding,
|
||||
|
||||
/// A line of source code.
|
||||
SourceLine(SectionSourceLine),
|
||||
|
||||
/// Source line annotations
|
||||
/// (marks and labels).
|
||||
SourceLineMark(LineMark<'d>),
|
||||
|
||||
/// A label that is not rendered as a line annotation.
|
||||
///
|
||||
/// Footnotes are intended too appear at the end of a [`Section`] and
|
||||
/// contain supplemental information.
|
||||
Footnote(Level, Label<'d>),
|
||||
}
|
||||
|
||||
|
@ -637,6 +721,10 @@ impl<'d> SectionLine<'d> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Attempt to convert a line into a footnote.
|
||||
///
|
||||
/// If there is no [`Level`] and [`Label`] available,
|
||||
/// [`None`] is returned.
|
||||
fn into_footnote(self) -> Option<Self> {
|
||||
match self {
|
||||
Self::SourceLinePadding => None,
|
||||
|
@ -661,6 +749,7 @@ impl<'d> Display for SectionLine<'d> {
|
|||
}
|
||||
}
|
||||
|
||||
/// A [`SourceLine`] displayed within a [`Section`].
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
struct SectionSourceLine(SourceLine);
|
||||
|
||||
|
@ -684,6 +773,12 @@ impl Display for SectionSourceLine {
|
|||
|
||||
/// A type of line annotation that marks columns and provides labels,
|
||||
/// if available.
|
||||
///
|
||||
/// Marks are displayed below a [`SectionSourceLine`] and are intended to
|
||||
/// visually display a [`Span`].
|
||||
/// Column resolution
|
||||
/// (see [`super::resolver`])
|
||||
/// exists primarily for mark rendering.
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
struct LineMark<'d> {
|
||||
level: Level,
|
||||
|
@ -715,13 +810,14 @@ impl<'d> Display for LineMark<'d> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Mark styling.
|
||||
trait MarkChar {
|
||||
/// Character used to underline the columns applicable to a given span
|
||||
/// underneath a source line.
|
||||
fn mark_char(&self) -> char;
|
||||
}
|
||||
|
||||
impl MarkChar for Level {
|
||||
/// Character used to underline the columns applicable to a given span
|
||||
/// underneath a source line.
|
||||
fn mark_char(&self) -> char {
|
||||
match self {
|
||||
Level::InternalError => '!',
|
||||
|
|
Loading…
Reference in New Issue