compiler-core
src
ast
language_server
parse
type_
···
144
144
location: SrcSpan,
145
145
type_: Arc<Type>,
146
146
expression: Option<Box<Self>>,
147
147
+
message: Option<Box<Self>>,
147
148
},
148
149
149
150
BitArray {
···
242
243
}
243
244
}
244
245
245
245
-
Self::Echo { expression, .. } => expression
246
246
+
Self::Echo {
247
247
+
expression,
248
248
+
message,
249
249
+
..
250
250
+
} => expression
246
251
.as_ref()
247
252
.and_then(|expression| expression.find_node(byte_index))
253
253
+
.or_else(|| {
254
254
+
message
255
255
+
.as_ref()
256
256
+
.and_then(|message| message.find_node(byte_index))
257
257
+
})
248
258
.or_else(|| self.self_if_contains_location(byte_index)),
249
259
250
260
Self::Todo { kind, .. } => match kind {
···
507
517
tuple: expression, ..
508
518
} => expression.find_statement(byte_index),
509
519
510
510
-
Self::Echo { expression, .. } => expression
520
520
+
Self::Echo {
521
521
+
expression,
522
522
+
message,
523
523
+
..
524
524
+
} => expression
511
525
.as_ref()
512
512
-
.and_then(|expression| expression.find_statement(byte_index)),
526
526
+
.and_then(|expression| expression.find_statement(byte_index))
527
527
+
.or_else(|| {
528
528
+
message
529
529
+
.as_ref()
530
530
+
.and_then(|message| message.find_statement(byte_index))
531
531
+
}),
513
532
514
533
Self::BitArray { segments, .. } => segments
515
534
.iter()
···
111
111
112
112
Echo {
113
113
location: SrcSpan,
114
114
+
/// This is the position where the echo keyword ends:
115
115
+
/// ```gleam
116
116
+
/// echo wibble
117
117
+
/// // ^ ends here!
118
118
+
/// ```
119
119
+
keyword_end: u32,
114
120
expression: Option<Box<Self>>,
115
121
message: Option<Box<Self>>,
116
122
},
···
92
92
location: &'ast SrcSpan,
93
93
type_: &'ast Arc<Type>,
94
94
expression: &'ast Option<Box<TypedExpr>>,
95
95
+
message: &'ast Option<Box<TypedExpr>>,
95
96
) {
96
96
-
visit_typed_expr_echo(self, location, type_, expression);
97
97
+
visit_typed_expr_echo(self, location, type_, expression, message);
97
98
}
98
99
99
100
fn visit_typed_expr_int(
···
857
858
TypedExpr::Echo {
858
859
location,
859
860
expression,
861
861
+
message,
860
862
type_,
861
861
-
} => v.visit_typed_expr_echo(location, type_, expression),
863
863
+
} => v.visit_typed_expr_echo(location, type_, expression, message),
862
864
}
863
865
}
864
866
···
1097
1099
_location: &'a SrcSpan,
1098
1100
_type_: &'a Arc<Type>,
1099
1101
expression: &'a Option<Box<TypedExpr>>,
1102
1102
+
message: &'a Option<Box<TypedExpr>>,
1100
1103
) where
1101
1104
V: Visit<'a> + ?Sized,
1102
1105
{
1103
1106
if let Some(expression) = expression {
1104
1107
v.visit_typed_expr(expression)
1108
1108
+
}
1109
1109
+
if let Some(message) = message {
1110
1110
+
v.visit_typed_expr(message)
1105
1111
}
1106
1112
}
1107
1113
···
330
330
331
331
UntypedExpr::Echo {
332
332
location,
333
333
+
keyword_end,
333
334
expression,
334
335
message,
335
335
-
} => self.fold_echo(location, expression, message),
336
336
+
} => self.fold_echo(location, keyword_end, expression, message),
336
337
337
338
UntypedExpr::Panic { location, message } => self.fold_panic(location, message),
338
339
···
382
383
UntypedExpr::Echo {
383
384
location,
384
385
expression,
386
386
+
keyword_end,
385
387
message,
386
388
} => UntypedExpr::Echo {
387
389
location,
390
390
+
keyword_end,
388
391
expression: expression.map(|expression| Box::new(self.fold_expr(*expression))),
389
392
message: message.map(|message| Box::new(self.fold_expr(*message))),
390
393
},
···
857
860
fn fold_echo(
858
861
&mut self,
859
862
location: SrcSpan,
863
863
+
keyword_end: u32,
860
864
expression: Option<Box<UntypedExpr>>,
861
865
message: Option<Box<UntypedExpr>>,
862
866
) -> UntypedExpr {
863
867
UntypedExpr::Echo {
864
868
location,
869
869
+
keyword_end,
865
870
expression,
866
871
message,
867
872
}
···
6705
6705
location: &'ast SrcSpan,
6706
6706
type_: &'ast Arc<Type>,
6707
6707
expression: &'ast Option<Box<TypedExpr>>,
6708
6708
+
message: &'ast Option<Box<TypedExpr>>,
6708
6709
) {
6709
6710
// We also want to trigger the action if we're hovering over the expression
6710
6711
// being printed. So we create a unique span starting from the start of echo
···
6745
6746
}
6746
6747
}
6747
6748
6748
6748
-
ast::visit::visit_typed_expr_echo(self, location, type_, expression);
6749
6749
+
ast::visit::visit_typed_expr_echo(self, location, type_, expression, message);
6749
6750
}
6750
6751
6751
6752
fn visit_typed_pipeline_assignment(&mut self, assignment: &'ast TypedPipelineAssignment) {
···
547
547
}
548
548
}
549
549
550
550
-
Some((start, Token::Echo, end)) => {
550
550
+
Some((start, Token::Echo, echo_end)) => {
551
551
self.advance();
552
552
if context == ExpressionUnitContext::FollowingPipe {
553
553
// If an echo is used as a step in a pipeline (`|> echo`)
554
554
// then it cannot be followed by an expression.
555
555
let message = self.maybe_parse_as_message()?;
556
556
-
let end = message.as_ref().map_or(end, |m| m.location().end);
556
556
+
let end = message.as_ref().map_or(echo_end, |m| m.location().end);
557
557
UntypedExpr::Echo {
558
558
location: SrcSpan { start, end },
559
559
+
keyword_end: echo_end,
559
560
expression: None,
560
561
message,
561
562
}
···
567
568
// stop analysis from happening everywhere and be fault
568
569
// tolerant like everything else.
569
570
let expression = self.parse_expression()?;
570
570
-
let end = expression.as_ref().map_or(end, |e| e.location().end);
571
571
+
let end = expression.as_ref().map_or(echo_end, |e| e.location().end);
571
572
572
573
let message = self.maybe_parse_as_message()?;
573
574
let end = message.as_ref().map_or(end, |m| m.location().end);
574
575
575
576
UntypedExpr::Echo {
576
577
location: SrcSpan { start, end },
578
578
+
keyword_end: echo_end,
577
579
expression: expression.map(Box::new),
578
580
message,
579
581
}
···
9
9
start: 0,
10
10
end: 26,
11
11
},
12
12
+
keyword_end: 4,
12
13
expression: Some(
13
14
PipeLine {
14
15
expressions: [
···
9
9
start: 0,
10
10
end: 11,
11
11
},
12
12
+
keyword_end: 4,
12
13
expression: Some(
13
14
Var {
14
15
location: SrcSpan {
···
9
9
start: 0,
10
10
end: 10,
11
11
},
12
12
+
keyword_end: 4,
12
13
expression: Some(
13
14
BinOp {
14
15
location: SrcSpan {
···
9
9
start: 0,
10
10
end: 28,
11
11
},
12
12
+
keyword_end: 4,
12
13
expression: Some(
13
14
PipeLine {
14
15
expressions: [
···
32
32
start: 12,
33
33
end: 42,
34
34
},
35
35
+
keyword_end: 16,
35
36
expression: Some(
36
37
Int {
37
38
location: SrcSpan {
···
14
14
start: 7,
15
15
end: 40,
16
16
},
17
17
+
keyword_end: 11,
17
18
expression: Some(
18
19
Var {
19
20
location: SrcSpan {
···
32
32
start: 12,
33
33
end: 42,
34
34
},
35
35
+
keyword_end: 16,
35
36
expression: Some(
36
37
Int {
37
38
location: SrcSpan {
···
14
14
start: 7,
15
15
end: 40,
16
16
},
17
17
+
keyword_end: 11,
17
18
expression: Some(
18
19
Var {
19
20
location: SrcSpan {
···
14
14
start: 7,
15
15
end: 42,
16
16
},
17
17
+
keyword_end: 11,
17
18
expression: Some(
18
19
BinOp {
19
20
location: SrcSpan {
···
9
9
start: 0,
10
10
end: 14,
11
11
},
12
12
+
keyword_end: 4,
12
13
expression: Some(
13
14
Block {
14
15
location: SrcSpan {
···
9
9
start: 0,
10
10
end: 34,
11
11
},
12
12
+
keyword_end: 4,
12
13
expression: Some(
13
14
Var {
14
15
location: SrcSpan {
···
14
14
start: 15,
15
15
end: 45,
16
16
},
17
17
+
keyword_end: 19,
17
18
expression: Some(
18
19
Int {
19
20
location: SrcSpan {
···
14
14
start: 15,
15
15
end: 45,
16
16
},
17
17
+
keyword_end: 19,
17
18
expression: Some(
18
19
Int {
19
20
location: SrcSpan {
···
9
9
start: 0,
10
10
end: 15,
11
11
},
12
12
+
keyword_end: 4,
12
13
expression: None,
13
14
message: Some(
14
15
Var {
···
9
9
start: 0,
10
10
end: 17,
11
11
},
12
12
+
keyword_end: 4,
12
13
expression: Some(
13
14
Panic {
14
15
location: SrcSpan {
···
9
9
start: 0,
10
10
end: 17,
11
11
},
12
12
+
keyword_end: 4,
12
13
expression: Some(
13
14
Panic {
14
15
location: SrcSpan {
···
9
9
start: 0,
10
10
end: 24,
11
11
},
12
12
+
keyword_end: 4,
12
13
expression: Some(
13
14
Panic {
14
15
location: SrcSpan {
···
9
9
start: 0,
10
10
end: 22,
11
11
},
12
12
+
keyword_end: 4,
12
13
expression: Some(
13
14
Var {
14
15
location: SrcSpan {
···
9
9
start: 0,
10
10
end: 24,
11
11
},
12
12
+
keyword_end: 4,
12
13
expression: Some(
13
14
Var {
14
15
location: SrcSpan {
···
15
15
start: 9,
16
16
end: 22,
17
17
},
18
18
+
keyword_end: 13,
18
19
expression: Some(
19
20
String {
20
21
location: SrcSpan {
···
15
15
start: 9,
16
16
end: 31,
17
17
},
18
18
+
keyword_end: 13,
18
19
expression: Some(
19
20
Var {
20
21
location: SrcSpan {
···
9
9
start: 0,
10
10
end: 16,
11
11
},
12
12
+
keyword_end: 4,
12
13
expression: Some(
13
14
Echo {
14
15
location: SrcSpan {
15
16
start: 5,
16
17
end: 16,
17
18
},
19
19
+
keyword_end: 9,
18
20
expression: Some(
19
21
Echo {
20
22
location: SrcSpan {
21
23
start: 10,
22
24
end: 16,
23
25
},
26
26
+
keyword_end: 14,
24
27
expression: Some(
25
28
Int {
26
29
location: SrcSpan {
···
427
427
428
428
UntypedExpr::Echo {
429
429
location,
430
430
+
keyword_end,
430
431
expression,
431
432
message,
432
432
-
} => Ok(self.infer_echo(location, expression, message)),
433
433
+
} => Ok(self.infer_echo(location, keyword_end, expression, message)),
433
434
434
435
UntypedExpr::Var { location, name, .. } => {
435
436
self.infer_var(name, location, ReferenceRegistration::RegisterReferences)
···
581
582
582
583
self.purity = Purity::Impure;
583
584
584
584
-
let message = message.map(|message| {
585
585
-
// If there is a message expression then it must be a string.
586
586
-
let message = self.infer(*message);
587
587
-
if let Err(error) = unify(string(), message.type_()) {
588
588
-
self.problems
589
589
-
.error(convert_unify_error(error, message.location()));
590
590
-
}
591
591
-
Box::new(message)
592
592
-
});
593
593
-
585
585
+
let message = message.map(|message| Box::new(self.infer_and_unify(*message, string())));
594
586
TypedExpr::Todo {
595
587
location,
596
588
type_,
···
615
607
PanicKind::Placeholder => {}
616
608
}
617
609
618
618
-
let message = match message {
619
619
-
None => None,
620
620
-
Some(message) => {
621
621
-
let message = self.infer(*message);
622
622
-
if let Err(error) = unify(string(), message.type_()) {
623
623
-
self.problems
624
624
-
.error(convert_unify_error(error, message.location()))
625
625
-
}
626
626
-
Some(Box::new(message))
627
627
-
}
628
628
-
};
610
610
+
let message = message.map(|message| Box::new(self.infer_and_unify(*message, string())));
629
611
self.previous_panics = true;
630
612
TypedExpr::Panic {
631
613
location,
···
637
619
fn infer_echo(
638
620
&mut self,
639
621
location: SrcSpan,
622
622
+
keyword_end: u32,
640
623
expression: Option<Box<UntypedExpr>>,
641
624
message: Option<Box<UntypedExpr>>,
642
625
) -> TypedExpr {
643
626
self.environment.echo_found = true;
644
627
self.purity = Purity::Impure;
645
628
646
646
-
if let Some(expression) = expression {
629
629
+
let expression = if let Some(expression) = expression {
647
630
let expression = self.infer(*expression);
648
631
if self.previous_panics {
649
632
self.warn_for_unreachable_code(location, PanicPosition::EchoExpression);
650
633
}
651
651
-
TypedExpr::Echo {
652
652
-
location,
653
653
-
type_: expression.type_(),
654
654
-
expression: Some(Box::new(expression)),
655
655
-
}
634
634
+
expression
656
635
} else {
636
636
+
let location = SrcSpan {
637
637
+
start: location.start,
638
638
+
end: keyword_end,
639
639
+
};
657
640
self.problems
658
641
.error(Error::EchoWithNoFollowingExpression { location });
659
642
self.error_expr(location)
643
643
+
};
644
644
+
645
645
+
TypedExpr::Echo {
646
646
+
location,
647
647
+
type_: expression.type_(),
648
648
+
expression: Some(Box::new(expression)),
649
649
+
message: message.map(|message| Box::new(self.infer_and_unify(*message, string()))),
660
650
}
661
651
}
662
652
···
1589
1579
/// Same as `self.infer_or_error` but instead of returning a `Result` with an error,
1590
1580
/// records the error and returns an invalid expression.
1591
1581
///
1592
1592
-
fn infer(&mut self, expression: UntypedExpr) -> TypedExpr {
1582
1582
+
pub fn infer(&mut self, expression: UntypedExpr) -> TypedExpr {
1593
1583
let location = expression.location();
1594
1584
match self.infer_or_error(expression) {
1595
1585
Ok(result) => result,
···
1598
1588
self.error_expr(location)
1599
1589
}
1600
1590
}
1591
1591
+
}
1592
1592
+
1593
1593
+
/// Infers the type of the given function and tries to unify it with the
1594
1594
+
/// given type, recording any unification error that might take place.
1595
1595
+
/// The typed expression is returned in any case.
1596
1596
+
///
1597
1597
+
pub fn infer_and_unify(&mut self, expression: UntypedExpr, type_: Arc<Type>) -> TypedExpr {
1598
1598
+
let expression = self.infer(expression);
1599
1599
+
if let Err(error) = unify(type_, expression.type_()) {
1600
1600
+
self.problems
1601
1601
+
.error(convert_unify_error(error, expression.location()))
1602
1602
+
}
1603
1603
+
expression
1601
1604
}
1602
1605
1603
1606
fn infer_binop(
···
1949
1952
FeatureKind::LetAssertWithMessage,
1950
1953
message.location(),
1951
1954
);
1952
1952
-
let message = self.infer_or_error(message).unwrap_or_else(|error| {
1953
1953
-
self.problems.error(error);
1954
1954
-
self.error_expr(location)
1955
1955
-
});
1956
1956
-
1957
1957
-
let _ = unify(string(), message.type_()).map_err(|e| {
1958
1958
-
self.problems
1959
1959
-
.error(convert_unify_error(e, message.location()))
1960
1960
-
});
1961
1961
-
Some(message)
1955
1955
+
Some(self.infer_and_unify(message, string()))
1962
1956
}
1963
1957
None => None,
1964
1958
};
···
1996
1990
.error(convert_unify_error(error, value_location)),
1997
1991
}
1998
1992
1999
1999
-
let message = message.map(|message| {
2000
2000
-
let message_location = message.location();
2001
2001
-
match self.infer_assert_message(message) {
2002
2002
-
Ok(value) => value,
2003
2003
-
Err(error) => {
2004
2004
-
self.problems.error(error);
2005
2005
-
self.error_expr(message_location)
2006
2006
-
}
2007
2007
-
}
2008
2008
-
});
2009
2009
-
1993
1993
+
let message = message.map(|message| self.infer_and_unify(message, string()));
2010
1994
self.track_feature_usage(FeatureKind::BoolAssert, location);
2011
1995
2012
1996
Assert {
···
2014
1998
value,
2015
1999
message,
2016
2000
}
2017
2017
-
}
2018
2018
-
2019
2019
-
fn infer_assert_message(&mut self, message: UntypedExpr) -> Result<TypedExpr, Error> {
2020
2020
-
let message_location = message.location();
2021
2021
-
let message = self.infer(message);
2022
2022
-
2023
2023
-
match unify(string(), message.type_()) {
2024
2024
-
Ok(()) => {}
2025
2025
-
Err(error) => self
2026
2026
-
.problems
2027
2027
-
.error(convert_unify_error(error, message_location)),
2028
2028
-
}
2029
2029
-
2030
2030
-
Ok(message)
2031
2001
}
2032
2002
2033
2003
fn infer_case(
···
182
182
183
183
UntypedExpr::Echo {
184
184
location,
185
185
+
keyword_end: _,
185
186
expression: None,
186
187
message,
187
188
} => {
···
198
199
location,
199
200
expression: None,
200
201
type_: self.argument_type.clone(),
202
202
+
message: message.map(|message| {
203
203
+
Box::new(self.expr_typer.infer_and_unify(*message, string()))
204
204
+
}),
201
205
},
202
206
)
203
207
}
···
2873
2873
}
2874
2874
2875
2875
#[test]
2876
2876
+
fn echo_followed_by_no_expression_and_message() {
2877
2877
+
assert_error!("echo as \"wibble\"");
2878
2878
+
}
2879
2879
+
2880
2880
+
#[test]
2881
2881
+
fn echo_followed_by_no_expression_and_invalid_message() {
2882
2882
+
assert_error!("echo as 1");
2883
2883
+
}
2884
2884
+
2885
2885
+
#[test]
2886
2886
+
fn echo_followed_by_invalid_message() {
2887
2887
+
assert_error!("echo 11 as { True || False }");
2888
2888
+
}
2889
2889
+
2890
2890
+
#[test]
2876
2891
fn echo_followed_by_no_expression_2() {
2877
2892
assert_module_error!(
2878
2893
r#"
···
1
1
+
---
2
2
+
source: compiler-core/src/type_/tests/errors.rs
3
3
+
expression: "echo 11 as { True || False }"
4
4
+
---
5
5
+
----- SOURCE CODE
6
6
+
echo 11 as { True || False }
7
7
+
8
8
+
----- ERROR
9
9
+
error: Type mismatch
10
10
+
┌─ /src/one/two.gleam:1:12
11
11
+
│
12
12
+
1 │ echo 11 as { True || False }
13
13
+
│ ^^^^^^^^^^^^^^^^^
14
14
+
15
15
+
Expected type:
16
16
+
17
17
+
String
18
18
+
19
19
+
Found type:
20
20
+
21
21
+
Bool
···
1
1
+
---
2
2
+
source: compiler-core/src/type_/tests/errors.rs
3
3
+
expression: echo as 1
4
4
+
---
5
5
+
----- SOURCE CODE
6
6
+
echo as 1
7
7
+
8
8
+
----- ERROR
9
9
+
error: Invalid echo use
10
10
+
┌─ /src/one/two.gleam:1:1
11
11
+
│
12
12
+
1 │ echo as 1
13
13
+
│ ^^^^ I was expecting a value after this
14
14
+
15
15
+
The `echo` keyword should be followed by a value to print.
16
16
+
17
17
+
error: Type mismatch
18
18
+
┌─ /src/one/two.gleam:1:9
19
19
+
│
20
20
+
1 │ echo as 1
21
21
+
│ ^
22
22
+
23
23
+
Expected type:
24
24
+
25
25
+
String
26
26
+
27
27
+
Found type:
28
28
+
29
29
+
Int
···
1
1
+
---
2
2
+
source: compiler-core/src/type_/tests/errors.rs
3
3
+
expression: "echo as \"wibble\""
4
4
+
---
5
5
+
----- SOURCE CODE
6
6
+
echo as "wibble"
7
7
+
8
8
+
----- ERROR
9
9
+
error: Invalid echo use
10
10
+
┌─ /src/one/two.gleam:1:1
11
11
+
│
12
12
+
1 │ echo as "wibble"
13
13
+
│ ^^^^ I was expecting a value after this
14
14
+
15
15
+
The `echo` keyword should be followed by a value to print.