Changeset - 94751a00b9b7
[Not reviewed]
0 8 0
mh - 4 years ago 2021-05-31 10:43:41
contact@maxhenger.nl
Better error spans for expressions
8 files changed with 184 insertions and 127 deletions:
0 comments (0 inline, 0 general)
src/protocol/ast.rs
Show inline comments
 
@@ -1224,22 +1224,44 @@ impl Expression {
 
        }
 
    }
 

	
 
    pub fn span(&self) -> InputSpan {
 
    /// Returns operator span, function name, a binding's "let" span, etc. An
 
    /// indicator for the kind of expression that is being applied.
 
    pub fn operation_span(&self) -> InputSpan {
 
        match self {
 
            Expression::Assignment(expr) => expr.operator_span,
 
            Expression::Binding(expr) => expr.operator_span,
 
            Expression::Conditional(expr) => expr.operator_span,
 
            Expression::Binary(expr) => expr.operator_span,
 
            Expression::Unary(expr) => expr.operator_span,
 
            Expression::Indexing(expr) => expr.operator_span,
 
            Expression::Slicing(expr) => expr.slicing_span,
 
            Expression::Select(expr) => expr.operator_span,
 
            Expression::Literal(expr) => expr.span,
 
            Expression::Cast(expr) => expr.cast_span,
 
            Expression::Call(expr) => expr.func_span,
 
            Expression::Variable(expr) => expr.identifier.span,
 
        }
 
    }
 

	
 
    /// Returns the span covering the entire expression (i.e. including the
 
    /// spans of the arguments as well).
 
    pub fn full_span(&self) -> InputSpan {
 
        match self {
 
            Expression::Assignment(expr) => expr.span,
 
            Expression::Binding(expr) => expr.span,
 
            Expression::Conditional(expr) => expr.span,
 
            Expression::Binary(expr) => expr.span,
 
            Expression::Unary(expr) => expr.span,
 
            Expression::Indexing(expr) => expr.span,
 
            Expression::Slicing(expr) => expr.span,
 
            Expression::Select(expr) => expr.span,
 
            Expression::Assignment(expr) => expr.full_span,
 
            Expression::Binding(expr) => expr.full_span,
 
            Expression::Conditional(expr) => expr.full_span,
 
            Expression::Binary(expr) => expr.full_span,
 
            Expression::Unary(expr) => expr.full_span,
 
            Expression::Indexing(expr) => expr.full_span,
 
            Expression::Slicing(expr) => expr.full_span,
 
            Expression::Select(expr) => expr.full_span,
 
            Expression::Literal(expr) => expr.span,
 
            Expression::Cast(expr) => expr.span,
 
            Expression::Call(expr) => expr.span,
 
            Expression::Cast(expr) => expr.full_span,
 
            Expression::Call(expr) => expr.full_span,
 
            Expression::Variable(expr) => expr.identifier.span,
 
        }
 
    }
 

	
 
    // TODO: @cleanup
 
    pub fn parent(&self) -> &ExpressionParent {
 
        match self {
 
@@ -1304,7 +1326,8 @@ pub enum AssignmentOperator {
 
pub struct AssignmentExpression {
 
    pub this: AssignmentExpressionId,
 
    // Parsing
 
    pub span: InputSpan, // of the operator
 
    pub operator_span: InputSpan,
 
    pub full_span: InputSpan,
 
    pub left: ExpressionId,
 
    pub operation: AssignmentOperator,
 
    pub right: ExpressionId,
 
@@ -1317,7 +1340,8 @@ pub struct AssignmentExpression {
 
pub struct BindingExpression {
 
    pub this: BindingExpressionId,
 
    // Parsing
 
    pub span: InputSpan, // of the binding keyword
 
    pub operator_span: InputSpan,
 
    pub full_span: InputSpan,
 
    pub bound_to: ExpressionId,
 
    pub bound_from: ExpressionId,
 
    // Validator/Linker
 
@@ -1329,7 +1353,8 @@ pub struct BindingExpression {
 
pub struct ConditionalExpression {
 
    pub this: ConditionalExpressionId,
 
    // Parsing
 
    pub span: InputSpan, // of question mark operator
 
    pub operator_span: InputSpan,
 
    pub full_span: InputSpan,
 
    pub test: ExpressionId,
 
    pub true_expression: ExpressionId,
 
    pub false_expression: ExpressionId,
 
@@ -1365,7 +1390,8 @@ pub enum BinaryOperator {
 
pub struct BinaryExpression {
 
    pub this: BinaryExpressionId,
 
    // Parsing
 
    pub span: InputSpan, // of the operator
 
    pub operator_span: InputSpan,
 
    pub full_span: InputSpan,
 
    pub left: ExpressionId,
 
    pub operation: BinaryOperator,
 
    pub right: ExpressionId,
 
@@ -1386,7 +1412,8 @@ pub enum UnaryOperator {
 
pub struct UnaryExpression {
 
    pub this: UnaryExpressionId,
 
    // Parsing
 
    pub span: InputSpan, // of the operator
 
    pub operator_span: InputSpan,
 
    pub full_span: InputSpan,
 
    pub operation: UnaryOperator,
 
    pub expression: ExpressionId,
 
    // Validator/Linker
 
@@ -1398,7 +1425,8 @@ pub struct UnaryExpression {
 
pub struct IndexingExpression {
 
    pub this: IndexingExpressionId,
 
    // Parsing
 
    pub span: InputSpan,
 
    pub operator_span: InputSpan,
 
    pub full_span: InputSpan,
 
    pub subject: ExpressionId,
 
    pub index: ExpressionId,
 
    // Validator/Linker
 
@@ -1410,7 +1438,8 @@ pub struct IndexingExpression {
 
pub struct SlicingExpression {
 
    pub this: SlicingExpressionId,
 
    // Parsing
 
    pub span: InputSpan, // from '[' to ']';
 
    pub slicing_span: InputSpan, // from '[' to ']'
 
    pub full_span: InputSpan, // includes subject
 
    pub subject: ExpressionId,
 
    pub from_index: ExpressionId,
 
    pub to_index: ExpressionId,
 
@@ -1423,7 +1452,8 @@ pub struct SlicingExpression {
 
pub struct SelectExpression {
 
    pub this: SelectExpressionId,
 
    // Parsing
 
    pub span: InputSpan, // of the '.'
 
    pub operator_span: InputSpan, // of the '.'
 
    pub full_span: InputSpan, // includes subject and field
 
    pub subject: ExpressionId,
 
    pub field_name: Identifier,
 
    // Validator/Linker
 
@@ -1435,7 +1465,8 @@ pub struct SelectExpression {
 
pub struct CastExpression {
 
    pub this: CastExpressionId,
 
    // Parsing
 
    pub span: InputSpan, // of the "cast" keyword,
 
    pub cast_span: InputSpan, // of the "cast" keyword,
 
    pub full_span: InputSpan, // includes the cast subject
 
    pub to_type: ParserType,
 
    pub subject: ExpressionId,
 
    // Validator/linker
 
@@ -1447,7 +1478,8 @@ pub struct CastExpression {
 
pub struct CallExpression {
 
    pub this: CallExpressionId,
 
    // Parsing
 
    pub span: InputSpan,
 
    pub func_span: InputSpan, // of the function name
 
    pub full_span: InputSpan, // includes the arguments and parentheses
 
    pub parser_type: ParserType, // of the function call, not the return type
 
    pub method: Method,
 
    pub arguments: Vec<ExpressionId>,
src/protocol/eval/error.rs
Show inline comments
 
@@ -81,7 +81,7 @@ impl EvalError {
 

	
 
        let expr = &heap[expr_id];
 
        let statements = vec![
 
            ErrorStatement::from_source_at_span(StatementKind::Error, last_module_source, expr.span(), msg)
 
            ErrorStatement::from_source_at_span(StatementKind::Error, last_module_source, expr.full_span(), msg)
 
        ];
 

	
 
        EvalError{ statements, frames }
src/protocol/eval/executor.rs
Show inline comments
 
@@ -587,6 +587,16 @@ impl Prompt {
 
                                    let msg_value = cur_frame.expr_values.pop_front().unwrap();
 
                                    let deref_msg_value = self.store.maybe_read_ref(&msg_value).clone();
 

	
 
                                    match deref_msg_value {
 
                                        Value::Message(_) => {},
 
                                        _ => {
 
                                            return Err(EvalError::new_error_at_expr(
 
                                                self, modules, heap, expr_id,
 
                                                String::from("Calls to `put` are currently restricted to only send instances of `msg` types. This will change in the future")
 
                                            ));
 
                                        }
 
                                    }
 

	
 
                                    if ctx.did_put(deref_port_value.clone()) {
 
                                        // We're fine, deallocate in case the expression value stack
 
                                        // held an owned value
src/protocol/parser/pass_definitions.rs
Show inline comments
 
@@ -861,7 +861,8 @@ impl PassDefinitions {
 
                });
 
                let assignment_expr_id = ctx.heap.alloc_assignment_expression(|this| AssignmentExpression{
 
                    this,
 
                    span: assign_span,
 
                    operator_span: assign_span,
 
                    full_span: InputSpan::from_positions(memory_span.begin, initial_expr_end_pos),
 
                    left: variable_expr_id.upcast(),
 
                    operation: AssignmentOperator::Set,
 
                    right: initial_expr_id,
 
@@ -944,14 +945,19 @@ impl PassDefinitions {
 

	
 
        let expr = self.consume_conditional_expression(module, iter, ctx)?;
 
        if let Some(operation) = parse_assignment_operator(iter.next()) {
 
            let span = iter.next_span();
 
            let operator_span = iter.next_span();
 
            iter.consume();
 

	
 
            let left = expr;
 
            let right = self.consume_expression(module, iter, ctx)?;
 

	
 
            let full_span = InputSpan::from_positions(
 
                ctx.heap[left].full_span().begin,
 
                ctx.heap[right].full_span().end,
 
            );
 

	
 
            Ok(ctx.heap.alloc_assignment_expression(|this| AssignmentExpression{
 
                this, span, left, operation, right,
 
                this, operator_span, full_span, left, operation, right,
 
                parent: ExpressionParent::None,
 
                unique_id_in_definition: -1,
 
            }).upcast())
 
@@ -965,15 +971,21 @@ impl PassDefinitions {
 
    ) -> Result<ExpressionId, ParseError> {
 
        let result = self.consume_concat_expression(module, iter, ctx)?;
 
        if let Some(TokenKind::Question) = iter.next() {
 
            let span = iter.next_span();
 
            let operator_span = iter.next_span();
 
            iter.consume();
 

	
 
            let test = result;
 
            let true_expression = self.consume_expression(module, iter, ctx)?;
 
            consume_token(&module.source, iter, TokenKind::Colon)?;
 
            let false_expression = self.consume_expression(module, iter, ctx)?;
 

	
 
            let full_span = InputSpan::from_positions(
 
                ctx.heap[test].full_span().begin,
 
                ctx.heap[false_expression].full_span().end,
 
            );
 

	
 
            Ok(ctx.heap.alloc_conditional_expression(|this| ConditionalExpression{
 
                this, span, test, true_expression, false_expression,
 
                this, operator_span, full_span, test, true_expression, false_expression,
 
                parent: ExpressionParent::None,
 
                unique_id_in_definition: -1,
 
            }).upcast())
 
@@ -1150,15 +1162,15 @@ impl PassDefinitions {
 

	
 
        let next = iter.next();
 
        if let Some(operation) = parse_prefix_token(next) {
 
            let span = iter.next_span();
 
            let operator_span = iter.next_span();
 
            iter.consume();
 

	
 
            let expression = self.consume_prefix_expression(module, iter, ctx)?;
 
            let full_span = InputSpan::from_positions(
 
                operator_span.begin, ctx.heap[expression].full_span().end,
 
            );
 
            Ok(ctx.heap.alloc_unary_expression(|this| UnaryExpression {
 
                this,
 
                span,
 
                operation,
 
                expression,
 
                this, operator_span, full_span, operation, expression,
 
                parent: ExpressionParent::None,
 
                unique_id_in_definition: -1,
 
            }).upcast())
 
@@ -1192,16 +1204,16 @@ impl PassDefinitions {
 
        let mut next = iter.next();
 
        while has_matching_postfix_token(next) {
 
            let token = next.unwrap();
 
            let mut span = iter.next_span();
 
            let mut operator_span = iter.next_span();
 
            iter.consume();
 

	
 
            if token == TokenKind::PlusPlus {
 
                return Err(ParseError::new_error_str_at_span(
 
                    &module.source, span, "postfix increment is not supported in this language"
 
                    &module.source, operator_span, "postfix increment is not supported in this language"
 
                ));
 
            } else if token == TokenKind::MinusMinus {
 
                return Err(ParseError::new_error_str_at_span(
 
                    &module.source, span, "prefix increment is not supported in this language"
 
                    &module.source, operator_span, "prefix increment is not supported in this language"
 
                ));
 
            } else if token == TokenKind::OpenSquare {
 
                let subject = result;
 
@@ -1214,19 +1226,28 @@ impl PassDefinitions {
 

	
 
                    let to_index = self.consume_expression(module, iter, ctx)?;
 
                    let end_span = consume_token(&module.source, iter, TokenKind::CloseSquare)?;
 
                    span.end = end_span.end;
 
                    operator_span.end = end_span.end;
 
                    let full_span = InputSpan::from_positions(
 
                        ctx.heap[subject].full_span().begin, operator_span.end
 
                    );
 

	
 
                    result = ctx.heap.alloc_slicing_expression(|this| SlicingExpression{
 
                        this, span, subject, from_index, to_index,
 
                        this,
 
                        slicing_span: operator_span,
 
                        full_span, subject, from_index, to_index,
 
                        parent: ExpressionParent::None,
 
                        unique_id_in_definition: -1,
 
                    }).upcast();
 
                } else if Some(TokenKind::CloseSquare) == next {
 
                    let end_span = consume_token(&module.source, iter, TokenKind::CloseSquare)?;
 
                    span.end = end_span.end;
 
                    operator_span.end = end_span.end;
 

	
 
                    let full_span = InputSpan::from_positions(
 
                        ctx.heap[subject].full_span().begin, operator_span.end
 
                    );
 

	
 
                    result = ctx.heap.alloc_indexing_expression(|this| IndexingExpression{
 
                        this, span, subject,
 
                        this, operator_span, full_span, subject,
 
                        index: from_index,
 
                        parent: ExpressionParent::None,
 
                        unique_id_in_definition: -1,
 
@@ -1241,8 +1262,11 @@ impl PassDefinitions {
 
                let subject = result;
 
                let field_name = consume_ident_interned(&module.source, iter, ctx)?;
 

	
 
                let full_span = InputSpan::from_positions(
 
                    ctx.heap[subject].full_span().begin, field_name.span.end
 
                );
 
                result = ctx.heap.alloc_select_expression(|this| SelectExpression{
 
                    this, span, subject, field_name,
 
                    this, operator_span, full_span, subject, field_name,
 
                    parent: ExpressionParent::None,
 
                    unique_id_in_definition: -1,
 
                }).upcast();
 
@@ -1408,11 +1432,14 @@ impl PassDefinitions {
 
                            },
 
                            Definition::Component(_) => {
 
                                // Component instantiation
 
                                let arguments = self.consume_expression_list(module, iter, ctx, None)?;
 
                                let func_span = parser_type.full_span;
 
                                let mut full_span = func_span;
 
                                let arguments = self.consume_expression_list(
 
                                    module, iter, ctx, Some(&mut full_span.end)
 
                                )?;
 

	
 
                                ctx.heap.alloc_call_expression(|this| CallExpression{
 
                                    this,
 
                                    span: parser_type.full_span,
 
                                    this, func_span, full_span,
 
                                    parser_type,
 
                                    method: Method::UserComponent,
 
                                    arguments,
 
@@ -1438,14 +1465,14 @@ impl PassDefinitions {
 
                                };
 

	
 
                                // Function call: consume the arguments
 
                                let arguments = self.consume_expression_list(module, iter, ctx, None)?;
 
                                let func_span = parser_type.full_span;
 
                                let mut full_span = func_span;
 
                                let arguments = self.consume_expression_list(
 
                                    module, iter, ctx, Some(&mut full_span.end)
 
                                )?;
 

	
 
                                ctx.heap.alloc_call_expression(|this| CallExpression{
 
                                    this,
 
                                    span: parser_type.full_span,
 
                                    parser_type,
 
                                    method,
 
                                    arguments,
 
                                    this, func_span, full_span, parser_type, method, arguments,
 
                                    definition: target_definition_id,
 
                                    parent: ExpressionParent::None,
 
                                    unique_id_in_definition: -1,
 
@@ -1481,18 +1508,19 @@ impl PassDefinitions {
 
                    }).upcast()
 
                } else if ident_text == KW_LET {
 
                    // Binding expression
 
                    let keyword_span = iter.next_span();
 
                    let operator_span = iter.next_span();
 
                    iter.consume();
 

	
 
                    let bound_to = self.consume_prefix_expression(module, iter, ctx)?;
 
                    consume_token(&module.source, iter, TokenKind::Equal)?;
 
                    let bound_from = self.consume_prefix_expression(module, iter, ctx)?;
 

	
 
                    let full_span = InputSpan::from_positions(
 
                        operator_span.begin, ctx.heap[bound_from].full_span().end,
 
                    );
 

	
 
                    ctx.heap.alloc_binding_expression(|this| BindingExpression{
 
                        this,
 
                        span: keyword_span,
 
                        bound_to,
 
                        bound_from,
 
                        this, operator_span, full_span, bound_to, bound_from,
 
                        parent: ExpressionParent::None,
 
                        unique_id_in_definition: -1,
 
                    }).upcast()
 
@@ -1521,13 +1549,14 @@ impl PassDefinitions {
 

	
 
                    consume_token(&module.source, iter, TokenKind::OpenParen)?;
 
                    let subject = self.consume_expression(module, iter, ctx)?;
 
                    let mut full_span = iter.next_span();
 
                    full_span.begin = to_type.full_span.begin;
 
                    consume_token(&module.source, iter, TokenKind::CloseParen)?;
 

	
 
                    ctx.heap.alloc_cast_expression(|this| CastExpression{
 
                        this,
 
                        span: ident_span,
 
                        to_type,
 
                        subject,
 
                        cast_span: to_type.full_span,
 
                        full_span, to_type, subject,
 
                        parent: ExpressionParent::None,
 
                        unique_id_in_definition: -1,
 
                    }).upcast()
 
@@ -1590,14 +1619,19 @@ impl PassDefinitions {
 
    ) -> Result<ExpressionId, ParseError> {
 
        let mut result = higher_precedence_fn(self, module, iter, ctx)?;
 
        while let Some(operation) = match_fn(iter.next()) {
 
            let span = iter.next_span();
 
            let operator_span = iter.next_span();
 
            iter.consume();
 

	
 
            let left = result;
 
            let right = higher_precedence_fn(self, module, iter, ctx)?;
 

	
 
            let full_span = InputSpan::from_positions(
 
                ctx.heap[left].full_span().begin,
 
                ctx.heap[right].full_span().end,
 
            );
 

	
 
            result = ctx.heap.alloc_binary_expression(|this| BinaryExpression{
 
                this, span, left, operation, right,
 
                this, operator_span, full_span, left, operation, right,
 
                parent: ExpressionParent::None,
 
                unique_id_in_definition: -1,
 
            }).upcast();
src/protocol/parser/pass_typing.rs
Show inline comments
 
@@ -1429,7 +1429,7 @@ impl PassTyping {
 
                    };
 
                    let poly_vars = ctx.heap[definition].poly_vars();
 
                    return Err(ParseError::new_error_at_span(
 
                        &ctx.module.source, expr.span(), format!(
 
                        &ctx.module.source, expr.operation_span(), format!(
 
                            "could not fully infer the type of polymorphic variable '{}' of this expression (got '{}')",
 
                            poly_vars[poly_idx].value.as_str(), poly_type.display_name(&ctx.heap)
 
                        )
 
@@ -1456,7 +1456,7 @@ impl PassTyping {
 
                } else {
 
                    let expr = &ctx.heap[infer_expr.expr_id];
 
                    return Err(ParseError::new_error_at_span(
 
                        &ctx.module.source, expr.span(), format!(
 
                        &ctx.module.source, expr.full_span(), format!(
 
                            "could not fully infer the type of this expression (got '{}')",
 
                            expr_type.display_name(&ctx.heap)
 
                        )
 
@@ -2439,10 +2439,10 @@ impl PassTyping {
 
            let cast_expr = &ctx.heap[id];
 
            let subject_expr = &ctx.heap[cast_expr.subject];
 
            return Err(ParseError::new_error_str_at_span(
 
                &ctx.module.source, cast_expr.span, "invalid casting operation"
 
                &ctx.module.source, cast_expr.full_span, "invalid casting operation"
 
            ).with_info_at_span(
 
                &ctx.module.source, subject_expr.span(), format!(
 
                    "cannot cast this type '{}' to the cast type '{}'",
 
                &ctx.module.source, subject_expr.full_span(), format!(
 
                    "cannot cast the argument type '{}' to the cast type '{}'",
 
                    subject_type.display_name(&ctx.heap),
 
                    expr_type.display_name(&ctx.heap)
 
                )
 
@@ -2781,9 +2781,9 @@ impl PassTyping {
 

	
 
        if infer_res == DualInferenceResult::Incompatible {
 
            // TODO: Check if I still need to use this
 
            let outer_span = ctx.heap[outer_expr_id].span();
 
            let outer_span = ctx.heap[outer_expr_id].full_span();
 
            let (span_name, span) = match expr_id {
 
                Some(expr_id) => ("argument's", ctx.heap[expr_id].span()),
 
                Some(expr_id) => ("argument's", ctx.heap[expr_id].full_span()),
 
                None => ("type's", outer_span)
 
            };
 
            let (signature_display_type, expression_display_type) = unsafe { (
 
@@ -3468,12 +3468,12 @@ impl PassTyping {
 
        let arg_type = &self.expr_types[arg_expr_idx as usize].expr_type;
 

	
 
        return ParseError::new_error_at_span(
 
            &ctx.module.source, expr.span(), format!(
 
            &ctx.module.source, expr.operation_span(), format!(
 
                "incompatible types: this expression expected a '{}'",
 
                expr_type.display_name(&ctx.heap)
 
            )
 
        ).with_info_at_span(
 
            &ctx.module.source, arg_expr.span(), format!(
 
            &ctx.module.source, arg_expr.full_span(), format!(
 
                "but this expression yields a '{}'",
 
                arg_type.display_name(&ctx.heap)
 
            )
 
@@ -3494,15 +3494,15 @@ impl PassTyping {
 
        let arg2_type = &self.expr_types[arg2_idx as usize].expr_type;
 

	
 
        return ParseError::new_error_str_at_span(
 
            &ctx.module.source, expr.span(),
 
            &ctx.module.source, expr.operation_span(),
 
            "incompatible types: cannot apply this expression"
 
        ).with_info_at_span(
 
            &ctx.module.source, arg1.span(), format!(
 
            &ctx.module.source, arg1.full_span(), format!(
 
                "Because this expression has type '{}'",
 
                arg1_type.display_name(&ctx.heap)
 
            )
 
        ).with_info_at_span(
 
            &ctx.module.source, arg2.span(), format!(
 
            &ctx.module.source, arg2.full_span(), format!(
 
                "But this expression has type '{}'",
 
                arg2_type.display_name(&ctx.heap)
 
            )
 
@@ -3517,7 +3517,7 @@ impl PassTyping {
 
        let expr_type = &self.expr_types[expr_idx as usize].expr_type;
 

	
 
        return ParseError::new_error_at_span(
 
            &ctx.module.source, expr.span(), format!(
 
            &ctx.module.source, expr.full_span(), format!(
 
                "incompatible types: got a '{}' but expected a '{}'",
 
                expr_type.display_name(&ctx.heap), 
 
                InferenceType::partial_display_name(&ctx.heap, template)
 
@@ -3593,7 +3593,7 @@ impl PassTyping {
 
                Expression::Call(expr) => {
 
                    let (poly_var, func_name) = get_poly_var_and_definition_name(ctx, poly_var_idx, poly_data.definition_id);
 
                    return ParseError::new_error_at_span(
 
                        &ctx.module.source, expr.span, format!(
 
                        &ctx.module.source, expr.func_span, format!(
 
                            "Conflicting type for polymorphic variable '{}' of '{}'",
 
                            poly_var, func_name
 
                        )
 
@@ -3611,7 +3611,7 @@ impl PassTyping {
 
                Expression::Select(expr) => {
 
                    let (poly_var, struct_name) = get_poly_var_and_definition_name(ctx, poly_var_idx, poly_data.definition_id);
 
                    return ParseError::new_error_at_span(
 
                        &ctx.module.source, expr.span, format!(
 
                        &ctx.module.source, expr.full_span, format!(
 
                            "Conflicting type for polymorphic variable '{}' while accessing field '{}' of '{}'",
 
                            poly_var, expr.field_name.value.as_str(), struct_name
 
                        )
 
@@ -3657,7 +3657,7 @@ impl PassTyping {
 
        ) {
 
            return construct_main_error(ctx, poly_data, poly_idx, expr)
 
                .with_info_at_span(
 
                    &ctx.module.source, expr.span(), format!(
 
                    &ctx.module.source, expr.full_span(), format!(
 
                        "The {} inferred the conflicting types '{}' and '{}'",
 
                        expr_return_name,
 
                        InferenceType::partial_display_name(&ctx.heap, section_a),
 
@@ -3679,7 +3679,7 @@ impl PassTyping {
 
                        // Same argument
 
                        let arg = &ctx.heap[expr_args[arg_a_idx]];
 
                        return error.with_info_at_span(
 
                            &ctx.module.source, arg.span(), format!(
 
                            &ctx.module.source, arg.full_span(), format!(
 
                                "This argument inferred the conflicting types '{}' and '{}'",
 
                                InferenceType::partial_display_name(&ctx.heap, section_a),
 
                                InferenceType::partial_display_name(&ctx.heap, section_b)
 
@@ -3689,12 +3689,12 @@ impl PassTyping {
 
                        let arg_a = &ctx.heap[expr_args[arg_a_idx]];
 
                        let arg_b = &ctx.heap[expr_args[arg_b_idx]];
 
                        return error.with_info_at_span(
 
                            &ctx.module.source, arg_a.span(), format!(
 
                            &ctx.module.source, arg_a.full_span(), format!(
 
                                "This argument inferred it to '{}'",
 
                                InferenceType::partial_display_name(&ctx.heap, section_a)
 
                            )
 
                        ).with_info_at_span(
 
                            &ctx.module.source, arg_b.span(), format!(
 
                            &ctx.module.source, arg_b.full_span(), format!(
 
                                "While this argument inferred it to '{}'",
 
                                InferenceType::partial_display_name(&ctx.heap, section_b)
 
                            )
 
@@ -3708,13 +3708,13 @@ impl PassTyping {
 
                let arg = &ctx.heap[expr_args[arg_a_idx]];
 
                return construct_main_error(ctx, poly_data, poly_idx, expr)
 
                    .with_info_at_span(
 
                        &ctx.module.source, arg.span(), format!(
 
                        &ctx.module.source, arg.full_span(), format!(
 
                            "This argument inferred it to '{}'",
 
                            InferenceType::partial_display_name(&ctx.heap, section_arg)
 
                        )
 
                    )
 
                    .with_info_at_span(
 
                        &ctx.module.source, expr.span(), format!(
 
                        &ctx.module.source, expr.full_span(), format!(
 
                            "While the {} inferred it to '{}'",
 
                            expr_return_name,
 
                            InferenceType::partial_display_name(&ctx.heap, section_ret)
 
@@ -3730,7 +3730,7 @@ impl PassTyping {
 
                let arg = &ctx.heap[expr_args[arg_idx]];
 
                return construct_main_error(ctx, poly_data, poly_idx, expr)
 
                    .with_info_at_span(
 
                        &ctx.module.source, arg.span(), format!(
 
                        &ctx.module.source, arg.full_span(), format!(
 
                            "The polymorphic variable has type '{}' (which might have been partially inferred) while the argument inferred it to '{}'",
 
                            InferenceType::partial_display_name(&ctx.heap, poly_section),
 
                            InferenceType::partial_display_name(&ctx.heap, arg_section)
 
@@ -3742,7 +3742,7 @@ impl PassTyping {
 
        if let Some((poly_idx, poly_section, ret_section)) = has_explicit_poly_mismatch(&poly_data.poly_vars, &poly_data.returned) {
 
            return construct_main_error(ctx, poly_data, poly_idx, expr)
 
                .with_info_at_span(
 
                    &ctx.module.source, expr.span(), format!(
 
                    &ctx.module.source, expr.full_span(), format!(
 
                        "The polymorphic variable has type '{}' (which might have been partially inferred) while the {} inferred it to '{}'",
 
                        InferenceType::partial_display_name(&ctx.heap, poly_section),
 
                        expr_return_name,
src/protocol/parser/pass_validation_linking.rs
Show inline comments
 
@@ -463,10 +463,11 @@ impl Visitor for PassValidationLinking {
 
        // Although we call assignment an expression to simplify the compiler's
 
        // code (mainly typechecking), we disallow nested use in expressions
 
        match self.expr_parent {
 
            // Look at us: lying through our teeth while providing error messages.
 
            ExpressionParent::ExpressionStmt(_) => {},
 
            _ => return Err(ParseError::new_error_str_at_span(
 
                &ctx.module.source, assignment_expr.span,
 
                "assignments may only appear at the statement level"
 
                &ctx.module.source, assignment_expr.full_span,
 
                "assignments are statements, and cannot be used in expressions"
 
            )),
 
        }
 

	
 
@@ -478,7 +479,7 @@ impl Visitor for PassValidationLinking {
 
        self.next_expr_index += 1;
 

	
 
        self.expr_parent = ExpressionParent::Expression(upcast_id, 0);
 
        self.must_be_assignable = Some(assignment_expr.span);
 
        self.must_be_assignable = Some(assignment_expr.operator_span);
 
        self.visit_expr(ctx, left_expr_id)?;
 
        self.expr_parent = ExpressionParent::Expression(upcast_id, 1);
 
        self.must_be_assignable = None;
 
@@ -500,7 +501,7 @@ impl Visitor for PassValidationLinking {
 
        if self.in_test_expr.is_invalid() {
 
            let binding_expr = &ctx.heap[id];
 
            return Err(ParseError::new_error_str_at_span(
 
                &ctx.module.source, binding_expr.span,
 
                &ctx.module.source, binding_expr.full_span,
 
                "binding expressions can only be used inside the testing expression of 'if' and 'while' statements"
 
            ));
 
        }
 
@@ -509,10 +510,10 @@ impl Visitor for PassValidationLinking {
 
            let binding_expr = &ctx.heap[id];
 
            let previous_expr = &ctx.heap[self.in_binding_expr];
 
            return Err(ParseError::new_error_str_at_span(
 
                &ctx.module.source, binding_expr.span,
 
                &ctx.module.source, binding_expr.full_span,
 
                "nested binding expressions are not allowed"
 
            ).with_info_str_at_span(
 
                &ctx.module.source, previous_expr.span,
 
                &ctx.module.source, previous_expr.operator_span,
 
                "the outer binding expression is found here"
 
            ));
 
        }
 
@@ -550,10 +551,10 @@ impl Visitor for PassValidationLinking {
 
                let binding_expr = &ctx.heap[id];
 
                let parent_expr = &ctx.heap[seeking_parent.as_expression()];
 
                return Err(ParseError::new_error_str_at_span(
 
                    &ctx.module.source, binding_expr.span,
 
                    &ctx.module.source, binding_expr.full_span,
 
                    "only the logical-and operator (&&) may be applied to binding expressions"
 
                ).with_info_str_at_span(
 
                    &ctx.module.source, parent_expr.span(),
 
                    &ctx.module.source, parent_expr.operation_span(),
 
                    "this was the disallowed operation applied to the result from a binding expression"
 
                ));
 
            }
 
@@ -583,7 +584,7 @@ impl Visitor for PassValidationLinking {
 
            _ => {
 
                let binding_expr = &ctx.heap[id];
 
                return Err(ParseError::new_error_str_at_span(
 
                    &ctx.module.source, binding_expr.span,
 
                    &ctx.module.source, binding_expr.operator_span,
 
                    "the left hand side of a binding expression may only be a variable or a literal expression"
 
                ));
 
            },
 
@@ -980,13 +981,13 @@ impl Visitor for PassValidationLinking {
 
            Method::Get => {
 
                if !self.def_type.is_primitive() {
 
                    return Err(ParseError::new_error_str_at_span(
 
                        &ctx.module.source, call_expr.span,
 
                        &ctx.module.source, call_expr.func_span,
 
                        "a call to 'get' may only occur in primitive component definitions"
 
                    ));
 
                }
 
                if self.in_sync.is_invalid() {
 
                    return Err(ParseError::new_error_str_at_span(
 
                        &ctx.module.source, call_expr.span,
 
                        &ctx.module.source, call_expr.func_span,
 
                        "a call to 'get' may only occur inside synchronous blocks"
 
                    ));
 
                }
 
@@ -994,13 +995,13 @@ impl Visitor for PassValidationLinking {
 
            Method::Put => {
 
                if !self.def_type.is_primitive() {
 
                    return Err(ParseError::new_error_str_at_span(
 
                        &ctx.module.source, call_expr.span,
 
                        &ctx.module.source, call_expr.func_span,
 
                        "a call to 'put' may only occur in primitive component definitions"
 
                    ));
 
                }
 
                if self.in_sync.is_invalid() {
 
                    return Err(ParseError::new_error_str_at_span(
 
                        &ctx.module.source, call_expr.span,
 
                        &ctx.module.source, call_expr.func_span,
 
                        "a call to 'put' may only occur inside synchronous blocks"
 
                    ));
 
                }
 
@@ -1008,13 +1009,13 @@ impl Visitor for PassValidationLinking {
 
            Method::Fires => {
 
                if !self.def_type.is_primitive() {
 
                    return Err(ParseError::new_error_str_at_span(
 
                        &ctx.module.source, call_expr.span,
 
                        &ctx.module.source, call_expr.func_span,
 
                        "a call to 'fires' may only occur in primitive component definitions"
 
                    ));
 
                }
 
                if self.in_sync.is_invalid() {
 
                    return Err(ParseError::new_error_str_at_span(
 
                        &ctx.module.source, call_expr.span,
 
                        &ctx.module.source, call_expr.func_span,
 
                        "a call to 'fires' may only occur inside synchronous blocks"
 
                    ));
 
                }
 
@@ -1024,13 +1025,13 @@ impl Visitor for PassValidationLinking {
 
            Method::Assert => {
 
                if self.def_type.is_function() {
 
                    return Err(ParseError::new_error_str_at_span(
 
                        &ctx.module.source, call_expr.span,
 
                        &ctx.module.source, call_expr.func_span,
 
                        "assert statement may only occur in components"
 
                    ));
 
                }
 
                if self.in_sync.is_invalid() {
 
                    return Err(ParseError::new_error_str_at_span(
 
                        &ctx.module.source, call_expr.span,
 
                        &ctx.module.source, call_expr.func_span,
 
                        "assert statements may only occur inside synchronous blocks"
 
                    ));
 
                }
 
@@ -1044,14 +1045,14 @@ impl Visitor for PassValidationLinking {
 
        if expected_wrapping_new_stmt {
 
            if !self.expr_parent.is_new() {
 
                return Err(ParseError::new_error_str_at_span(
 
                    &ctx.module.source, call_expr.span,
 
                    &ctx.module.source, call_expr.func_span,
 
                    "cannot call a component, it can only be instantiated by using 'new'"
 
                ));
 
            }
 
        } else {
 
            if self.expr_parent.is_new() {
 
                return Err(ParseError::new_error_str_at_span(
 
                    &ctx.module.source, call_expr.span,
 
                    &ctx.module.source, call_expr.func_span,
 
                    "only components can be instantiated, this is a function"
 
                ));
 
            }
 
@@ -1069,7 +1070,7 @@ impl Visitor for PassValidationLinking {
 
        if num_provided_args != num_expected_args {
 
            let argument_text = if num_expected_args == 1 { "argument" } else { "arguments" };
 
            return Err(ParseError::new_error_at_span(
 
                &ctx.module.source, call_expr.span, format!(
 
                &ctx.module.source, call_expr.full_span, format!(
 
                    "expected {} {}, but {} were provided",
 
                    num_expected_args, argument_text, num_provided_args
 
                )
 
@@ -1153,7 +1154,7 @@ impl Visitor for PassValidationLinking {
 
                        &ctx.module.source, var_expr.identifier.span,
 
                        "illegal location for binding variable: binding variables may only be nested under a binding expression, or a struct, union or array literal"
 
                    ).with_info_at_span(
 
                        &ctx.module.source, binding_expr.span, format!(
 
                        &ctx.module.source, binding_expr.operator_span, format!(
 
                            "'{}' was interpreted as a binding variable because the variable is not declared and it is nested under this binding expression",
 
                            var_expr.identifier.value.as_str()
 
                        )
src/protocol/tests/parser_inference.rs
Show inline comments
 
@@ -388,8 +388,8 @@ fn test_failed_polymorph_inference() {
 
        "
 
    ).error(|e| { e
 
        .assert_num(2)
 
        .assert_any_msg_has("type 'Uninteresting<s8>'")
 
        .assert_any_msg_has("type 'Uninteresting<s32>'");
 
        .assert_msg_has(0, "type 'Uninteresting<s8>'")
 
        .assert_msg_has(1, "type 'Uninteresting<s32>'");
 
    });
 

	
 
    Tester::new_single_source_expect_err(
 
@@ -405,7 +405,7 @@ fn test_failed_polymorph_inference() {
 
    ).error(|e| { e
 
        .assert_num(3)
 
        .assert_ctx_has(0, "holder.a")
 
        .assert_occurs_at(0, ".")
 
        .assert_occurs_at(0, "holder.a")
 
        .assert_msg_has(0, "Conflicting type for polymorphic variable 'Shazam'")
 
        .assert_msg_has(1, "inferred it to 's8'")
 
        .assert_msg_has(2, "inferred it to 's32'");
src/protocol/tests/utils.rs
Show inline comments
 
@@ -569,7 +569,7 @@ impl<'a> FunctionTester<'a> {
 
        // Use the inner match index to find the expression
 
        let expr_id = seek_expr_in_stmt(
 
            &self.ctx.heap, self.def.body.upcast(),
 
            &|expr| expr.span().begin.offset as usize == inner_match_idx
 
            &|expr| expr.operation_span().begin.offset as usize == inner_match_idx
 
        );
 
        assert!(
 
            expr_id.is_some(),
 
@@ -814,26 +814,6 @@ impl<'a> ErrorTester<'a> {
 
        self
 
    }
 

	
 
    // TODO: @tokenizer This should really be removed, as compilation should be
 
    //  deterministic, but we're currently using rather inefficient hashsets for
 
    //  the type inference, so remove once compiler architecture has changed.
 
    pub(crate) fn assert_any_msg_has(self, msg: &str) -> Self {
 
        let mut is_present = false;
 
        for statement in &self.error.statements {
 
            if statement.message.contains(msg) {
 
                is_present = true;
 
                break;
 
            }
 
        }
 

	
 
        assert!(
 
            is_present, "[{}] Expected an error statement to contain '{}' for {}",
 
            self.test_name, msg, self.assert_postfix()
 
        );
 
        
 
        self
 
    }
 

	
 
    /// Seeks the index of the pattern in the context message, then checks if
 
    /// the input position corresponds to that index.
 
    pub (crate) fn assert_occurs_at(self, idx: usize, pattern: &str) -> Self {
0 comments (0 inline, 0 general)