Skip to content

Commit 421b979

Browse files
authored
Update proposal.md
Make it not be an error to omit an `override` modifier, and leave that to the existing lint.
1 parent 7fca2e9 commit 421b979

File tree

1 file changed

+26
-20
lines changed

1 file changed

+26
-20
lines changed

working/1610 - override/proposal.md

Lines changed: 26 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,9 @@ _The `override` is in `dart:core` rather than `package:meta`, and has been since
1414

1515
## Proposal
1616

17-
The simplest solution is to make `override` a built-in identifier and allow it as a modifier on instance member declarations. Example:
17+
The proposed solution is to make `override` a built-in identifier and allow it syntactically as a modifier on all `class`, `mixin`, `mixin class` and `enum` instance member declarations.
18+
19+
Example:
1820

1921
```dart
2022
class Foo {
@@ -23,52 +25,56 @@ class Foo {
2325
}
2426
```
2527

26-
Then we make it a compile-time error to not have the `override` modifier on something which overrides, and a compile-time error to have it on something which doesn't override.
28+
We make it a compile-time error to have the `override` modifier on a declaration which does not “override” any superclass or superinterface member declaration.
29+
30+
There is no error for *not* having an `override` modifier on a declaration which does override a superclass member. The `annotate_overrides` lint will instead be an opt-in way to catch those, the same way it does today for the `@override` annotation.
2731

2832
The modifier goes before all other modifiers (including `external`). It refers to the context and to the declaration itself, so it feels fitting to put it "around" the entire declaration, more than those modifiers which merely change implementation details. The formatter can choose to put it on a line of its own, for maximum backwards compatibility, but that's easily configurable.
2933

30-
When applied to a non-final instance variable, we lose some precision. We propose no syntax to make the override apply to only the setter or the getter of a variable declaration, but it's possible for the super-interfaces to contain only a getter or a setter. In that case we allow and require the `override` modifier if the declaration overrides at least a getter or a setter from a super-interface.
34+
When applied to a non-final instance variable, we lose some precision, but since we cannot tell whether the setter or the getter is intended to override. It’s accepted to have the `override` modifier as long as at least one of them overrides a superinterface member.
3135

3236
In summary:
3337

