From 898ef7445753b297c09a98cbddf5f6795b480baf Mon Sep 17 00:00:00 2001 From: rvcas Date: Wed, 20 Mar 2024 17:26:30 -0400 Subject: [PATCH] fix: spans for backpassing args closes #882 Co-authored-by: Kasey White --- crates/aiken-lang/src/ast.rs | 23 ++++++- crates/aiken-lang/src/expr.rs | 4 +- crates/aiken-lang/src/format.rs | 2 + .../definition/snapshots/def_test_fail.snap | 1 + .../snapshots/function_assignment_only.snap | 1 + .../aiken-lang/src/parser/expr/assignment.rs | 60 +++++++------------ .../src/parser/expr/snapshots/block_let.snap | 2 + .../src/parser/expr/snapshots/expect.snap | 1 + .../expr/snapshots/expect_bool_sugar.snap | 1 + .../expr/snapshots/expect_expect_let.snap | 2 + .../expr/snapshots/expect_let_in_let.snap | 2 + .../snapshots/expect_let_in_let_parens.snap | 2 + .../snapshots/expect_let_in_let_return.snap | 2 + .../expr/snapshots/expect_trace_if_false.snap | 1 + .../expr/snapshots/function_invoke.snap | 2 + .../snapshots/int_numeric_underscore.snap | 3 + .../parser/expr/snapshots/let_bindings.snap | 1 + .../parser/expr/snapshots/parse_tuple.snap | 1 + .../parser/expr/snapshots/parse_tuple2.snap | 1 + .../expr/when/snapshots/when_basic.snap | 1 + .../function_ambiguous_sequence.snap | 4 ++ .../src/snapshots/parse_unicode_offset_1.snap | 1 + .../src/snapshots/parse_unicode_offset_2.snap | 1 + crates/aiken-lang/src/tests/check.rs | 24 ++++++++ crates/aiken-lang/src/tipo/expr.rs | 44 ++++++++++---- 25 files changed, 136 insertions(+), 51 deletions(-) diff --git a/crates/aiken-lang/src/ast.rs b/crates/aiken-lang/src/ast.rs index 750d7cc4..abade95d 100644 --- a/crates/aiken-lang/src/ast.rs +++ b/crates/aiken-lang/src/ast.rs @@ -1176,6 +1176,21 @@ impl Pattern { } } +impl UntypedPattern { + pub fn true_(location: Span) -> UntypedPattern { + UntypedPattern::Constructor { + location, + name: "True".to_string(), + arguments: vec![], + constructor: (), + with_spread: false, + tipo: (), + module: None, + is_record: false, + } + } +} + impl TypedPattern { pub fn find_node<'a>(&'a self, byte_index: usize, value: &Rc) -> Option> { if !self.location().contains(byte_index) { @@ -1440,13 +1455,19 @@ impl Default for Bls12_381Point { pub struct AssignmentPattern { pub pattern: UntypedPattern, pub annotation: Option, + pub location: Span, } impl AssignmentPattern { - pub fn new(pattern: UntypedPattern, annotation: Option) -> AssignmentPattern { + pub fn new( + pattern: UntypedPattern, + annotation: Option, + location: Span, + ) -> AssignmentPattern { Self { pattern, annotation, + location, } } } diff --git a/crates/aiken-lang/src/expr.rs b/crates/aiken-lang/src/expr.rs index b97d608f..0ae888d0 100644 --- a/crates/aiken-lang/src/expr.rs +++ b/crates/aiken-lang/src/expr.rs @@ -1300,7 +1300,7 @@ impl UntypedExpr { } pub fn lambda( - names: Vec<(String, Option)>, + names: Vec<(String, Span, Option)>, expressions: Vec, location: Span, ) -> Self { @@ -1309,7 +1309,7 @@ impl UntypedExpr { fn_style: FnStyle::Plain, arguments: names .into_iter() - .map(|(name, annotation)| Arg { + .map(|(name, location, annotation)| Arg { location, doc: None, annotation, diff --git a/crates/aiken-lang/src/format.rs b/crates/aiken-lang/src/format.rs index a65e55e9..3dfb5944 100644 --- a/crates/aiken-lang/src/format.rs +++ b/crates/aiken-lang/src/format.rs @@ -697,6 +697,7 @@ impl<'comments> Formatter<'comments> { name, module: None, .. }, annotation, + location: _, } if name == "True" && annotation.is_none() && kind.is_expect() @@ -709,6 +710,7 @@ impl<'comments> Formatter<'comments> { |AssignmentPattern { pattern, annotation, + location: _, }| { self.pop_empty_lines(pattern.location().end); diff --git a/crates/aiken-lang/src/parser/definition/snapshots/def_test_fail.snap b/crates/aiken-lang/src/parser/definition/snapshots/def_test_fail.snap index 199199fa..d3b30a4c 100644 --- a/crates/aiken-lang/src/parser/definition/snapshots/def_test_fail.snap +++ b/crates/aiken-lang/src/parser/definition/snapshots/def_test_fail.snap @@ -27,6 +27,7 @@ Test( tipo: (), }, annotation: None, + location: 38..42, }, ], kind: Expect { diff --git a/crates/aiken-lang/src/parser/definition/snapshots/function_assignment_only.snap b/crates/aiken-lang/src/parser/definition/snapshots/function_assignment_only.snap index f7345b59..bb48c17e 100644 --- a/crates/aiken-lang/src/parser/definition/snapshots/function_assignment_only.snap +++ b/crates/aiken-lang/src/parser/definition/snapshots/function_assignment_only.snap @@ -32,6 +32,7 @@ Fn( name: "x", }, annotation: None, + location: 17..18, }, ], kind: Let { diff --git a/crates/aiken-lang/src/parser/expr/assignment.rs b/crates/aiken-lang/src/parser/expr/assignment.rs index 8e3f6b6c..c7960ff0 100644 --- a/crates/aiken-lang/src/parser/expr/assignment.rs +++ b/crates/aiken-lang/src/parser/expr/assignment.rs @@ -1,5 +1,5 @@ use crate::{ - ast, + ast::{self, Span}, expr::UntypedExpr, parser::{annotation, error::ParseError, pattern, token::Token}, }; @@ -9,17 +9,7 @@ pub fn let_( r: Recursive<'_, Token, UntypedExpr, ParseError>, ) -> impl Parser + '_ { just(Token::Let) - .ignore_then( - pattern() - .then(just(Token::Colon).ignore_then(annotation()).or_not()) - .map(|(pattern, annotation)| ast::AssignmentPattern { - pattern, - annotation, - }) - .separated_by(just(Token::Comma)) - .allow_trailing() - .at_least(1), - ) + .ignore_then(assignment_patterns()) .then(choice((just(Token::Equal), just(Token::LArrow)))) .then(r.clone()) .validate(move |((patterns, kind), value), span, emit| { @@ -42,20 +32,25 @@ pub fn let_( }) } +fn assignment_patterns() -> impl Parser, Error = ParseError> { + pattern() + .then(just(Token::Colon).ignore_then(annotation()).or_not()) + .map_with_span(|(pattern, annotation), span| ast::AssignmentPattern { + pattern, + annotation, + location: span, + }) + .separated_by(just(Token::Comma)) + .allow_trailing() + .at_least(1) +} + pub fn expect( r: Recursive<'_, Token, UntypedExpr, ParseError>, ) -> impl Parser + '_ { just(Token::Expect) .ignore_then( - pattern() - .then(just(Token::Colon).ignore_then(annotation()).or_not()) - .map(|(pattern, annotation)| ast::AssignmentPattern { - pattern, - annotation, - }) - .separated_by(just(Token::Comma)) - .allow_trailing() - .at_least(1) + assignment_patterns() .then(choice((just(Token::Equal), just(Token::LArrow)))) .or_not(), ) @@ -66,22 +61,13 @@ pub fn expect( } let (patterns, kind) = opt_pattern.unwrap_or_else(|| { - ( - vec![ast::AssignmentPattern { - pattern: ast::UntypedPattern::Constructor { - is_record: false, - location: span, - name: "True".to_string(), - arguments: vec![], - module: None, - constructor: (), - with_spread: false, - tipo: (), - }, - annotation: None, - }], - Token::Equal, - ) + let filler_true = ast::AssignmentPattern::new( + ast::UntypedPattern::true_(span), + None, + Span::empty(), + ); + + (vec![filler_true], Token::Equal) }); let patterns = patterns diff --git a/crates/aiken-lang/src/parser/expr/snapshots/block_let.snap b/crates/aiken-lang/src/parser/expr/snapshots/block_let.snap index 0d242c2b..79814a8c 100644 --- a/crates/aiken-lang/src/parser/expr/snapshots/block_let.snap +++ b/crates/aiken-lang/src/parser/expr/snapshots/block_let.snap @@ -23,6 +23,7 @@ Assignment { name: "x", }, annotation: None, + location: 16..17, }, ], kind: Let { @@ -53,6 +54,7 @@ Assignment { name: "b", }, annotation: None, + location: 4..5, }, ], kind: Let { diff --git a/crates/aiken-lang/src/parser/expr/snapshots/expect.snap b/crates/aiken-lang/src/parser/expr/snapshots/expect.snap index 4f1e050a..4588bfd5 100644 --- a/crates/aiken-lang/src/parser/expr/snapshots/expect.snap +++ b/crates/aiken-lang/src/parser/expr/snapshots/expect.snap @@ -34,6 +34,7 @@ Assignment { tipo: (), }, annotation: None, + location: 7..14, }, ], kind: Expect { diff --git a/crates/aiken-lang/src/parser/expr/snapshots/expect_bool_sugar.snap b/crates/aiken-lang/src/parser/expr/snapshots/expect_bool_sugar.snap index 92382eb5..e0e8e104 100644 --- a/crates/aiken-lang/src/parser/expr/snapshots/expect_bool_sugar.snap +++ b/crates/aiken-lang/src/parser/expr/snapshots/expect_bool_sugar.snap @@ -33,6 +33,7 @@ Assignment { tipo: (), }, annotation: None, + location: 0..0, }, ], kind: Expect { diff --git a/crates/aiken-lang/src/parser/expr/snapshots/expect_expect_let.snap b/crates/aiken-lang/src/parser/expr/snapshots/expect_expect_let.snap index 016864c8..98f8b175 100644 --- a/crates/aiken-lang/src/parser/expr/snapshots/expect_expect_let.snap +++ b/crates/aiken-lang/src/parser/expr/snapshots/expect_expect_let.snap @@ -23,6 +23,7 @@ Assignment { name: "a", }, annotation: None, + location: 13..14, }, ], kind: Let { @@ -44,6 +45,7 @@ Assignment { tipo: (), }, annotation: None, + location: 0..0, }, ], kind: Expect { diff --git a/crates/aiken-lang/src/parser/expr/snapshots/expect_let_in_let.snap b/crates/aiken-lang/src/parser/expr/snapshots/expect_let_in_let.snap index 21a383d3..f5fa2ed0 100644 --- a/crates/aiken-lang/src/parser/expr/snapshots/expect_let_in_let.snap +++ b/crates/aiken-lang/src/parser/expr/snapshots/expect_let_in_let.snap @@ -23,6 +23,7 @@ Assignment { name: "b", }, annotation: None, + location: 14..15, }, ], kind: Let { @@ -38,6 +39,7 @@ Assignment { name: "a", }, annotation: None, + location: 4..5, }, ], kind: Let { diff --git a/crates/aiken-lang/src/parser/expr/snapshots/expect_let_in_let_parens.snap b/crates/aiken-lang/src/parser/expr/snapshots/expect_let_in_let_parens.snap index d4a6f3db..b98f6169 100644 --- a/crates/aiken-lang/src/parser/expr/snapshots/expect_let_in_let_parens.snap +++ b/crates/aiken-lang/src/parser/expr/snapshots/expect_let_in_let_parens.snap @@ -23,6 +23,7 @@ Assignment { name: "b", }, annotation: None, + location: 14..15, }, ], kind: Let { @@ -38,6 +39,7 @@ Assignment { name: "a", }, annotation: None, + location: 4..5, }, ], kind: Let { diff --git a/crates/aiken-lang/src/parser/expr/snapshots/expect_let_in_let_return.snap b/crates/aiken-lang/src/parser/expr/snapshots/expect_let_in_let_return.snap index f0acd435..aa16630d 100644 --- a/crates/aiken-lang/src/parser/expr/snapshots/expect_let_in_let_return.snap +++ b/crates/aiken-lang/src/parser/expr/snapshots/expect_let_in_let_return.snap @@ -23,6 +23,7 @@ Assignment { name: "b", }, annotation: None, + location: 16..17, }, ], kind: Let { @@ -42,6 +43,7 @@ Assignment { name: "a", }, annotation: None, + location: 4..5, }, ], kind: Let { diff --git a/crates/aiken-lang/src/parser/expr/snapshots/expect_trace_if_false.snap b/crates/aiken-lang/src/parser/expr/snapshots/expect_trace_if_false.snap index add9e781..cc3515dc 100644 --- a/crates/aiken-lang/src/parser/expr/snapshots/expect_trace_if_false.snap +++ b/crates/aiken-lang/src/parser/expr/snapshots/expect_trace_if_false.snap @@ -24,6 +24,7 @@ Assignment { tipo: (), }, annotation: None, + location: 0..0, }, ], kind: Expect { diff --git a/crates/aiken-lang/src/parser/expr/snapshots/function_invoke.snap b/crates/aiken-lang/src/parser/expr/snapshots/function_invoke.snap index 7d3d16f6..b1e1a4d5 100644 --- a/crates/aiken-lang/src/parser/expr/snapshots/function_invoke.snap +++ b/crates/aiken-lang/src/parser/expr/snapshots/function_invoke.snap @@ -34,6 +34,7 @@ Sequence { name: "x", }, annotation: None, + location: 4..5, }, ], kind: Let { @@ -124,6 +125,7 @@ Sequence { name: "map_add_x", }, annotation: None, + location: 24..33, }, ], kind: Let { diff --git a/crates/aiken-lang/src/parser/expr/snapshots/int_numeric_underscore.snap b/crates/aiken-lang/src/parser/expr/snapshots/int_numeric_underscore.snap index 4b8b0093..ea702270 100644 --- a/crates/aiken-lang/src/parser/expr/snapshots/int_numeric_underscore.snap +++ b/crates/aiken-lang/src/parser/expr/snapshots/int_numeric_underscore.snap @@ -21,6 +21,7 @@ Sequence { name: "i", }, annotation: None, + location: 8..9, }, ], kind: Let { @@ -43,6 +44,7 @@ Sequence { name: "j", }, annotation: None, + location: 28..29, }, ], kind: Let { @@ -69,6 +71,7 @@ Sequence { name: "k", }, annotation: None, + location: 48..49, }, ], kind: Let { diff --git a/crates/aiken-lang/src/parser/expr/snapshots/let_bindings.snap b/crates/aiken-lang/src/parser/expr/snapshots/let_bindings.snap index 5d7430f9..74cd03c8 100644 --- a/crates/aiken-lang/src/parser/expr/snapshots/let_bindings.snap +++ b/crates/aiken-lang/src/parser/expr/snapshots/let_bindings.snap @@ -35,6 +35,7 @@ Assignment { name: "thing", }, annotation: None, + location: 4..9, }, ], kind: Let { diff --git a/crates/aiken-lang/src/parser/expr/snapshots/parse_tuple.snap b/crates/aiken-lang/src/parser/expr/snapshots/parse_tuple.snap index ac27b641..91d9a513 100644 --- a/crates/aiken-lang/src/parser/expr/snapshots/parse_tuple.snap +++ b/crates/aiken-lang/src/parser/expr/snapshots/parse_tuple.snap @@ -47,6 +47,7 @@ Sequence { name: "tuple", }, annotation: None, + location: 4..9, }, ], kind: Let { diff --git a/crates/aiken-lang/src/parser/expr/snapshots/parse_tuple2.snap b/crates/aiken-lang/src/parser/expr/snapshots/parse_tuple2.snap index 9b57ec8a..bc87b56f 100644 --- a/crates/aiken-lang/src/parser/expr/snapshots/parse_tuple2.snap +++ b/crates/aiken-lang/src/parser/expr/snapshots/parse_tuple2.snap @@ -34,6 +34,7 @@ Sequence { name: "a", }, annotation: None, + location: 4..5, }, ], kind: Let { diff --git a/crates/aiken-lang/src/parser/expr/when/snapshots/when_basic.snap b/crates/aiken-lang/src/parser/expr/when/snapshots/when_basic.snap index b1f9628a..929746ae 100644 --- a/crates/aiken-lang/src/parser/expr/when/snapshots/when_basic.snap +++ b/crates/aiken-lang/src/parser/expr/when/snapshots/when_basic.snap @@ -92,6 +92,7 @@ When { name: "amazing", }, annotation: None, + location: 55..62, }, ], kind: Let { diff --git a/crates/aiken-lang/src/snapshots/function_ambiguous_sequence.snap b/crates/aiken-lang/src/snapshots/function_ambiguous_sequence.snap index d6459358..4113a500 100644 --- a/crates/aiken-lang/src/snapshots/function_ambiguous_sequence.snap +++ b/crates/aiken-lang/src/snapshots/function_ambiguous_sequence.snap @@ -26,6 +26,7 @@ Module { name: "a", }, annotation: None, + location: 19..20, }, ], kind: Let { @@ -70,6 +71,7 @@ Module { name: "a", }, annotation: None, + location: 56..57, }, ], kind: Let { @@ -125,6 +127,7 @@ Module { name: "a", }, annotation: None, + location: 93..94, }, ], kind: Let { @@ -176,6 +179,7 @@ Module { name: "a", }, annotation: None, + location: 126..127, }, ], kind: Let { diff --git a/crates/aiken-lang/src/snapshots/parse_unicode_offset_1.snap b/crates/aiken-lang/src/snapshots/parse_unicode_offset_1.snap index dc3f0e3d..8c257e71 100644 --- a/crates/aiken-lang/src/snapshots/parse_unicode_offset_1.snap +++ b/crates/aiken-lang/src/snapshots/parse_unicode_offset_1.snap @@ -31,6 +31,7 @@ Module { name: "x", }, annotation: None, + location: 17..18, }, ], kind: Let { diff --git a/crates/aiken-lang/src/snapshots/parse_unicode_offset_2.snap b/crates/aiken-lang/src/snapshots/parse_unicode_offset_2.snap index 2dec2f89..f4555fe9 100644 --- a/crates/aiken-lang/src/snapshots/parse_unicode_offset_2.snap +++ b/crates/aiken-lang/src/snapshots/parse_unicode_offset_2.snap @@ -29,6 +29,7 @@ Module { name: "x", }, annotation: None, + location: 17..18, }, ], kind: Let { diff --git a/crates/aiken-lang/src/tests/check.rs b/crates/aiken-lang/src/tests/check.rs index 53bc42c0..23501e35 100644 --- a/crates/aiken-lang/src/tests/check.rs +++ b/crates/aiken-lang/src/tests/check.rs @@ -1984,3 +1984,27 @@ fn allow_expect_on_var_patterns_that_are_opaque() { assert!(check(parse(source_code)).is_ok()) } + +#[test] +fn correct_span_for_backpassing_args() { + let source_code = r#" + fn fold(list: List, acc: b, f: fn(a, b) -> b) -> b { + when list is { + [] -> acc + [x, ..xs] -> fold(xs, f(x, acc), f) + } + } + + pub fn sum(list: List) -> Int { + let a, b <- fold(list, 0) + + a + 1 + } + "#; + + let (warnings, _ast) = check(parse(source_code)).unwrap(); + + assert!( + matches!(&warnings[0], Warning::UnusedVariable { ref name, location } if name == "b" && location.start == 245 && location.end == 246) + ); +} diff --git a/crates/aiken-lang/src/tipo/expr.rs b/crates/aiken-lang/src/tipo/expr.rs index f1cedb2a..6ed64c7e 100644 --- a/crates/aiken-lang/src/tipo/expr.rs +++ b/crates/aiken-lang/src/tipo/expr.rs @@ -268,6 +268,7 @@ impl<'a, 'b> ExprTyper<'a, 'b> { let AssignmentPattern { pattern, annotation, + location: _, } = patterns.into_vec().swap_remove(0); self.infer_assignment(pattern, *value, kind, &annotation, location) @@ -959,7 +960,8 @@ impl<'a, 'b> ExprTyper<'a, 'b> { value: UntypedExpr::Assignment { location, value: untyped_value.into(), - patterns: AssignmentPattern::new(untyped_pattern, Some(ann)).into(), + patterns: AssignmentPattern::new(untyped_pattern, Some(ann), Span::empty()) + .into(), kind, }, }); @@ -1008,15 +1010,24 @@ impl<'a, 'b> ExprTyper<'a, 'b> { name: "...".to_string(), location: Span::empty(), }), - patterns: AssignmentPattern::new(untyped_pattern, None) - .into(), + patterns: AssignmentPattern::new( + untyped_pattern, + None, + Span::empty(), + ) + .into(), kind: AssignmentKind::Let { backpassing: true }, } } _ => UntypedExpr::Assignment { location: Span::empty(), value: Box::new(untyped_value), - patterns: AssignmentPattern::new(untyped_pattern, None).into(), + patterns: AssignmentPattern::new( + untyped_pattern, + None, + Span::empty(), + ) + .into(), kind: AssignmentKind::let_(), }, }, @@ -1725,7 +1736,7 @@ impl<'a, 'b> ExprTyper<'a, 'b> { mut continuation: Vec, ) -> UntypedExpr { let UntypedExpr::Assignment { - location: assign_location, + location: _, value, kind, patterns, @@ -1750,6 +1761,7 @@ impl<'a, 'b> ExprTyper<'a, 'b> { let AssignmentPattern { pattern, annotation, + location: assignment_pattern_location, } = assignment_pattern; // In case where we have a Pattern that isn't simply a let-binding to a name, we do insert an extra let-binding @@ -1759,7 +1771,7 @@ impl<'a, 'b> ExprTyper<'a, 'b> { Pattern::Var { name, location: _ } | Pattern::Discard { name, location: _ } if kind.is_let() => { - names.push((name.clone(), annotation)); + names.push((name.clone(), assignment_pattern_location, annotation)); } _ => { let name = format!("{}_{}", ast::BACKPASS_VARIABLE, index); @@ -1767,13 +1779,18 @@ impl<'a, 'b> ExprTyper<'a, 'b> { continuation.insert( 0, UntypedExpr::Assignment { - location: assign_location, + location: assignment_pattern_location, value: UntypedExpr::Var { - location: value_location, + location: assignment_pattern_location, name: name.clone(), } .into(), - patterns: AssignmentPattern::new(pattern, annotation.clone()).into(), + patterns: AssignmentPattern::new( + pattern, + annotation.clone(), + assignment_pattern_location, + ) + .into(), // erase backpassing while preserving assignment kind. kind: match kind { AssignmentKind::Let { .. } => AssignmentKind::let_(), @@ -1782,7 +1799,7 @@ impl<'a, 'b> ExprTyper<'a, 'b> { }, ); - names.push((name, annotation)); + names.push((name, assignment_pattern_location, annotation)); } } } @@ -2192,7 +2209,12 @@ impl<'a, 'b> ExprTyper<'a, 'b> { sample: UntypedExpr::Assignment { location: Span::empty(), value: Box::new(subject.clone()), - patterns: AssignmentPattern::new(clauses[0].patterns[0].clone(), None).into(), + patterns: AssignmentPattern::new( + clauses[0].patterns[0].clone(), + None, + Span::empty(), + ) + .into(), kind: AssignmentKind::let_(), }, });