Skip to content

Commit 0d6b4d3

Browse files
giacomocavalierilpil
authored andcommitted
parse opaque private types and error during analysis
The parser would previously error if it found `opaque` at the start of a type definition and would only accept `pub opaque`. This would result in a confusing error message, and analysis stopping altogether for the entire module. With this change a private `opaque` type is actually parsed and the error is deferred to the analysis step.
1 parent fe9cb5d commit 0d6b4d3

File tree

9 files changed

+177
-2
lines changed

9 files changed

+177
-2
lines changed

CHANGELOG.md

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,34 @@
3636

3737
([Surya Rose](https://github.com/GearsDatapacks))
3838

39+
- The compiler now emits a better error message for private types marked as
40+
opaque. For example, the following piece of code:
41+
42+
```gleam
43+
opaque type Wibble {
44+
Wobble
45+
}
46+
```
47+
48+
Would result in the following error:
49+
50+
```
51+
error: Private opaque type
52+
┌─ /src/one/two.gleam:2:1
53+
54+
2 │ opaque type Wibble {
55+
│ ^^^^^^ You can safely remove this.
56+
57+
Only a public type can be opaque.
58+
```
59+
60+
([Giacomo Cavalieri](https://github.com/giacomocavalieri))
61+
62+
- The parsing of opaque private types is now fault tolerant: having a private
63+
opaque type in a module no longer stops the compiler from highlighting other
64+
errors.
65+
([Giacomo Cavalieri](https://github.com/giacomocavalieri))
66+
3967
### Build tool
4068

4169
- New projects are generated using OTP28 on GitHub Actions.

compiler-core/src/analyse.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1326,6 +1326,15 @@ impl<'a, A> ModuleAnalyzer<'a, A> {
13261326
});
13271327
}
13281328

1329+
if *opaque && publicity.is_private() {
1330+
self.problems.error(Error::PrivateOpaqueType {
1331+
location: SrcSpan {
1332+
start: location.start,
1333+
end: location.start + 6,
1334+
},
1335+
});
1336+
}
1337+
13291338
Ok(())
13301339
}
13311340

compiler-core/src/error.rs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3877,6 +3877,22 @@ and explain your usecase for this pattern, and how you would expect it to behave
38773877
extra_labels: vec![],
38783878
}),
38793879
},
3880+
3881+
TypeError::PrivateOpaqueType { location } => Diagnostic {
3882+
title: "Private opaque type".to_string(),
3883+
text: wrap("Only a public type can be opaque."),
3884+
hint: None,
3885+
level: Level::Error,
3886+
location: Some(Location {
3887+
label: Label {
3888+
text: Some("You can safely remove this.".to_string()),
3889+
span: *location,
3890+
},
3891+
path: path.clone(),
3892+
src: src.clone(),
3893+
extra_labels: vec![],
3894+
}),
3895+
},
38803896
})
38813897
.collect_vec(),
38823898

compiler-core/src/parse.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -348,6 +348,13 @@ where
348348
self.advance();
349349
self.parse_custom_type(start, true, false, &mut attributes)
350350
}
351+
(Some((start, Token::Opaque, _)), Some((_, Token::Type, _))) => {
352+
// A private opaque type makes no sense! We still want to parse it
353+
// and return an error later during the analysis phase.
354+
self.advance();
355+
self.advance();
356+
self.parse_custom_type(start, false, true, &mut attributes)
357+
}
351358

