Skip to content

Commit

Permalink
feat(compiler): error diagnostics v2 (#4416)
Browse files Browse the repository at this point in the history
The Wing compiler now has the capability to provide more detailed information with each compiler error. I've implemented a few examples in this pull request to demonstrate the feature:

Example 1: showing the original symbol definition in "use before defined" errors:
<img width="425" alt="Screenshot 2023-10-04 at 6 34 41 PM" src="https://github.com/winglang/wing/assets/5008987/8749876c-9fd2-455e-9af5-93b4679af492">

Example 2: showing the previous definition in "symbol already defined" errors:
<img width="403" alt="Screenshot 2023-10-04 at 6 36 01 PM" src="https://github.com/winglang/wing/assets/5008987/4106b141-db79-452d-a517-e279b956fb62">

Example 3: showing the variable definition in "variable is not reassignable" errors:
<img width="499" alt="Screenshot 2023-10-04 at 6 31 04 PM" src="https://github.com/winglang/wing/assets/5008987/ea2e2042-6e57-4d4e-b7e0-15bc783edd02">

These errors are also displayed as related information in the LSP with clickable links:
<img width="452" alt="Screenshot 2023-10-04 at 6 21 01 PM" src="https://github.com/winglang/wing/assets/5008987/68680896-adc6-4de7-931e-be2589bc9f08">

I haven't tested what happens when the annotations refer to other files, but in theory it should also work...

## Checklist

- [x] Title matches [Winglang's style guide](https://www.winglang.io/contributing/start-here/pull_requests#how-are-pull-request-titles-formatted)
- [x] Description explains motivation and solution
- [x] Tests added (always)
- [ ] Docs updated (only required for features)
- [ ] Added `pr/e2e-full` label if this feature requires end-to-end testing

*By submitting this pull request, I confirm that my contribution is made under the terms of the [Wing Cloud Contribution License](https://github.com/winglang/wing/blob/main/CONTRIBUTION_LICENSE.md)*.
  • Loading branch information
Chriscbr authored Oct 5, 2023
1 parent c898c57 commit 713f29a
Show file tree
Hide file tree
Showing 18 changed files with 225 additions and 65 deletions.
26 changes: 24 additions & 2 deletions apps/wing-console/console/server/src/utils/format-wing-error.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,12 +42,12 @@ export const formatWingError = async (error: unknown) => {
const result = [];

for (const error of errors) {
const { message, span } = error;
const { message, span, annotations } = error;
let files: File[] = [];
let labels: Label[] = [];

// file_id might be "" if the span is synthetic (see #2521)
if (span !== null && span.file_id) {
if (span !== null && span !== undefined && span.file_id) {
// `span` should only be null if source file couldn't be read etc.
const source = await readFile(span.file_id, "utf8");
const start = offsetFromLineAndColumn(
Expand All @@ -70,6 +70,28 @@ export const formatWingError = async (error: unknown) => {
});
}

for (const annotation of annotations) {
const source = await readFile(annotation.span.file_id, "utf8");
const start = offsetFromLineAndColumn(
source,
annotation.span.start.line,
annotation.span.start.col,
);
const end = offsetFromLineAndColumn(
source,
annotation.span.end.line,
annotation.span.end.col,
);
files.push({ name: annotation.span.file_id, source });
labels.push({
fileId: annotation.span.file_id,
rangeStart: start,
rangeEnd: end,
message: annotation.message,
style: "secondary",
});
}

console.log("pre diagnostic");
const diagnosticText = emitDiagnostic(
files,
Expand Down
24 changes: 23 additions & 1 deletion apps/wing/src/commands/compile.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ export async function compile(entrypoint: string, options: CompileOptions): Prom
const result = [];

for (const diagnostic of diagnostics) {
const { message, span } = diagnostic;
const { message, span, annotations } = diagnostic;
let files: File[] = [];
let labels: Label[] = [];

Expand All @@ -74,6 +74,28 @@ export async function compile(entrypoint: string, options: CompileOptions): Prom
});
}

for (const annotation of annotations) {
const source = await fsPromise.readFile(annotation.span.file_id, "utf8");
const start = byteOffsetFromLineAndColumn(
source,
annotation.span.start.line,
annotation.span.start.col
);
const end = byteOffsetFromLineAndColumn(
source,
annotation.span.end.line,
annotation.span.end.col
);
files.push({ name: annotation.span.file_id, source });
labels.push({
fileId: annotation.span.file_id,
rangeStart: start,
rangeEnd: end,
message: annotation.message,
style: "secondary",
});
}

const diagnosticText = emitDiagnostic(
files,
{
Expand Down
13 changes: 12 additions & 1 deletion apps/wing/src/commands/lsp.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {
Diagnostic,
Range,
DocumentUri,
Location,
} from "vscode-languageserver/node";

export async function lsp() {
Expand Down Expand Up @@ -130,7 +131,17 @@ export async function lsp() {
const diagnosticUri = "file://" + rd.span.file_id;
const diag = Diagnostic.create(
Range.create(rd.span.start.line, rd.span.start.col, rd.span.end.line, rd.span.end.col),
rd.message
rd.message,
undefined,
undefined,
undefined,
rd.annotations.map((a) => ({
location: Location.create(
"file://" + a.span.file_id,
Range.create(a.span.start.line, a.span.start.col, a.span.end.line, a.span.end.col)
),
message: a.message,
}))
);

if (!allDiagnostics.has(diagnosticUri)) {
Expand Down
1 change: 1 addition & 0 deletions libs/wingc/src/comp_ctx.rs
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,7 @@ pub fn set_custom_panic_hook() {
CompilationContext::get_phase()
),
span: Some(CompilationContext::get_span()),
annotations: vec![],
})
}));
}
8 changes: 8 additions & 0 deletions libs/wingc/src/diagnostic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -187,9 +187,16 @@ impl PartialOrd for WingSpan {
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize)]
pub struct Diagnostic {
pub message: String,
pub annotations: Vec<DiagnosticAnnotation>,
pub span: Option<WingSpan>,
}

#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize)]
pub struct DiagnosticAnnotation {
pub message: String,
pub span: WingSpan,
}

impl std::fmt::Display for Diagnostic {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
if let Some(span) = &self.span {
Expand Down Expand Up @@ -283,6 +290,7 @@ pub fn reset_diagnostics() {
pub struct TypeError {
pub message: String,
pub span: WingSpan,
pub annotations: Vec<DiagnosticAnnotation>,
}

impl std::fmt::Display for TypeError {
Expand Down
1 change: 1 addition & 0 deletions libs/wingc/src/files.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ impl From<FilesError> for Diagnostic {
Self {
message: err.to_string(),
span: None,
annotations: vec![],
}
}
}
Expand Down
2 changes: 2 additions & 0 deletions libs/wingc/src/jsify.rs
Original file line number Diff line number Diff line change
Expand Up @@ -404,6 +404,7 @@ impl<'a> JSifier<'a> {
report_diagnostic(Diagnostic {
message: "Cannot reference an inflight value from within a preflight expression".to_string(),
span: Some(expression.span.clone()),
annotations: vec![],
});

return "<ERROR>".to_string();
Expand Down Expand Up @@ -1089,6 +1090,7 @@ impl<'a> JSifier<'a> {
report_diagnostic(Diagnostic {
message: format!("Failed to resolve extern \"{external_spec}\": {err}"),
span: Some(func_def.span.clone()),
annotations: vec![],
});
format!("/* unresolved: \"{external_spec}\" */")
}
Expand Down
2 changes: 1 addition & 1 deletion libs/wingc/src/json_schema_generator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ impl JsonSchemaGenerator {
fn get_struct_schema_required_fields(&self, env: &SymbolEnv) -> CodeMaker {
let mut code = CodeMaker::default();
code.open("required: [");
for (field_name, (_stmt_idx, kind)) in env.symbol_map.iter() {
for (field_name, (_stmt_idx, _, kind)) in env.symbol_map.iter() {
if !matches!(*kind.as_variable().unwrap().type_, Type::Optional(_)) {
code.line(format!("\"{}\",", field_name));
}
Expand Down
3 changes: 3 additions & 0 deletions libs/wingc/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,7 @@ pub unsafe extern "C" fn wingc_compile(ptr: u32, len: u32) -> u64 {
report_diagnostic(Diagnostic {
message: format!("Expected 3 arguments to wingc_compile, got {}", split.len()),
span: None,
annotations: vec![],
});
return WASM_RETURN_ERROR;
}
Expand All @@ -176,6 +177,7 @@ pub unsafe extern "C" fn wingc_compile(ptr: u32, len: u32) -> u64 {
report_diagnostic(Diagnostic {
message: format!("Source path cannot be found: {}", source_path),
span: None,
annotations: vec![],
});
return WASM_RETURN_ERROR;
}
Expand Down Expand Up @@ -338,6 +340,7 @@ pub fn compile(
report_diagnostic(Diagnostic {
message: format!("Project directory must be absolute: {}", project_dir),
span: None,
annotations: vec![],
});
return Err(());
}
Expand Down
5 changes: 4 additions & 1 deletion libs/wingc/src/lifting.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,11 +56,12 @@ impl<'a> LiftVisitor<'a> {
if let Some(env) = self.ctx.current_env() {
if matches!(
env.lookup_ext(symbol, Some(self.ctx.current_stmt_idx())),
LookupResult::DefinedLater
LookupResult::DefinedLater(_)
) {
report_diagnostic(Diagnostic {
span: Some(symbol.span.clone()),
message: format!("Cannot access \"{symbol}\" because it is shadowed by another symbol with the same name"),
annotations: vec![],
});
}
}
Expand Down Expand Up @@ -215,6 +216,7 @@ impl<'a> Visit<'a> for LiftVisitor<'a> {
expr_type.to_string()
),
span: Some(node.span.clone()),
annotations: vec![],
});

return;
Expand Down Expand Up @@ -279,6 +281,7 @@ impl<'a> Visit<'a> for LiftVisitor<'a> {
message: format!(
"Cannot qualify access to a lifted type \"{udt_type}\" (see https://github.com/winglang/wing/issues/76 for more details)"),
span: Some(node.span.clone()),
annotations: vec![],
});

return;
Expand Down
4 changes: 2 additions & 2 deletions libs/wingc/src/lsp/completions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -510,7 +510,7 @@ fn get_current_scope_completions(
true
}
}) {
let symbol_kind = &symbol_data.1 .1;
let symbol_kind = &symbol_data.1 .2;

if let Some(completion) = format_symbol_kind_as_completion(symbol_data.0, symbol_kind) {
completions.push(completion);
Expand Down Expand Up @@ -731,7 +731,7 @@ fn get_completions_from_namespace(
.envs
.iter()
.flat_map(|env| env.symbol_map.iter())
.flat_map(|(name, symbol)| format_symbol_kind_as_completion(name, &symbol.1))
.flat_map(|(name, symbol)| format_symbol_kind_as_completion(name, &symbol.2))
.chain(util_completions.into_iter())
.collect()
}
Expand Down
6 changes: 6 additions & 0 deletions libs/wingc/src/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,7 @@ pub fn parse_wing_project(
formatted_cycle.trim_end()
),
span: None,
annotations: vec![],
});

// return a list of all files just so we can continue type-checking
Expand Down Expand Up @@ -359,6 +360,7 @@ impl<'s> Parser<'s> {
let diag = Diagnostic {
message: message.to_string(),
span: Some(span),
annotations: vec![],
};
report_diagnostic(diag);
}
Expand All @@ -367,6 +369,7 @@ impl<'s> Parser<'s> {
let diag = Diagnostic {
message: message.to_string(),
span: Some(self.node_span(node)),
annotations: vec![],
};
report_diagnostic(diag);

Expand Down Expand Up @@ -996,6 +999,7 @@ impl<'s> Parser<'s> {
message: "Static class fields not supported yet, see https://github.com/winglang/wing/issues/1668"
.to_string(),
span: Some(self.node_span(&class_element)),
annotations: vec![],
});
}

Expand Down Expand Up @@ -2115,6 +2119,7 @@ impl<'s> Parser<'s> {
let diag = Diagnostic {
message: "Expected ';'".to_string(),
span: Some(self.node_span(&target_node)),
annotations: vec![],
};
report_diagnostic(diag);
} else if node.kind() == "AUTOMATIC_BLOCK" {
Expand All @@ -2135,6 +2140,7 @@ impl<'s> Parser<'s> {
let diag = Diagnostic {
message: format!("Expected '{}'", node.kind()),
span: Some(self.node_span(&target_node)),
annotations: vec![],
};
report_diagnostic(diag);
}
Expand Down
Loading

0 comments on commit 713f29a

Please sign in to comment.