Skip to content

Commit ce9e665

Browse files
Allow for multi-span errors (#296)
* Test * Specialized error message for one type of error. * Missing function arguments. * Move format methods to be inside of CompileError and CompileWarning. * Better error messages. * TooManyArgumentsForFunction message. * ReassignmentToNonVariable. * Simplify iterator stuff.
1 parent 703ba49 commit ce9e665

File tree

15 files changed

+280
-108
lines changed

15 files changed

+280
-108
lines changed

Cargo.lock

Lines changed: 2 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

core_lang/Cargo.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,8 @@ sha2 = "0.9"
2222
structopt = { version = "0.3", default-features = false, optional = true }
2323
thiserror = "1.0"
2424
uuid-b64 = "0.1"
25-
25+
line-col = "0.2"
26+
source-span = "2.4"
2627

2728
[[bin]]
2829
name = "selector-debug"

core_lang/src/error.rs

Lines changed: 184 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,11 @@ use crate::span::Span;
22
use crate::{parser::Rule, types::MaybeResolvedType};
33
use inflector::cases::classcase::to_class_case;
44
use inflector::cases::snakecase::to_snake_case;
5+
use line_col::LineColLookup;
6+
use source_span::{
7+
fmt::{Formatter, Style},
8+
Position,
9+
};
510
use thiserror::Error;
611

712
macro_rules! check {
@@ -137,6 +142,34 @@ impl<'sc> CompileWarning<'sc> {
137142
self.span.end_pos().line_col().into(),
138143
)
139144
}
145+
146+
pub fn format(&self, fmt: &mut Formatter) -> source_span::fmt::Formatted {
147+
let input = self.span.input();
148+
let chars = input.chars().map(|x| -> Result<_, ()> { Ok(x) });
149+
150+
let metrics = source_span::DEFAULT_METRICS;
151+
let buffer = source_span::SourceBuffer::new(chars, Position::default(), metrics);
152+
153+
for c in buffer.iter() {
154+
let _ = c.unwrap(); // report eventual errors.
155+
}
156+
157+
let (start_pos, end_pos) = self.span();
158+
let lookup = LineColLookup::new(input);
159+
let (start_line, start_col) = lookup.get(start_pos);
160+
let (end_line, end_col) = lookup.get(end_pos - 1);
161+
162+
let err_start = Position::new(start_line - 1, start_col - 1);
163+
let err_end = Position::new(end_line - 1, end_col - 1);
164+
let err_span = source_span::Span::new(err_start, err_end, err_end.next_column());
165+
fmt.add(
166+
err_span,
167+
Some(self.to_friendly_warning_string()),
168+
Style::Warning,
169+
);
170+
171+
fmt.render(buffer.iter(), buffer.span(), &metrics).unwrap()
172+
}
140173
}
141174

