Skip to content

Commit

Permalink
Continue implementing diagnostics infrastructure
Browse files Browse the repository at this point in the history
  • Loading branch information
DCNick3 committed Oct 22, 2023
1 parent ea5f751 commit efe0e8b
Show file tree
Hide file tree
Showing 15 changed files with 362 additions and 132 deletions.
31 changes: 31 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion junk/src/buffer_parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ struct SpriteVertex {
#[derive(BinRead, Debug)]
#[brw(little)]
struct SpriteVertices {
#[br(parse_with = binrw::until_eof)]
#[br(parse_with = binrw::helpers::until_eof)]
pub vertices: Vec<SpriteVertex>,
}

Expand Down
3 changes: 3 additions & 0 deletions shin-asm/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@ rustc-hash = "1.1.0"
smol_str = "0.2.0"
la-arena = "0.3.1"

strip-ansi-escapes = "0.2.0"
itertools = "0.11.0"

binrw = { workspace = true }

unicode-xid = "0.2.4"
Expand Down
4 changes: 4 additions & 0 deletions shin-asm/src/compile/db.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,13 @@ pub struct Jar(
file::Program,
diagnostics::SourceDiagnosticAccumulator,
diagnostics::HirDiagnosticAccumulator,
diagnostics::char_map,
def_map::build_def_map,
hir::HirBlockBodies,
hir::HirBlockBodies_get_block,
hir::HirBlockBodySourceMaps,
hir::HirBlockBodySourceMaps_get_block,
hir::collect_file_bodies_with_source_maps,
hir::collect_file_bodies,
);

Expand Down
116 changes: 95 additions & 21 deletions shin-asm/src/compile/diagnostics.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
use crate::compile::from_hir::{HirBlockId, HirId, HirIdWithBlock};
use crate::compile::{Db, File, MakeWithFile, WithFile};
use std::collections::hash_map::Entry;

use std::fmt::Debug;
use std::fmt::{Debug, Display};

use ariadne::Span as _;
use ariadne::{Source, Span as _};
use rustc_hash::FxHashMap;
use text_size::TextRange;

/// A text range associated with a file. Fully identifies a span of text in the program. Final form of the diagnostic location
Expand All @@ -18,49 +20,77 @@ impl Span {
pub fn file(&self) -> File {
self.0.file
}

pub fn to_char_span(&self, db: &dyn Db) -> CharSpan {
let file = self.file();
let char_map = char_map(db, file);
let start: usize = self.0.value.start().into();
let end: usize = self.0.value.end().into();
let range = (char_map[start], char_map[end]);
CharSpan(WithFile::new(range, file))
}
}

#[salsa::tracked]
pub fn char_map(db: &dyn Db, file: File) -> Vec<u32> {
// stolen from https://github.com/apollographql/apollo-rs/pull/668/files#diff-19fd09cc90a56224f51027101143c2190b9b913993ae35727bfbde19b96f87f7R24
let contents = file.contents(db);
let mut map = vec![0; contents.len() + 1];
let mut char_index = 0;
for (byte_index, _) in contents.char_indices() {
map[byte_index] = char_index;
char_index += 1;
}

// Support 1 past the end of the string, for use in exclusive ranges.
map[contents.len()] = char_index;

map
}

impl ariadne::Span for Span {
/// Same as [`Span`], but in terms of characters instead of bytes. This is required until https://github.com/zesterer/ariadne/issues/8 is fixed
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
pub struct CharSpan(WithFile<(u32, u32)>);

impl ariadne::Span for CharSpan {
type SourceId = File;

fn source(&self) -> &Self::SourceId {
&self.0.file
}

fn start(&self) -> usize {
self.0.value.start().into()
self.0.value.0 as usize
}

fn end(&self) -> usize {
self.0.value.end().into()
self.0.value.1 as usize
}
}

trait DiagnosticLocation: Debug + Copy + 'static {
type Context<'a>: Copy;

fn span(&self, context: Self::Context<'_>) -> Span;
fn span(&self, db: &dyn Db) -> Span;
}

impl DiagnosticLocation for Span {
type Context<'a> = ();
fn span(&self, _: Self::Context<'_>) -> Span {
fn span(&self, _: &dyn Db) -> Span {
*self
}
}

pub type HirLocation = WithFile<HirIdWithBlock>;

impl DiagnosticLocation for HirLocation {
type Context<'a> = &'a dyn Db;
fn span(&self, db: &dyn Db) -> Span {
let &WithFile { file, value: node } = self;

fn span(&self, _db: Self::Context<'_>) -> Span {
let WithFile {
file: _file,
value: _node,
} = self;
let (_, maps) = collect_file_bodies_with_source_maps(db, file);
let map = match node.block_id {
HirBlockId::Block(block) => maps.get_block(db, block).unwrap(),
HirBlockId::Alias(_) => todo!(),
};

todo!("Collect HIR source maps and use them to get the location")
Span::new(file, map.get_text_range(node.id).unwrap())
}
}

Expand Down Expand Up @@ -109,6 +139,7 @@ macro_rules! make_diagnostic {
$crate::compile::diagnostics::Diagnostic::new(format!($($fmt),+), $span)
};
}
use crate::compile::hir::collect_file_bodies_with_source_maps;
pub(crate) use make_diagnostic;

impl Diagnostic<TextRange> {
Expand All @@ -117,6 +148,12 @@ impl Diagnostic<TextRange> {
}
}

impl Diagnostic<Span> {
pub fn into_ariadne(self, db: &dyn Db) -> ariadne::Report<'static, CharSpan> {
lower_diagnostic_into_ariadne(db, self)
}
}

impl Diagnostic<HirId> {
pub fn in_block(self, block: impl Into<HirBlockId>) -> Diagnostic<HirIdWithBlock> {
let block = block.into();
Expand All @@ -130,11 +167,21 @@ impl Diagnostic<HirIdWithBlock> {
}
}

impl Diagnostic<HirLocation> {
pub fn into_source(self, db: &dyn Db) -> Diagnostic<Span> {
self.map_location(|location| location.span(db))
}

pub fn into_ariadne(self, db: &dyn Db) -> ariadne::Report<'static, CharSpan> {
lower_diagnostic_into_ariadne(db, self)
}
}

fn lower_diagnostic_into_ariadne<L: DiagnosticLocation>(
context: L::Context<'_>,
db: &dyn Db,
diagnostic: Diagnostic<L>,
) -> ariadne::Report<'static, Span> {
let span = diagnostic.location.span(context);
) -> ariadne::Report<'static, CharSpan> {
let span = diagnostic.location.span(db).to_char_span(db);

ariadne::Report::build(ariadne::ReportKind::Error, *span.source(), span.start())
.with_message(diagnostic.message)
Expand All @@ -144,13 +191,40 @@ fn lower_diagnostic_into_ariadne<L: DiagnosticLocation>(
.additional_labels
.into_iter()
.map(|(message, location)| {
let span = location.span(context);
let span = location.span(db).to_char_span(db);
ariadne::Label::new(span).with_message(message)
}),
)
.finish()
}

