fix: error reporting sometimes used token position instead of code position
This commit is contained in:
parent
9225114658
commit
2a52615153
@ -4,7 +4,10 @@ use crate::{
|
||||
ErrorContainer, ErrorLabel,
|
||||
},
|
||||
lexic::token::{Token, TokenType},
|
||||
syntax::{utils::parse_token_type, ParsingError, ParsingResult},
|
||||
syntax::{
|
||||
utils::{parse_token_type, Tokenizer},
|
||||
ParsingError, ParsingResult,
|
||||
},
|
||||
};
|
||||
|
||||
use super::super::{
|
||||
@ -93,14 +96,15 @@ pub fn parse_params_list(tokens: &Vec<Token>, pos: usize) -> ParsingResult<Param
|
||||
start: opening_paren.position,
|
||||
end: opening_paren.get_end_position(),
|
||||
};
|
||||
let label_2_pos = tokens.code_position_from_idx(current_pos);
|
||||
let label_2 = ErrorLabel {
|
||||
message: String::from("The code ends here without closing the parameter list"),
|
||||
start: current_pos,
|
||||
end: current_pos + 1,
|
||||
start: label_2_pos,
|
||||
end: label_2_pos + 1,
|
||||
};
|
||||
let econtainer = ErrorContainer {
|
||||
error_code: SYNTAX_INCOMPLETE_PARAMETER_LIST,
|
||||
error_offset: current_pos,
|
||||
error_offset: tokens.code_position_from_idx(current_pos),
|
||||
labels: vec![label_1, label_2],
|
||||
note: None,
|
||||
help: None,
|
||||
@ -172,7 +176,7 @@ fn parse_param_definition(tokens: &Vec<Token>, pos: usize) -> ParsingResult<Para
|
||||
};
|
||||
let econtainer = ErrorContainer {
|
||||
error_code: SYNTAX_INVALID_PARAMETER_DECLARATION,
|
||||
error_offset: datatype_token.position,
|
||||
error_offset: datatype_token.get_end_position(),
|
||||
labels: vec![label],
|
||||
note: None,
|
||||
help: None,
|
||||
@ -290,4 +294,126 @@ mod tests {
|
||||
assert_eq!(second_param.datatype, "String");
|
||||
assert_eq!(second_param.identifier, "y");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_fail_on_incomplete_params_list() {
|
||||
let tokens = get_tokens(&String::from(" ( ")).unwrap();
|
||||
let result = parse_params_list(&tokens, 0);
|
||||
|
||||
match result {
|
||||
Err(ParsingError::Err(err)) => {
|
||||
assert_eq!(SYNTAX_INCOMPLETE_PARAMETER_LIST, err.error_code);
|
||||
assert_eq!(err.error_offset, 4);
|
||||
|
||||
let label = &err.labels[0];
|
||||
assert_eq!(label.message, "The parameter list starts here");
|
||||
assert_eq!(label.start, 3);
|
||||
assert_eq!(label.end, 4);
|
||||
|
||||
let label = &err.labels[1];
|
||||
assert_eq!(
|
||||
label.message,
|
||||
"The code ends here without closing the parameter list"
|
||||
);
|
||||
assert_eq!(label.start, 4);
|
||||
assert_eq!(label.end, 5);
|
||||
}
|
||||
_ => panic!("Expected a ParsingError::Err"),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_fail_on_invalid_params_closing() {
|
||||
let tokens = get_tokens(&String::from(" ( &")).unwrap();
|
||||
let result = parse_params_list(&tokens, 0);
|
||||
|
||||
match result {
|
||||
Err(ParsingError::Err(err)) => {
|
||||
assert_eq!(SYNTAX_INCOMPLETE_PARAMETER_LIST, err.error_code);
|
||||
assert_eq!(err.error_offset, 7);
|
||||
|
||||
let label = &err.labels[0];
|
||||
assert_eq!(label.message, "The parameter list starts here");
|
||||
assert_eq!(label.start, 3);
|
||||
assert_eq!(label.end, 4);
|
||||
|
||||
let label = &err.labels[1];
|
||||
assert_eq!(label.message, "Expected a closing paren `)` here");
|
||||
assert_eq!(label.start, 7);
|
||||
assert_eq!(label.end, 8);
|
||||
}
|
||||
_ => panic!("Expected a ParsingError::Err"),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_fail_on_invalid_params_closing_2() {
|
||||
let tokens = get_tokens(&String::from(" ( Int i &")).unwrap();
|
||||
let result = parse_params_list(&tokens, 0);
|
||||
|
||||
match result {
|
||||
Err(ParsingError::Err(err)) => {
|
||||
assert_eq!(SYNTAX_INCOMPLETE_PARAMETER_LIST, err.error_code);
|
||||
assert_eq!(err.error_offset, 13);
|
||||
|
||||
let label = &err.labels[0];
|
||||
assert_eq!(label.message, "The parameter list starts here");
|
||||
assert_eq!(label.start, 3);
|
||||
assert_eq!(label.end, 4);
|
||||
|
||||
let label = &err.labels[1];
|
||||
assert_eq!(label.message, "Expected a closing paren `)` here");
|
||||
assert_eq!(label.start, 13);
|
||||
assert_eq!(label.end, 14);
|
||||
}
|
||||
_ => panic!("Expected a ParsingError::Err"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod params_tests {
|
||||
use super::*;
|
||||
use crate::lexic::get_tokens;
|
||||
|
||||
#[test]
|
||||
fn should_fail_on_missing_identifier() {
|
||||
let tokens = get_tokens(&String::from(" Int ")).unwrap();
|
||||
let result = parse_param_definition(&tokens, 0);
|
||||
|
||||
match result {
|
||||
Err(ParsingError::Err(err)) => {
|
||||
assert_eq!(SYNTAX_INVALID_PARAMETER_DECLARATION, err.error_code);
|
||||
assert_eq!(err.error_offset, 5);
|
||||
|
||||
let label = &err.labels[0];
|
||||
assert_eq!(label.message, "Expected an identifier after this datatype");
|
||||
assert_eq!(label.start, 2);
|
||||
assert_eq!(label.end, 5);
|
||||
}
|
||||
_ => panic!("Expected a ParsingError::Err"),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_fail_on_wrong_identifier() {
|
||||
let tokens = get_tokens(&String::from(" Int 322")).unwrap();
|
||||
let result = parse_param_definition(&tokens, 0);
|
||||
|
||||
match result {
|
||||
Err(ParsingError::Err(err)) => {
|
||||
assert_eq!(SYNTAX_INVALID_PARAMETER_DECLARATION, err.error_code);
|
||||
assert_eq!(err.error_offset, 6);
|
||||
|
||||
let label = &err.labels[0];
|
||||
assert_eq!(
|
||||
label.message,
|
||||
"Expected an identifier here, found this instead"
|
||||
);
|
||||
assert_eq!(label.start, 6);
|
||||
assert_eq!(label.end, 9);
|
||||
}
|
||||
_ => panic!("Expected a ParsingError::Err"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -4,6 +4,7 @@ use super::{ParsingError, ParsingResult};
|
||||
|
||||
pub trait Tokenizer {
|
||||
fn get_significant<'a>(&'a self, index: usize) -> Option<(&'a Token, usize)>;
|
||||
fn code_position_from_idx(&self, idx: usize) -> usize;
|
||||
}
|
||||
|
||||
impl Tokenizer for Vec<Token> {
|
||||
@ -30,6 +31,30 @@ impl Tokenizer for Vec<Token> {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the position in the code from the token idx.
|
||||
///
|
||||
/// If the token at `idx` exists, returns `tokens[idx].position`.
|
||||
///
|
||||
/// Otherwise returns `tokens[idx - 1].get_end_position()`
|
||||
fn code_position_from_idx(&self, idx: usize) -> usize {
|
||||
// try to get the token at idx
|
||||
match self.get(idx) {
|
||||
Some(t) if t.token_type == TokenType::EOF => {
|
||||
// If idx points at EOF, return the end position of the previous token
|
||||
// This shouldnt fail
|
||||
self[idx - 1].get_end_position()
|
||||
}
|
||||
Some(t) => t.position,
|
||||
None => {
|
||||
// this should never happen.
|
||||
// the token stream always ends with an EOF token,
|
||||
// and the parser should never be able to go
|
||||
// to a position after that EOF token
|
||||
unreachable!("Compiler error: Tried to get an out of bound token. This means that somewhere a token beyond EOF was requested.")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Expects the token at `pos` to be an operator of value `operator`. Doesn't ignore whitespace or newlines
|
||||
|
Loading…
Reference in New Issue
Block a user