142175
#[derive(Debug, Clone)]
@@ -390,10 +423,15 @@ pub enum CompileError<'sc> {
390423
ReassignmentToNonVariable {
391424
name: &'sc str,
392425
kind: &'sc str,
393-
span: Span<'sc>,
426+
decl_span: Span<'sc>,
427+
usage_span: Span<'sc>,
428+
},
429+
#[error("Assignment to immutable variable. Variable {name} is not declared as mutable.")]
430+
AssignmentToNonMutable {
431+
name: &'sc str,
432+
decl_span: Span<'sc>,
433+
usage_span: Span<'sc>,
394434
},
395-
#[error("Assignment to immutable variable. Variable {0} is not declared as mutable.")]
396-
AssignmentToNonMutable(String, Span<'sc>),
397435
#[error(
398436
"Generic type \"{name}\" is not in scope. Perhaps you meant to specify type parameters in \
399437
the function signature? For example: \n`fn \
@@ -631,7 +669,8 @@ pub enum CompileError<'sc> {
631669
"Function \"{method_name}\" expects {expected} arguments but you provided {received}."
632670
)]
633671
TooManyArgumentsForFunction {
634-
span: Span<'sc>,
672+
decl_span: Span<'sc>,
673+
usage_span: Span<'sc>,
635674
method_name: &'sc str,
636675
expected: usize,
637676
received: usize,
@@ -640,7 +679,8 @@ pub enum CompileError<'sc> {
640679
"Function \"{method_name}\" expects {expected} arguments but you provided {received}."
641680
)]
642681
TooFewArgumentsForFunction {
643-
span: Span<'sc>,
682+
decl_span: Span<'sc>,
683+
usage_span: Span<'sc>,
644684
method_name: &'sc str,
645685
expected: usize,
646686
received: usize,
@@ -792,8 +832,8 @@ impl<'sc> CompileError<'sc> {
792832
PredicateMainDoesNotReturnBool(span) => span,
793833
NoScriptMainFunction(span) => span,
794834
MultipleScriptMainFunctions(span) => span,
795-
ReassignmentToNonVariable { span, .. } => span,
796-
AssignmentToNonMutable(_, span) => span,
835+
ReassignmentToNonVariable { usage_span, .. } => usage_span,
836+
AssignmentToNonMutable { usage_span, .. } => usage_span,
797837
TypeParameterNotInTypeScope { span, .. } => span,
798838
MultipleImmediates(span) => span,
799839
MismatchedTypeInTrait { span, .. } => span,
@@ -848,8 +888,8 @@ impl<'sc> CompileError<'sc> {
848888
UnnecessaryEnumInstantiator { span, .. } => span,
849889
TraitNotFound { span, .. } => span,
850890
InvalidExpressionOnLhs { span, .. } => span,
851-
TooManyArgumentsForFunction { span, .. } => span,
852-
TooFewArgumentsForFunction { span, .. } => span,
891+
TooManyArgumentsForFunction { usage_span, .. } => usage_span,
892+
TooFewArgumentsForFunction { usage_span, .. } => usage_span,
853893
InvalidAbiType { span, .. } => span,
854894
InvalidNumberOfAbiParams { span, .. } => span,
855895
NotAnAbi { span, .. } => span,
@@ -870,4 +910,139 @@ impl<'sc> CompileError<'sc> {
870910
self.internal_span().end_pos().line_col().into(),
871911
)
872912
}
913+
914+
pub fn format(&self, fmt: &mut Formatter) -> source_span::fmt::Formatted {
915+
match self {
916+
CompileError::AssignmentToNonMutable {
917+
name,
918+
decl_span,
919+
usage_span,
920+
} => self.format_one_hint_one_err(
921+
fmt,
922+
decl_span,
923+
format!(
924+
"Variable {} not declared as mutable. Try adding 'mut'.",
925+
name
926+
),
927+
usage_span,
928+
format!("Assignment to immutable variable {}.", name),
929+
),
930+
CompileError::TooFewArgumentsForFunction {
931+
decl_span,
932+
usage_span,
933+
method_name,
934+
expected,
935+
received,
936+
} => self.format_one_hint_one_err(
937+
fmt,
938+
decl_span,
939+
format!("Function {} declared here.", method_name),
940+
usage_span,
941+
format!(
942+
"Function {} expected {} arguments and recieved {}.",
943+
method_name, expected, received
944+
),
945+
),
946+
CompileError::TooManyArgumentsForFunction {
947+
decl_span,
948+
usage_span,
949+
method_name,
950+
expected,
951+
received,
952+
} => self.format_one_hint_one_err(
953+
fmt,
954+
decl_span,
955+
format!("Function {} declared here.", method_name),
956+
usage_span,
957+
format!(
958+
"Function {} expected {} arguments and recieved {}.",
959+
method_name, expected, received
960+
),
961+
),
962+
CompileError::ReassignmentToNonVariable {
963+
name,
964+
kind,
965+
decl_span,
966+
usage_span,
967+
} => self.format_one_hint_one_err(
968+
fmt,
969+
decl_span,
970+
format!("Symbol {} declared here.", name),
971+
usage_span,
972+
format!("Attempted to reassign to a symbol that is not a variable. Symbol {} is not a mutable \
973+
variable, it is a {}.", name, kind)
974+
),
975+
_ => self.format_err_simple(fmt),
976+
}
977+
}
978+
979+
fn format_err_simple(&self, fmt: &mut Formatter) -> source_span::fmt::Formatted {
980+
let input = self.internal_span().input();
981+
let chars = input.chars().map(Result::<_, String>::Ok);
982+
983+
let metrics = source_span::DEFAULT_METRICS;
984+
let buffer = source_span::SourceBuffer::new(chars, Position::default(), metrics);
985+
986+
for c in buffer.iter() {
987+
let _ = c.unwrap(); // report eventual errors.
988+
}
989+
990+
let (start_pos, end_pos) = self.span();
991+
let lookup = LineColLookup::new(input);
992+
let (start_line, start_col) = lookup.get(start_pos);
993+
let (end_line, end_col) = lookup.get(if end_pos == 0 { 0 } else { end_pos - 1 });
994+
995+
let err_start = Position::new(start_line - 1, start_col - 1);
996+
let err_end = Position::new(end_line - 1, end_col - 1);
997+
let err_span = source_span::Span::new(err_start, err_end, err_end.next_column());
998+
fmt.add(
999+
err_span,
1000+
Some(self.to_friendly_error_string()),
1001+
Style::Error,
1002+
);
1003+
1004+
fmt.render(buffer.iter(), buffer.span(), &metrics).unwrap()
1005+
}
1006+
1007+
fn format_one_hint_one_err(
1008+
&self,
1009+
fmt: &mut Formatter,
1010+
hint_span: &Span<'sc>,
1011+
hint_message: String,
1012+
err_span: &Span<'sc>,
1013+
err_message: String,
1014+
) -> source_span::fmt::Formatted {
1015+
self.format_one(fmt, hint_span.clone(), Style::Note, hint_message);
1016+
self.format_one(fmt, err_span.clone(), Style::Error, err_message);
1017+
1018+
let span = crate::utils::join_spans(hint_span.clone(), err_span.clone());
1019+
let input = span.input();
1020+
let chars = input.chars().map(Result::<_, String>::Ok);
1021+
let metrics = source_span::DEFAULT_METRICS;
1022+
let buffer = source_span::SourceBuffer::new(chars, Position::default(), metrics);
1023+
for c in buffer.iter() {
1024+
let _ = c.unwrap(); // report eventual errors.
1025+
}
1026+
1027+
fmt.render(buffer.iter(), buffer.span(), &metrics).unwrap()
1028+
}
1029+
1030+
fn format_one(
1031+
&self,
1032+
fmt: &mut Formatter,
1033+
span: Span<'sc>,
1034+
style: source_span::fmt::Style,
1035+
friendly_string: String,
1036+
) {
1037+
let input = span.input();
1038+
let (start_pos, end_pos) = (span.start(), span.end());
1039+
let lookup = LineColLookup::new(input);
1040+
let (start_line, start_col) = lookup.get(start_pos);
1041+
let (end_line, end_col) = lookup.get(if end_pos == 0 { 0 } else { end_pos - 1 });
1042+
1043+
let err_start = Position::new(start_line - 1, start_col - 1);
1044+
let err_end = Position::new(end_line - 1, end_col - 1);
1045+
let err_span = source_span::Span::new(err_start, err_end, err_end.next_column());
1046+
fmt.add(err_span, Some(friendly_string.clone()), style);
1047+
}
8731048
}

core_lang/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ use pest::iterators::Pair;
2424
use pest::Parser;
2525
use semantic_analysis::{TreeType, TypedParseTree};
2626
pub mod types;
27-
pub(crate) mod utils;
27+
pub mod utils;
2828
pub use crate::parse_tree::{Declaration, Expression, UseStatement, WhileLoop};
2929
use std::collections::{HashMap, HashSet};
3030

core_lang/src/semantic_analysis/ast_node/declaration.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ impl<'sc> TypedDeclaration<'sc> {
5454
ErrorRecovery => "error",
5555
}
5656
}
57+
5758
pub(crate) fn return_type(&self) -> CompileResult<'sc, MaybeResolvedType<'sc>> {
5859
ok(
5960
match self {

core_lang/src/semantic_analysis/ast_node/expression/typed_expression.rs

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -324,7 +324,7 @@ impl<'sc> TypedExpression<'sc> {
324324
fn type_check_function_application(
325325
name: CallPath<'sc>,
326326
arguments: Vec<Expression<'sc>>,
327-
span: Span<'sc>,
327+
app_span: Span<'sc>,
328328
namespace: &mut Namespace<'sc>,
329329
self_type: &MaybeResolvedType<'sc>,
330330
build_config: &BuildConfig,
@@ -345,6 +345,7 @@ impl<'sc> TypedExpression<'sc> {
345345
parameters,
346346
return_type,
347347
body,
348+
span,
348349
..
349350
} = decl.clone();
350351
if arguments.len() > parameters.len() {
@@ -356,7 +357,8 @@ impl<'sc> TypedExpression<'sc> {
356357
|acc, arg| crate::utils::join_spans(acc, arg.span()),
357358
);
358359
errors.push(CompileError::TooManyArgumentsForFunction {
359-
span: arguments_span,
360+
decl_span: span.clone(),
361+
usage_span: arguments_span,
360362
method_name: name.suffix.primary_name,
361363
expected: parameters.len(),
362364
received: arguments.len(),
@@ -370,7 +372,8 @@ impl<'sc> TypedExpression<'sc> {
370372
|acc, arg| crate::utils::join_spans(acc, arg.span()),
371373
);
372374
errors.push(CompileError::TooFewArgumentsForFunction {
373-
span: arguments_span,
375+
decl_span: span.clone(),
376+
usage_span: arguments_span,
374377
method_name: name.suffix.primary_name,
375378
expected: parameters.len(),
376379
received: arguments.len(),
@@ -419,7 +422,7 @@ impl<'sc> TypedExpression<'sc> {
419422
function_body: body.clone(),
420423
selector: None, // regular functions cannot be in a contract call; only methods
421424
},
422-
span,
425+
span: app_span,
423426
}
424427
}
425428
a => {

core_lang/src/semantic_analysis/ast_node/expression/typed_expression/method_application.rs

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,8 @@ pub(crate) fn type_check_method_application<'sc>(
7070
MethodName::FromModule { method_name } => {
7171
if args_buf.len() > method.parameters.len() {
7272
errors.push(CompileError::TooManyArgumentsForFunction {
73-
span: span.clone(),
73+
decl_span: method.span.clone(),
74+
usage_span: span.clone(),
7475
method_name: method_name.primary_name,
7576
expected: method.parameters.len(),
7677
received: args_buf.len(),
@@ -79,7 +80,8 @@ pub(crate) fn type_check_method_application<'sc>(
7980

8081
if args_buf.len() < method.parameters.len() {
8182
errors.push(CompileError::TooFewArgumentsForFunction {
82-
span: span.clone(),
83+
decl_span: method.span.clone(),
84+
usage_span: span.clone(),
8385
method_name: method_name.primary_name,
8486
expected: method.parameters.len(),
8587
received: args_buf.len(),
@@ -133,7 +135,8 @@ pub(crate) fn type_check_method_application<'sc>(
133135
MethodName::FromType { ref call_path, .. } => {
134136
if args_buf.len() > method.parameters.len() {
135137
errors.push(CompileError::TooManyArgumentsForFunction {
136-
span: span.clone(),
138+
decl_span: method.span.clone(),
139+
usage_span: span.clone(),
137140
method_name: method_name.easy_name(),
138141
expected: method.parameters.len(),
139142
received: args_buf.len(),
@@ -142,7 +145,8 @@ pub(crate) fn type_check_method_application<'sc>(
142145

143146
if args_buf.len() < method.parameters.len() {
144147
errors.push(CompileError::TooFewArgumentsForFunction {
145-
span: span.clone(),
148+
decl_span: method.span.clone(),
149+
usage_span: span.clone(),
146150
method_name: method_name.easy_name(),
147151
expected: method.parameters.len(),
148152
received: args_buf.len(),

0 commit comments

Comments
 (0)