Skip to content
Closed
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
61 changes: 46 additions & 15 deletions scarb/src/bin/scarb/commands/metadata.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ use toml::de::Error as TomlParseError;
use scarb::core::Config;
use scarb::core::errors::{ManifestErrorWithSource, ManifestParseError};
use scarb::core::{
ManifestDiagnosticMessage, ManifestDiagnosticSpan, ManifestMessageKind, ManifestSemanticError,
MachineDiagnostic, MachineDiagnosticKind, MachineDiagnosticSeverity, MachineDiagnosticSpan,
MachineRelatedLocation, ManifestSemanticError,
};
use scarb::ops;
use scarb_ui::OutputFormat;
Expand Down Expand Up @@ -42,7 +43,7 @@ pub fn run(args: MetadataArgs, config: &Config) -> Result<()> {
}

fn emit_manifest_diagnostic(config: &Config, error: &anyhow::Error) {
let (file, message, span, related) = if let Some(sem) = error
let diagnostic = if let Some(sem) = error
.chain()
.find_map(|c| c.downcast_ref::<ManifestSemanticError>())
{
Expand All @@ -59,7 +60,29 @@ fn emit_manifest_diagnostic(config: &Config, error: &anyhow::Error) {
})
.unwrap_or_default();
let file = src.map(|src| src.path.to_string());
(file, sem.to_string(), span, related)
let mut diagnostic = MachineDiagnostic::new(
MachineDiagnosticKind::ManifestDiagnostic,
sem.to_string(),
MachineDiagnosticSeverity::Error,
file.unwrap_or_else(|| "<unknown>".to_string()),
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: i'd assign the <unknown> string to some consts and have it named properly, so its clear it describes "no file" state

span.map(|span| MachineDiagnosticSpan {
start: span.start,
end: span.end,
})
.unwrap_or(MachineDiagnosticSpan { start: 0, end: 0 }),
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this be default for MachineDiagnosticSpan?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1, if the span is no longer an Option, the 0,0 span should be set as a default

);
diagnostic.related = related
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nitpick: maybe use a builder pattern instead? could get rid of setting the defaults too.

.into_iter()
.map(|related| MachineRelatedLocation {
message: related.message,
file: None,
span: MachineDiagnosticSpan {
start: related.span.start,
end: related.span.end,
},
})
.collect();
diagnostic
} else if let Some(parse_err) = error
.chain()
.find_map(|c| c.downcast_ref::<ManifestParseError>())
Expand All @@ -76,11 +99,21 @@ fn emit_manifest_diagnostic(config: &Config, error: &anyhow::Error) {
};
let span = toml_err
.and_then(|e| e.span())
.map(|s| ManifestDiagnosticSpan {
.map(|s| MachineDiagnosticSpan {
start: s.start,
end: s.end,
});
(Some(parse_err.path().to_string()), message, span, vec![])
MachineDiagnostic::new(
MachineDiagnosticKind::ManifestDiagnostic,
message,
MachineDiagnosticSeverity::Error,
parse_err.path().to_string(),
span.map(|span| MachineDiagnosticSpan {
start: span.start,
end: span.end,
})
.unwrap_or(MachineDiagnosticSpan { start: 0, end: 0 }),
)
} else if let Some(src) = error
.chain()
.find_map(|c| c.downcast_ref::<ManifestErrorWithSource>())
Expand All @@ -89,18 +122,16 @@ fn emit_manifest_diagnostic(config: &Config, error: &anyhow::Error) {
.source()
.map(|err| err.to_string())
.unwrap_or_else(|| error.to_string());
(Some(src.path.to_string()), message, None, vec![])
MachineDiagnostic::new(
MachineDiagnosticKind::ManifestDiagnostic,
message,
MachineDiagnosticSeverity::Error,
src.path.to_string(),
MachineDiagnosticSpan { start: 0, end: 0 },
)
} else {
return;
};

config
.ui()
.force_print(MachineMessage(ManifestDiagnosticMessage {
kind: ManifestMessageKind::ManifestDiagnostic,
message,
file,
span,
related,
}));
config.ui().force_print(MachineMessage(diagnostic));
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nitpick: could be just Message impl for MachineDiagnostic.

}
9 changes: 8 additions & 1 deletion scarb/src/compiler/helpers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use cairo_lang_compiler::CompilerConfig;
use cairo_lang_compiler::diagnostics::DiagnosticsReporter;
use cairo_lang_diagnostics::{FormattedDiagnosticEntry, Severity};
use cairo_lang_filesystem::db::FilesGroup;
use cairo_lang_filesystem::ids::CrateId;
use cairo_lang_filesystem::ids::{CrateId, CrateInput};
use itertools::Itertools;
use salsa::Database;
use serde::Serialize;
Expand Down Expand Up @@ -41,6 +41,13 @@ impl<W: Write> Write for CountingWriter<W> {
}
}