pub struct AriadneDbCache<'db> {
db: &'db dyn Db,
sources: FxHashMap<File, Source>,
}

impl<'db> AriadneDbCache<'db> {
pub fn new(db: &'db dyn Db) -> Self {
Self {
db,
sources: FxHashMap::default(),
}
}
}

impl ariadne::Cache<File> for AriadneDbCache<'_> {
fn fetch(&mut self, &id: &File) -> Result<&Source, Box<dyn Debug + '_>> {
Ok(match self.sources.entry(id) {
Entry::Occupied(entry) => entry.into_mut(),
Entry::Vacant(entry) => entry.insert(Source::from(id.contents(self.db))),
})
}

fn display<'a>(&self, &id: &'a File) -> Option<Box<dyn Display + 'a>> {
Some(Box::new(id.path(self.db)))
}
}

impl Diagnostic<Span> {
pub fn emit(self, db: &dyn Db) {
SourceDiagnosticAccumulator::push(db, self)
Expand Down
4 changes: 2 additions & 2 deletions shin-asm/src/compile/from_hir.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,8 @@ pub struct HirIdWithBlock {
// TODO: naming is unclear...
// InBlock -> identifies inside a block or HirId wrapped with a block id (like InFile)
// Probably should rename the InFile to WithFile
id: HirId,
block_id: HirBlockId,
pub id: HirId,
pub block_id: HirBlockId,
}

impl HirIdWithBlock {
Expand Down
2 changes: 1 addition & 1 deletion shin-asm/src/compile/hir/from_ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -178,7 +178,7 @@ impl HirBlockCollector {
instructions: self.instructions,
},
BlockSourceMap {
exprs_source_map: self.exprs_source_map,
expressions_source_map: self.exprs_source_map,
instructions_source_map: self.instructions_source_map,
},
self.diagnostics,
Expand Down
29 changes: 4 additions & 25 deletions shin-asm/src/compile/hir/lower/block.rs
Original file line number Diff line number Diff line change
Expand Up @@ -115,38 +115,17 @@ impl LoweredBlock {
#[cfg(test)]
mod tests {
use super::LoweredBlock;
use crate::compile::hir::lower::test_utils;
use expect_test::{expect, Expect};

fn check_from_hir_ok(source: &str, expected: Expect) {
use crate::compile::{
db::Database,
diagnostics::{HirDiagnosticAccumulator, SourceDiagnosticAccumulator},
file::File,
from_hir::HirDiagnosticCollector,
hir,
resolve::ResolveContext,
db::Database, from_hir::HirDiagnosticCollector, resolve::ResolveContext,
};

let db = Database::default();
let db = &db;
let file = File::new(db, "test.sal".to_string(), source.to_string());

let bodies = hir::collect_file_bodies(db, file);

let hir_errors =
hir::collect_file_bodies::accumulated::<HirDiagnosticAccumulator>(db, file);
let source_errors =
hir::collect_file_bodies::accumulated::<SourceDiagnosticAccumulator>(db, file);
if !source_errors.is_empty() || !hir_errors.is_empty() {
panic!(
"hir lowering produced errors:\n\
source-level: {source_errors:?}\n\
hir-level: {hir_errors:?}"
);
}

let block_id = bodies.get_block_ids(db)[0];
let block = bodies.get_block(db, block_id).unwrap();
let (file, block_id, block) = test_utils::lower_hir_block_ok(db, source);

let mut diagnostics = HirDiagnosticCollector::new();
let resolve_ctx = ResolveContext::new(db);
Expand All @@ -161,7 +140,7 @@ mod tests {
panic!(
"hir lowering produced errors:\n\
{:#?}",
diagnostics
test_utils::diagnostic_collector_to_str(db, diagnostics)
);
}

Expand Down
Loading

0 comments on commit efe0e8b

Please sign in to comment.