352359
(t0, _) => {
353360
self.tok0 = t0;
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
---
2+
source: compiler-core/src/parse/tests.rs
3+
expression: "opaque type Wibble { Wobble }"
4+
---
5+
Parsed {
6+
module: Module {
7+
name: "",
8+
documentation: [],
9+
type_info: (),
10+
definitions: [
11+
TargetedDefinition {
12+
definition: CustomType(
13+
CustomType {
14+
location: SrcSpan {
15+
start: 0,
16+
end: 18,
17+
},
18+
end_position: 29,
19+
name: "Wibble",
20+
name_location: SrcSpan {
21+
start: 12,
22+
end: 18,
23+
},
24+
publicity: Private,
25+
constructors: [
26+
RecordConstructor {
27+
location: SrcSpan {
28+
start: 21,
29+
end: 27,
30+
},
31+
name_location: SrcSpan {
32+
start: 21,
33+
end: 27,
34+
},
35+
name: "Wobble",
36+
arguments: [],
37+
documentation: None,
38+
deprecation: NotDeprecated,
39+
},
40+
],
41+
documentation: None,
42+
deprecation: NotDeprecated,
43+
opaque: true,
44+
parameters: [],
45+
typed_parameters: [],
46+
},
47+
),
48+
target: None,
49+
},
50+
],
51+
names: Names {
52+
local_types: {},
53+
imported_modules: {},
54+
type_variables: {},
55+
local_value_constructors: {},
56+
},
57+
unused_definition_positions: {},
58+
},
59+
extra: ModuleExtra {
60+
module_comments: [],
61+
doc_comments: [],
62+
comments: [],
63+
empty_lines: [],
64+
new_lines: [],
65+
trailing_commas: [],
66+
},
67+
}

compiler-core/src/parse/tests.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1907,3 +1907,8 @@ fn operator_in_pattern_size() {
19071907
fn correct_precedence_in_pattern_size() {
19081908
assert_parse!("let assert <<size, payload:size(size + 2 * 8)>> = <<>>");
19091909
}
1910+
1911+
#[test]
1912+
fn private_opaque_type_is_parsed() {
1913+
assert_parse_module!("opaque type Wibble { Wobble }");
1914+
}

compiler-core/src/type_/error.rs

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -635,6 +635,19 @@ pub enum Error {
635635
NonUtf8StringAssignmentInBitArray {
636636
location: SrcSpan,
637637
},
638+
639+
/// This happens when a private type is marked as opaque. Only public types
640+
/// can be opaque.
641+
///
642+
/// ```gleam
643+
/// opaque type Wibble {
644+
/// Wobble
645+
/// }
646+
/// ```
647+
///
648+
PrivateOpaqueType {
649+
location: SrcSpan,
650+
},
638651
}
639652

640653
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
@@ -1198,8 +1211,8 @@ impl Error {
11981211
| Error::IntOperatorOnFloats { location, .. }
11991212
| Error::StringConcatenationWithAddInt { location }
12001213
| Error::DoubleVariableAssignmentInBitArray { location }
1201-
| Error::NonUtf8StringAssignmentInBitArray { location } => location.start,
1202-
1214+
| Error::NonUtf8StringAssignmentInBitArray { location }
1215+
| Error::PrivateOpaqueType { location } => location.start,
12031216
Error::UnknownLabels { unknown, .. } => {
12041217
unknown.iter().map(|(_, s)| s.start).min().unwrap_or(0)
12051218
}

compiler-core/src/type_/tests/errors.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3283,3 +3283,14 @@ pub fn main() {
32833283
"
32843284
);
32853285
}
3286+
3287+
#[test]
3288+
fn private_opaque_type() {
3289+
assert_module_error!(
3290+
"
3291+
opaque type Wibble {
3292+
Wobble
3293+
}
3294+
"
3295+
);
3296+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
---
2+
source: compiler-core/src/type_/tests/errors.rs
3+
expression: "\nopaque type Wibble {\n Wobble\n}\n"
4+
---
5+
----- SOURCE CODE
6+
7+
opaque type Wibble {
8+
Wobble
9+
}
10+
11+
12+
----- ERROR
13+
error: Private opaque type
14+
┌─ /src/one/two.gleam:2:1
15+
16+
2opaque type Wibble {
17+
│ ^^^^^^ You can safely remove this.
18+
19+
Only a public type can be opaque.

0 commit comments

Comments
 (0)