diff --git a/compiler/hash-ast-desugaring/src/desugaring.rs b/compiler/hash-ast-desugaring/src/desugaring.rs index a8b3a330b..a70f63e83 100644 --- a/compiler/hash-ast-desugaring/src/desugaring.rs +++ b/compiler/hash-ast-desugaring/src/desugaring.rs @@ -7,7 +7,7 @@ use hash_utils::thin_vec::{thin_vec, ThinVec}; use crate::visitor::AstDesugaring; -impl<'s> AstDesugaring<'s> { +impl AstDesugaring { /// This function is responsible for converting a [ForLoopBlock] into a /// simpler [ast::LoopBlock]. This is not obvious from the function /// definition because it accepts a [ast::AstNode], not @@ -39,12 +39,9 @@ impl<'s> AstDesugaring<'s> { // Since this function expects it to be a for-loop block, we match it and unwrap let block = match node { ast::Block::For(body) => body, - block => panic_on_span!( - parent_span, - self.source_map, - "lowering: expected for-loop, got {}", - block.as_str() - ), + block => { + panic_on_span!(parent_span, "lowering: expected for-loop, got {}", block.as_str()) + } }; let ast::ForLoopBlock { pat, iterator, for_body } = block; @@ -189,7 +186,6 @@ impl<'s> AstDesugaring<'s> { ast::Block::While(body) => body, block => panic_on_span!( parent_span, - self.source_map, "lowering: expected while-block, got {}", block.as_str() ), @@ -365,12 +361,9 @@ impl<'s> AstDesugaring<'s> { // Since this function expects it to be a for-loop block, we match it and unwrap let block = match node { ast::Block::If(body) => body, - block => panic_on_span!( - parent_span, - self.source_map, - "lowering: expected if-block, got {}", - block.as_str() - ), + block => { + panic_on_span!(parent_span, "lowering: expected if-block, got {}", block.as_str()) + } }; // We don't case about 'clauses' because we can use the visitor to transform diff --git a/compiler/hash-ast-desugaring/src/lib.rs b/compiler/hash-ast-desugaring/src/lib.rs index 3deaa2e7f..69154ce9b 100644 --- a/compiler/hash-ast-desugaring/src/lib.rs +++ b/compiler/hash-ast-desugaring/src/lib.rs @@ -69,14 +69,13 @@ impl CompilerStage for AstDesugaringPass { let AstDesugaringCtx { workspace, pool } = &mut ctx.data(); let node_map = &mut workspace.node_map; - let source_map = &workspace.source_map; let source_stage_info = &mut workspace.source_stage_info; pool.scope(|scope| { // De-sugar the target if it isn't already de-sugared if !source_stage_info.get(entry_point).is_desugared() && entry_point.is_interactive() { let source = node_map.get_interactive_block_mut(entry_point.into()); - let mut desugarer = AstDesugaring::new(source_map); + let mut desugarer = AstDesugaring::new(); desugarer.visit_body_block(source.node_ref_mut()).unwrap(); } @@ -102,7 +101,7 @@ impl CompilerStage for AstDesugaringPass { // investigating this in the future. for expr in module.node_mut().contents.iter_mut() { scope.spawn(move |_| { - let mut desugarer = AstDesugaring::new(source_map); + let mut desugarer = AstDesugaring::new(); desugarer.visit_expr(expr.ast_ref_mut()).unwrap() }) } diff --git a/compiler/hash-ast-desugaring/src/visitor.rs b/compiler/hash-ast-desugaring/src/visitor.rs index df8563135..b1706b833 100644 --- a/compiler/hash-ast-desugaring/src/visitor.rs +++ b/compiler/hash-ast-desugaring/src/visitor.rs @@ -5,22 +5,18 @@ use hash_ast::{ ast_visitor_mut_default_impl, visitor::{walk_mut, AstVisitorMut}, }; -use hash_source::SourceMap; #[derive(Debug)] -pub struct AstDesugaring<'s> { - pub(crate) source_map: &'s SourceMap, -} +pub struct AstDesugaring {} -impl<'s> AstDesugaring<'s> { - /// Create a new [AstDesugaring]. Contains the [SourceMap] and the - /// current id of the source in reference. - pub fn new(source_map: &'s SourceMap) -> Self { - Self { source_map } +impl AstDesugaring { + /// Create a new [AstDesugaring]. + pub fn new() -> Self { + Self {} } } -impl<'s> AstVisitorMut for AstDesugaring<'s> { +impl AstVisitorMut for AstDesugaring { type Error = Infallible; ast_visitor_mut_default_impl!(hiding: Block); diff --git a/compiler/hash-ast-expand/src/attr.rs b/compiler/hash-ast-expand/src/attr.rs index b27c6845f..ed1028633 100644 --- a/compiler/hash-ast-expand/src/attr.rs +++ b/compiler/hash-ast-expand/src/attr.rs @@ -75,7 +75,7 @@ impl AstExpander<'_> { if should_dump { let mode = self.settings.ast_settings.dump_mode; let character_set = self.settings.character_set; - dump_ast(subject, mode, character_set, self.sources, &mut self.stdout).unwrap(); + dump_ast(subject, mode, character_set, &mut self.stdout).unwrap(); } } @@ -162,11 +162,8 @@ impl AstExpander<'_> { // If we can't convert this into an attribute value, then we // can't properly check the invocation. - let attr_value = match AttrValueKind::try_from_expr( - arg.value.body(), - self.sources, - ptr_size, - ) { + let attr_value = match AttrValueKind::try_from_expr(arg.value.body(), ptr_size) + { Ok(Some(value)) => value, Ok(None) => { let expr_kind = AttrTarget::classify_expr(arg.value.body()); diff --git a/compiler/hash-ast-expand/src/expander.rs b/compiler/hash-ast-expand/src/expander.rs index 9fff9c23b..4a9dd59c1 100644 --- a/compiler/hash-ast-expand/src/expander.rs +++ b/compiler/hash-ast-expand/src/expander.rs @@ -27,7 +27,7 @@ use hash_ast_utils::attr::AttrNode; use hash_attrs::checks::AttrChecker; use hash_pipeline::{interface::CompilerOutputStream, settings::CompilerSettings}; use hash_reporting::diagnostic::DiagnosticsMut; -use hash_source::{SourceId, SourceMap}; +use hash_source::SourceId; use hash_target::data_layout::TargetDataLayout; use hash_utils::crossbeam_channel::Sender; @@ -44,8 +44,6 @@ pub struct AstExpander<'ctx> { /// A reference to the compiler settings. pub settings: &'ctx CompilerSettings, - pub sources: &'ctx SourceMap, - pub stdout: CompilerOutputStream, /// Any diagnostics that have been emitted during the expansion stage. @@ -58,13 +56,11 @@ impl<'ctx> AstExpander<'ctx> { pub fn new( id: SourceId, settings: &'ctx CompilerSettings, - sources: &'ctx SourceMap, data_layout: &'ctx TargetDataLayout, stdout: CompilerOutputStream, ) -> Self { Self { settings, - sources, stdout, diagnostics: ExpansionDiagnostics::new(), checker: AttrChecker::new(id, data_layout), diff --git a/compiler/hash-ast-expand/src/lib.rs b/compiler/hash-ast-expand/src/lib.rs index 0670b5d1f..4df513ab8 100644 --- a/compiler/hash-ast-expand/src/lib.rs +++ b/compiler/hash-ast-expand/src/lib.rs @@ -71,11 +71,10 @@ impl CompilerStage for AstExpansionPass { let (sender, receiver) = unbounded::(); let node_map = &mut workspace.node_map; - let sources = &mut workspace.source_map; let source_stage_info = &mut workspace.source_stage_info; let make_expander = - |source| AstExpander::new(source, settings, sources, data_layout, stdout.clone()); + |source| AstExpander::new(source, settings, data_layout, stdout.clone()); pool.scope(|scope| { let source_info = source_stage_info.get(entry_point); diff --git a/compiler/hash-ast-utils/src/dump.rs b/compiler/hash-ast-utils/src/dump.rs index 82204f6fc..55d057f07 100644 --- a/compiler/hash-ast-utils/src/dump.rs +++ b/compiler/hash-ast-utils/src/dump.rs @@ -2,7 +2,6 @@ use std::fmt; -use hash_source::SourceMap; use hash_utils::{ clap, tree_writing::{CharacterSet, TreeWriter, TreeWriterConfig}, @@ -36,7 +35,6 @@ pub fn dump_ast( node: AttrNode<'_>, mode: AstDumpMode, character_set: CharacterSet, - source_map: &SourceMap, writer: &mut impl std::io::Write, ) -> std::io::Result<()> { match mode { @@ -46,7 +44,7 @@ pub fn dump_ast( } AstDumpMode::Tree => { // In the tree mode, we prepend the output with the item that we dumped. - writeln!(writer, "AST for `{}`:", source_map.fmt_location(node.id().span()))?; + writeln!(writer, "AST for `{}`:", node.id().span().fmt_path())?; let tree = node.visit(AstTreePrinter).unwrap(); let config = TreeWriterConfig::from_character_set(character_set); diff --git a/compiler/hash-ast/src/ast.rs b/compiler/hash-ast/src/ast.rs index 19aaf2c95..0ae83505f 100644 --- a/compiler/hash-ast/src/ast.rs +++ b/compiler/hash-ast/src/ast.rs @@ -4,7 +4,6 @@ use std::{ fmt::Display, hash::Hash, ops::{Deref, DerefMut}, - path::PathBuf, }; use hash_source::{ @@ -2009,7 +2008,7 @@ define_tree! { #[node] pub struct Import { pub path: InternedStr, - pub resolved_path: PathBuf, + pub source: SourceId, } /// A variable expression. diff --git a/compiler/hash-ast/src/lit.rs b/compiler/hash-ast/src/lit.rs index 7364f4bd2..aff44a239 100644 --- a/compiler/hash-ast/src/lit.rs +++ b/compiler/hash-ast/src/lit.rs @@ -19,12 +19,9 @@ use std::num; use hash_reporting::{hash_error_codes::error_codes::HashErrorCode, reporter::Reporter}; -use hash_source::{ - constant::{ - BigIntTy, FloatConstant, FloatTy, IntConstant, IntTy, InternedFloat, InternedInt, - NormalisedIntTy, SIntTy, Size, UIntTy, - }, - SourceMap, +use hash_source::constant::{ + BigIntTy, FloatConstant, FloatTy, IntConstant, IntTy, InternedFloat, InternedInt, + NormalisedIntTy, SIntTy, Size, UIntTy, }; pub use hash_token::{FloatLitKind, IntLitKind}; use num_bigint::BigInt; @@ -179,7 +176,6 @@ impl IntValue { pub fn parse_int_const_from_lit( lit: &IntLit, annotation: Option, - sources: &SourceMap, ptr_size: Size, allow_big: bool, ) -> LitParseResult { @@ -187,7 +183,7 @@ pub fn parse_int_const_from_lit( let base: u32 = lit.base.into(); // We have to cleanup the hunk, remove any underscores - let mut hunk = sources.hunk(lit.hunk.span()).to_string(); + let mut hunk = lit.hunk.span().contents(); hunk.retain(|c| c != '_'); macro_rules! parse { @@ -247,12 +243,11 @@ pub fn parse_int_const_from_lit( pub fn parse_float_const_from_lit( lit: &FloatLit, annotation: Option, - sources: &SourceMap, ) -> LitParseResult { let ty = annotation.unwrap_or_default(); // We have to cleanup the hunk, remove any underscores - let mut hunk = sources.hunk(lit.hunk.span()).to_string(); + let mut hunk = lit.hunk.span().contents(); hunk.retain(|c| c != '_'); macro_rules! parse { diff --git a/compiler/hash-ast/src/node_map.rs b/compiler/hash-ast/src/node_map.rs index 136614445..2f50636e8 100644 --- a/compiler/hash-ast/src/node_map.rs +++ b/compiler/hash-ast/src/node_map.rs @@ -7,10 +7,7 @@ use std::{ }; use hash_source::{InteractiveId, ModuleId, SourceId}; -use hash_utils::{ - index_vec::{index_vec, IndexVec}, - path::adjust_canonicalisation, -}; +use hash_utils::index_vec::{index_vec, IndexVec}; use crate::ast::{AstNode, BodyBlock, Module, OwnsAstNode}; @@ -69,44 +66,35 @@ impl OwnsAstNode for InteractiveBlock { pub struct ModuleEntry { /// The absolute path of the module on disk. pub path: PathBuf, + /// The generated AST for the module, set when parsing is complete. - node: Option>, + node: AstNode, } impl ModuleEntry { /// Create a new [ModuleEntry] with a specified `path` and the `node being /// set to [None]. - pub fn new(path: PathBuf) -> Self { - Self { path, node: None } + pub fn new(path: PathBuf, node: AstNode) -> Self { + Self { path, node } } /// Get the `path` from the [Module]. pub fn path(&self) -> &Path { &self.path } - - /// Get the canonicalised `path` from the [Module]. - pub fn canonicalised_path(&self) -> String { - adjust_canonicalisation(self.path()) - } - - /// Set the `node` for given [Module] - pub fn set_node(&mut self, node: AstNode) { - self.node = Some(node); - } } impl OwnsAstNode for ModuleEntry { /// Get a reference to node within [ModuleEntry]. This /// assumes that the node had already been set. fn node(&self) -> &AstNode { - self.node.as_ref().unwrap() + &self.node } /// Get a mutable reference to node within [ModuleEntry]. This /// assumes that the node had already been set. fn node_mut(&mut self) -> &mut AstNode { - self.node.as_mut().unwrap() + &mut self.node } } diff --git a/compiler/hash-attrs/src/attr.rs b/compiler/hash-attrs/src/attr.rs index 8f1a46825..5cf95adf0 100644 --- a/compiler/hash-attrs/src/attr.rs +++ b/compiler/hash-attrs/src/attr.rs @@ -10,7 +10,6 @@ use hash_ast::{ use hash_source::{ constant::{InternedFloat, InternedInt, InternedStr}, identifier::Identifier, - SourceMap, }; use hash_storage::store::{DefaultPartialStore, PartialStore}; use hash_target::{abi::Integer, data_layout::HasDataLayout, primitives::IntTy, size::Size}; @@ -171,22 +170,17 @@ pub enum AttrValueKind { impl AttrValueKind { /// Try to convert an [ast::Expr] into an [AttrValue]. - pub fn try_from_expr( - expr: &ast::Expr, - sources: &SourceMap, - ptr_size: Size, - ) -> LitParseResult> { + pub fn try_from_expr(expr: &ast::Expr, ptr_size: Size) -> LitParseResult> { match expr { ast::Expr::Lit(ast::LitExpr { data }) => match data.body() { ast::Lit::Str(ast::StrLit { data }) => Ok(Some(Self::Str(*data))), ast::Lit::Char(ast::CharLit { data }) => Ok(Some(Self::Char(*data))), ast::Lit::Int(int_lit) => { - let value = - parse_int_const_from_lit(int_lit, None, sources, ptr_size, false)?.small(); + let value = parse_int_const_from_lit(int_lit, None, ptr_size, false)?.small(); Ok(Some(Self::Int(value))) } ast::Lit::Float(float_lit) => { - let value = parse_float_const_from_lit(float_lit, None, sources)?; + let value = parse_float_const_from_lit(float_lit, None)?; Ok(Some(Self::Float(value))) } _ => Ok(None), diff --git a/compiler/hash-codegen-llvm/src/lib.rs b/compiler/hash-codegen-llvm/src/lib.rs index 7a0f291d6..38779170f 100644 --- a/compiler/hash-codegen-llvm/src/lib.rs +++ b/compiler/hash-codegen-llvm/src/lib.rs @@ -37,8 +37,8 @@ use hash_pipeline::{ settings::CompilerSettings, workspace::Workspace, }; -use hash_reporting::writer::ReportWriter; -use hash_source::ModuleId; +use hash_reporting::report::Report; +use hash_source::{ModuleId, SourceMapUtils}; use hash_storage::store::{statics::StoreId, Store}; use hash_utils::{ stream_writeln, @@ -204,11 +204,7 @@ impl<'b, 'm> LLVMBackend<'b> { let report = info_report(format!("wrote assembly file to `{}`", asm_path.to_string_lossy())); - stream_writeln!( - self.stdout, - "{}", - ReportWriter::new(vec![report], &self.workspace.source_map) - ); + stream_writeln!(self.stdout, "{}", report); } self.target_machine @@ -307,11 +303,7 @@ impl<'b, 'm> LLVMBackend<'b> { let mut stdout = self.stdout.clone(); let func = FunctionPrinter::new(body.info.name(), ctx.get_fn(instance)); - stream_writeln!( - stdout, - "{}", - ReportWriter::new(vec![func.into()], &self.workspace.source_map) - ); + stream_writeln!(stdout, "{}", Report::from(func)); } } } @@ -326,11 +318,8 @@ impl<'b> CompilerBackend<'b> for LLVMBackend<'b> { // object, or if we emit a single module object for the entire program. // Currently, we are emitting a single module for the entire program // that is being compiled in in the workspace. - let entry_point = self - .workspace - .source_map - .entry_point() - .expect("expected a defined entry point for executable"); + let entry_point = + SourceMapUtils::entry_point().expect("expected a defined entry point for executable"); let context = LLVMContext::create(); @@ -370,7 +359,7 @@ impl<'b> CompilerBackend<'b> for LLVMBackend<'b> { time_item(self, "optimise", |this| this.optimise(&module))?; time_item(self, "write", |this| { - this.write_module(&module, entry_point).map_err(|err| vec![err.into()]) + this.write_module(&module, entry_point.into()).map_err(|err| vec![err.into()]) }) } } diff --git a/compiler/hash-driver/src/driver.rs b/compiler/hash-driver/src/driver.rs index 3539b1c05..0e293b510 100644 --- a/compiler/hash-driver/src/driver.rs +++ b/compiler/hash-driver/src/driver.rs @@ -6,13 +6,13 @@ use std::{ time::Duration, }; -use hash_ast::node_map::{InteractiveBlock, ModuleEntry}; +use hash_ast::node_map::InteractiveBlock; use hash_pipeline::{ - fs::{read_in_path, resolve_path, PRELUDE}, + fs::{resolve_path, PRELUDE}, interface::{CompilerInterface, CompilerResult, CompilerStage}, settings::CompilerStageKind, }; -use hash_reporting::writer::ReportWriter; +use hash_reporting::reporter::Reporter; use hash_source::{ModuleKind, SourceId}; use hash_utils::{log, stream_writeln, timing::timed}; @@ -267,10 +267,7 @@ impl Driver { if self.compiler.diagnostics().iter().any(|r| r.is_error()) { panic!( "failed to bootstrap compiler: {}", - ReportWriter::new( - self.compiler.diagnostics().to_owned(), - self.compiler.source_map(), - ) + Reporter::from_reports(self.compiler.diagnostics().to_owned()) ); } @@ -285,7 +282,7 @@ impl Driver { let mut stderr = self.compiler.error_stream(); // @@Copying: Ideally, we would not want to copy here! - for diagnostic in self.compiler.diagnostics().iter().cloned() { + for diagnostic in self.compiler.diagnostics().iter() { if diagnostic.is_error() { err_count += 1; } @@ -294,11 +291,7 @@ impl Driver { warn_count += 1; } - stream_writeln!( - stderr, - "{}", - ReportWriter::single(diagnostic, self.compiler.source_map()) - ); + stream_writeln!(stderr, "{}", diagnostic); } // ##Hack: to prevent the compiler from printing this message when the pipeline @@ -334,28 +327,9 @@ impl Driver { /// off the disk, store it within the [Workspace] and invoke /// [`Self::run`] pub fn run_filename(&mut self, filename: impl AsRef, kind: ModuleKind) { - let contents = read_in_path(&filename); - - if let Err(err) = contents { - self.compiler.diagnostics_mut().push(err.into()); - - // Since this a fatal error because we couldn't read the file, we - // emit the diagnostics (if specified to emit) and terminate. - if self.compiler.settings().emit_errors { - self.emit_diagnostics(); - } - - return; - }; - - // Create the module and run! - let module = self.compiler.workspace_mut().add_module( - contents.unwrap(), - ModuleEntry::new(filename.as_ref().to_path_buf()), - kind, - ); - - self.run(module) + // Reserve a module id for the module that we are about to add. + let id = self.workspace_mut().add_module(filename.as_ref().to_path_buf(), kind); + self.run(id) } /// Run the compiler pipeline on the entry point specified in the settings. @@ -374,7 +348,8 @@ impl Driver { /// Run the compiler on a interactive line input. pub fn run_interactive(&mut self, input: String) { - let source = self.compiler.add_interactive_block(input, InteractiveBlock::new()); + let source = + self.compiler.workspace_mut().add_interactive_block(input, InteractiveBlock::new()); self.run(source) } } diff --git a/compiler/hash-driver/src/lib.rs b/compiler/hash-driver/src/lib.rs index 7501b7808..ac6ee6935 100644 --- a/compiler/hash-driver/src/lib.rs +++ b/compiler/hash-driver/src/lib.rs @@ -35,7 +35,7 @@ use hash_reporting::report::Report; use hash_semantics::{ SemanticAnalysis, SemanticAnalysisCtx, SemanticAnalysisCtxQuery, SemanticStorage, }; -use hash_source::{SourceId, SourceMap}; +use hash_source::SourceId; use hash_untyped_semantics::{ UntypedSemanticAnalysis, UntypedSemanticAnalysisCtx, UntypedSemanticAnalysisCtxQuery, }; @@ -49,7 +49,8 @@ pub struct CompilerBuilder; impl CompilerBuilder { /// Create a new [Compiler] with the default stage configuration. pub fn build_with_settings(settings: CompilerSettings) -> Driver { - let session = utils::emit_on_fatal_error(|| Compiler::new(settings)); + let stream = CompilerOutputStream::Stdout(std::io::stdout()); + let session = utils::emit_on_fatal_error(stream, || Compiler::new(settings)); Self::build_with_interface(session) } @@ -89,16 +90,16 @@ impl CompilerBuilder { } pub mod utils { - use hash_reporting::{report::Report, writer::ReportWriter}; - use hash_source::SourceMap; - use hash_utils::stream_less_ewriteln; + use hash_pipeline::interface::CompilerOutputStream; + use hash_reporting::report::Report; + use hash_utils::stream_writeln; /// Emit a fatal compiler error and exit the compiler. These kind of errors /// are not **panics** but they are neither recoverable. This function /// will convert the error into a [Report] and then write it to the /// error stream. - pub fn emit_fatal_error>(error: E, sources: &SourceMap) -> ! { - stream_less_ewriteln!("{}", ReportWriter::single(error.into(), sources)); + pub fn emit_fatal_error>(mut stream: CompilerOutputStream, error: E) -> ! { + stream_writeln!(stream, "{}", error.into()); std::process::exit(-1); } @@ -108,15 +109,13 @@ pub mod utils { /// The error type is intentionally not specified so that this function can /// be used in contexts where the error type is known to implementing the /// [Into] trait. - pub fn emit_on_fatal_error>(f: impl FnOnce() -> Result) -> T { - // ##Hack: we have to create a dummy source map here so that we can use it - // to report errors in the case that the compiler fails to start up. After the - // workspace is initiated, it is replaced with the real source map. - let source_map = SourceMap::new(); - + pub fn emit_on_fatal_error>( + stream: CompilerOutputStream, + f: impl FnOnce() -> Result, + ) -> T { match f() { Ok(value) => value, - Err(err) => emit_fatal_error(err, &source_map), + Err(err) => emit_fatal_error(stream, err), } } } @@ -208,7 +207,7 @@ impl Compiler { // @@Fixme: ideally this error should be handled else-where let layout_info = target .parse_data_layout() - .unwrap_or_else(|err| utils::emit_fatal_error(err, &workspace.source_map)); + .unwrap_or_else(|err| utils::emit_fatal_error(error_stream(), err)); Self { error_stream: Box::new(error_stream), @@ -282,11 +281,6 @@ impl CompilerInterface for Compiler { fn node_map(&self) -> &NodeMap { &self.workspace.node_map } - - /// Get a reference to [SourceMap] for the current [Workspace]. - fn source_map(&self) -> &SourceMap { - &self.workspace.source_map - } } impl ParserCtxQuery for Compiler { diff --git a/compiler/hash-ir/src/write/pretty.rs b/compiler/hash-ir/src/write/pretty.rs index d8ddce82f..33218ea6b 100644 --- a/compiler/hash-ir/src/write/pretty.rs +++ b/compiler/hash-ir/src/write/pretty.rs @@ -2,7 +2,6 @@ use std::fmt; -use hash_source::SourceMap; use hash_utils::itertools::Itertools; use super::WriteIr; @@ -156,7 +155,6 @@ impl fmt::Display for IrBodyWriter<'_> { /// Dump all of the provided [Body]s to standard output using the `dot` format. pub fn dump_ir_bodies( - source_map: &SourceMap, bodies: &[Body], dump_all: bool, prelude_is_quiet: bool, @@ -184,7 +182,7 @@ pub fn dump_ir_bodies( "IR dump for {} `{}` defined at {}\n{}", body.info().source(), body.info().name(), - source_map.fmt_location(body.span()), + body.span().fmt_path(), IrBodyWriter::new(body) )?; } diff --git a/compiler/hash-lexer/src/lib.rs b/compiler/hash-lexer/src/lib.rs index 489f3511b..3fbada2be 100644 --- a/compiler/hash-lexer/src/lib.rs +++ b/compiler/hash-lexer/src/lib.rs @@ -8,8 +8,8 @@ use hash_reporting::diagnostic::AccessToDiagnosticsMut; use hash_source::{ self, identifier::{Identifier, IDENTS}, - location::{ByteRange, Span}, - Source, SourceId, + location::{ByteRange, Span, SpannedSource}, + SourceId, }; use hash_target::primitives::{FloatTy, IntTy}; use hash_token::{ @@ -37,7 +37,7 @@ pub struct Lexer<'a> { offset: Cell, /// The contents that are to be lexed. - contents: Source<'a>, + contents: SpannedSource<'a>, /// Representative module index of the current source. source_id: SourceId, @@ -60,7 +60,7 @@ pub struct Lexer<'a> { impl<'a> Lexer<'a> { /// Create a new [Lexer] from the given string input. - pub fn new(contents: Source<'a>, source_id: SourceId) -> Self { + pub fn new(contents: SpannedSource<'a>, source_id: SourceId) -> Self { Lexer { offset: Cell::new(0), source_id, diff --git a/compiler/hash-lower/src/build/constant.rs b/compiler/hash-lower/src/build/constant.rs index 84bb58627..2f1dc9073 100644 --- a/compiler/hash-lower/src/build/constant.rs +++ b/compiler/hash-lower/src/build/constant.rs @@ -10,7 +10,6 @@ use hash_reporting::macros::panic_on_span; use hash_source::constant::{FloatConstant, FloatConstantValue, IntConstant, InternedFloat}; use hash_storage::store::statics::StoreId; use hash_tir::{ - environment::env::AccessToEnv, lits::{Lit, LitId}, terms::Term, }; @@ -39,11 +38,7 @@ impl<'tcx> BodyBuilder<'tcx> { pub(crate) fn lower_constant_expr(&mut self, term: &Term, origin: AstNodeId) -> ConstKind { match term { Term::Lit(lit) => self.as_constant(*lit), - _ => panic_on_span!( - origin.span(), - self.source_map(), - "cannot lower non-literal expression into constant" - ), + _ => panic_on_span!(origin.span(), "cannot lower non-literal expression into constant"), } } diff --git a/compiler/hash-lower/src/build/into.rs b/compiler/hash-lower/src/build/into.rs index f2d60bc1d..b9a267862 100644 --- a/compiler/hash-lower/src/build/into.rs +++ b/compiler/hash-lower/src/build/into.rs @@ -250,11 +250,7 @@ impl<'tcx> BodyBuilder<'tcx> { // start of the loop block, and when this is a break, we need to // **jump** to the proceeding block of the loop block let Some(LoopBlockInfo { loop_body, next_block }) = self.loop_block_info else { - panic_on_span!( - span.span(), - self.env().source_map(), - "`continue` or `break` outside of loop" - ); + panic_on_span!(span.span(), "`continue` or `break` outside of loop"); }; // Add terminators to this block to specify where this block will jump... @@ -374,8 +370,7 @@ impl<'tcx> BodyBuilder<'tcx> { // if let IrTy::Fn { params, .. } = fn_ty { // if args.len() != params.len() { // panic_on_span!( - // span.into_location(self.source_id), - // self.env().source_map(), + // origin.span(), // "default arguments on functions are not currently supported", // ); // } @@ -522,7 +517,6 @@ impl<'tcx> BodyBuilder<'tcx> { if args.len() != field_count { panic_on_span!( origin.span(), - self.source_map(), "default arguments on constructors are not currently supported, params={} args={}", field_count, args.len(), diff --git a/compiler/hash-lower/src/build/matches/declarations.rs b/compiler/hash-lower/src/build/matches/declarations.rs index 1e158a4b6..5a7c86194 100644 --- a/compiler/hash-lower/src/build/matches/declarations.rs +++ b/compiler/hash-lower/src/build/matches/declarations.rs @@ -12,7 +12,6 @@ use hash_tir::{ arrays::ArrayPat, control::{IfPat, OrPat}, data::CtorPat, - environment::env::AccessToEnv, node::NodesId, pats::{Pat, PatId}, scopes::{BindingPat, DeclTerm}, @@ -59,7 +58,6 @@ impl<'tcx> BodyBuilder<'tcx> { } else { panic_on_span!( decl_origin.span(), - self.source_map(), "expected initialisation value, declaration are expected to have values (for now)." ); }; diff --git a/compiler/hash-lower/src/build/matches/test.rs b/compiler/hash-lower/src/build/matches/test.rs index 48cb7a4fd..362463a7f 100644 --- a/compiler/hash-lower/src/build/matches/test.rs +++ b/compiler/hash-lower/src/build/matches/test.rs @@ -21,7 +21,6 @@ use hash_tir::{ atom_info::ItemInAtomInfo, control::IfPat, data::CtorPat, - environment::env::AccessToEnv, node::NodesId, params::ParamIndex, pats::{Pat, PatId, RangePat, Spread}, @@ -200,7 +199,6 @@ impl<'tcx> BodyBuilder<'tcx> { if adt.flags.is_struct() { panic_on_span!( origin.span(), - self.source_map(), "attempt to test simplify-able pattern, `{}`", (pair.pat) ) @@ -250,15 +248,12 @@ impl<'tcx> BodyBuilder<'tcx> { Pat::If(IfPat { pat, .. }) => { self.test_match_pair(&MatchPair { pat, place: pair.place.clone() }) } - Pat::Or(_) => panic_on_span!( - origin.span(), - self.source_map(), - "or patterns should be handled by `test_or_pat`" - ), + Pat::Or(_) => { + panic_on_span!(origin.span(), "or patterns should be handled by `test_or_pat`") + } Pat::Tuple(_) | Pat::Binding(_) => { panic_on_span!( origin.span(), - self.source_map(), "attempt to test simplify-able pattern, `{}`", (pair.pat) ) diff --git a/compiler/hash-lower/src/ctx.rs b/compiler/hash-lower/src/ctx.rs index 271b0b82b..6fc82edd1 100644 --- a/compiler/hash-lower/src/ctx.rs +++ b/compiler/hash-lower/src/ctx.rs @@ -81,13 +81,8 @@ impl<'ir> BuilderCtx<'ir> { semantic_storage, workspace, ir_storage, layout_storage, settings, .. } = ctx; - let env = Env::new( - &semantic_storage.context, - &workspace.node_map, - &workspace.source_map, - settings.target(), - entry, - ); + let env = + Env::new(&semantic_storage.context, &workspace.node_map, settings.target(), entry); let intrinsics = match semantic_storage.intrinsics_or_unset.get() { Some(intrinsics) => intrinsics, diff --git a/compiler/hash-lower/src/discover.rs b/compiler/hash-lower/src/discover.rs index 26431c9ee..8806b197f 100644 --- a/compiler/hash-lower/src/discover.rs +++ b/compiler/hash-lower/src/discover.rs @@ -120,7 +120,7 @@ impl FnDiscoverer<'_> { // @@Incomplete: mod-blocks that are already lowered won't be caught by // the queue-deduplication. match mod_def_id.borrow().kind { - ModKind::Source(id, _) if !self.stage_info.get(id).is_lowered() => {} + ModKind::Source(id) if !self.stage_info.get(id).is_lowered() => {} _ => continue, }; diff --git a/compiler/hash-lower/src/lib.rs b/compiler/hash-lower/src/lib.rs index a7339a472..274bf2732 100644 --- a/compiler/hash-lower/src/lib.rs +++ b/compiler/hash-lower/src/lib.rs @@ -209,8 +209,7 @@ impl CompilerStage for IrOptimiser { } fn run(&mut self, _: SourceId, ctx: &mut Ctx) -> CompilerResult<()> { - let LoweringCtx { workspace, ir_storage, settings, .. } = ctx.data(); - let source_map = &mut workspace.source_map; + let LoweringCtx { ir_storage, settings, .. } = ctx.data(); let bodies = &mut ir_storage.bodies; let body_data = &ir_storage.ctx; @@ -220,14 +219,14 @@ impl CompilerStage for IrOptimiser { // pool.scope(|scope| { // for body in &mut ir_storage.generated_bodies { // scope.spawn(|_| { - // let optimiser = Optimiser::new(body_data, source_map, settings); + // let optimiser = Optimiser::new(body_data, settings); // optimiser.optimise(body); // }); // } // }); for body in bodies.iter_mut() { - let optimiser = Optimiser::new(body_data, source_map, settings); + let optimiser = Optimiser::new(body_data, settings); optimiser.optimise(body); } }); @@ -236,8 +235,7 @@ impl CompilerStage for IrOptimiser { } fn cleanup(&mut self, _entry_point: SourceId, ctx: &mut Ctx) { - let LoweringCtx { workspace, ir_storage, mut stdout, settings, .. } = ctx.data(); - let source_map = &mut workspace.source_map; + let LoweringCtx { ir_storage, mut stdout, settings, .. } = ctx.data(); // we need to check if any of the bodies have been marked for `dumping` // and emit the IR that they have generated. @@ -247,14 +245,7 @@ impl CompilerStage for IrOptimiser { if settings.lowering_settings.dump_mode == IrDumpMode::Graph { graphviz::dump_ir_bodies(&ir_storage.bodies, dump, quiet_prelude, &mut stdout).unwrap(); } else { - pretty::dump_ir_bodies( - source_map, - &ir_storage.bodies, - dump, - quiet_prelude, - &mut stdout, - ) - .unwrap(); + pretty::dump_ir_bodies(&ir_storage.bodies, dump, quiet_prelude, &mut stdout).unwrap(); } } } diff --git a/compiler/hash-lower/src/lower_ty.rs b/compiler/hash-lower/src/lower_ty.rs index 6a737b87b..3af5bdf01 100644 --- a/compiler/hash-lower/src/lower_ty.rs +++ b/compiler/hash-lower/src/lower_ty.rs @@ -151,7 +151,7 @@ impl<'ir> BuilderCtx<'ir> { format!("all types should be monomorphised before lowering, type: `{}`", ty); if let Some(location) = id.span() { - panic_on_span!(location, self.source_map(), format!("{message}")) + panic_on_span!(location, format!("{message}")) } else { panic!("{message}") } diff --git a/compiler/hash-lower/src/optimise/mod.rs b/compiler/hash-lower/src/optimise/mod.rs index 32ad7c5bb..d8a375374 100644 --- a/compiler/hash-lower/src/optimise/mod.rs +++ b/compiler/hash-lower/src/optimise/mod.rs @@ -8,7 +8,6 @@ use hash_ir::{ir::Body, IrCtx}; use hash_pipeline::settings::{CompilerSettings, OptimisationLevel}; -use hash_source::SourceMap; // Various passes that are used to optimise the generated IR bodies. mod cleanup_locals; @@ -34,9 +33,6 @@ pub trait IrOptimisationPass { pub struct Optimiser<'ir> { store: &'ir IrCtx, - /// The compiler source map. - _source_map: &'ir SourceMap, - /// Stores all of the lowering settings that are used to /// determine which passes are enabled. settings: &'ir CompilerSettings, @@ -47,14 +43,9 @@ pub struct Optimiser<'ir> { } impl<'ir> Optimiser<'ir> { - pub fn new( - store: &'ir IrCtx, - source_map: &'ir SourceMap, - settings: &'ir CompilerSettings, - ) -> Self { + pub fn new(store: &'ir IrCtx, settings: &'ir CompilerSettings) -> Self { Self { store, - _source_map: source_map, settings, passes: vec![ Box::new(simplify_graph::SimplifyGraphPass), diff --git a/compiler/hash-parser/src/import_resolver.rs b/compiler/hash-parser/src/import_resolver.rs index 4f21393a5..479b41f5b 100644 --- a/compiler/hash-parser/src/import_resolver.rs +++ b/compiler/hash-parser/src/import_resolver.rs @@ -1,9 +1,9 @@ //! Self hosted hash parser, this function contains the implementations for //! `hash-ast` which provides a general interface to write a parser. -use std::path::{Path, PathBuf}; +use std::path::PathBuf; -use hash_pipeline::fs::{read_in_path, resolve_path, ImportError}; -use hash_source::{constant::InternedStr, SourceId}; +use hash_pipeline::fs::{resolve_path, ImportError}; +use hash_source::{constant::InternedStr, ModuleKind, SourceId, SourceMapUtils}; use hash_utils::crossbeam_channel::Sender; use crate::ParserAction; @@ -15,7 +15,7 @@ pub struct ImportResolver<'p> { /// The associated [SourceId] with the import resolution. source_id: SourceId, /// Working directory from where the import path resolution occurs. - root_dir: &'p Path, + root_dir: &'p PathBuf, /// The parser message queue sender. sender: Sender, } @@ -25,7 +25,7 @@ impl<'p> ImportResolver<'p> { /// directory and a message queue sender. pub(crate) fn new( source_id: SourceId, - root_dir: &'p Path, + root_dir: &'p PathBuf, sender: Sender, ) -> Self { Self { root_dir, sender, source_id } @@ -40,20 +40,23 @@ impl<'p> ImportResolver<'p> { /// contents of the provided `import_path`, resolve the contents of the /// module, and then proceed to send a [ParserAction::ParseImport] /// through the message queue. - pub(crate) fn resolve_import(&self, path: InternedStr) -> Result { + pub(crate) fn resolve_import(&self, path: InternedStr) -> Result { // Read the contents of the file let resolved_path = resolve_path(path, self.root_dir)?; - let contents = read_in_path(resolved_path.as_path())?; - // Send over the resolved path and the contents of the file + // Check if we have already parsed this file + if let Some(source) = SourceMapUtils::id_by_path(&resolved_path) { + return Ok(source); + } + + // Otherwise, we reserve a module id for the file. + // + // Send over the resolved path and the contents of the file. + let source = SourceMapUtils::reserve_module(resolved_path, ModuleKind::Normal); self.sender - .send(ParserAction::ParseImport { - resolved_path: resolved_path.clone(), - contents, - sender: self.sender.clone(), - }) + .send(ParserAction::ParseImport { source, sender: self.sender.clone() }) .unwrap(); - Ok(resolved_path) + Ok(source) } /// Yield a [Sender] whilst consuming self. diff --git a/compiler/hash-parser/src/lib.rs b/compiler/hash-parser/src/lib.rs index 9b73ae8b3..3d0fee2ae 100644 --- a/compiler/hash-parser/src/lib.rs +++ b/compiler/hash-parser/src/lib.rs @@ -7,11 +7,12 @@ mod import_resolver; pub mod parser; mod source; -use std::{env, ops::AddAssign, path::PathBuf, time::Duration}; +use std::{env, ops::AddAssign, time::Duration}; use hash_ast::{ast, node_map::ModuleEntry}; use hash_lexer::Lexer; use hash_pipeline::{ + fs::read_in_path, interface::{CompilerInterface, CompilerStage, StageMetrics}, settings::CompilerStageKind, workspace::Workspace, @@ -21,7 +22,7 @@ use hash_reporting::{ report::Report, reporter::Reports, }; -use hash_source::{InteractiveId, ModuleId, ModuleKind, SourceId}; +use hash_source::{location::SpannedSource, InteractiveId, ModuleId, SourceId, SourceMapUtils}; use hash_utils::{ crossbeam_channel::{unbounded, Sender}, indexmap::IndexMap, @@ -53,6 +54,7 @@ impl Parser { // However, this might make the metrics output more difficult to read, so // perhaps there should be a "light" metrics mode, and a more verbose // one. + self.metrics.entry("read").or_default().add_assign(metrics.read); self.metrics.entry("tokenise").or_default().add_assign(metrics.tokenise); self.metrics.entry("gen").or_default().add_assign(metrics.gen); } @@ -100,12 +102,9 @@ impl CompilerStage for Parser { assert!(pool.current_num_threads() > 1, "Parser loop requires at least 2 workers"); let node_map = &mut workspace.node_map; - let source_map = &mut workspace.source_map; // Parse the entry point - let entry_source_kind = - ParseSource::from_source(entry_point, node_map, source_map, current_dir); - parse_source(entry_source_kind, sender); + parse_source(ParseSource::from_source(entry_point, Some(current_dir)), sender); pool.scope(|scope| { while let Ok(message) = receiver.recv() { @@ -118,24 +117,20 @@ impl CompilerStage for Parser { ParserAction::SetModuleNode { id, node, diagnostics, timings } => { collected_diagnostics.extend(diagnostics); self.merge_metrics(timings); - node_map.get_module_mut(id).set_node(node); + + let path = SourceMapUtils::map(id, |source| source.path().to_path_buf()); + node_map.add_module(ModuleEntry::new(path, node)); } - ParserAction::ParseImport { resolved_path, contents, sender } => { - if source_map.get_id_by_path(&resolved_path).is_some() { - continue; - } - - let module_id = source_map.add_module( - resolved_path.clone(), - contents, - ModuleKind::Normal, - ); - - let module = ModuleEntry::new(resolved_path); - node_map.add_module(module); - - let source = ParseSource::from_module(module_id, node_map, source_map); - scope.spawn(move |_| parse_source(source, sender)); + ParserAction::ParseImport { source, sender } => { + // ##Note: import de-duplication is already ensured by the + // sender. The resolved path of the specified module is looked + // up in the source map before sending this message. If an existing + // `SourceId` is already present, then the message is not sent. + + scope.spawn(move |_| { + // reserve a module id for the module we are about to parse. + parse_source(ParseSource::from_source(source, None), sender) + }); } ParserAction::Error { diagnostics, timings } => { collected_diagnostics.extend(diagnostics); @@ -178,6 +173,9 @@ pub struct ParseTimings { /// The amound of time the parser took to generate AST for the /// source. gen: Duration, + + /// The amount of time it took to read in the source from disk. + read: Duration, } impl AccessToMetrics for ParseTimings { @@ -185,6 +183,7 @@ impl AccessToMetrics for ParseTimings { match name { "tokenise" => self.tokenise = time, "gen" => self.gen = time, + "read" => self.read = time, _ => unreachable!(), } } @@ -195,7 +194,7 @@ impl AccessToMetrics for ParseTimings { pub enum ParserAction { /// A worker has specified that a module should be put in the queue for /// lexing and parsing. - ParseImport { resolved_path: PathBuf, contents: String, sender: Sender }, + ParseImport { source: SourceId, sender: Sender }, /// A unrecoverable error occurred during the parsing or lexing of a module. Error { diagnostics: Vec, timings: ParseTimings }, @@ -237,16 +236,45 @@ pub enum ParserAction { /// Parse a specific source specified by [ParseSource]. fn parse_source(source: ParseSource, sender: Sender) { - let source_id = source.id(); let mut timings = ParseTimings::default(); + let id = source.id(); + + // Read in the contents from disk if this is a module, otherwise copy + // from the already inserted interactive line. + let contents = time_item(&mut timings, "read", |_| { + if id.is_interactive() { + // @@Dumbness: We have to copy out the contents of the interactive line. + Ok(SourceMapUtils::map(id, |source| source.owned_contents())) + } else { + let path = SourceMapUtils::map(id, |source| source.path().to_path_buf()); + read_in_path(path.as_path()) + } + }); + + let Ok(contents) = contents else { + // We failed to read in the contents of the path, we emit a + // an error to the diagnostics channel. + sender + .send(ParserAction::Error { diagnostics: vec![contents.unwrap_err().into()], timings }) + .unwrap(); + return; + }; + + let spanned = SpannedSource::from_string(contents.as_str()); // Lex the contents of the module or interactive block - let mut lexer = Lexer::new(source.contents(), source_id); + let mut lexer = Lexer::new(spanned, id); let tokens = time_item(&mut timings, "tokenise", |_| lexer.tokenise()); // Check if the lexer has errors... if lexer.has_errors() { sender.send(ParserAction::Error { diagnostics: lexer.into_reports(), timings }).unwrap(); + + // We need to finally put the sources into the source map. + if id.is_module() { + SourceMapUtils::set_module_source(id, contents); + } + return; } @@ -254,16 +282,17 @@ fn parse_source(source: ParseSource, sender: Sender) { // Create a new import resolver in the event of more modules that // are encountered whilst parsing this module. - let resolver = ImportResolver::new(source_id, source.path(), sender); + let resolver = ImportResolver::new(id, source.parent(), sender); let diagnostics = ParserDiagnostics::new(); - let mut gen = AstGen::new(source.contents(), &tokens, &trees, &resolver, &diagnostics); + let mut gen = AstGen::new(spanned, &tokens, &trees, &resolver, &diagnostics); // Perform the parsing operation now... and send the result through the // message queue, regardless of it being an error or not. - let id = source.id(); let action = match id.is_interactive() { false => { let node = time_item(&mut timings, "gen", |_| gen.parse_module()); + SourceMapUtils::set_module_source(id, contents); + ParserAction::SetModuleNode { id: id.into(), node, diff --git a/compiler/hash-parser/src/parser/expr.rs b/compiler/hash-parser/src/parser/expr.rs index 2904b360f..6c18ee68c 100644 --- a/compiler/hash-parser/src/parser/expr.rs +++ b/compiler/hash-parser/src/parser/expr.rs @@ -482,9 +482,9 @@ impl<'stream, 'resolver> AstGen<'stream, 'resolver> { // Attempt to add the module via the resolver match self.resolver.resolve_import(path) { - Ok(resolved_path) => Ok(self.node_with_joined_span( + Ok(source) => Ok(self.node_with_joined_span( Expr::Import(ImportExpr { - data: self.node_with_joined_span(Import { path, resolved_path }, start), + data: self.node_with_joined_span(Import { path, source }, start), }), start, )), diff --git a/compiler/hash-parser/src/parser/mod.rs b/compiler/hash-parser/src/parser/mod.rs index 98daeb5ca..ee94ee5c1 100644 --- a/compiler/hash-parser/src/parser/mod.rs +++ b/compiler/hash-parser/src/parser/mod.rs @@ -16,10 +16,7 @@ use std::cell::Cell; use hash_ast::ast::*; use hash_reporting::diagnostic::AccessToDiagnostics; -use hash_source::{ - location::{ByteRange, Span}, - Source, -}; +use hash_source::location::{ByteRange, Span, SpannedSource}; use hash_token::{delimiter::Delimiter, Token, TokenKind}; use hash_utils::thin_vec::{thin_vec, ThinVec}; @@ -88,7 +85,7 @@ pub struct AstGen<'stream, 'resolver> { /// report it as an expected expression. parent_span: ByteRange, - source: Source<'stream>, + source: SpannedSource<'stream>, /// The token stream stream: &'stream [Token], @@ -109,7 +106,7 @@ pub struct AstGen<'stream, 'resolver> { impl<'stream, 'resolver> AstGen<'stream, 'resolver> { /// Create new AST generator from a token stream. pub fn new( - source: Source<'stream>, + source: SpannedSource<'stream>, stream: &'stream [Token], token_trees: &'stream [Vec], resolver: &'resolver ImportResolver, diff --git a/compiler/hash-parser/src/source.rs b/compiler/hash-parser/src/source.rs index 501474044..5298ba6a3 100644 --- a/compiler/hash-parser/src/source.rs +++ b/compiler/hash-parser/src/source.rs @@ -3,66 +3,43 @@ use std::path::PathBuf; -use hash_ast::node_map::NodeMap; -use hash_source::{Source, SourceId, SourceMap}; +use hash_source::{SourceId, SourceMapUtils}; /// A [ParseSource] represents the pre-processed information before a module -/// or an interactive block gets lexed and parsed. Logic related to -/// [ParseSource] is used to organise information about the source before like -/// parsing, such as the contents, id and path of the actual source. +/// or an interactive block gets lexed and parsed. pub struct ParseSource { - /// The absolute path for the current source, `current_dir` if it is an + /// The absolute path for the parent directory of the source, `current_dir` + /// if it is an interactive block. This is used to resolve other module + /// that are specified relative to the current [ParseSource] module or /// interactive block. - path: PathBuf, + parent: PathBuf, - /// The raw contents of the source. - contents: String, - - /// The [SourceId] of the source + /// The [SourceId] of the source, could be a module or interactive. id: SourceId, } impl ParseSource { - /// Create a new [ParseSource] from a [SourceId]. - pub(crate) fn from_module(id: SourceId, node_map: &NodeMap, source_map: &SourceMap) -> Self { - let module = node_map.get_module(id.into()); - let contents = source_map.contents(id).0.to_owned(); - - Self { id, contents, path: module.path().parent().unwrap().to_owned() } - } - /// Create a new [ParseSource] from a [InteractiveId]. - fn from_interactive(id: SourceId, source_map: &SourceMap, current_dir: PathBuf) -> Self { - let contents = source_map.contents(id).0.to_owned(); - - Self { id, contents, path: current_dir } - } - /// Create a [ParseSource] from a general [SourceId] - pub fn from_source( - id: SourceId, - node_map: &NodeMap, - source_map: &SourceMap, - current_dir: PathBuf, - ) -> Self { + pub fn from_source(id: SourceId, current_dir: Option) -> Self { if id.is_interactive() { - Self::from_interactive(id, source_map, current_dir) + Self { id, parent: current_dir.unwrap() } } else { - Self::from_module(id, node_map, source_map) + Self { + id, + parent: SourceMapUtils::map(id, |source| { + source.path().parent().unwrap().to_owned() + }), + } } } - /// Get the contents from the [ParseSource] - pub fn contents(&self) -> Source<'_> { - Source(self.contents.as_str()) - } - /// Get the associated [SourceId] from the [ParseSource] pub fn id(&self) -> SourceId { self.id } /// Get the `associated_path` with the [ParseSource] - pub fn path(&self) -> &PathBuf { - &self.path + pub fn parent(&self) -> &PathBuf { + &self.parent } } diff --git a/compiler/hash-pipeline/src/fs.rs b/compiler/hash-pipeline/src/fs.rs index 5d8bfa765..3b76b8031 100644 --- a/compiler/hash-pipeline/src/fs.rs +++ b/compiler/hash-pipeline/src/fs.rs @@ -2,7 +2,7 @@ use std::{ env, fmt::Display, - fs, + fs, io, path::{Path, PathBuf, MAIN_SEPARATOR}, }; @@ -70,6 +70,22 @@ impl From for Report { } } +impl From<(PathBuf, io::Error)> for ImportError { + fn from(value: (PathBuf, io::Error)) -> Self { + match value.1.kind() { + io::ErrorKind::NotFound => ImportError { + path: value.0.to_str().unwrap().into(), + kind: ImportErrorKind::NotFound, + }, + io::ErrorKind::PermissionDenied => ImportError { + path: value.0.to_str().unwrap().into(), + kind: ImportErrorKind::UnreadableFile, + }, + err => panic!("unexpected IO error occurred during import resolution: {err:?}"), + } + } +} + /// Function that builds a module map of the standard library that is shipped /// with the compiler distribution. Standard library modules are referenced /// within imports @@ -161,15 +177,18 @@ pub fn resolve_path<'p>( let modules = get_stdlib_modules(STDLIB); + let canonicalise = |path: &Path| path.canonicalize().map_err(|err| (path.to_path_buf(), err)); + // check if the given path is equal to any of the standard library paths, and // if so we prefix it with the standard library path. if modules.contains(&import_path.to_path_buf()) { let path = Path::new(STDLIB).join(import_path.with_extension("hash")); - return Ok(path); + + return Ok(canonicalise(&path)?); } // otherwise, we have to resolve the module path based on the working directory - let work_dir = wd.canonicalize().unwrap(); + let work_dir = canonicalise(wd)?; let raw_path = work_dir.join(import_path); // If the provided path is a directory, we assume that the user is referencing @@ -180,13 +199,13 @@ pub fn resolve_path<'p>( let idx_path = raw_path.join("index.hash"); if idx_path.exists() { - return Ok(idx_path); + return Ok(canonicalise(&idx_path)?); } // ok now check if the user is referencing a module instead of the dir let raw_path_hash = raw_path.with_extension("hash"); if raw_path_hash.exists() { - return Ok(raw_path_hash); + return Ok(canonicalise(&raw_path_hash)?); } Err(ImportError { path: path.into(), kind: ImportErrorKind::MissingIndex }) @@ -212,7 +231,7 @@ pub fn resolve_path<'p>( // Only try to check this route if the provided file did not already have an // extension if raw_path.extension().is_none() && raw_path_hash.exists() { - Ok(raw_path_hash) + Ok(canonicalise(&raw_path_hash)?) } else { Err(ImportError { path: path.into(), kind: ImportErrorKind::NotFound }) } diff --git a/compiler/hash-pipeline/src/interface.rs b/compiler/hash-pipeline/src/interface.rs index 99abc93c2..3275207b6 100644 --- a/compiler/hash-pipeline/src/interface.rs +++ b/compiler/hash-pipeline/src/interface.rs @@ -7,9 +7,9 @@ use std::{ time::Duration, }; -use hash_ast::node_map::{InteractiveBlock, ModuleEntry, NodeMap}; +use hash_ast::node_map::NodeMap; use hash_reporting::report::Report; -use hash_source::{ModuleKind, SourceId, SourceMap}; +use hash_source::SourceId; use crate::{ settings::{CompilerSettings, CompilerStageKind}, @@ -159,17 +159,4 @@ pub trait CompilerInterface { /// Get a reference to the current [NodeMap]. fn node_map(&self) -> &NodeMap; - - /// Get a reference to the current [SourceMap]. - fn source_map(&self) -> &SourceMap; - - /// Add a [ModuleEntry] via the [Workspace]. - fn add_module(&mut self, contents: String, module: ModuleEntry, kind: ModuleKind) -> SourceId { - self.workspace_mut().add_module(contents, module, kind) - } - - /// Add a [InteractiveBlock] via the [Workspace]. - fn add_interactive_block(&mut self, input: String, block: InteractiveBlock) -> SourceId { - self.workspace_mut().add_interactive_block(input, block) - } } diff --git a/compiler/hash-pipeline/src/workspace.rs b/compiler/hash-pipeline/src/workspace.rs index 116343cde..e93c8ab9d 100644 --- a/compiler/hash-pipeline/src/workspace.rs +++ b/compiler/hash-pipeline/src/workspace.rs @@ -5,17 +5,14 @@ //! given [ModuleEntry]. This can only be known by the [SourceMap] which stores //! all of the relevant [SourceId]s and their corresponding sources. -use std::{ - collections::HashSet, - path::{Path, PathBuf}, -}; +use std::{collections::HashSet, path::PathBuf}; use hash_ast::{ ast::OwnsAstNode, - node_map::{InteractiveBlock, ModuleEntry, NodeMap}, + node_map::{InteractiveBlock, NodeMap}, }; use hash_ast_utils::dump::{dump_ast, AstDumpMode}; -use hash_source::{ModuleId, ModuleKind, SourceId, SourceMap}; +use hash_source::{ModuleId, ModuleKind, SourceId, SourceMapUtils}; use hash_target::HasTarget; use hash_utils::{ bitflags::bitflags, @@ -178,10 +175,6 @@ pub struct Workspace { /// Dependency map between sources and modules. dependencies: FxHashMap>, - /// Stores all of the raw file contents of the interactive blocks and - /// modules. - pub source_map: SourceMap, - /// Stores all of the generated AST for modules and nodes. pub node_map: NodeMap, @@ -211,7 +204,6 @@ impl Workspace { name, output_directory, executable_path, - source_map: SourceMap::new(), node_map: NodeMap::new(), dependencies: FxHashMap::default(), code_map: CodeMap::default(), @@ -261,7 +253,7 @@ impl Workspace { /// Check whether this [Workspace] will yield an executable. pub fn yields_executable(&self, settings: &CompilerSettings) -> bool { - settings.stage >= CompilerStageKind::Build && self.source_map.entry_point().is_some() + settings.stage >= CompilerStageKind::Build && SourceMapUtils::entry_point().is_some() } /// Get the bitcode path for a particular [ModuleId]. This does not @@ -270,14 +262,12 @@ impl Workspace { /// module that is about to be emitted. pub fn module_bitcode_path(&self, module: ModuleId, extension: &'static str) -> PathBuf { let mut path = self.output_directory.clone(); - let module_path = self.source_map.module_path(module); + let module_path = SourceMapUtils::map(module, |source| { + source.path().file_stem().unwrap().to_str().unwrap().to_string() + }); path.push("build"); - path.push(format!( - "{}-{}.{extension}", - module_path.file_stem().unwrap().to_str().unwrap(), - module.raw() - )); + path.push(format!("{}-{}.{extension}", module_path, module.raw())); path } @@ -285,7 +275,7 @@ impl Workspace { /// the [InteractiveBlock]. Returns the created [InteractiveId] from /// adding it to the source map. pub fn add_interactive_block(&mut self, input: String, block: InteractiveBlock) -> SourceId { - let id = self.source_map.add_interactive_block(input); + let id = SourceMapUtils::add_interactive_block(input); // Add this source to the node map, and to the stage info self.node_map.add_interactive_block(block); @@ -297,29 +287,16 @@ impl Workspace { /// Add a module to the [Workspace] by providing the contents and the /// [ModuleEntry]. Returns the created [SourceId] from adding it to the /// source map. - pub fn add_module( - &mut self, - contents: String, - module: ModuleEntry, - kind: ModuleKind, - ) -> SourceId { - let id = self.source_map.add_module(module.path.to_owned(), contents, kind); + pub fn add_module(&mut self, path: PathBuf, kind: ModuleKind) -> SourceId { + let id = SourceMapUtils::reserve_module(path, kind); // Add this source to the node map, and to the stage info - self.node_map.add_module(module); + // self.node_map.add_module(module); self.source_stage_info.add(id, SourceStageInfo::empty()); id } - /// Get the [SourceId] of the module by the specified [Path]. - /// - /// N.B. This function will never return a [SourceId] for an interactive - /// block. - pub fn get_id_by_path(&self, path: &Path) -> Option { - self.source_map.get_id_by_path(path) - } - /// Add a module dependency specified by a [SourceId] to a specific source /// specified by a [SourceId]. pub fn add_dependency(&mut self, source_id: SourceId, dependency: ModuleId) { @@ -337,11 +314,11 @@ impl Workspace { ) -> std::io::Result<()> { if source.is_interactive() { let node = self.node_map.get_interactive_block(source.into()).node().ast_ref(); - dump_ast(node.into(), mode, character_set, &self.source_map, writer)?; + dump_ast(node.into(), mode, character_set, writer)?; } for module in self.node_map.iter_modules() { - dump_ast(module.node_ref().into(), mode, character_set, &self.source_map, writer)?; + dump_ast(module.node_ref().into(), mode, character_set, writer)?; } Ok(()) diff --git a/compiler/hash-reporting/src/lib.rs b/compiler/hash-reporting/src/lib.rs index 755a85b22..5b0fcd4be 100644 --- a/compiler/hash-reporting/src/lib.rs +++ b/compiler/hash-reporting/src/lib.rs @@ -7,6 +7,5 @@ pub mod macros; mod render; pub mod report; pub mod reporter; -pub mod writer; pub use hash_error_codes; diff --git a/compiler/hash-reporting/src/macros.rs b/compiler/hash-reporting/src/macros.rs index fe2f8ea2c..3a292fb3e 100644 --- a/compiler/hash-reporting/src/macros.rs +++ b/compiler/hash-reporting/src/macros.rs @@ -8,7 +8,7 @@ use hash_utils::stream_less_ewriteln; /// is a useful utility to denote where a panic occurs and provide additional /// context about where the panic occurred in regards to traversing the sources. pub macro panic_on_span { - ($location:expr, $sources:expr, $fmt: expr) => { + ($location:expr, $fmt: expr) => { { let mut reporter = $crate::reporter::Reporter::new(); @@ -16,12 +16,12 @@ pub macro panic_on_span { .title($fmt) .add_labelled_span($location, "here"); - stream_less_ewriteln!("{}", $crate::writer::ReportWriter::new(reporter.into_reports(), $sources)); + stream_less_ewriteln!("{}", reporter); std::panic::panic_any("A fatal error occurred during compilation on the reported node"); } }, - ($location:expr, $sources:expr, $fmt: expr, $($arg:tt)*) => { - panic_on_span!($location, $sources, format!($fmt, $($arg)*)) + ($location:expr, $fmt: expr, $($arg:tt)*) => { + panic_on_span!($location, format!($fmt, $($arg)*)) } } @@ -29,8 +29,8 @@ pub macro panic_on_span { /// standard output, this does not panic, it is intended as a debugging utility /// to quickly print the `span` of something and the `message` associated with /// it. -pub macro compiler_note { - ($location:expr, $sources:expr, $fmt: expr) => { +pub macro note_on_span { + ($location:expr, $fmt: expr) => { { use hash_utils::stream_less_ewriteln; @@ -39,10 +39,10 @@ pub macro compiler_note { .title($fmt) .add_labelled_span($location, "here"); - stream_less_ewriteln!("{}", $crate::writer::ReportWriter::new(reporter.into_reports(), $sources)); + stream_less_ewriteln!("{}", reporter); } }, - ($location:expr, $sources:expr, $fmt: expr, $($arg:tt)*) => { - compiler_note!($location, $sources, format!($fmt, $($arg)*)) + ($location:expr, $fmt: expr, $($arg:tt)*) => { + compiler_note!($location, format!($fmt, $($arg)*)) } } diff --git a/compiler/hash-reporting/src/render.rs b/compiler/hash-reporting/src/render.rs index 0d9ee81a0..25985be67 100644 --- a/compiler/hash-reporting/src/render.rs +++ b/compiler/hash-reporting/src/render.rs @@ -14,10 +14,13 @@ use std::{ }; use hash_source::{ - location::{RowCol, RowColRange, Span}, - SourceMap, + location::{RowColRange, Span}, + Source, SourceMapUtils, +}; +use hash_utils::{ + highlight::{highlight, Colour, Modifier}, + range_map::Range, }; -use hash_utils::highlight::{highlight, Colour, Modifier}; use crate::report::{ ReportCodeBlock, ReportCodeBlockInfo, ReportElement, ReportKind, ReportNote, ReportNoteKind, @@ -65,54 +68,41 @@ fn compute_buffers(start_row: usize, end_row: usize) -> (usize, usize) { impl ReportCodeBlock { // Get the indent widths of this code block as (outer, inner). - pub(crate) fn info(&self, sources: &SourceMap) -> ReportCodeBlockInfo { - match self.info.get() { - Some(info) => info, - None => { - let Span { range: span, id } = self.source_location; - let source = sources.line_ranges(id); - - // Compute offset rows and columns from the provided span - let start @ RowCol { row: start_row, .. } = source.get_row_col(span.start()); - let end @ RowCol { row: end_row, .. } = source.get_row_col(span.end()); - let RowCol { row: last_row, .. } = - source.get_row_col(sources.contents(id).0.len() - 1); - - // Compute the selected span outside of the diagnostic span - let (top_buf, bottom_buf) = compute_buffers(start_row, end_row); - - // Compute the size of the indent based on the line numbers - let indent_width = (start_row.saturating_sub(top_buf) + 1) - .max((end_row + bottom_buf).min(last_row) + 1) - .to_string() - .chars() - .count(); - - let span = RowColRange::new(start, end); - let info = ReportCodeBlockInfo { indent_width, span }; - - self.info.replace(Some(info)); - info - } - } + pub(crate) fn info(&self, source: &Source) -> &ReportCodeBlockInfo { + self.info.get_or_init(|| { + let Span { range, .. } = self.span; + let ranges = source.line_ranges(); + let (start, end, last) = ( + ranges.get_row_col(range.start()), + ranges.get_row_col(range.end()), + ranges.get_row_col(source.contents().0.len() - 1), + ); + + // Compute the selected span outside of the diagnostic span + let (top_buf, bottom_buf) = compute_buffers(start.row, end.row); + + // Compute the size of the indent based on the line numbers + let indent_width = (start.row.saturating_sub(top_buf) + 1) + .max((end.row + bottom_buf).min(last.row) + 1) + .to_string() + .chars() + .count(); + + let span = RowColRange::new(start, end); + ReportCodeBlockInfo { indent_width, span } + }) } /// Function to extract the block of the code that will be used to display /// the span of the diagnostic. - fn get_source_view<'a>( - &self, - modules: &'a SourceMap, - ) -> impl Iterator { - // Get the actual contents of the erroneous span - let source_id = self.source_location.id; - let source = modules.contents(source_id); - - let ReportCodeBlockInfo { span, .. } = self.info(modules); + fn get_source_view<'a>(&self, source: &'a Source) -> impl Iterator { + let ReportCodeBlockInfo { span, .. } = self.info(source); let (start_row, end_row) = span.rows(); - let (top_buffer, bottom_buffer) = compute_buffers(start_row, end_row); + // Get the actual contents of the erroneous span source + .contents() .0 .lines() .enumerate() @@ -137,13 +127,12 @@ impl ReportCodeBlock { fn render_line_view( &self, f: &mut fmt::Formatter, - modules: &SourceMap, + source: &Source, longest_indent_width: usize, - report_kind: ReportKind, + kind: ReportKind, ) -> fmt::Result { - let error_view = self.get_source_view(modules); - - let ReportCodeBlockInfo { span, .. } = self.info(modules); + let error_view = self.get_source_view(source); + let ReportCodeBlockInfo { span, .. } = self.info(source); let (start_row, end_row) = span.rows(); let (start_column, end_column) = span.columns(); @@ -153,7 +142,7 @@ impl ReportCodeBlock { let index_str = format!("{:>longest_indent_width$}", index + 1); let line_number = if (start_row..=end_row).contains(&index) { - highlight(report_kind.as_colour(), &index_str) + highlight(kind.as_colour(), &index_str) } else { index_str }; @@ -187,7 +176,7 @@ impl ReportCodeBlock { "{} {} {}", " ".repeat(longest_indent_width), highlight(Colour::Blue, "|"), - highlight(report_kind.as_colour(), &code_note) + highlight(kind.as_colour(), &code_note) )?; } } @@ -223,13 +212,12 @@ impl ReportCodeBlock { fn render_block_view( &self, f: &mut fmt::Formatter, - modules: &SourceMap, + source: &Source, longest_indent_width: usize, report_kind: ReportKind, ) -> fmt::Result { - let error_view = self.get_source_view(modules); - - let ReportCodeBlockInfo { span, .. } = self.info(modules); + let error_view = self.get_source_view(source); + let ReportCodeBlockInfo { span, .. } = self.info(source); // If the difference between the rows is longer than `LINE_SKIP_THRESHOLD` // lines, then we essentially begin to collapse the view by using `...` @@ -239,7 +227,7 @@ impl ReportCodeBlock { let skip_lines_range = if end_row - start_row > LINE_SKIP_THRESHOLD { let mid = LINE_SKIP_THRESHOLD / 2; - Some((start_row + mid)..=(end_row - mid)) + Some(Range::new(start_row + mid, end_row - mid)) } else { None }; @@ -270,8 +258,8 @@ impl ReportCodeBlock { }; // So if we're at the start of the 'skip' range, use '...' instead - if let Some(range) = skip_lines_range.clone() { - if *range.start() == index { + if let Some(range) = skip_lines_range { + if range.start() == index { let range_line_number = format!( "{: fmt::Result { - let source_id = self.source_location.id; - let ReportCodeBlockInfo { span, .. } = self.info(source_map); - - // Print the filename of the code block... - writeln!( - f, - "{}{} {}", - " ".repeat(longest_indent_width), - highlight(Colour::Blue, "-->"), - highlight( - Modifier::Underline, - format!("{}:{}", source_map.canonicalised_path_by_id(source_id), span.start) - ) - )?; - - // Now we can determine whether we want to use the `block` or the `line` view. - // The block view is for displaying large spans for multiple lines, - // whilst the line view is for a single line span. - let (start_row, end_row) = span.rows(); + SourceMapUtils::map(self.span.id, |source| { + let ReportCodeBlockInfo { span, .. } = self.info(source); - if start_row == end_row { - self.render_line_view(f, source_map, longest_indent_width, report_kind) - } else { - self.render_block_view(f, source_map, longest_indent_width, report_kind) - } + // Print the filename of the code block... + writeln!( + f, + "{}{} {}", + " ".repeat(longest_indent_width), + highlight(Colour::Blue, "-->"), + highlight( + Modifier::Underline, + format!("{}:{}", source.canonicalised_path().display(), span.start) + ) + )?; + + // Now we can determine whether we want to use the `block` or the `line` view. + // The block view is for displaying large spans for multiple lines, + // whilst the line view is for a single line span. + let (start_row, end_row) = span.rows(); + + if start_row == end_row { + self.render_line_view(f, source, longest_indent_width, report_kind) + } else { + self.render_block_view(f, source, longest_indent_width, report_kind) + } + }) } } @@ -425,13 +413,12 @@ impl ReportElement { pub(crate) fn render( &self, f: &mut fmt::Formatter, - modules: &SourceMap, longest_indent_width: usize, report_kind: ReportKind, ) -> fmt::Result { match self { ReportElement::CodeBlock(code_block) => { - code_block.render(f, modules, longest_indent_width, report_kind) + code_block.render(f, longest_indent_width, report_kind) } ReportElement::Note(note) => note.render(f, longest_indent_width), } diff --git a/compiler/hash-reporting/src/report.rs b/compiler/hash-reporting/src/report.rs index 5875c9302..506ce0c17 100644 --- a/compiler/hash-reporting/src/report.rs +++ b/compiler/hash-reporting/src/report.rs @@ -1,8 +1,11 @@ //! Hash diagnostic report data structures. -use std::{cell::Cell, fmt}; +use std::{cell::OnceCell, fmt}; use hash_error_codes::error_codes::HashErrorCode; -use hash_source::location::{RowColRange, Span}; +use hash_source::{ + location::{RowColRange, Span}, + SourceMapUtils, +}; use hash_utils::highlight::{highlight, Colour, Modifier}; /// A data type representing a comment/message on a specific span in a code @@ -117,15 +120,19 @@ impl ReportNote { /// optional [ReportCodeBlockInfo] which adds a message pointed to a code item. #[derive(Debug, Clone)] pub struct ReportCodeBlock { - pub source_location: Span, + pub span: Span, pub code_message: String, - pub(crate) info: Cell>, + pub(crate) info: OnceCell, } impl ReportCodeBlock { /// Create a new [ReportCodeBlock] from a [Span] and a message. pub fn new(source_location: Span, code_message: impl ToString) -> Self { - Self { source_location, code_message: code_message.to_string(), info: Cell::new(None) } + Self { + span: source_location, + code_message: code_message.to_string(), + info: OnceCell::new(), + } } } @@ -137,6 +144,13 @@ pub enum ReportElement { Note(ReportNote), } +/// Create a help note with the given message. +pub macro help { + ($($arg:tt)*) => { + ReportElement::Note(ReportNote::new(ReportNoteKind::Help, format!($($arg)*))) + } +} + /// The report data type represents the entire report which might contain many /// [ReportElement]s. The report also contains a general [ReportKind] and a /// general message. @@ -253,9 +267,40 @@ impl Default for Report { } } -/// Create a help note with the given message. -pub macro help { - ($($arg:tt)*) => { - ReportElement::Note(ReportNote::new(ReportNoteKind::Help, format!($($arg)*))) +impl fmt::Display for Report { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + // Add the optional error code to the general message... + let error_code_fmt = match self.error_code { + Some(error_code) => highlight( + self.kind.as_colour() | Modifier::Bold, + format!("[{:0>4}]", error_code.to_num()), + ), + None => String::new(), + }; + + // Add the general note about the report... + writeln!(f, "{}{}: {}", self.kind, error_code_fmt, highlight(Modifier::Bold, &self.title),)?; + + let longest_indent_width = + self.contents.iter().fold(0, |longest_indent_width, element| match element { + ReportElement::CodeBlock(code_block) => { + SourceMapUtils::map(code_block.span.id, |source| { + code_block.info(source).indent_width.max(longest_indent_width) + }) + } + ReportElement::Note(_) => longest_indent_width, + }); + + let mut iter = self.contents.iter().peekable(); + + while let Some(note) = iter.next() { + note.render(f, longest_indent_width, self.kind)?; + + if matches!(iter.peek(), Some(ReportElement::CodeBlock(_))) { + writeln!(f)?; + } + } + + Ok(()) } } diff --git a/compiler/hash-reporting/src/reporter.rs b/compiler/hash-reporting/src/reporter.rs index f1aabec28..6a41e644e 100644 --- a/compiler/hash-reporting/src/reporter.rs +++ b/compiler/hash-reporting/src/reporter.rs @@ -1,6 +1,8 @@ //! A diagnostic reporter for the Hash compiler. //! //! Has a fluent API for creating reports in a declarative way. +use std::fmt; + use crate::report::{Report, ReportKind}; pub type Reports = Vec; @@ -53,3 +55,12 @@ impl Reporter { self.reports } } + +impl fmt::Display for Reporter { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + for report in &self.reports { + write!(f, "{report}")?; + } + Ok(()) + } +} diff --git a/compiler/hash-reporting/src/writer.rs b/compiler/hash-reporting/src/writer.rs deleted file mode 100644 index a4c02d555..000000000 --- a/compiler/hash-reporting/src/writer.rs +++ /dev/null @@ -1,70 +0,0 @@ -//! Hash diagnostic report writing utilities and definitions. -use std::fmt; - -use hash_source::SourceMap; -use hash_utils::highlight::{highlight, Modifier}; - -use crate::{ - report::{Report, ReportElement}, - reporter::Reports, -}; - -/// General data type for displaying [Report]s. This is needed due to the -/// [Report] rendering process needing access to the program modules to get -/// access to the source code. -pub struct ReportWriter<'m> { - reports: Reports, - sources: &'m SourceMap, -} - -impl<'m> ReportWriter<'m> { - pub fn new(reports: Reports, sources: &'m SourceMap) -> Self { - Self { reports, sources } - } - pub fn single(report: Report, sources: &'m SourceMap) -> Self { - Self { reports: vec![report], sources } - } -} - -impl fmt::Display for ReportWriter<'_> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - for report in &self.reports { - // Add the optional error code to the general message... - let error_code_fmt = match report.error_code { - Some(error_code) => highlight( - report.kind.as_colour() | Modifier::Bold, - format!("[{:0>4}]", error_code.to_num()), - ), - None => String::new(), - }; - - // Add the general note about the report... - writeln!( - f, - "{}{}: {}", - report.kind, - error_code_fmt, - highlight(Modifier::Bold, &report.title), - )?; - - let longest_indent_width = - report.contents.iter().fold(0, |longest_indent_width, element| match element { - ReportElement::CodeBlock(code_block) => { - code_block.info(self.sources).indent_width.max(longest_indent_width) - } - ReportElement::Note(_) => longest_indent_width, - }); - - let mut iter = report.contents.iter().peekable(); - - while let Some(note) = iter.next() { - note.render(f, self.sources, longest_indent_width, report.kind)?; - - if matches!(iter.peek(), Some(ReportElement::CodeBlock(_))) { - writeln!(f)?; - } - } - } - Ok(()) - } -} diff --git a/compiler/hash-semantics/src/lib.rs b/compiler/hash-semantics/src/lib.rs index 5aaa13a8d..f92570051 100644 --- a/compiler/hash-semantics/src/lib.rs +++ b/compiler/hash-semantics/src/lib.rs @@ -123,7 +123,6 @@ impl CompilerStage for SemanticAnalysis { let env = Env::new( &semantic_storage.context, &workspace.node_map, - &workspace.source_map, settings.target(), ¤t_source_info, ); diff --git a/compiler/hash-semantics/src/passes/discovery/defs.rs b/compiler/hash-semantics/src/passes/discovery/defs.rs index 3238dc643..099e8e5a3 100644 --- a/compiler/hash-semantics/src/passes/discovery/defs.rs +++ b/compiler/hash-semantics/src/passes/discovery/defs.rs @@ -3,7 +3,7 @@ use std::{fmt::Display, option::Option}; use hash_ast::ast::{self, AstNode, AstNodeId, AstNodeRef, OwnsAstNode}; use hash_reporting::macros::panic_on_span; -use hash_source::{identifier::Identifier, ModuleId}; +use hash_source::{identifier::Identifier, ModuleId, SourceMapUtils}; use hash_storage::store::{ statics::{SequenceStoreValue, StoreId}, DefaultPartialStore, PartialStore, SequenceStoreKey, StoreKey, @@ -191,32 +191,21 @@ impl<'tc> DiscoveryPass<'tc> { None => { // Create a new module definition. let source_id = module_id.into(); - let module_name: Identifier = self.source_map().source_name(source_id).into(); + let module_name: Identifier = + SourceMapUtils::map(source_id, |source| source.name().into()); + // @@MissingOrigin - let mod_def_id = Node::create_at( + Node::create_at( ModDef { name: SymbolId::from_name(module_name, NodeOrigin::Generated), - kind: ModKind::Source( - source_id, - // @@Hack: leak the path to still allow ModKind to implement Copy. - // We need the path inside ModKind so that we can print it without - // requiring access to source map. Ideally SourceMap should be static - // so that this is not needed. - Box::leak( - self.source_map() - .source_path(source_id) - .to_path_buf() - .into_boxed_path(), - ), - ), + kind: ModKind::Source(source_id), members: Node::create_at( Node::::empty_seq(), NodeOrigin::Generated, ), }, NodeOrigin::Generated, - ); - mod_def_id + ) } } } @@ -430,7 +419,7 @@ impl<'tc> DiscoveryPass<'tc> { Some(ast::Expr::Block(block)) => block.data.id(), Some(_) => node.value.as_ref().unwrap().id(), _ => { - panic_on_span!(node.span(), self.source_map(), "Found declaration without value") + panic_on_span!(node.span(), "Found declaration without value") } }; @@ -438,8 +427,7 @@ impl<'tc> DiscoveryPass<'tc> { Some(value) => match value.body() { // Import ast::Expr::Import(import_expr) => { - let source_id = - self.source_map().get_id_by_path(&import_expr.data.resolved_path).unwrap(); + let source_id = import_expr.data.source; let imported_mod_def_id = self.create_or_get_module_mod_def(source_id.into()); Some(ModMember { name, value: ModMemberValue::Mod(imported_mod_def_id) }) } @@ -507,7 +495,6 @@ impl<'tc> DiscoveryPass<'tc> { /// It leaves types as holes and values as `None`, because they will be /// resolved at a later stage. pub(super) fn add_stack_members_in_pat_to_buf( - &self, node: AstNodeRef, buf: &mut SmallVec<[(AstNodeId, Decl); 3]>, ) { @@ -541,18 +528,14 @@ impl<'tc> DiscoveryPass<'tc> { } ast::Pat::Module(_) => { // This should have been handled pre-tc semantics - panic_on_span!( - node.span(), - self.source_map(), - "Found module pattern in stack definition" - ) + panic_on_span!(node.span(), "Found module pattern in stack definition") } ast::Pat::Tuple(ast::TuplePat { fields, spread }) => { if let Some(spread_node) = &spread { register_spread_pat(spread_node, buf); } for field in fields.ast_ref_iter() { - self.add_stack_members_in_pat_to_buf(field.pat.ast_ref(), buf); + Self::add_stack_members_in_pat_to_buf(field.pat.ast_ref(), buf); } } ast::Pat::Constructor(ast::ConstructorPat { fields, spread, .. }) => { @@ -560,11 +543,11 @@ impl<'tc> DiscoveryPass<'tc> { register_spread_pat(spread_node, buf); } for field in fields.ast_ref_iter() { - self.add_stack_members_in_pat_to_buf(field.pat.ast_ref(), buf); + Self::add_stack_members_in_pat_to_buf(field.pat.ast_ref(), buf); } } ast::Pat::Macro(ast::PatMacroInvocation { subject, .. }) => { - self.add_stack_members_in_pat_to_buf(subject.ast_ref(), buf) + Self::add_stack_members_in_pat_to_buf(subject.ast_ref(), buf) } ast::Pat::Array(ast::ArrayPat { fields, spread }) => { if let Some(spread_node) = &spread { @@ -572,16 +555,18 @@ impl<'tc> DiscoveryPass<'tc> { } for field in fields.ast_ref_iter() { - self.add_stack_members_in_pat_to_buf(field, buf); + Self::add_stack_members_in_pat_to_buf(field, buf); } } ast::Pat::Or(or_pat) => match or_pat.variants.get(0) { // @@Invariant: Here we assume that each branch of the or pattern has the same // members This should have already been checked at pre-tc semantics. - Some(pat) => self.add_stack_members_in_pat_to_buf(pat.ast_ref(), buf), - None => panic_on_span!(node.span(), self.source_map(), "Found empty or pattern"), + Some(pat) => Self::add_stack_members_in_pat_to_buf(pat.ast_ref(), buf), + None => panic_on_span!(node.span(), "Found empty or pattern"), }, - ast::Pat::If(if_pat) => self.add_stack_members_in_pat_to_buf(if_pat.pat.ast_ref(), buf), + ast::Pat::If(if_pat) => { + Self::add_stack_members_in_pat_to_buf(if_pat.pat.ast_ref(), buf) + } ast::Pat::Wild(_) => buf.push(( node.id(), Decl { @@ -625,7 +610,7 @@ impl<'tc> DiscoveryPass<'tc> { found_members .push((node.id(), Decl { name: declaration_name, ty: None, value: None })) } - _ => self.add_stack_members_in_pat_to_buf(node, &mut found_members), + _ => Self::add_stack_members_in_pat_to_buf(node, &mut found_members), } for (node_id, stack_member) in found_members { members.push((node_id, stack_member.into())); diff --git a/compiler/hash-semantics/src/passes/discovery/visitor.rs b/compiler/hash-semantics/src/passes/discovery/visitor.rs index c9e071b55..c47a28c25 100644 --- a/compiler/hash-semantics/src/passes/discovery/visitor.rs +++ b/compiler/hash-semantics/src/passes/discovery/visitor.rs @@ -86,7 +86,6 @@ impl<'tc> ast::AstVisitor for DiscoveryPass<'tc> { DefId::Data(_) => { panic_on_span!( node.span(), - self.source_map(), "found declaration in data definition scope, which should have been handled earlier" ) } @@ -111,7 +110,6 @@ impl<'tc> ast::AstVisitor for DiscoveryPass<'tc> { DefId::Fn(_) => { panic_on_span!( node.span(), - self.source_map(), "found declaration in function scope, which should instead be in a stack scope" ) } @@ -119,16 +117,11 @@ impl<'tc> ast::AstVisitor for DiscoveryPass<'tc> { Some(ItemId::Ty(_)) => { panic_on_span!( node.span(), - self.source_map(), "found declaration in function type scope, which should instead be in a stack scope" ) } None => { - panic_on_span!( - node.span(), - self.source_map(), - "found declaration before any scopes" - ) + panic_on_span!(node.span(), "found declaration before any scopes") } }; @@ -466,9 +459,8 @@ impl<'tc> ast::AstVisitor for DiscoveryPass<'tc> { type ImportRet = (); fn visit_import(&self, node: AstNodeRef) -> Result { - let source_id = self.source_map().get_id_by_path(&node.resolved_path).unwrap(); self.current_source_info() - .with_source_id(source_id, || DiscoveryPass::new(self.sem_env()).pass_source())?; + .with_source_id(node.source, || DiscoveryPass::new(self.sem_env()).pass_source())?; Ok(()) } } diff --git a/compiler/hash-semantics/src/passes/evaluation/mod.rs b/compiler/hash-semantics/src/passes/evaluation/mod.rs index 74b921ea8..ffd9faefb 100644 --- a/compiler/hash-semantics/src/passes/evaluation/mod.rs +++ b/compiler/hash-semantics/src/passes/evaluation/mod.rs @@ -42,7 +42,7 @@ impl EvaluationPass<'_> { /// Find the main module definition, if it exists. fn find_and_construct_main_call(&self) -> SemanticResult> { let source_id = self.current_source_info().source_id(); - let kind = self.source_map().module_kind_by_id(source_id); + let kind = source_id.module_kind(); match kind { None | Some(ModuleKind::Normal | ModuleKind::Prelude) => Ok(None), Some(ModuleKind::EntryPoint) => { diff --git a/compiler/hash-semantics/src/passes/resolution/defs.rs b/compiler/hash-semantics/src/passes/resolution/defs.rs index 126ad6794..5ca2266ba 100644 --- a/compiler/hash-semantics/src/passes/resolution/defs.rs +++ b/compiler/hash-semantics/src/passes/resolution/defs.rs @@ -213,10 +213,7 @@ impl<'tc> ResolutionPass<'tc> { } ast::Expr::Import(import_expr) => { // If it's an import, resolve the source - let source_id = self - .source_map() - .get_id_by_path(&import_expr.data.resolved_path) - .unwrap(); + let source_id = import_expr.data.source; self.current_source_info().with_source_id(source_id, || { ResolutionPass::new(self.sem_env()).pass_source() })?; diff --git a/compiler/hash-semantics/src/passes/resolution/exprs.rs b/compiler/hash-semantics/src/passes/resolution/exprs.rs index ec84433c4..8a66e0659 100644 --- a/compiler/hash-semantics/src/passes/resolution/exprs.rs +++ b/compiler/hash-semantics/src/passes/resolution/exprs.rs @@ -191,8 +191,7 @@ impl<'tc> ResolutionPass<'tc> { } ast::Expr::Import(import_expr) => { - let source_id = - self.source_map().get_id_by_path(&import_expr.data.resolved_path).unwrap(); + let source_id = import_expr.data.source; self.current_source_info().with_source_id(source_id, || { ResolutionPass::new(self.sem_env()).pass_source() })?; @@ -261,11 +260,7 @@ impl<'tc> ResolutionPass<'tc> { } ast::PropertyKind::NumericField(_) => { // Should have been caught at semantics - panic_on_span!( - node.span(), - self.source_map(), - "Namespace followed by numeric field found" - ) + panic_on_span!(node.span(), "Namespace followed by numeric field found") } }, ast::AccessKind::Property => Ok(None), @@ -352,11 +347,7 @@ impl<'tc> ResolutionPass<'tc> { Ok(Term::from(Term::FnRef(*fn_def_id), origin)) } TerminalResolvedPathComponent::CtorPat(_) => { - panic_on_span!( - original_node_id.span(), - self.source_map(), - "found CtorPat in value ast path" - ) + panic_on_span!(original_node_id.span(), "found CtorPat in value ast path") } TerminalResolvedPathComponent::CtorTerm(ctor_term) => { // Constructor @@ -771,7 +762,6 @@ impl<'tc> ResolutionPass<'tc> { .unwrap_or_else(|| { panic_on_span!( node.span(), - self.source_map(), "Found non-stack body block in make_term_from_ast_body_block" ) }) @@ -823,7 +813,6 @@ impl<'tc> ResolutionPass<'tc> { ast::Block::For(_) | ast::Block::While(_) | ast::Block::If(_) => { panic_on_span!( node.span(), - self.source_map(), "Found non-desugared block in make_term_from_ast_block_expr" ) } diff --git a/compiler/hash-semantics/src/passes/resolution/mod.rs b/compiler/hash-semantics/src/passes/resolution/mod.rs index fd6f478bf..4900544ea 100644 --- a/compiler/hash-semantics/src/passes/resolution/mod.rs +++ b/compiler/hash-semantics/src/passes/resolution/mod.rs @@ -6,7 +6,6 @@ use hash_ast::ast::{self}; use hash_intrinsics::intrinsics::{AccessToIntrinsics, DefinedIntrinsics}; -use hash_source::ModuleKind; use hash_tir::environment::env::AccessToEnv; use self::scoping::{ContextKind, Scoping}; @@ -81,10 +80,9 @@ impl<'tc> AstPass for ResolutionPass<'tc> { } let mod_def_id = self.resolve_ast_module_inner_terms(node)?; + let source = self.current_source_info().source_id(); - if let Some(ModuleKind::Prelude) = - self.source_map().module_kind_by_id(self.current_source_info().source_id()) - { + if source.is_prelude() { let _ = self.prelude_or_unset().set(mod_def_id); } diff --git a/compiler/hash-semantics/src/passes/resolution/pats.rs b/compiler/hash-semantics/src/passes/resolution/pats.rs index ffcf33554..5da09efc1 100644 --- a/compiler/hash-semantics/src/passes/resolution/pats.rs +++ b/compiler/hash-semantics/src/passes/resolution/pats.rs @@ -15,7 +15,6 @@ use hash_tir::{ arrays::ArrayPat, control::{IfPat, OrPat}, data::CtorPat, - environment::env::AccessToEnv, lits::{CharLit, Lit, LitPat, StrLit}, node::{Node, NodeId, NodeOrigin}, params::ParamIndex, @@ -235,7 +234,6 @@ impl ResolutionPass<'_> { TerminalResolvedPathComponent::CtorTerm(_) => { panic_on_span!( original_node_id.span(), - self.source_map(), "Found constructor term in pattern, expected constructor pattern" ) } @@ -338,11 +336,7 @@ impl ResolutionPass<'_> { } ast::Pat::Module(_) => { // This should be handled earlier - panic_on_span!( - node.span(), - self.source_map(), - "Found module pattern during symbol resolution" - ) + panic_on_span!(node.span(), "Found module pattern during symbol resolution") } ast::Pat::Tuple(tuple_pat) => Node::create_at( Pat::Tuple(TuplePat { diff --git a/compiler/hash-semantics/src/passes/resolution/tys.rs b/compiler/hash-semantics/src/passes/resolution/tys.rs index df08e6e65..46f367799 100644 --- a/compiler/hash-semantics/src/passes/resolution/tys.rs +++ b/compiler/hash-semantics/src/passes/resolution/tys.rs @@ -13,7 +13,6 @@ use hash_storage::store::statics::SequenceStoreValue; use hash_tir::{ args::{Arg, ArgsId}, data::DataTy, - environment::env::AccessToEnv, fns::FnCallTerm, node::{Node, NodeOrigin}, params::ParamIndex, @@ -140,11 +139,7 @@ impl<'tc> ResolutionPass<'tc> { Ok(Term::from(Term::FnRef(*fn_def_id), origin).as_ty()) } TerminalResolvedPathComponent::CtorPat(_) => { - panic_on_span!( - original_node_id.span(), - self.source_map(), - "found CtorPat in type ast path" - ) + panic_on_span!(original_node_id.span(), "found CtorPat in type ast path") } TerminalResolvedPathComponent::CtorTerm(ctor_term) => { Ok(Term::from(Term::Ctor(**ctor_term), origin).as_ty()) @@ -405,7 +400,7 @@ impl<'tc> ResolutionPass<'tc> { Ty::from(expr, NodeOrigin::Given(node.id())) } ast::Ty::Union(_) => { - panic_on_span!(node.span(), self.source_map(), "Found union type after discovery") + panic_on_span!(node.span(), "Found union type after discovery") } }; diff --git a/compiler/hash-source/src/lib.rs b/compiler/hash-source/src/lib.rs index d44b49a48..a9e35f379 100644 --- a/compiler/hash-source/src/lib.rs +++ b/compiler/hash-source/src/lib.rs @@ -7,19 +7,18 @@ pub mod identifier; pub mod location; use std::{ + collections::HashMap, fmt, - ops::{Deref, DerefMut}, path::{Path, PathBuf}, }; -use bimap::BiMap; use hash_utils::{ index_vec::{define_index_type, index_vec, IndexVec}, + parking_lot::RwLock, path::adjust_canonicalisation, - range_map::RangeMap, }; -use location::{ByteRange, RowCol, RowColRange, Span}; -use once_cell::sync::OnceCell; +use location::{ByteRange, LineRanges, RowColRange, SpannedSource}; +use once_cell::sync::{Lazy, OnceCell}; /// Used to check what kind of [SourceId] is being /// stored, i.e. the most significant bit denotes whether @@ -95,16 +94,19 @@ impl SourceId { } /// Check whether the [SourceId] points to a module. + #[inline] pub fn is_module(&self) -> bool { self._raw >> 31 == 1 } /// Check whether the [SourceId] points to a interactive block. + #[inline] pub fn is_interactive(&self) -> bool { self._raw >> 31 == 0 } /// Check whether the [SourceId] points to the prelude. + #[inline] pub fn is_prelude(&self) -> bool { self._raw == SOURCE_KIND_MASK } @@ -115,6 +117,16 @@ impl SourceId { // clear the last bit self._raw & 0x7fff_ffff } + + /// Check the [ModuleKind] of a given [SourceId]. + pub fn module_kind(&self) -> Option { + if self.is_interactive() { + return None; + } + + let value = self.value(); + SOURCE_MAP.read().modules.get(value as usize).map(|module| module.kind) + } } impl From for SourceId { @@ -164,155 +176,139 @@ pub enum ModuleKind { EntryPoint, } -/// This struct is used a wrapper for a [RangeMap] in order to -/// implement a nice display format, amongst other things. However, -/// it is a bit of a @@Hack, but I don't think there is really any other -/// better way to do this. #[derive(Debug)] -pub struct LineRanges(RangeMap); - -impl LineRanges { - /// Create a line range from a string slice. - pub fn new_from_str(s: &str) -> Self { - // Pre-allocate the line ranges to a specific size by counting the number of - // newline characters within the module source. - let mut ranges = Self(RangeMap::with_capacity(bytecount::count(s.as_bytes(), b'\n'))); - - // Now, iterate through the source and record the position of each newline - // range, and push it into the map. - let mut count = 0; - - for line in s.lines() { - ranges.append(count..=(count + line.len()), ()); - count += line.len() + 1; - } +enum SourceKind { + Module { + /// The path of the module. + path: PathBuf, + }, + + /// An interactive block. + Interactive, +} - ranges - } +#[derive(Debug)] +pub struct Source { + /// The contents of the module. + contents: String, - /// Get a [RowCol] from a given byte index. - pub fn get_row_col(&self, index: usize) -> RowCol { - let ranges = &self.0; - let line = ranges.index_wrapping(index); - let key = ranges.key_wrapping(index); - let offset = key.start(); + /// Line ranges for fast lookups, this is only computed when it is needed. + line_ranges: OnceCell, - RowCol { row: line, column: index - offset } + /// Canonicalised version of the path. + canonicalised_path: OnceCell, + + extra: SourceKind, +} + +impl Source { + fn new(contents: String, extra: SourceKind) -> Self { + Self { contents, line_ranges: OnceCell::new(), canonicalised_path: OnceCell::new(), extra } } - /// Returns the line and column of the given [ByteRange] - pub fn row_cols(&self, range: ByteRange) -> RowColRange { - let start = self.get_row_col(range.start()); - let end = self.get_row_col(range.end()); + pub fn is_interactive(&self) -> bool { + matches!(self.extra, SourceKind::Interactive) + } - RowColRange { start, end } + /// Get the contents of the module as a [SpannedSource]. + pub fn contents(&self) -> SpannedSource<'_> { + SpannedSource(&self.contents) } -} -impl Deref for LineRanges { - type Target = RangeMap; + /// Get the contents of the module as a [String]. + pub fn owned_contents(&self) -> String { + self.contents.clone() + } - fn deref(&self) -> &Self::Target { - &self.0 + /// Get a hunk of the source by the specified [ByteRange]. + pub fn hunk(&self, range: ByteRange) -> &str { + &self.contents[range.start()..range.end()] } -} -impl DerefMut for LineRanges { - fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.0 + pub fn row_cols(&self, range: ByteRange) -> RowColRange { + self.line_ranges().row_cols(range) } -} -impl fmt::Display for LineRanges { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - for (index, (key, _)) in self.iter().enumerate() { - writeln!(f, "{key}: {}", index + 1)?; + /// Get the line ranges for this particular module. + pub fn line_ranges(&self) -> &LineRanges { + self.line_ranges.get_or_init(|| LineRanges::new_from_str(&self.contents)) + } + + /// Get the path of the module. + pub fn path(&self) -> &Path { + match &self.extra { + SourceKind::Module { path, .. } => path.as_path(), + SourceKind::Interactive => Path::new(""), } + } - Ok(()) + pub fn canonicalised_path(&self) -> &Path { + self.canonicalised_path + .get_or_init(|| match &self.extra { + SourceKind::Module { path } => adjust_canonicalisation(path), + SourceKind::Interactive => PathBuf::from(""), + }) + .as_path() } -} -/// A [Source] is a wrapper around the contents of a source file that -/// is stored in [SourceMap]. It features useful methods for extracting -/// and reading sections of the source by using [Span] or [ByteRange]s. -#[derive(Clone, Copy)] -pub struct Source<'s>(pub &'s str); + /// Get the name of the [Source]. + pub fn name(&self) -> &str { + match &self.extra { + SourceKind::Module { path, .. } => { + let prefix = path.file_prefix().unwrap(); -impl<'s> Source<'s> { - /// Create a [Source] from a [String]. - pub fn from_string(s: &'s str) -> Self { - Self(s) - } + // deal with `index.hash` case... + if prefix == "index" { + if let Some(parent) = path.parent() { + // Now we should be at the `parent` direct + return parent.file_name().unwrap_or(prefix).to_str().unwrap(); + } + } - /// Get a hunk of the source by the specified [ByteRange]. - pub fn hunk(&self, range: ByteRange) -> &'s str { - &self.0[range.start()..range.end()] + // If it is a normal filename, then just use the resultant prefix, or default + // to this if trying to extract the name of the parent fails... for example + // `/index.hash` + prefix.to_str().unwrap() + } + SourceKind::Interactive => "", + } } } /// Stores all of the relevant source information about a particular module. #[derive(Debug)] pub struct Module { - /// The contents of the module. - contents: String, + source: Source, /// The kind of the module. kind: ModuleKind, - - /// Line ranges for fast lookups, this is only computed when it is needed. - line_ranges: OnceCell, } impl Module { /// Create a new [Module]. - pub fn new(source: String, kind: ModuleKind) -> Self { - Self { contents: source, kind, line_ranges: OnceCell::new() } + pub fn new(contents: String, path: PathBuf, kind: ModuleKind) -> Self { + Self { source: Source::new(contents, SourceKind::Module { path }), kind } } - /// Get the source of the module. - pub fn contents(&self) -> Source<'_> { - Source(&self.contents) - } - - /// Get the kind of the module. - pub fn kind(&self) -> ModuleKind { - self.kind - } - - /// Get the line ranges for this particular module. - pub fn line_ranges(&self) -> &LineRanges { - self.line_ranges.get_or_init(|| LineRanges::new_from_str(&self.contents)) + /// Create a dummy [Module] entry, this only used. + fn empty(path: PathBuf, kind: ModuleKind) -> Module { + Self { source: Source::new(String::new(), SourceKind::Module { path }), kind } } } -/// A single entry within the interactive mode, this is used to store the -/// contents of the interactive block. An [InteractiveBlock] is only part -/// of the `` module, and thus does not imply the same behaviour -/// or handling as a [Module]. +// /// A single entry within the interactive mode, this is used to store the +// /// contents of the interactive block. An [InteractiveBlock] is only part +// /// of the `` module, and thus does not imply the same behaviour +// /// or handling as a [Module]. #[derive(Debug)] pub struct InteractiveBlock { - /// The contents of the interactive block. - contents: String, - - /// Line ranges for fast lookups, this is only computed when it is needed. - line_ranges: OnceCell, + source: Source, } impl InteractiveBlock { /// Create a new [InteractiveBlock]. pub fn new(contents: String) -> Self { - Self { contents, line_ranges: OnceCell::new() } - } - - /// Get the contents of the interactive block. - pub fn contents(&self) -> Source<'_> { - Source(&self.contents) - } - - /// Get the line ranges for this particular interactive block. - pub fn line_ranges(&self) -> &LineRanges { - self.line_ranges.get_or_init(|| LineRanges::new_from_str(&self.contents)) + Self { source: Source::new(contents, SourceKind::Interactive) } } } @@ -325,7 +321,7 @@ impl InteractiveBlock { pub struct SourceMap { /// A map between [ModuleId] and [PathBuf]. This is a bi-directional map /// and such value and key lookups are available. - module_paths: BiMap, + module_paths: HashMap, /// A map between [ModuleId] and the actual sources of the module. modules: IndexVec, @@ -337,107 +333,26 @@ pub struct SourceMap { impl SourceMap { /// Create a new [SourceMap] - pub fn new() -> Self { - Self { module_paths: BiMap::new(), modules: index_vec![], interactive_blocks: index_vec![] } - } - - /// Get a [Path] by a specific [SourceId]. If it is interactive, the path - /// is always set as ``. - pub fn source_path(&self, id: SourceId) -> &Path { - if id.is_interactive() { - Path::new("") - } else { - self.module_path(id.into()) + fn new() -> Self { + Self { + module_paths: HashMap::new(), + modules: index_vec![], + interactive_blocks: index_vec![], } } - /// Get a [Path] for a specific [ModuleId]. - pub fn module_path(&self, id: ModuleId) -> &Path { - self.module_paths.get_by_left(&id).unwrap().as_path() - } - - /// Get a canonicalised version of a [Path] for a [SourceId]. If it is - /// interactive, the path is always set as ``. The function - /// automatically converts the value into a string. - pub fn canonicalised_path_by_id(&self, id: SourceId) -> String { + fn source(&self, id: SourceId) -> &Source { if id.is_interactive() { - String::from("") - } else { - let value = id.into(); - adjust_canonicalisation(self.module_paths.get_by_left(&value).unwrap()) - } - } - - /// Get the name of a [SourceId] by extracting the path and further - /// retrieving the stem of the filename as the name of the module. This - /// function adheres to the rules of module naming conventions which are - /// specified within the documentation book. - pub fn source_name(&self, id: SourceId) -> &str { - let path = self.source_path(id); - - // for interactive, there is no file and so we just default to using the whole - // path - if id.is_interactive() { - path.to_str().unwrap() - } else { - let prefix = path.file_prefix().unwrap(); - - // deal with `index.hash` case... - if prefix == "index" { - if let Some(parent) = path.parent() { - // Now we should be at the `parent` direct - return parent.file_name().unwrap_or(prefix).to_str().unwrap(); - } - } - - // If it is a normal filename, then just use the resultant prefix, or default - // to this if trying to extract the name of the parent fails... for example - // `/index.hash` - prefix.to_str().unwrap() - } - } - - /// Get a [ModuleId] by a specific [Path]. The function checks if there - /// is an entry for the specified `path` yielding a [ModuleId]. - /// - /// N.B. This never returns a [InteractiveId] value. - pub fn get_id_by_path(&self, path: &Path) -> Option { - self.module_paths.get_by_right(path).copied().map(SourceId::from) - } - - /// Get the raw contents of a module or interactive block by the - /// specified [SourceId] - pub fn contents(&self, source_id: SourceId) -> Source<'_> { - if source_id.is_interactive() { - self.interactive_blocks.get(source_id.value() as usize).unwrap().contents() - } else { - self.modules.get(source_id.value() as usize).unwrap().contents() - } - } - - /// Get a hunk of the source by the specified [SourceId] and [Span]. - pub fn hunk(&self, span: Span) -> &str { - self.contents(span.id).hunk(span.range) - } - - /// Get the [LineRanges] for a specific [SourceId]. - pub fn line_ranges(&self, source_id: SourceId) -> &LineRanges { - if source_id.is_interactive() { - self.interactive_blocks.get(source_id.value() as usize).unwrap().line_ranges() + &self.interactive_blocks.get(id.value() as usize).unwrap().source } else { - self.modules.get(source_id.value() as usize).unwrap().line_ranges() + &self.modules.get(id.value() as usize).unwrap().source } } - /// Get the [ModuleKind] by [SourceId]. If the `id` is - /// [InteractiveId], then the resultant [ModuleKind] is [None]. - pub fn module_kind_by_id(&self, source_id: SourceId) -> Option { - if source_id.is_interactive() { - return None; - } - - let value = source_id.value(); - self.modules.get(value as usize).map(|module| module.kind) + fn add_interactive_block(&mut self, contents: String) -> SourceId { + let id = self.interactive_blocks.len() as u32; + self.interactive_blocks.push(InteractiveBlock::new(contents)); + SourceId::new_interactive(id) } /// Get the entry point that has been registered with the [SourceMap]. @@ -449,44 +364,58 @@ impl SourceMap { .position(|module| matches!(module.kind, ModuleKind::EntryPoint)) .map(|index| index.into()) } +} - /// Get a module by the specific [ModuleId]. - pub fn get_module(&self, id: ModuleId) -> &Module { - self.modules.get(id).unwrap() - } +static SOURCE_MAP: Lazy> = Lazy::new(|| { + let map = SourceMap::new(); + RwLock::new(map) +}); - /// Add a module to the [SourceMap] with the specified resolved file path, - /// contents and a kind of module. - pub fn add_module(&mut self, path: PathBuf, contents: String, kind: ModuleKind) -> SourceId { - let id = self.modules.len() as u32; - self.modules.push(Module::new(contents, kind)); +pub struct SourceMapUtils; + +impl SourceMapUtils { + /// Reserve a [SourceId] a given module. + pub fn reserve_module(path: PathBuf, kind: ModuleKind) -> SourceId { + let mut map = SOURCE_MAP.write(); + + let id = map.modules.len() as u32; + map.modules.push(Module::empty(path.clone(), kind)); // Create references for the paths reverse let id = ModuleId::from_raw(id); - self.module_paths.insert(id, path); + map.module_paths.insert(path, id); id.into() } + pub fn set_module_source(id: SourceId, contents: String) { + let mut map = SOURCE_MAP.write(); + let id: ModuleId = id.into(); + + // Update the entry in the `module` vector. We don't need to put a + // path in since that happens when the module is reserved. + map.modules[id].source.contents = contents; + } + /// Add an interactive block to the [SourceMap] - pub fn add_interactive_block(&mut self, contents: String) -> SourceId { - let id = self.interactive_blocks.len() as u32; - self.interactive_blocks.push(InteractiveBlock::new(contents)); - SourceId::new_interactive(id) + pub fn add_interactive_block(contents: String) -> SourceId { + SOURCE_MAP.write().add_interactive_block(contents) } - /// Function to get a friendly representation of the [Span] in - /// terms of row and column positions. - pub fn get_row_col_for(&self, location: Span) -> RowColRange { - self.line_ranges(location.id).row_cols(location.range) + pub fn entry_point() -> Option { + SOURCE_MAP.read().entry_point().map(SourceId::from) } - /// Convert a [Span] in terms of the filename, row and column. + /// Get a [SourceId] by a specific [Path]. The function checks if there + /// is an entry for the specified `path` yielding a [SourceId]. /// - /// @@cleanup: move this out of here. - pub fn fmt_location(&self, location: Span) -> String { - let name = self.canonicalised_path_by_id(location.id); - let span = self.get_row_col_for(location); + /// **Note**: this function has no effect when calling on `interactive` + /// sources. + pub fn id_by_path(path: &Path) -> Option { + SOURCE_MAP.read().module_paths.get(path).copied().map(SourceId::from) + } - format!("{name}:{span}") + pub fn map(id: impl Into, f: impl FnOnce(&Source) -> T) -> T { + let map = SOURCE_MAP.read(); + f(map.source(id.into())) } } diff --git a/compiler/hash-source/src/location.rs b/compiler/hash-source/src/location.rs index a8a049fa4..5d10fc83f 100644 --- a/compiler/hash-source/src/location.rs +++ b/compiler/hash-source/src/location.rs @@ -2,11 +2,12 @@ use std::{ convert::TryInto, fmt::{self, Display}, + ops::{Deref, DerefMut}, }; -use hash_utils::derive_more::Constructor; +use hash_utils::{derive_more::Constructor, range_map::RangeMap}; -use crate::SourceId; +use crate::{SourceId, SourceMapUtils}; /// [ByteRange] represents a location of a range of tokens within the source. /// @@ -122,6 +123,31 @@ impl Span { pub fn is_empty(&self) -> bool { self.range.is_empty() } + + /// Formaat the [Span] into a file path with a column and row number. + /// + /// The span is formatted into the following format: + /// ```notrust + /// :::: + /// ``` + pub fn fmt_path(&self) -> String { + SourceMapUtils::map(self.id, |source| { + format!("{}:{}", source.canonicalised_path().display(), source.row_cols(self.range)) + }) + } + + /// Get the contents of the [Span] from the [SpannedSource]. + pub fn contents(&self) -> String { + SourceMapUtils::map(self.id, |source| source.hunk(self.range).to_string()) + } + + /// Map the contents of the [Span]. + pub fn map_contents(&self, f: F) -> T + where + F: FnOnce(&str) -> T, + { + SourceMapUtils::map(self.id, |source| f(source.hunk(self.range))) + } } /// Represents a position within a source using a `row` and `column` @@ -178,3 +204,90 @@ impl Display for RowColRange { } } } + +/// A [SpannedSource] is a wrapper around the contents of a source file that +/// is stored in [SourceMap]. It features useful methods for extracting +/// and reading sections of the source by using [Span] or [ByteRange]s. +#[derive(Clone, Copy)] +pub struct SpannedSource<'s>(pub &'s str); + +impl<'s> SpannedSource<'s> { + /// Create a [SpannedSource] from a [String]. + pub fn from_string(s: &'s str) -> Self { + Self(s) + } + + /// Get a hunk of the source by the specified [ByteRange]. + pub fn hunk(&self, range: ByteRange) -> &'s str { + &self.0[range.start()..range.end()] + } +} + +/// This struct is used a wrapper for a [RangeMap] in order to +/// implement a nice display format, amongst other things. However, +/// it is a bit of a @@Hack, but I don't think there is really any other +/// better way to do this. +#[derive(Debug)] +pub struct LineRanges(RangeMap); + +impl LineRanges { + /// Create a line range from a string slice. + pub fn new_from_str(s: &str) -> Self { + // Pre-allocate the line ranges to a specific size by counting the number of + // newline characters within the module source. + let mut ranges = Self(RangeMap::with_capacity(bytecount::count(s.as_bytes(), b'\n'))); + + // Now, iterate through the source and record the position of each newline + // range, and push it into the map. + let mut count = 0; + + for line in s.lines() { + ranges.append(count..=(count + line.len()), ()); + count += line.len() + 1; + } + + ranges + } + + /// Get a [RowCol] from a given byte index. + pub fn get_row_col(&self, index: usize) -> RowCol { + let ranges = &self.0; + let line = ranges.index_wrapping(index); + let key = ranges.key_wrapping(index); + let offset = key.start(); + + RowCol { row: line, column: index - offset } + } + + /// Returns the line and column of the given [ByteRange] + pub fn row_cols(&self, range: ByteRange) -> RowColRange { + let start = self.get_row_col(range.start()); + let end = self.get_row_col(range.end()); + + RowColRange { start, end } + } +} + +impl Deref for LineRanges { + type Target = RangeMap; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl DerefMut for LineRanges { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } +} + +impl fmt::Display for LineRanges { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + for (index, (key, _)) in self.iter().enumerate() { + writeln!(f, "{key}: {}", index + 1)?; + } + + Ok(()) + } +} diff --git a/compiler/hash-tir/src/environment/env.rs b/compiler/hash-tir/src/environment/env.rs index ca2d24662..8809a3478 100644 --- a/compiler/hash-tir/src/environment/env.rs +++ b/compiler/hash-tir/src/environment/env.rs @@ -1,5 +1,4 @@ use hash_ast::node_map::NodeMap; -use hash_source::SourceMap; use hash_target::Target; use super::source_info::CurrentSourceInfo; @@ -50,7 +49,6 @@ macro_rules! env { env! { context: Context, node_map: NodeMap, - source_map: SourceMap, target: Target, current_source_info: CurrentSourceInfo, } diff --git a/compiler/hash-tir/src/lits.rs b/compiler/hash-tir/src/lits.rs index 34f1add7f..99a690676 100644 --- a/compiler/hash-tir/src/lits.rs +++ b/compiler/hash-tir/src/lits.rs @@ -60,9 +60,9 @@ impl IntLit { /// Check whether that value is negative. /// /// **Note**: For raw values, we just check if the value starts with a `-`. - pub fn is_negative(&self, env: &Env<'_>) -> bool { + pub fn is_negative(&self) -> bool { match self.value { - LitValue::Raw(lit) => env.source_map().hunk(lit.hunk.span()).starts_with('-'), + LitValue::Raw(lit) => lit.hunk.span().map_contents(|s| s.starts_with('-')), LitValue::Value(value) => value.is_negative(), } } @@ -98,13 +98,8 @@ impl IntLit { /// term. pub fn bake(&mut self, env: &Env<'_>, int_ty: IntTy) -> LitParseResult<()> { if let LitValue::Raw(lit) = self.value { - let value = parse_int_const_from_lit( - &lit, - Some(int_ty), - env.source_map(), - env.target().ptr_size(), - true, - )?; + let value = + parse_int_const_from_lit(&lit, Some(int_ty), env.target().ptr_size(), true)?; match value { IntValue::Small(value) => { @@ -209,9 +204,9 @@ impl FloatLit { /// /// This function does not do anyttihng if the literal has already been /// baked. - pub fn bake(&mut self, env: &Env<'_>, float_ty: FloatTy) -> LitParseResult<()> { + pub fn bake(&mut self, float_ty: FloatTy) -> LitParseResult<()> { if let LitValue::Raw(lit) = self.value { - let value = parse_float_const_from_lit(&lit, Some(float_ty), env.source_map())?; + let value = parse_float_const_from_lit(&lit, Some(float_ty))?; self.value = LitValue::Value(value); } diff --git a/compiler/hash-tir/src/mods.rs b/compiler/hash-tir/src/mods.rs index 535253558..feba838d2 100644 --- a/compiler/hash-tir/src/mods.rs +++ b/compiler/hash-tir/src/mods.rs @@ -1,8 +1,8 @@ //! Definitions related to modules. -use std::{fmt::Display, path::Path}; +use std::fmt::Display; -use hash_source::{identifier::Identifier, SourceId}; +use hash_source::{identifier::Identifier, SourceId, SourceMapUtils}; use hash_storage::{ get, store::{statics::StoreId, SequenceStore, Store, StoreKey, TrivialSequenceStoreKey}, @@ -24,9 +24,7 @@ pub enum ModKind { /// Defined as a module (`mod` block). ModBlock, /// Defined as a file module or interactive. - /// - /// Also contains the path to the file. - Source(SourceId, &'static Path), + Source(SourceId), /// Transparent /// /// Added by the compiler, used for primitives @@ -155,15 +153,15 @@ impl Display for ModDef { ModKind::ModBlock => { write!(f, "mod [name={}, type=block] {{\n{}}}", self.name, indent(&members, " ")) } - ModKind::Source(_source_id, source_name) => { + ModKind::Source(source) => SourceMapUtils::map(source, |source| { write!( f, "mod [name={}, type=file, src=\"{:?}\"] {{\n{}}}", self.name, - source_name, + source.path(), indent(&members, " ") ) - } + }), ModKind::Transparent => { write!( f, diff --git a/compiler/hash-tir/src/utils/mods.rs b/compiler/hash-tir/src/utils/mods.rs new file mode 100644 index 000000000..eefbb12d5 --- /dev/null +++ b/compiler/hash-tir/src/utils/mods.rs @@ -0,0 +1,54 @@ +//! Module-related utilities. +use hash_ast::ast::OwnsAstNode; +use hash_source::{identifier::Identifier, ModuleId, SourceMapUtils}; +use hash_storage::store::statics::SequenceStoreValue; +use hash_utils::derive_more::Constructor; + +use crate::{ + environment::{ + env::{AccessToEnv, Env}, + stores::tir_stores, + }, + impl_access_to_env, + mods::{ModDef, ModDefId, ModKind, ModMember}, + node::{Node, NodeOrigin}, + symbols::SymbolId, +}; + +/// Operations related to module definitions. +#[derive(Constructor)] +pub struct ModUtils<'tc> { + env: &'tc Env<'tc>, +} + +impl_access_to_env!(ModUtils<'tc>); + +impl<'tc> ModUtils<'tc> { + /// Create or get an existing module definition by `[SourceId]`. + pub fn create_or_get_module_mod_def(&self, module_id: ModuleId) -> ModDefId { + let source_node_id = self.node_map().get_module(module_id).node_ref().id(); + match tir_stores().ast_info().mod_defs().get_data_by_node(source_node_id) { + Some(existing) => existing, + None => { + // Create a new module definition. + let source_id = module_id.into(); + let module_name: Identifier = + SourceMapUtils::map(source_id, |source| source.name().into()); + let mod_def_id = Node::create_at( + ModDef { + // @@MissingOrigin + name: SymbolId::from_name(module_name, NodeOrigin::Generated), + kind: ModKind::Source(source_id), + members: Node::create_at( + Node::::empty_seq(), + NodeOrigin::Generated, + ), + }, + NodeOrigin::Generated, + ); + tir_stores().ast_info().mod_defs().insert(source_node_id, mod_def_id); + mod_def_id + } + } + } +} diff --git a/compiler/hash-typecheck/src/errors.rs b/compiler/hash-typecheck/src/errors.rs index 04851b69c..e41ddf29a 100644 --- a/compiler/hash-typecheck/src/errors.rs +++ b/compiler/hash-typecheck/src/errors.rs @@ -5,12 +5,11 @@ use hash_reporting::{ diagnostic::IntoCompound, hash_error_codes::error_codes::HashErrorCode, reporter::{Reporter, Reports}, - writer::ReportWriter, }; use hash_source::location::Span; use hash_storage::store::SequenceStoreKey; use hash_tir::{ - environment::env::{AccessToEnv, Env}, + environment::env::Env, fns::FnDefId, impl_access_to_env, node::{HasAstNodeId, NodeId, NodeOrigin}, @@ -133,7 +132,7 @@ impl_access_to_env!(TcErrorReporter<'env>); impl fmt::Display for TcErrorReporter<'_> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let reports = Self::format_error(&TcError::Signal); - write!(f, "{}", ReportWriter::new(reports, self.source_map())) + write!(f, "{}", Reporter::from_reports(reports)) } } diff --git a/compiler/hash-typecheck/src/inference.rs b/compiler/hash-typecheck/src/inference.rs index a9e1ba166..0ed89b9b5 100644 --- a/compiler/hash-typecheck/src/inference.rs +++ b/compiler/hash-typecheck/src/inference.rs @@ -443,7 +443,7 @@ impl InferenceOps<'_, T> { if let Some(float_ty) = self.try_use_ty_as_float_ty(inferred_ty) { lit.modify(|float| match &mut float.data { - Lit::Float(fl) => fl.bake(self.env(), float_ty), + Lit::Float(fl) => fl.bake(float_ty), _ => unreachable!(), })?; } @@ -508,8 +508,7 @@ impl InferenceOps<'_, T> { // type, // then return `None` and the unification will fail. if numeric.is_float - || (!numeric.is_signed - && int_lit.is_negative(self.env())) + || (!numeric.is_signed && int_lit.is_negative()) { None } else { @@ -944,12 +943,11 @@ impl InferenceOps<'_, T> { let has_entry_point_attr = attr_store().node_has_attr(fn_def_id.node_id_or_default(), attrs::ENTRY_POINT); + let kind = self.current_source_info().source_id().module_kind(); + let entry_point = if has_entry_point_attr { Some(EntryPointKind::Named(fn_def_name)) - } else if fn_def_name == IDENTS.main - && self.source_map().module_kind_by_id(self.current_source_info().source_id()) - == Some(ModuleKind::EntryPoint) - { + } else if fn_def_name == IDENTS.main && kind == Some(ModuleKind::EntryPoint) { Some(EntryPointKind::Main) } else { None diff --git a/compiler/hash-untyped-semantics/src/analysis/block.rs b/compiler/hash-untyped-semantics/src/analysis/block.rs index a2c184ecb..31c4e587a 100644 --- a/compiler/hash-untyped-semantics/src/analysis/block.rs +++ b/compiler/hash-untyped-semantics/src/analysis/block.rs @@ -12,7 +12,7 @@ use hash_ast::{ use super::SemanticAnalyser; use crate::diagnostics::error::AnalysisErrorKind; -impl SemanticAnalyser<'_> { +impl SemanticAnalyser { /// This function will verify that all of the given expressions are /// declarations. Additionally, the function checks that all of the /// declarations within the scope do not attempt to declare the binding diff --git a/compiler/hash-untyped-semantics/src/analysis/mod.rs b/compiler/hash-untyped-semantics/src/analysis/mod.rs index e78ee3d62..1450177ab 100644 --- a/compiler/hash-untyped-semantics/src/analysis/mod.rs +++ b/compiler/hash-untyped-semantics/src/analysis/mod.rs @@ -6,7 +6,6 @@ pub(crate) mod params; use hash_ast::{ast::AstNodeRef, origin::BlockOrigin}; use hash_reporting::diagnostic::AccessToDiagnosticsMut; -use hash_source::SourceMap; use hash_utils::crossbeam_channel::Sender; use crate::diagnostics::{ @@ -15,7 +14,7 @@ use crate::diagnostics::{ AnalyserDiagnostics, AnalysisDiagnostic, }; -pub struct SemanticAnalyser<'s> { +pub struct SemanticAnalyser { /// Whether the current visitor is within a loop construct. pub(crate) is_in_loop: bool, @@ -25,9 +24,6 @@ pub struct SemanticAnalyser<'s> { /// Whether the analyser is currently checking a literal pattern pub(crate) is_in_lit_pat: bool, - /// A reference to the sources of the current job. - pub(crate) source_map: &'s SourceMap, - /// The current scope of the traversal, representing which block the /// analyser is walking. pub(crate) current_block: BlockOrigin, @@ -36,15 +32,20 @@ pub struct SemanticAnalyser<'s> { pub(crate) diagnostics: AnalyserDiagnostics, } -impl<'s> SemanticAnalyser<'s> { +impl Default for SemanticAnalyser { + fn default() -> Self { + Self::new() + } +} + +impl SemanticAnalyser { /// Create a new semantic analyser - pub fn new(source_map: &'s SourceMap) -> Self { + pub fn new() -> Self { Self { is_in_loop: false, is_in_fn: false, is_in_lit_pat: false, diagnostics: AnalyserDiagnostics::default(), - source_map, current_block: BlockOrigin::Root, } } diff --git a/compiler/hash-untyped-semantics/src/analysis/params.rs b/compiler/hash-untyped-semantics/src/analysis/params.rs index 9f6d3c582..b1e9e00c5 100644 --- a/compiler/hash-untyped-semantics/src/analysis/params.rs +++ b/compiler/hash-untyped-semantics/src/analysis/params.rs @@ -28,7 +28,7 @@ impl Display for FieldNamingExpectation { } } -impl SemanticAnalyser<'_> { +impl SemanticAnalyser { /// Function to check whether the naming convention of a set of fields is /// consistent. Consistency is determined whether all of the fields /// are named or if they are all un-named. diff --git a/compiler/hash-untyped-semantics/src/diagnostics/mod.rs b/compiler/hash-untyped-semantics/src/diagnostics/mod.rs index 386dbf8d9..a74bc9b74 100644 --- a/compiler/hash-untyped-semantics/src/diagnostics/mod.rs +++ b/compiler/hash-untyped-semantics/src/diagnostics/mod.rs @@ -51,7 +51,7 @@ pub struct AnalyserDiagnostics { pub(crate) store: DiagnosticStore, } -impl AccessToDiagnosticsMut for SemanticAnalyser<'_> { +impl AccessToDiagnosticsMut for SemanticAnalyser { type Diagnostics = DiagnosticStore; fn diagnostics(&mut self) -> &mut Self::Diagnostics { diff --git a/compiler/hash-untyped-semantics/src/lib.rs b/compiler/hash-untyped-semantics/src/lib.rs index e6070d6ad..fc800c7c1 100644 --- a/compiler/hash-untyped-semantics/src/lib.rs +++ b/compiler/hash-untyped-semantics/src/lib.rs @@ -55,7 +55,6 @@ impl CompilerStage for UntypedSemanti let (sender, receiver) = unbounded::(); let UntypedSemanticAnalysisCtx { workspace, pool } = stage_data.data(); - let source_map = &workspace.source_map; let node_map = &mut workspace.node_map; let source_stage_info = &mut workspace.source_stage_info; @@ -66,7 +65,7 @@ impl CompilerStage for UntypedSemanti let source = node_map.get_interactive_block(entry_point.into()); // setup a visitor and the context - let mut visitor = SemanticAnalyser::new(source_map); + let mut visitor = SemanticAnalyser::new(); visitor.visit_body_block(source.node_ref()).unwrap(); visitor.emit_diagnostics_to(&sender); @@ -83,7 +82,7 @@ impl CompilerStage for UntypedSemanti continue; } - let mut visitor = SemanticAnalyser::new(source_map); + let mut visitor = SemanticAnalyser::new(); // Check that all of the root scope statements are only declarations let errors = visitor.visit_module(module.node_ref()).unwrap(); @@ -100,7 +99,7 @@ impl CompilerStage for UntypedSemanti let sender = sender.clone(); scope.spawn(move |_| { - let mut visitor = SemanticAnalyser::new(source_map); + let mut visitor = SemanticAnalyser::new(); visitor.visit_expr(expr.ast_ref()).unwrap(); visitor.emit_diagnostics_to(&sender); diff --git a/compiler/hash-untyped-semantics/src/visitor.rs b/compiler/hash-untyped-semantics/src/visitor.rs index c74dcc03d..14261fde3 100644 --- a/compiler/hash-untyped-semantics/src/visitor.rs +++ b/compiler/hash-untyped-semantics/src/visitor.rs @@ -22,7 +22,7 @@ use crate::{ diagnostics::{error::AnalysisErrorKind, warning::AnalysisWarningKind}, }; -impl<'s> SemanticAnalyser<'s> { +impl SemanticAnalyser { /// This function is used by directives that require their usage to be /// within a constant block, i.e. the `dump_ir` directive must only /// accept declarations that are in constant blocks. This function will @@ -46,7 +46,7 @@ impl<'s> SemanticAnalyser<'s> { } } -impl AstVisitorMutSelf for SemanticAnalyser<'_> { +impl AstVisitorMutSelf for SemanticAnalyser { type Error = Infallible; ast_visitor_mut_self_default_impl!( @@ -147,7 +147,6 @@ impl AstVisitorMutSelf for SemanticAnalyser<'_> { ) -> Result { panic_on_span!( node.span(), - self.source_map, "hit non de-sugared for-block whilst performing semantic analysis" ); } @@ -160,7 +159,6 @@ impl AstVisitorMutSelf for SemanticAnalyser<'_> { ) -> Result { panic_on_span!( node.span(), - self.source_map, "hit non de-sugared while-block whilst performing semantic analysis" ); } @@ -193,7 +191,6 @@ impl AstVisitorMutSelf for SemanticAnalyser<'_> { ) -> Result { panic_on_span!( node.span(), - self.source_map, "hit non de-sugared if-clause whilst performing semantic analysis" ); } @@ -206,7 +203,6 @@ impl AstVisitorMutSelf for SemanticAnalyser<'_> { ) -> Result { panic_on_span!( node.span(), - self.source_map, "hit non de-sugared if-block whilst performing semantic analysis" ); } diff --git a/compiler/hash-utils/src/path.rs b/compiler/hash-utils/src/path.rs index b9e6509d1..fa38f40fb 100644 --- a/compiler/hash-utils/src/path.rs +++ b/compiler/hash-utils/src/path.rs @@ -1,22 +1,24 @@ //! Path manipulation and canonicalisation utilities. -use std::{fs::canonicalize, path::Path}; +use std::{ + fs::canonicalize, + path::{Path, PathBuf}, +}; /// Function to apply formatting onto a path when printing it. -#[cfg(not(target_os = "windows"))] -pub fn adjust_canonicalisation>(p: P) -> String { - canonicalize(p.as_ref()).unwrap_or_else(|_| p.as_ref().to_path_buf()).display().to_string() +// #[cfg(not(target_os = "windows"))] +pub fn adjust_canonicalisation>(p: P) -> PathBuf { + canonicalize(p.as_ref()).unwrap_or_else(|_| p.as_ref().to_path_buf()) } /// Function to apply formatting onto a path when printing it. #[cfg(target_os = "windows")] -pub fn adjust_canonicalisation>(p: P) -> String { +pub fn adjust_canonicalisation>(p: P) -> PathBuf { const VERBATIM_PREFIX: &str = r"\\?\"; - let path = - canonicalize(p.as_ref()).unwrap_or_else(|_| p.as_ref().to_path_buf()).display().to_string(); + let path = canonicalize(p.as_ref()).unwrap_or_else(|_| p.as_ref().to_path_buf()); - if let Some(stripped_path) = path.strip_prefix(VERBATIM_PREFIX) { - stripped_path.to_string() + if let Ok(stripped_path) = path.strip_prefix(VERBATIM_PREFIX) { + stripped_path.to_path_buf() } else { path } diff --git a/tests/runner.rs b/tests/runner.rs index 079c747c6..9437a467f 100644 --- a/tests/runner.rs +++ b/tests/runner.rs @@ -37,7 +37,7 @@ use hash_pipeline::{ settings::CompilerSettings, workspace::Workspace, }; -use hash_reporting::{report::Report, writer::ReportWriter}; +use hash_reporting::report::Report; use hash_testing_internal::{ metadata::{HandleWarnings, TestResult}, TestingInput, @@ -59,7 +59,7 @@ fn stringify_test_dir_path(path: &Path) -> String { // to escape them. #[cfg(target_os = "windows")] let test_dir = { - let mut dir = regex::escape(&adjust_canonicalisation(path)); + let mut dir = regex::escape(&adjust_canonicalisation(path).display().to_string()); // @@Hack: on windows, the separator is a backslash, which is problematic // for ui-tests, since they expect a `/` (forward slash) as the @@ -72,7 +72,7 @@ fn stringify_test_dir_path(path: &Path) -> String { #[cfg(not(target_os = "windows"))] let test_dir = { - let mut dir = adjust_canonicalisation(path); + let mut dir = adjust_canonicalisation(path).display().to_string(); dir.push('/'); dir }; @@ -105,14 +105,10 @@ fn strip_contents(contents: &str, test: &TestingInput) -> String { fn compare_emitted_diagnostics( input: &TestingInput, diagnostics: Vec, - sources: &Workspace, ) -> std::io::Result<()> { // First, convert the diagnostics into a string. - let contents = diagnostics - .into_iter() - .map(|report| format!("{}", ReportWriter::single(report, &sources.source_map))) - .collect::>() - .join("\n"); + let contents = + diagnostics.into_iter().map(|report| format!("{}", report)).collect::>().join("\n"); compare_output(input, OutputKind::Stderr, contents.as_str()) } @@ -216,7 +212,6 @@ fn compare_stream( fn handle_failure_case( test: TestingInput, diagnostics: Vec, - sources: &Workspace, output_stream: &Arc>>, ) -> std::io::Result<()> { // verify that the case failed, as in reports where generated @@ -249,7 +244,7 @@ fn handle_failure_case( }) .collect(); - compare_emitted_diagnostics(&test, diagnostics, sources)?; + compare_emitted_diagnostics(&test, diagnostics)?; compare_stream(&test, OutputKind::Stdout, output_stream) } @@ -260,7 +255,6 @@ fn handle_failure_case( fn handle_pass_case( test: TestingInput, diagnostics: Vec, - sources: &Workspace, output_stream: &Arc>>, ) -> std::io::Result<()> { let did_pass = match test.metadata.warnings { @@ -276,7 +270,7 @@ fn handle_pass_case( "\ntest case did not pass:\n{}", diagnostics .into_iter() - .map(|report| format!("{}", ReportWriter::single(report, &sources.source_map))) + .map(|report| format!("{}", report)) .collect::>() .join("\n") ); @@ -284,7 +278,7 @@ fn handle_pass_case( // If we need to compare the output of the warnings, to the previous result... if test.metadata.warnings == HandleWarnings::Compare { - compare_emitted_diagnostics(&test, diagnostics, sources)?; + compare_emitted_diagnostics(&test, diagnostics)?; } compare_stream(&test, OutputKind::Stdout, output_stream) @@ -343,17 +337,15 @@ fn handle_test(test: TestingInput) { // // Now parse the module and store the result compiler.run_on_entry_point(); - let workspace = compiler.workspace(); - // @@Copying: we shouldn't really need to clone the diagnostics here!! let diagnostics = compiler.diagnostics().to_owned(); // Based on the specified metadata within the test case itself, we know // whether the test should fail or not if test.metadata.completion == TestResult::Fail { - handle_failure_case(test, diagnostics, workspace, &output_stream).unwrap(); + handle_failure_case(test, diagnostics, &output_stream).unwrap(); } else { - handle_pass_case(test, diagnostics, workspace, &output_stream).unwrap(); + handle_pass_case(test, diagnostics, &output_stream).unwrap(); } }