Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: let all compiler errors carry a Location instead of a Span #7486

Merged
merged 18 commits into from
Feb 25, 2025
Merged
Show file tree
Hide file tree
Changes from 13 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
218 changes: 94 additions & 124 deletions Cargo.lock

Large diffs are not rendered by default.

14 changes: 7 additions & 7 deletions compiler/noirc_driver/src/abi_gen.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use iter_extended::vecmap;
use noirc_abi::{
Abi, AbiErrorType, AbiParameter, AbiReturnType, AbiType, AbiValue, AbiVisibility, Sign,
};
use noirc_errors::Span;
use noirc_errors::Location;
use noirc_evaluator::ErrorType;
use noirc_frontend::ast::{Signedness, Visibility};
use noirc_frontend::TypeBinding;
Expand Down Expand Up @@ -42,19 +42,19 @@ pub(super) fn gen_abi(
}

// Get the Span of the root crate's main function, or else a dummy span if that fails
fn get_main_function_span(context: &Context) -> Span {
fn get_main_function_location(context: &Context) -> Location {
if let Some(func_id) = context.get_main_function(context.root_crate_id()) {
context.function_meta(&func_id).location.span
context.function_meta(&func_id).location
} else {
Span::default()
Location::dummy()
}
}

fn build_abi_error_type(context: &Context, typ: ErrorType) -> AbiErrorType {
match typ {
ErrorType::Dynamic(typ) => {
if let Type::FmtString(len, item_types) = typ {
let span = get_main_function_span(context);
let span = get_main_function_location(context);
let length = len.evaluate_to_u32(span).expect("Cannot evaluate fmt length");
let Type::Tuple(item_types) = item_types.as_ref() else {
unreachable!("FmtString items must be a tuple")
Expand All @@ -74,7 +74,7 @@ pub(super) fn abi_type_from_hir_type(context: &Context, typ: &Type) -> AbiType {
match typ {
Type::FieldElement => AbiType::Field,
Type::Array(size, typ) => {
let span = get_main_function_span(context);
let span = get_main_function_location(context);
let length = size
.evaluate_to_u32(span)
.expect("Cannot have variable sized arrays as a parameter to main");
Expand Down Expand Up @@ -103,7 +103,7 @@ pub(super) fn abi_type_from_hir_type(context: &Context, typ: &Type) -> AbiType {
}
Type::Bool => AbiType::Boolean,
Type::String(size) => {
let span = get_main_function_span(context);
let span = get_main_function_location(context);
let size = size
.evaluate_to_u32(span)
.expect("Cannot have variable sized strings as a parameter to main");
Expand Down
5 changes: 3 additions & 2 deletions compiler/noirc_driver/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -352,9 +352,10 @@ pub fn check_crate(
let crate_files = context.crate_files(&crate_id);
let warnings_and_errors: Vec<FileDiagnostic> = diagnostics
.into_iter()
.map(|(error, file_id)| {
.map(|error| {
let location = error.location();
let diagnostic = CustomDiagnostic::from(&error);
diagnostic.in_file(file_id)
diagnostic.in_file(location.file)
})
.filter(|diagnostic| {
// We filter out any warnings if they're going to be ignored later on to free up memory.
Expand Down
66 changes: 29 additions & 37 deletions compiler/noirc_errors/src/reporter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,12 +50,12 @@ impl CustomDiagnostic {
fn simple_with_kind(
primary_message: String,
secondary_message: String,
secondary_span: Span,
secondary_location: Location,
kind: DiagnosticKind,
) -> CustomDiagnostic {
CustomDiagnostic {
message: primary_message,
secondaries: vec![CustomLabel::new(secondary_message, secondary_span, None)],
secondaries: vec![CustomLabel::new(secondary_message, secondary_location)],
notes: Vec::new(),
kind,
deprecated: false,
Expand All @@ -67,50 +67,50 @@ impl CustomDiagnostic {
pub fn simple_error(
primary_message: String,
secondary_message: String,
secondary_span: Span,
secondary_location: Location,
) -> CustomDiagnostic {
Self::simple_with_kind(
primary_message,
secondary_message,
secondary_span,
secondary_location,
DiagnosticKind::Error,
)
}

pub fn simple_warning(
primary_message: String,
secondary_message: String,
secondary_span: Span,
secondary_location: Location,
) -> CustomDiagnostic {
Self::simple_with_kind(
primary_message,
secondary_message,
secondary_span,
secondary_location,
DiagnosticKind::Warning,
)
}

pub fn simple_info(
primary_message: String,
secondary_message: String,
secondary_span: Span,
secondary_location: Location,
) -> CustomDiagnostic {
Self::simple_with_kind(
primary_message,
secondary_message,
secondary_span,
secondary_location,
DiagnosticKind::Info,
)
}

pub fn simple_bug(
primary_message: String,
secondary_message: String,
secondary_span: Span,
secondary_location: Location,
) -> CustomDiagnostic {
CustomDiagnostic {
message: primary_message,
secondaries: vec![CustomLabel::new(secondary_message, secondary_span, None)],
secondaries: vec![CustomLabel::new(secondary_message, secondary_location)],
notes: Vec::new(),
kind: DiagnosticKind::Bug,
deprecated: false,
Expand All @@ -132,12 +132,8 @@ impl CustomDiagnostic {
self.notes.push(message);
}

pub fn add_secondary(&mut self, message: String, span: Span) {
self.secondaries.push(CustomLabel::new(message, span, None));
}

pub fn add_secondary_with_file(&mut self, message: String, span: Span, file: fm::FileId) {
self.secondaries.push(CustomLabel::new(message, span, Some(file)));
pub fn add_secondary(&mut self, message: String, location: Location) {
self.secondaries.push(CustomLabel::new(message, location));
}

pub fn is_error(&self) -> bool {
Expand Down Expand Up @@ -176,13 +172,12 @@ impl std::fmt::Display for CustomDiagnostic {
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct CustomLabel {
pub message: String,
pub span: Span,
pub file: Option<fm::FileId>,
pub location: Location,
}

impl CustomLabel {
fn new(message: String, span: Span, file: Option<fm::FileId>) -> CustomLabel {
CustomLabel { message, span, file }
fn new(message: String, location: Location) -> CustomLabel {
CustomLabel { message, location }
}
}

Expand Down Expand Up @@ -217,15 +212,14 @@ impl FileDiagnostic {
files: &'files impl Files<'files, FileId = fm::FileId>,
deny_warnings: bool,
) -> bool {
report(files, &self.diagnostic, Some(self.file_id), deny_warnings)
report(files, &self.diagnostic, deny_warnings)
}
}

/// Report the given diagnostic, and return true if it was an error
pub fn report<'files>(
files: &'files impl Files<'files, FileId = fm::FileId>,
custom_diagnostic: &CustomDiagnostic,
file: Option<fm::FileId>,
deny_warnings: bool,
) -> bool {
let color_choice =
Expand All @@ -234,15 +228,14 @@ pub fn report<'files>(
let config = term::Config::default();

let stack_trace = stack_trace(files, &custom_diagnostic.call_stack);
let diagnostic = convert_diagnostic(custom_diagnostic, file, stack_trace, deny_warnings);
let diagnostic = convert_diagnostic(custom_diagnostic, stack_trace, deny_warnings);
term::emit(&mut writer.lock(), &config, files, &diagnostic).unwrap();

deny_warnings || custom_diagnostic.is_error()
}

fn convert_diagnostic(
cd: &CustomDiagnostic,
file: Option<fm::FileId>,
stack_trace: String,
deny_warnings: bool,
) -> Diagnostic<fm::FileId> {
Expand All @@ -253,19 +246,18 @@ fn convert_diagnostic(
_ => Diagnostic::error(),
};

let secondary_labels = if let Some(file_id) = file {
cd.secondaries
.iter()
.map(|sl| {
let start_span = sl.span.start() as usize;
let end_span = sl.span.end() as usize;
let file = sl.file.unwrap_or(file_id);
Label::secondary(file, start_span..end_span).with_message(&sl.message)
})
.collect()
} else {
vec![]
};
let secondary_labels = cd
.secondaries
.iter()
.map(|custom_label| {
let location = custom_label.location;
let span = location.span;
let start_span = span.start() as usize;
let end_span = span.end() as usize;
let file = location.file;
Label::secondary(file, start_span..end_span).with_message(&custom_label.message)
})
.collect();

let mut notes = cd.notes.clone();
notes.push(stack_trace);
Expand Down
13 changes: 6 additions & 7 deletions compiler/noirc_evaluator/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
//! An Error of the latter is an error in the implementation of the compiler
use acvm::FieldElement;
use iter_extended::vecmap;
use noirc_errors::{CustomDiagnostic as Diagnostic, FileDiagnostic};
use noirc_errors::{CustomDiagnostic as Diagnostic, FileDiagnostic, Location};
use thiserror::Error;

use crate::ssa::ir::{call_stack::CallStack, types::NumericType};
Expand Down Expand Up @@ -85,8 +85,7 @@ impl From<SsaReport> for FileDiagnostic {
let call_stack = vecmap(call_stack, |location| location);
let file_id = call_stack.last().map(|location| location.file).unwrap_or_default();
let location = call_stack.last().expect("Expected RuntimeError to have a location");
let diagnostic =
Diagnostic::simple_warning(message, secondary_message, location.span);
let diagnostic = Diagnostic::simple_warning(message, secondary_message, *location);
diagnostic.with_call_stack(call_stack).in_file(file_id)
}
SsaReport::Bug(bug) => {
Expand All @@ -103,7 +102,7 @@ impl From<SsaReport> for FileDiagnostic {
let call_stack = vecmap(call_stack, |location| location);
let file_id = call_stack.last().map(|location| location.file).unwrap_or_default();
let location = call_stack.last().expect("Expected RuntimeError to have a location");
let diagnostic = Diagnostic::simple_bug(message, secondary_message, location.span);
let diagnostic = Diagnostic::simple_bug(message, secondary_message, *location);
diagnostic.with_call_stack(call_stack).in_file(file_id)
}
}
Expand Down Expand Up @@ -195,7 +194,7 @@ impl RuntimeError {
"Internal Consistency Evaluators Errors: \n
This is likely a bug. Consider opening an issue at https://github.com/noir-lang/noir/issues".to_owned(),
cause.to_string(),
noirc_errors::Span::inclusive(0, 0)
Location::dummy(),
)
}
RuntimeError::UnknownLoopBound { .. } => {
Expand All @@ -206,15 +205,15 @@ impl RuntimeError {
Diagnostic::simple_error(
primary_message,
"If attempting to fetch the length of a slice, try converting to an array. Slices only use dynamic lengths.".to_string(),
location.span,
*location,
)
}
_ => {
let message = self.to_string();
let location =
self.call_stack().last().unwrap_or_else(|| panic!("Expected RuntimeError to have a location. Error message: {message}"));

Diagnostic::simple_error(message, String::new(), location.span)
Diagnostic::simple_error(message, String::new(), *location)
}
}
}
Expand Down
8 changes: 4 additions & 4 deletions compiler/noirc_frontend/src/ast/expression.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ use acvm::{acir::AcirField, FieldElement};
use iter_extended::vecmap;
use noirc_errors::{Located, Location, Span};

use super::{AsTraitPath, TypePath};
use super::{AsTraitPath, TypePath, UnsafeExpression};

#[derive(Debug, PartialEq, Eq, Clone)]
pub enum ExpressionKind {
Expand All @@ -40,7 +40,7 @@ pub enum ExpressionKind {
Quote(Tokens),
Unquote(Box<Expression>),
Comptime(BlockExpression, Location),
Unsafe(BlockExpression, Location),
Unsafe(UnsafeExpression),
AsTraitPath(AsTraitPath),
TypePath(TypePath),

Expand Down Expand Up @@ -255,7 +255,7 @@ impl Expression {
match &self.kind {
ExpressionKind::Block(block_expression)
| ExpressionKind::Comptime(block_expression, _)
| ExpressionKind::Unsafe(block_expression, _) => {
| ExpressionKind::Unsafe(UnsafeExpression { block: block_expression, .. }) => {
if let Some(statement) = block_expression.statements.last() {
statement.type_location()
} else {
Expand Down Expand Up @@ -663,7 +663,7 @@ impl Display for ExpressionKind {
Lambda(lambda) => lambda.fmt(f),
Parenthesized(sub_expr) => write!(f, "({sub_expr})"),
Comptime(block, _) => write!(f, "comptime {block}"),
Unsafe(block, _) => write!(f, "unsafe {block}"),
Unsafe(UnsafeExpression { block, .. }) => write!(f, "unsafe {block}"),
Error => write!(f, "Error"),
Resolved(_) => write!(f, "?Resolved"),
Interned(_) => write!(f, "?Interned"),
Expand Down
Loading
Loading