diff --git a/src/instruction/block.rs b/src/instruction/block.rs index a61d6668..e5ae4091 100644 --- a/src/instruction/block.rs +++ b/src/instruction/block.rs @@ -20,7 +20,7 @@ use crate::{ log, typechecker::{CheckedType, TypeCtx}, - Context, Generic, InstrKind, Instruction, ObjectInstance, TypeCheck, + Context, Generic, InstrKind, Instruction, ObjectInstance, SpanTuple, TypeCheck, }; #[derive(Clone, Default)] @@ -28,6 +28,7 @@ pub struct Block { instructions: Vec>, is_statement: bool, cached_type: Option, + location: Option, } impl Block { @@ -38,6 +39,7 @@ impl Block { instructions: Vec::new(), is_statement: true, cached_type: None, + location: None, } } @@ -75,6 +77,11 @@ impl Block { pub fn set_statement(&mut self, is_statement: bool) { self.is_statement = is_statement; } + + /// Set block's location + pub fn set_location(&mut self, location: SpanTuple) { + self.location = Some(location) + } } impl Instruction for Block { @@ -117,6 +124,10 @@ impl Instruction for Block { true => None, } } + + fn location(&self) -> Option<&SpanTuple> { + self.location.as_ref() + } } impl TypeCheck for Block { diff --git a/src/instruction/if_else.rs b/src/instruction/if_else.rs index 368191f9..1a6c8ced 100644 --- a/src/instruction/if_else.rs +++ b/src/instruction/if_else.rs @@ -93,10 +93,14 @@ impl TypeCheck for IfElse { } if cond_ty != bool_checkedtype { - ctx.error(Error::new(ErrKind::TypeChecker).with_msg(format!( - "if condition should be a boolean, not a `{}`", - cond_ty - ))); + ctx.error( + Error::new(ErrKind::TypeChecker) + .with_msg(format!( + "if condition should be a boolean, not a `{}`", + cond_ty + )) + .with_loc(self.condition.location().cloned()), + ); } let if_ty = self.if_body.type_of(ctx); @@ -109,20 +113,28 @@ impl TypeCheck for IfElse { (CheckedType::Void, None) => CheckedType::Void, (if_ty, Some(else_ty)) => { if if_ty != else_ty { - ctx.error(Error::new(ErrKind::TypeChecker).with_msg(format!( - "incompatible types for `if` and `else` block: {} and {}", - if_ty, else_ty, - ))); + ctx.error( + Error::new(ErrKind::TypeChecker) + .with_msg(format!( + "incompatible types for `if` and `else` block: {} and {}", + if_ty, else_ty, + )) + .with_loc(self.if_body.location().cloned()), + ); CheckedType::Error } else { if_ty } } (if_ty, None) => { - ctx.error(Error::new(ErrKind::TypeChecker).with_msg(format!( - "`if` block has a return type ({}) but no else block to match it", - if_ty - ))); + ctx.error( + Error::new(ErrKind::TypeChecker) + .with_msg(format!( + "`if` block has a return type ({}) but no else block to match it", + if_ty + )) + .with_loc(self.if_body.location().cloned()), + ); CheckedType::Error } } diff --git a/src/location.rs b/src/location.rs index 6d196b7a..78c21de2 100644 --- a/src/location.rs +++ b/src/location.rs @@ -177,7 +177,15 @@ impl SpanTuple { for (i, line) in input.lines().skip(self.start.line() - 1).enumerate() { let start_col = match self.start.column { Column::EndOfLine => 1, - Column::Precise(nz) => nz.get(), + Column::Precise(nz) => { + // If we're gonna be printing multiple lines, print the whole + // start line + if self.start.line() < self.end.line() { + 1 + } else { + nz.get() + } + } }; let end_col = match self.end.column { Column::EndOfLine => line.len(), diff --git a/src/parser/constructs.rs b/src/parser/constructs.rs index 6170fc7c..53b7f666 100644 --- a/src/parser/constructs.rs +++ b/src/parser/constructs.rs @@ -230,7 +230,7 @@ fn unit(input: ParseInput) -> ParseResult> { } else if let Ok((input, _)) = Token::return_tok(input) { unit_return(input) } else if let Ok((input, _)) = Token::left_curly_bracket(input) { - unit_block(input) + unit_block(input, start_loc.into()) } else if let Ok((input, _)) = Token::left_parenthesis(input) { terminated(expr, Token::right_parenthesis)(input) } else if let Ok(res) = constant(input) { @@ -364,8 +364,15 @@ fn unit_return(input: ParseInput) -> ParseResult ParseResult> { - let (input, block) = inner_block(next(input))?; +fn unit_block( + input: ParseInput, + start_loc: Location, +) -> ParseResult> { + let (input, mut block) = inner_block(next(input))?; + let (input, end_loc) = position(input)?; + + block.set_location(SpanTuple::new(input.extra, start_loc, end_loc.into())); + Ok((input, Box::new(block))) } @@ -427,9 +434,19 @@ fn return_type(input: ParseInput) -> ParseResult> { /// block = '{' next inner_block pub fn block(input: ParseInput) -> ParseResult { + let (input, start_loc) = position(input)?; let (input, _) = Token::left_curly_bracket(input)?; let input = next(input); - inner_block(input) + let (input, mut block) = inner_block(input)?; + let (input, end_loc) = position(input)?; + + block.set_location(SpanTuple::new( + input.extra, + start_loc.into(), + end_loc.into(), + )); + + Ok((input, block)) } /// inner_block = '}'