3438
* We make `override` a built-in identifier.
35-
* We allow `override` to precede any instance member declaration, as the first modifier of the declaration. *(It's a compile-time error if `override` is applied to a non-instance member declaration)*.
36-
* We make it a compile-time error if:
37-
* A non-variable instance member declaration, or an instance variable declaration which introduces only a getter, with name *n* in a class declaration *C* has an `override` modifier, and no super-interface of *C* declares a member named *n*.
38-
* An instance variable declaration with name *n* in a class declaration *C*, which introduces both a getter and a setter, has an `override` modifier, and no super-interface of *C* declares a member named *n* or a setter name *n=*.
39-
* A non-variable instance member declaration, or an instance variable declaration which introduces only a getter, with name *n* in a class declaration *C* **does not** have an `override` modifier, and any super-interface of *C* declares a member named *n*.
40-
* An instance variable declaration with name *n* in a class declaration *C*, which introduces both a getter and a setter, **does not** have an `override` modifier, and any super-interface of *C* declares a member named *n* or a setter name *n=*.
39+
* We allow `override` to precede any `class`, `mixin class`, `mixin` or `enum` instance member declaration, as the first modifier of the declaration. (It's a **compile-time error** if `override` is applied to a non-instance member declaration, or to an instance member declaration of an `extension` — and maybe eventually an `inline class` — declaration.)
40+
* We make it a **compile-time error** if:
41+
* A non-variable instance member declaration, or an instance variable declaration which introduces only a getter, with name *n* in a declaration *C* has an `override` modifier, and no super-interface of *C* has a member named *n*.
42+
* An instance variable declaration with name *n* in a class declaration *C*, which introduces both a getter and a setter, has an `override` modifier, and no super-interface of *C* declares a member with base name *n*.
4143

4244
## Migration
4345

44-
This is a breaking change. It introduces new syntax, and requires you to use that new syntax. As such, all code must be migrated to the language version introducing the feature.
46+
This is a non-breaking change. An absence of an `override` modifier is not an error, and no existing code contains any `override` modifiers, so nothing breaks. It introduces new syntax, and *allows* you to use that new syntax to introduce errors.
4547

46-
Migration is easily automatable: Add `override` to all declarations which need it, remove existing occurrences of`@override` . Which declarations need an `override` can be determined entirely from declarations and name resolution, without even having to understand types or interface signatures. A super-interface declares a member with a name if the interface declaration has a declaration with that name, or if its transitive super-interfaces declares a member of that name.
48+
Migration from `@override` to `override` is easily automatable: Add `override` to all declarations which currently have an `@override` annotation, and remove the existing occurrences of`@override` .
49+
50+
That migration *can* be breaking, if it introduces an `override` modifier on a declaration which doesn’t actually override a superinterface declaration. That code would have an `@override` annotation today and would get an analyzer warning for not overriding anything. Any code which is actually migrated is likely to be maintained, and therefore very likely will *not* have such warnings.
4751

4852
## Tool support
4953

50-
Every tool needs to support the new syntax, and compilers and the analyzer needs to support the new compile-time errors. That parts should be mostly uncontroversial.
54+
Every tool needs to support the new syntax, and compilers and the analyzer needs to support the new compile-time errors. The analyzer would treat `override` very similarly to `@override`, except that the `override_non_override` warning would become a language error. In the longer run, the front-end should able to handle the checking completely generally, so that the backends, and possibly analyzer, won’t need to do anything. That part should be mostly uncontroversial.
5155

5256
### Formatter
5357

54-
The formatter needs to decide how to format the new modifier. We propose to keep it on a line by itself, like the current annotation. That'll cause minimal changes to existing code (you literally just remove the `@` from `@override`).
58+
The formatter needs to decide how to format the new modifier. We propose to keep it on a line by itself, like the current annotation. That'll cause minimal changes to existing code (you literally just remove the `@` from `@override`, possibly move it down if there are more annotations on the same declaration).
5559

5660
Since the modifier only applies to instance members, there are no complicated cases to consider.
5761

5862
### Migration tool
5963

6064
The migration tool should understand the rules, insert `override` and remove `@override` annotations when migrating.
6165

66+
That could also be made a fix for `dart fix`, with a corresponding warning of “Use override instead of @override”, since migration doesn’t have to happen immediately when switching to the new language version which allows the `override` modifier.
67+
6268
### IDE integration/analysis server
6369

6470
The IDE integration should offer quick-fixes for:
6571

6672
* Adding an `override` modifier to a declaration which needs it.
6773
* Removing an `override` modifier from a declaration which doesn't need it.
6874
* Renaming a declaration with an `override` modifier which doesn't need it, if there is possibly misspelled super-interface member that it was likely intending to override.
69-
* Renaming a declaration without an override modifier which clashes with a super-interface member of the same name, especially if the signatures don't match.
75+
* Renaming a declaration without an `override` modifier which clashes with a super-interface member of the same name, especially if the signatures don't match.
7076

71-
Further, it would make sense to auto-complete a cursor right after the word `override` (in a potentially valid position) with signatures for super-interface members. Maybe even allow initialisms of the name, so a cursor after `override fBZ` could offer completing to `override int fooBarZip()` if a super-interface declares that signature, and restrict the options to super-interface signatures after an `override`.
77+
Further, it would make sense to prioritize auto-complete after the word `override` (in a potentially valid position) to signatures for super-interface members. Maybe even allow initialisms of the name, so a cursor after `override fBZ` could offer completing to `override int fooBarZip()` if a super-interface declares that signature, and restrict the options to super-interface signatures after an `override`. An auto-complete of `fooBarZip()` without a leading `override` should insert the `override` if it would be valid and the project has the `annotate_overrides` lint enabled.
7278

7379
## Other languages
7480

@@ -84,11 +90,11 @@ The C++11 language added `override` as something you can write on virtual member
8490

8591
### Kotlin
8692

87-
Kotlin requires `override` on overriding members. It is equivalent to Dart with the lint, or this feature as written.
93+
Kotlin requires `override` on overriding members. It is equivalent to Dart with the lint.
8894

8995
Kotlin also allows you to seal a method against overriding by adding `final`. A plain `final` method is effectively non-virtual, a `final override` method just prevents further overrides.
9096

91-
Dart does not have `final` declarations. Adding them is an interesting possibility, but requires some extra thought because Dart doesn't distinguish between interfaces and classes. It's not an obvious addition to the `override` feature, more like a separate "sealing" feature of its own.
97+
Dart does not have `final` declarations. Adding them is an interesting possibility, but requires some extra thought because Dart doesn't always distinguish between interfaces and classes. It's not an obvious addition to the `override` feature, more like a separate "sealing" feature of its own.
9298

9399
### C#
94100

@@ -118,7 +124,7 @@ The `^` is intended to "point to the supertype". The same completions could be o
118124

119125
We currently allow you to omit types from instance member declarations, both return type and parameter types. If you override something, you will inherit types from that something. If not, you default to `dynamic`.
120126

121-
With the `override` feature, we could require you to write types on all non-overriding members, as a step towards "no implicit dynamic". Or we could do that later, as part of a more coherent "no implicit dynamic" feature.
127+
With the `override` feature, we could require you to write types on all non-overriding members, as a step towards "no implicit dynamic". Or we could do that later, as part of a more coherent "no implicit dynamic" feature. Since `override` is optional, requiring it in order to get “super-interface signature type inheritance” would be breaking.
122128

123129
## Interaction with future language features
124130

@@ -132,7 +138,7 @@ One such syntax proposal could be:
132138
int foo {get; set}
133139
```
134140

135-
which is equivalent to the current `int foo;` in that it introduces implicit default implementations for `get i` and `set i`.
141+
which is equivalent to the current `int foo;` in that it introduces implicit default implementations for `get foo` and `set foo`.
136142

137143
Something like `int foo {get}` would then be equivalent to `final int foo;` (getter only, no setter, can only be set by initializer), and you can write custom implementations as:
138144

@@ -149,7 +155,7 @@ In either case, with explicit declarations for the default getter and setter, we
149155
int foo {override get; set}
150156
```
151157

152-
would mean overriding a getter, and not a setter. That's something we can't currently declare for fields.
158+
would mean overriding a getter, and not a setter (and getting an error if there is no getter to override, but there is a setter). That's something we can't currently declare for fields.
153159

154160
All in all, the `override` feature seems to interact *positively* with better getter/setter declarations.
155161

0 commit comments

Comments
 (0)