pub fn all_crate_inputs(db: &dyn Database) -> Vec<CrateInput> {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Where is this used?

db.crates()
.iter()
.map(|crate_id| crate_id.long(db).clone().into_crate_input(db))
.collect_vec()
}

pub fn build_compiler_config<'c, 'db>(
db: &'db dyn Database,
unit: &CairoCompilationUnit,
Expand Down
83 changes: 83 additions & 0 deletions scarb/src/core/machine_diagnostic.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
use serde::Serialize;

#[derive(Debug, Clone, Serialize)]
pub struct MachineDiagnostic {
pub kind: MachineDiagnosticKind,
pub message: String,
pub file: String,
pub span: MachineDiagnosticSpan,
pub severity: MachineDiagnosticSeverity,
#[serde(skip_serializing_if = "Option::is_none")]
pub code: Option<String>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub related: Vec<MachineRelatedLocation>,
}

#[derive(Debug, Clone, Copy, Serialize, PartialEq, Eq)]
#[serde(rename_all = "snake_case")]
pub enum MachineDiagnosticKind {
Diagnostic,
ManifestDiagnostic,
}

#[derive(Debug, Clone, Copy, Serialize, PartialEq, Eq)]
#[serde(rename_all = "snake_case")]
pub enum MachineDiagnosticSeverity {
Error,
Warning,
}

#[derive(Debug, Clone, Serialize)]
pub struct MachineDiagnosticSpan {
pub start: usize,
pub end: usize,
}

#[derive(Debug, Clone, Serialize)]
pub struct MachineRelatedLocation {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit

Suggested change
pub struct MachineRelatedLocation {
pub struct MachineDiagnosticRelatedLocation {

pub message: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub file: Option<String>,
pub span: MachineDiagnosticSpan,
}

#[derive(Debug, Clone, Serialize)]
pub struct MachineDiagnosticData {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this used anywhere?

#[serde(skip_serializing_if = "Option::is_none")]
pub span: Option<MachineDiagnosticSpan>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub related: Vec<MachineRelatedLocation>,
}

impl MachineDiagnostic {
pub fn new(
kind: MachineDiagnosticKind,
message: String,
severity: MachineDiagnosticSeverity,
file: String,
span: MachineDiagnosticSpan,
) -> Self {
Self {
kind,
message,
severity,
code: None,
file,
span,
related: vec![],
}
}

pub fn severity(&self) -> MachineDiagnosticSeverity {
self.severity
}
}

impl From<std::ops::Range<usize>> for MachineDiagnosticSpan {
fn from(range: std::ops::Range<usize>) -> Self {
Self {
start: range.start,
end: range.end,
}
}
}
85 changes: 16 additions & 69 deletions scarb/src/core/manifest/diagnostic.rs
Original file line number Diff line number Diff line change
@@ -1,65 +1,12 @@
use crate::core::{PackageName, TargetKind};
use crate::core::{MachineDiagnosticSpan, PackageName, TargetKind};
use scarb_manifest_schema::FieldPath;
use serde::Serialize;
use toml_edit::{Document, Item, Table};

/// The serialized shape of a manifest diagnostic emitted in JSON output mode.
/// Both the `metadata` command (for parse/semantic errors) and the workspace
/// loader (for unknown-field warnings) use this type so the JSON output is
/// uniform.
#[derive(Serialize)]
pub struct ManifestDiagnosticMessage {
pub kind: ManifestMessageKind,
pub message: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub file: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub span: Option<ManifestDiagnosticSpan>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub related: Vec<ManifestRelatedLocation>,
}

#[derive(Serialize)]
#[serde(rename_all = "snake_case")]
pub enum ManifestMessageKind {
ManifestDiagnostic,
}

#[derive(Debug, Clone, Serialize)]
pub struct ManifestDiagnosticData {
#[serde(skip_serializing_if = "Option::is_none")]
pub span: Option<ManifestDiagnosticSpan>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub related: Vec<ManifestRelatedLocation>,
}

#[derive(Debug, Clone, Serialize)]
pub struct ManifestRelatedLocation {
pub message: String,
pub span: ManifestDiagnosticSpan,
}

#[derive(Debug, Clone)]
pub struct ManifestRelatedAnchor {
pub message: String,
pub anchor: ManifestDiagnosticAnchor,
}

#[derive(Debug, Clone, Serialize)]
pub struct ManifestDiagnosticSpan {
pub start: usize,
pub end: usize,
}

impl From<std::ops::Range<usize>> for ManifestDiagnosticSpan {
fn from(range: std::ops::Range<usize>) -> Self {
Self {
start: range.start,
end: range.end,
}
}
}

#[derive(Debug, Clone)]
pub enum ManifestDependencyTable {
Dependencies,
Expand Down Expand Up @@ -207,11 +154,11 @@ impl ManifestDiagnosticAnchor {

/// Returns the span of `key` in `table`, preferring the key token span over its value's span.
/// Example: in `version = "1.0.0"`, resolves to `version` (or to `"1.0.0"` if the key span is unavailable).
fn key_or_item_span(table: &Table, key: &str) -> Option<ManifestDiagnosticSpan> {
fn key_or_item_span(table: &Table, key: &str) -> Option<MachineDiagnosticSpan> {
let (key, item) = table.get_key_value(key)?;
key.span()
.map(ManifestDiagnosticSpan::from)
.or_else(|| item.span().map(ManifestDiagnosticSpan::from))
.map(MachineDiagnosticSpan::from)
.or_else(|| item.span().map(MachineDiagnosticSpan::from))
}

/// Returns the span of an entry in `table` by `key`.
Expand All @@ -221,22 +168,22 @@ fn table_entry_span(
table: &Table,
key: &str,
field: Option<&'static str>,
) -> Option<ManifestDiagnosticSpan> {
) -> Option<MachineDiagnosticSpan> {
let (entry_key, entry_item) = table.get_key_value(key)?;

let Some(field) = field else {
return entry_key
.span()
.map(ManifestDiagnosticSpan::from)
.or_else(|| entry_item.span().map(ManifestDiagnosticSpan::from));
.map(MachineDiagnosticSpan::from)
.or_else(|| entry_item.span().map(MachineDiagnosticSpan::from));
};

if let Some(inline_table) = entry_item.as_inline_table() {
let (key, item) = inline_table.get_key_value(field)?;
return key
.span()
.map(ManifestDiagnosticSpan::from)
.or_else(|| item.span().map(ManifestDiagnosticSpan::from));
.map(MachineDiagnosticSpan::from)
.or_else(|| item.span().map(MachineDiagnosticSpan::from));
}

entry_item
Expand All @@ -261,7 +208,7 @@ fn patch_source_table<'a>(root: &'a Table, source: &str) -> Option<&'a Table> {
pub fn resolve_manifest_anchor(
source: &str,
anchor: &ManifestDiagnosticAnchor,
) -> Option<ManifestDiagnosticSpan> {
) -> Option<MachineDiagnosticSpan> {
let document = Document::parse(source).ok()?;
resolve_anchor_in_doc(document.as_table(), anchor)
}
Expand All @@ -270,7 +217,7 @@ pub fn resolve_manifest_anchor(
pub fn resolve_anchor_in_doc(
root: &Table,
anchor: &ManifestDiagnosticAnchor,
) -> Option<ManifestDiagnosticSpan> {
) -> Option<MachineDiagnosticSpan> {
match anchor {
ManifestDiagnosticAnchor::PackageField { field } => {
table_at_path(root, &["package"]).and_then(|table| key_or_item_span(table, field))
Expand All @@ -295,23 +242,23 @@ pub fn resolve_anchor_in_doc(
};
field
.and_then(|field| key_or_item_span(table, field))
.or_else(|| table.span().map(ManifestDiagnosticSpan::from))
.or_else(|| table.span().map(MachineDiagnosticSpan::from))
}
ManifestDiagnosticAnchor::PatchRoot => {
let patch = table_at_path(root, &["patch"])?;
if !patch.is_implicit() {
patch.span().map(ManifestDiagnosticSpan::from)
patch.span().map(MachineDiagnosticSpan::from)
} else {
patch.iter().find_map(|(_, item)| match item {
Item::Table(table) => table.span().map(ManifestDiagnosticSpan::from),
Item::Table(table) => table.span().map(MachineDiagnosticSpan::from),
_ => None,
})
}
}
ManifestDiagnosticAnchor::PatchSource {
source: patch_source,
} => patch_source_table(root, patch_source.as_str())
.and_then(|t| t.span().map(ManifestDiagnosticSpan::from)),
.and_then(|t| t.span().map(MachineDiagnosticSpan::from)),
ManifestDiagnosticAnchor::PatchDependency {
source: patch_source,
name,
Expand Down Expand Up @@ -363,7 +310,7 @@ pub fn resolve_anchor_in_doc(
return Some(name_span);
}

section.span().map(ManifestDiagnosticSpan::from)
section.span().map(MachineDiagnosticSpan::from)
}
ManifestDiagnosticAnchor::RawTomlPath { path } => {
let (key, parent_segments) = path.split_last()?;
Expand Down
Loading
Loading