Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions Cargo.lock

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

32 changes: 13 additions & 19 deletions crates/cairo-lang-defs/src/cache/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,10 @@ use std::ops::{Deref, DerefMut};
use std::path::PathBuf;
use std::sync::Arc;

use cairo_lang_diagnostics::{DiagnosticLocation, DiagnosticNote, Maybe, Severity};
use cairo_lang_diagnostics::{DiagnosticNote, Maybe, Severity};
use cairo_lang_filesystem::flag::Flag;
use cairo_lang_filesystem::ids::{
CodeMapping, CrateId, CrateLongId, FileId, FileKind, FileLongId, VirtualFile,
CodeMapping, CrateId, CrateLongId, FileId, FileKind, FileLongId, SpanInFile, VirtualFile,
};
use cairo_lang_filesystem::span::{TextOffset, TextSpan, TextWidth};
use cairo_lang_syntax::node::ast::{
Expand Down Expand Up @@ -1757,7 +1757,7 @@ impl FileIdCached {

#[derive(Serialize, Deserialize, Clone, PartialEq, Eq)]
struct VirtualFileCached {
parent: Option<FileIdCached>,
parent: Option<SpanInFileCached>,
name: SmolStr,
content: String,
code_mappings: Vec<CodeMapping>,
Expand All @@ -1768,7 +1768,7 @@ struct VirtualFileCached {
impl VirtualFileCached {
fn new(virtual_file: &VirtualFile, ctx: &mut DefCacheSavingContext<'_>) -> Self {
Self {
parent: virtual_file.parent.map(|parent| FileIdCached::new(parent, ctx)),
parent: virtual_file.parent.map(|parent| SpanInFileCached::new(&parent, ctx)),
name: virtual_file.name.clone(),
content: String::from(&*(virtual_file.content)),
code_mappings: virtual_file.code_mappings.to_vec(),
Expand Down Expand Up @@ -1862,14 +1862,14 @@ type PluginFileDiagnosticNotesCached = OrderedHashMap<FileIdCached, DiagnosticNo
#[derive(Serialize, Deserialize, Clone)]
struct DiagnosticNoteCached {
text: String,
location: Option<DiagnosticLocationCached>,
location: Option<SpanInFileCached>,
}

impl DiagnosticNoteCached {
fn new(note: DiagnosticNote, ctx: &mut DefCacheSavingContext<'_>) -> DiagnosticNoteCached {
DiagnosticNoteCached {
text: note.text.clone(),
location: note.location.map(|location| DiagnosticLocationCached::new(&location, ctx)),
location: note.location.map(|location| SpanInFileCached::new(&location, ctx)),
}
}
fn embed(self, ctx: &mut DefCacheLoadingContext<'_>) -> DiagnosticNote {
Expand All @@ -1880,23 +1880,17 @@ impl DiagnosticNoteCached {
}
}

#[derive(Serialize, Deserialize, Clone)]
struct DiagnosticLocationCached {
#[derive(Serialize, Deserialize, Clone, PartialEq, Eq)]
struct SpanInFileCached {
file_id: FileIdCached,
span: TextSpan,
}

impl DiagnosticLocationCached {
fn new(
location: &DiagnosticLocation,
ctx: &mut DefCacheSavingContext<'_>,
) -> DiagnosticLocationCached {
DiagnosticLocationCached {
file_id: FileIdCached::new(location.file_id, ctx),
span: location.span,
}
impl SpanInFileCached {
fn new(location: &SpanInFile, ctx: &mut DefCacheSavingContext<'_>) -> SpanInFileCached {
SpanInFileCached { file_id: FileIdCached::new(location.file_id, ctx), span: location.span }
}
fn embed(self, ctx: &mut DefCacheLoadingContext<'_>) -> DiagnosticLocation {
DiagnosticLocation { file_id: self.file_id.embed(ctx), span: self.span }
fn embed(self, ctx: &mut DefCacheLoadingContext<'_>) -> SpanInFile {
SpanInFile { file_id: self.file_id.embed(ctx), span: self.span }
}
}
5 changes: 3 additions & 2 deletions crates/cairo-lang-defs/src/db.rs
Original file line number Diff line number Diff line change
Expand Up @@ -797,10 +797,11 @@ fn priv_module_sub_files(
}

if let Some(generated) = result.code {
let stable_ptr = item_ast.stable_ptr(db).untyped();
let generated_file_id = FileLongId::External(
PluginGeneratedFileLongId {
module_id,
stable_ptr: item_ast.stable_ptr(db).untyped(),
stable_ptr,
name: generated.name.clone(),
}
.intern(db)
Expand All @@ -814,7 +815,7 @@ fn priv_module_sub_files(
files.insert(
generated_file_id,
VirtualFile {
parent: Some(file_id),
parent: Some(stable_ptr.span_in_file(db)),
name: generated.name,
content: generated.content.into(),
code_mappings: generated.code_mappings.into(),
Expand Down
23 changes: 8 additions & 15 deletions crates/cairo-lang-defs/src/diagnostic_utils.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
use std::fmt;

use cairo_lang_debug::DebugWithDb;
use cairo_lang_diagnostics::DiagnosticLocation;
use cairo_lang_filesystem::ids::FileId;
use cairo_lang_filesystem::ids::{FileId, SpanInFile};
use cairo_lang_filesystem::span::{TextSpan, TextWidth};
use cairo_lang_syntax::node::db::SyntaxGroup;
use cairo_lang_syntax::node::ids::SyntaxStablePtrId;
Expand Down Expand Up @@ -50,33 +49,27 @@ impl StableLocation {
self.stable_ptr
}

/// Returns the [DiagnosticLocation] that corresponds to the [StableLocation].
pub fn diagnostic_location(&self, db: &dyn DefsGroup) -> DiagnosticLocation {
/// Returns the [SpanInFile] that corresponds to the [StableLocation].
pub fn diagnostic_location(&self, db: &dyn DefsGroup) -> SpanInFile {
match self.inner_span {
None => self.stable_ptr.span_in_file(db),
Some((start, width)) => {
let start = self.syntax_node(db).offset(db).add_width(start);
let end = start.add_width(width);
DiagnosticLocation { file_id: self.file_id(db), span: TextSpan { start, end } }
}
None => {
let syntax_node = self.syntax_node(db);
DiagnosticLocation {
file_id: self.file_id(db),
span: syntax_node.span_without_trivia(db),
}
SpanInFile { file_id: self.file_id(db), span: TextSpan { start, end } }
}
}
}

/// Returns the [DiagnosticLocation] that corresponds to the [StableLocation].
/// Returns the [SpanInFile] that corresponds to the [StableLocation].
pub fn diagnostic_location_until(
&self,
db: &dyn DefsGroup,
until_stable_ptr: SyntaxStablePtrId,
) -> DiagnosticLocation {
) -> SpanInFile {
let start = self.stable_ptr.lookup(db).span_start_without_trivia(db);
let end = until_stable_ptr.lookup(db).span_end_without_trivia(db);
DiagnosticLocation { file_id: self.stable_ptr.file_id(db), span: TextSpan { start, end } }
SpanInFile { file_id: self.stable_ptr.file_id(db), span: TextSpan { start, end } }
}
}

Expand Down
107 changes: 24 additions & 83 deletions crates/cairo-lang-diagnostics/src/diagnostics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,12 @@ use std::sync::Arc;

use cairo_lang_debug::debug::DebugWithDb;
use cairo_lang_filesystem::db::{FilesGroup, get_originating_location};
use cairo_lang_filesystem::ids::FileId;
use cairo_lang_filesystem::span::TextSpan;
use cairo_lang_filesystem::ids::{FileId, SpanInFile};
use cairo_lang_utils::Upcast;
use cairo_lang_utils::ordered_hash_map::OrderedHashMap;
use itertools::Itertools;

use crate::error_code::{ErrorCode, OptionErrorCodeExt};
use crate::location_marks::get_location_marks;

#[cfg(test)]
#[path = "diagnostics_test.rs"]
Expand All @@ -37,7 +35,7 @@ impl fmt::Display for Severity {
pub trait DiagnosticEntry: Clone + fmt::Debug + Eq + Hash {
type DbType: Upcast<dyn FilesGroup> + ?Sized;
fn format(&self, db: &Self::DbType) -> String;
fn location(&self, db: &Self::DbType) -> DiagnosticLocation;
fn location(&self, db: &Self::DbType) -> SpanInFile;
fn notes(&self, _db: &Self::DbType) -> &[DiagnosticNote] {
&[]
}
Expand All @@ -58,98 +56,41 @@ pub trait DiagnosticEntry: Clone + fmt::Debug + Eq + Hash {
/// [`FileId`].
pub type PluginFileDiagnosticNotes = OrderedHashMap<FileId, DiagnosticNote>;

// The representation of a source location inside a diagnostic.
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
pub struct DiagnosticLocation {
pub file_id: FileId,
pub span: TextSpan,
}
impl DiagnosticLocation {
/// Get the location of right after this diagnostic's location (with width 0).
pub fn after(&self) -> Self {
Self { file_id: self.file_id, span: self.span.after() }
}

/// Get the location of the originating user code.
pub fn user_location(&self, db: &dyn FilesGroup) -> Self {
let (file_id, span) = get_originating_location(db, self.file_id, self.span, None);
Self { file_id, span }
}

/// Get the location of the originating user code,
/// along with [`DiagnosticNote`]s for this translation.
/// The notes are collected from the parent files of the originating location.
pub fn user_location_with_plugin_notes(
&self,
db: &dyn FilesGroup,
file_notes: &PluginFileDiagnosticNotes,
) -> (Self, Vec<DiagnosticNote>) {
let mut parent_files = Vec::new();
let (file_id, span) =
get_originating_location(db, self.file_id, self.span, Some(&mut parent_files));
let diagnostic_notes = parent_files
.into_iter()
.rev()
.filter_map(|file_id| file_notes.get(&file_id).cloned())
.collect_vec();
(Self { file_id, span }, diagnostic_notes)
}

/// Helper function to format the location of a diagnostic.
pub fn fmt_location(&self, f: &mut fmt::Formatter<'_>, db: &dyn FilesGroup) -> fmt::Result {
let file_path = self.file_id.full_path(db);
let start = match self.span.start.position_in_file(db, self.file_id) {
Some(pos) => format!("{}:{}", pos.line + 1, pos.col + 1),
None => "?".into(),
};

let end = match self.span.end.position_in_file(db, self.file_id) {
Some(pos) => format!("{}:{}", pos.line + 1, pos.col + 1),
None => "?".into(),
};
write!(f, "{file_path}:{start}: {end}")
}
}

impl DebugWithDb<dyn FilesGroup> for DiagnosticLocation {
fn fmt(&self, f: &mut fmt::Formatter<'_>, db: &dyn FilesGroup) -> fmt::Result {
let file_path = self.file_id.full_path(db);
let mut marks = String::new();
let mut ending_pos = String::new();
let starting_pos = match self.span.start.position_in_file(db, self.file_id) {
Some(starting_text_pos) => {
if let Some(ending_text_pos) = self.span.end.position_in_file(db, self.file_id) {
if starting_text_pos.line != ending_text_pos.line {
ending_pos =
format!("-{}:{}", ending_text_pos.line + 1, ending_text_pos.col);
}
}
marks = get_location_marks(db, self, true);
format!("{}:{}", starting_text_pos.line + 1, starting_text_pos.col + 1)
}
None => "?".into(),
};
write!(f, "{file_path}:{starting_pos}{ending_pos}\n{marks}")
}
}

/// A note about a diagnostic.
/// May include a relevant diagnostic location.
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
pub struct DiagnosticNote {
pub text: String,
pub location: Option<DiagnosticLocation>,
pub location: Option<SpanInFile>,
}
impl DiagnosticNote {
pub fn text_only(text: String) -> Self {
Self { text, location: None }
}

pub fn with_location(text: String, location: DiagnosticLocation) -> Self {
pub fn with_location(text: String, location: SpanInFile) -> Self {
Self { text, location: Some(location) }
}
}

/// Get the location of the originating user code,
/// along with [`DiagnosticNote`]s for this translation.
/// The notes are collected from the parent files of the originating location.
pub fn user_location_with_plugin_notes(
db: &dyn FilesGroup,
location: SpanInFile,
file_notes: &PluginFileDiagnosticNotes,
) -> (SpanInFile, Vec<DiagnosticNote>) {
let mut parent_files = Vec::new();
let origin = get_originating_location(db, location, Some(&mut parent_files));
let diagnostic_notes = parent_files
.into_iter()
.rev()
.filter_map(|file_id| file_notes.get(&file_id).cloned())
.collect();
(origin, diagnostic_notes)
}

impl DebugWithDb<dyn FilesGroup> for DiagnosticNote {
fn fmt(&self, f: &mut fmt::Formatter<'_>, db: &(dyn FilesGroup + 'static)) -> fmt::Result {
write!(f, "{}", self.text)?;
Expand Down Expand Up @@ -244,7 +185,7 @@ impl<TEntry: DiagnosticEntry> Default for DiagnosticsBuilder<TEntry> {
pub fn format_diagnostics(
db: &(dyn FilesGroup + 'static),
message: &str,
location: DiagnosticLocation,
location: SpanInFile,
) -> String {
format!("{message}\n --> {:?}\n", location.debug(db))
}
Expand Down Expand Up @@ -321,7 +262,7 @@ impl<TEntry: DiagnosticEntry> Diagnostics<TEntry> {
let mut msg = String::new();
let diag_location = entry.location(db);
let (user_location, parent_file_notes) =
diag_location.user_location_with_plugin_notes(files_db, file_notes);
user_location_with_plugin_notes(files_db, diag_location, file_notes);

let include_generated_location = diag_location != user_location
&& std::env::var("CAIRO_DEBUG_GENERATED_CODE").is_ok();
Expand Down
8 changes: 4 additions & 4 deletions crates/cairo-lang-diagnostics/src/diagnostics_test.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
use cairo_lang_filesystem::db::FilesGroup;
use cairo_lang_filesystem::ids::{FileId, FileKind, FileLongId, VirtualFile};
use cairo_lang_filesystem::ids::{FileId, FileKind, FileLongId, SpanInFile, VirtualFile};
use cairo_lang_filesystem::span::{TextOffset, TextSpan, TextWidth};
use cairo_lang_filesystem::test_utils::FilesDatabaseForTesting;
use cairo_lang_utils::Intern;
use indoc::indoc;
use test_log::test;

use super::{DiagnosticEntry, DiagnosticLocation, DiagnosticsBuilder};
use super::{DiagnosticEntry, DiagnosticsBuilder};

// Test diagnostic.
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
Expand All @@ -20,8 +20,8 @@ impl DiagnosticEntry for SimpleDiag {
"Simple diagnostic.".into()
}

fn location(&self, _db: &dyn cairo_lang_filesystem::db::FilesGroup) -> DiagnosticLocation {
DiagnosticLocation {
fn location(&self, _db: &dyn cairo_lang_filesystem::db::FilesGroup) -> SpanInFile {
SpanInFile {
file_id: self.file_id,
span: TextSpan {
start: TextOffset::START,
Expand Down
8 changes: 3 additions & 5 deletions crates/cairo-lang-diagnostics/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,11 @@
//! source files.

pub use diagnostics::{
DiagnosticAdded, DiagnosticEntry, DiagnosticLocation, DiagnosticNote, Diagnostics,
DiagnosticsBuilder, FormattedDiagnosticEntry, Maybe, PluginFileDiagnosticNotes, Severity,
ToMaybe, ToOption, format_diagnostics, skip_diagnostic,
DiagnosticAdded, DiagnosticEntry, DiagnosticNote, Diagnostics, DiagnosticsBuilder,
FormattedDiagnosticEntry, Maybe, PluginFileDiagnosticNotes, Severity, ToMaybe, ToOption,
format_diagnostics, skip_diagnostic,
};
pub use error_code::{ErrorCode, OptionErrorCodeExt};
pub use location_marks::get_location_marks;

mod diagnostics;
mod error_code;
mod location_marks;
3 changes: 3 additions & 0 deletions crates/cairo-lang-filesystem/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ description = "Virtual filesystem for the compiler."
[dependencies]
cairo-lang-debug = { path = "../cairo-lang-debug", version = "~2.11.4" }
cairo-lang-utils = { path = "../cairo-lang-utils", version = "~2.11.4", features = ["serde"] }
itertools = { workspace = true, default-features = true }
path-clean.workspace = true
salsa.workspace = true
semver.workspace = true
Expand All @@ -18,6 +19,8 @@ toml.workspace = true

[dev-dependencies]
env_logger.workspace = true
indoc.workspace = true
pretty_assertions.workspace = true
serde_json.workspace = true
test-log.workspace = true

Expand Down
Loading