Skip to content

Commit 7803c6e

Browse files
alexghrkevaundray
authored andcommitted
feat: return compilation errors from noir_wasm (noir-lang#3091)
Co-authored-by: kevaundray <kevtheappdev@gmail.com>
1 parent d8a5a9c commit 7803c6e

File tree

4 files changed

+80
-21
lines changed

4 files changed

+80
-21
lines changed

Cargo.lock

+1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

compiler/wasm/Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ fm.workspace = true
1717
nargo.workspace = true
1818
noirc_driver.workspace = true
1919
noirc_frontend.workspace = true
20+
noirc_errors.workspace = true
2021
wasm-bindgen.workspace = true
2122
serde.workspace = true
2223
js-sys.workspace = true

compiler/wasm/src/compile.rs

+6-2
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,9 @@ pub fn compile(
5656

5757
if contracts.unwrap_or_default() {
5858
let compiled_contract = compile_contract(&mut context, crate_id, &compile_options)
59-
.map_err(|_| JsCompileError::new("Failed to compile contract".to_string()))?
59+
.map_err(|errs| {
60+
JsCompileError::new("Failed to compile contract", errs, &context.file_manager)
61+
})?
6062
.0;
6163

6264
let optimized_contract =
@@ -68,7 +70,9 @@ pub fn compile(
6870
Ok(<JsValue as JsValueSerdeExt>::from_serde(&preprocessed_contract).unwrap())
6971
} else {
7072
let compiled_program = compile_main(&mut context, crate_id, &compile_options, None, true)
71-
.map_err(|_| JsCompileError::new("Failed to compile program".to_string()))?
73+
.map_err(|errs| {
74+
JsCompileError::new("Failed to compile program", errs, &context.file_manager)
75+
})?
7276
.0;
7377

7478
let optimized_program =

compiler/wasm/src/errors.rs

+72-19
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,82 @@
1-
use js_sys::JsString;
2-
1+
use gloo_utils::format::JsValueSerdeExt;
2+
use serde::{Deserialize, Serialize};
33
use wasm_bindgen::prelude::*;
44

5+
use fm::FileManager;
6+
use noirc_errors::FileDiagnostic;
7+
58
#[wasm_bindgen(typescript_custom_section)]
6-
const COMPILE_ERROR: &'static str = r#"
7-
export type CompileError = Error;
9+
const DIAGNOSTICS: &'static str = r#"
10+
export type Diagnostic = {
11+
message: string;
12+
file_path: string;
13+
secondaries: ReadonlyArray<{
14+
message: string;
15+
start: number;
16+
end: number;
17+
}>;
18+
}
19+
20+
interface CompileError {
21+
diagnostics: ReadonlyArray<Diagnostic>;
22+
}
823
"#;
924

10-
/// `CompileError` is a raw js error.
11-
/// It'd be ideal that `CompileError` was a subclass of `Error`, but for that we'd need to use JS snippets or a js module.
12-
/// Currently JS snippets don't work with a nodejs target. And a module would be too much for just a custom error type.
13-
#[wasm_bindgen]
14-
extern "C" {
15-
#[wasm_bindgen(extends = js_sys::Error, js_name = "CompileError", typescript_type = "CompileError")]
16-
#[derive(Clone, Debug, PartialEq, Eq)]
17-
pub type JsCompileError;
18-
19-
#[wasm_bindgen(constructor, js_class = "Error")]
20-
fn constructor(message: JsString) -> JsCompileError;
25+
#[derive(Serialize, Deserialize)]
26+
struct JsDiagnosticLabel {
27+
message: String,
28+
start: u32,
29+
end: u32,
30+
}
31+
32+
#[derive(Serialize, Deserialize)]
33+
struct JsDiagnostic {
34+
message: String,
35+
file_path: String,
36+
secondaries: Vec<JsDiagnosticLabel>,
37+
}
38+
39+
impl JsDiagnostic {
40+
fn new(file_diagnostic: &FileDiagnostic, file_path: String) -> JsDiagnostic {
41+
let diagnostic = &file_diagnostic.diagnostic;
42+
let message = diagnostic.message.clone();
43+
44+
let secondaries = diagnostic
45+
.secondaries
46+
.iter()
47+
.map(|label| JsDiagnosticLabel {
48+
message: label.message.clone(),
49+
start: label.span.start(),
50+
end: label.span.end(),
51+
})
52+
.collect();
53+
54+
JsDiagnostic { message, file_path, secondaries }
55+
}
56+
}
57+
58+
#[wasm_bindgen(getter_with_clone, js_name = "CompileError")]
59+
pub struct JsCompileError {
60+
pub message: js_sys::JsString,
61+
pub diagnostics: JsValue,
2162
}
2263

2364
impl JsCompileError {
24-
/// Creates a new execution error with the given call stack.
25-
/// Call stacks won't be optional in the future, after removing ErrorLocation in ACVM.
26-
pub fn new(message: String) -> Self {
27-
JsCompileError::constructor(JsString::from(message))
65+
pub fn new(
66+
message: &str,
67+
file_diagnostics: Vec<FileDiagnostic>,
68+
file_manager: &FileManager,
69+
) -> JsCompileError {
70+
let diagnostics: Vec<_> = file_diagnostics
71+
.iter()
72+
.map(|err| {
73+
JsDiagnostic::new(err, file_manager.path(err.file_id).to_str().unwrap().to_string())
74+
})
75+
.collect();
76+
77+
JsCompileError {
78+
message: js_sys::JsString::from(message.to_string()),
79+
diagnostics: <JsValue as JsValueSerdeExt>::from_serde(&diagnostics).unwrap(),
80+
}
2881
}
2982
}

0 commit comments

Comments
 (0)