Skip to content

Commit 2a3a253

Browse files
Thoughts about errors, error recovery, and fixups for class modifiers (#2893)
I sent this in an email to some folks for comment, and the response was positive enough that it seemed worth including in the spec.
1 parent 8bf0623 commit 2a3a253

File tree

1 file changed

+133
-4
lines changed

1 file changed

+133
-4
lines changed

accepted/future-releases/class-modifiers/feature-specification.md

Lines changed: 133 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ Author: Bob Nystrom, Lasse Nielsen
44

55
Status: Accepted
66

7-
Version 1.5
7+
Version 1.6
88

99
Experiment flag: class-modifiers
1010

@@ -514,9 +514,7 @@ Many combinations don't make sense:
514514
* `sealed` types cannot be constructed so it's redundant to combine with
515515
`abstract`.
516516
* `sealed` types cannot be extended or implemented, so it's redundant to
517-
combine with `final`.
518-
* `sealed` types cannot be extended so it contradicts `base`.
519-
* `sealed` types cannot be implemented, so it contradicts `interface`.
517+
combine with `final`, `base`, or `interface`.
520518
* `sealed` types cannot be mixed in outside of their library, so it
521519
contradicts `mixin` on a class. *It's useful to allow `sealed` on a mixin
522520
declaration because the mixin can be applied within the same library.
@@ -933,8 +931,139 @@ as a mixin. If the class defines a generative constructor or extends anything
933931
other than `Object`, then it already cannot be used as a mixin and no change is
934932
needed.
935933

934+
## Implementation and documentation suggestions for usability
935+
936+
*This section is non-normative. It's a set of suggestions to implementation and
937+
documentation teams to help ensure that the feature is easy for users to use and
938+
discover.*
939+
940+
### Errors, error recovery, and fixups
941+
942+
First of all, to the extent that it's reasonably feasible to do so, we should
943+
try to make the parser understand that any time it sees a top level sequence of
944+
any of the keywords `sealed`, `abstract`, `final`, `interface`, `base`, `mixin`,
945+
or `class`, the user is trying to declare something class-like or mixin-like,
946+
even if they left out an important keyword, used conflicting keywords, or put
947+
keywords in the wrong order. That way we can issue errors whose IDE fixups will
948+
help the user clean up their class or mixin declaration, rather than just
949+
`unexpected {` or something. For example, this should be recognized by the
950+
parser as an attempt to make a mixin or class:
951+
952+
```dart
953+
interface sealed C {
954+
...
955+
}
956+
```
957+
958+
(The parser will obviously issue an error, but it should still fire the
959+
appropriate events to allow the analyzer to create a `ClassDeclaration` AST
960+
node, and it should analyze the things inside the curly braces as class
961+
members).
962+
963+
If the keywords aren't in the proper order (`sealed`/`abstract`, then
964+
`final`/`interface`/`base`, then `mixin`, then `class`), or if a keyword was
965+
repeated, the parser error should be on the first keyword token that's out of
966+
order or repeated, and the fixup should offer to fix the order by sorting and
967+
de-duplicating the keywords appropriately. So in the example above, the "wrong
968+
order" error should be on the keyword `sealed`, and the fixup should change it
969+
to `sealed interface`, which is still an error for other reasons, but is at
970+
least in the right order now.
971+
972+
With order and duplication out of the way, that leaves 127 possible combinations
973+
of the 7 keywords. The remaining error cases (and their associated IDE fixups)
974+
are:
975+
976+
- Did you say both `abstract` and `sealed`? Drop `abstract`; it’s redundant.
977+
Now there's only 95 possibilities.
978+
979+
- Did you say both `interface` and `final`? Drop `interface`; it’s redundant.
980+
Now there's only 71 possibilities.
981+
982+
- Did you say both `base` and `final`? Drop `base`; it’s redundant. Now
983+
there's only 59 possibilities.
984+
985+
- Did you say both `interface` and `base`? Say `final` instead. Now there's
986+
only 47 possibilities.
987+
988+
- Did you say neither `mixin` nor `class`? You have to pick one or the other or
989+
both. The fixup can probably safely assume you mean `class`. (Exception: if
990+
you just said `interface` and no other keywords, you probably mean `abstract
991+
class`). Now there's only 36 possibilities.
992+
993+
- Did you say both `sealed` and `final`? Drop `final`; it’s redundant. Now
994+
there's only 33 possibilities.
995+
996+
- Did you say both `sealed` and `base`? Drop `base`; it’s redundant. Now
997+
there's only 30 possibilities.
998+
999+
- Did you say both `sealed` and `interface`? Drop `interface`; it’s redundant.
1000+
Now there's only 27 possibilities.
1001+
1002+
- Did you say both `mixin` and `class`, as well as one of the following
1003+
keywords: `sealed`, `interface`, or `final`? Drop `class` and replace
1004+
`extends M` with `with M` wherever it appears in your library. Now there's
1005+
only 22 possibilities.
1006+
1007+
- Did you say both `abstract` and `mixin`, but not `class`? Drop `abstract`;
1008+
it’s redundant. Now we are down to the 18 permitted possibilities.
1009+
1010+
If we take this sort of approach, then users who don't love reading
1011+
documentation will be able to just experimentally string together combinations
1012+
of the keywords we've made available to them, and the errors and fixups will
1013+
guide them to something valid, and then they can play around and see the effect.
1014+
1015+
### Introducing the feature to users
1016+
1017+
If we assume that most users will have access to the IDE fixups noted above, it
1018+
suggests that a nice way to introduce the feature to folks would be to gloss
1019+
over what combinations are redundant or contradictory, and just tell them in
1020+
plain English what each keyword does. Users who love reading documentation can
1021+
read further and find out which combinations are prohibited; users who don't can
1022+
just try them out, and the IDE will train them which combinations are valid over
1023+
time. So the core of the feature becomes explainable in just seven lines, three
1024+
of which are just restatements of things the user was already familiar with.
1025+
Something like:
1026+
1027+
- `sealed` means "this type has a known set of direct subtypes, so switching on
1028+
it will require the switch to be exhaustive".
1029+
1030+
- `abstract` means "this type can't be constructed directly", but you already
1031+
knew that. It's only included in the list to help clarify that `abstract` is
1032+
one of the seven keywords users should try combining together at the top of
1033+
your declaration.
1034+
1035+
- `interface` means "this type can't be extended from outside this library".
1036+
1037+
- `base` means "this type can't be implemented from outside this library".
1038+
1039+
- `final` means "this type can neither be extended nor implemented from outside
1040+
this library".
1041+
1042+
- `mixin` means "this type can be used in mixin-like ways, i.e. it can appear in
1043+
the 'with' clause of other classes". Granted, this is kind of a circular
1044+
definition, but this explanation is intended for programmers familiar with
1045+
Dart 2.19, and they're already familiar with mixins.
1046+
1047+
- `class` means "this type can be used in class-like ways, i.e. it can be
1048+
extended, or constructed, unless otherwise forbidden". Again, this is a
1049+
circular definition, but our audience obviously already knows what classes
1050+
are. Including it in the list helps make it clear that we're putting mixins
1051+
and classes on equal footing, and helps clarify why `mixin class` is a
1052+
reasonable thing.
1053+
1054+
(Note that this list is deliberately in the order required by the grammar).
1055+
1056+
Obviously there are plenty of details left out of this description. But
1057+
hopefully it should be enough to get people started using the feature, and the
1058+
errors and fixups would help keep them on the rails.
1059+
9361060
## Changelog
9371061

1062+
1.6
1063+
1064+
- Add implementation suggestions about errors, error recovery, and fixups for
1065+
class modifiers.
1066+
9381067
1.5
9391068

9401069
- Fix mixin application grammar to match prose where `mixin` can't be applied

0 commit comments

Comments
 (0)