Skip to content

Commit d5f90cf

Browse files
authored
Add 'late' as a feature for final variables (#2071)
* Quality of life tooling changes for dartdoc * Add a CONTRIBUTING comment * Update appveyor call to grinder * Strip fragile checks from the sdk-analyzer test hack * Add 'late' as a feature for final variables * Review comments
1 parent 39680a3 commit d5f90cf

File tree

6 files changed

+67
-4
lines changed

6 files changed

+67
-4
lines changed

lib/src/model/field.dart

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,9 @@ class Field extends ModelElement
9292
return field.isFinal;
9393
}
9494

95+
@override
96+
bool get isLate => isFinal && field.isLate;
97+
9598
@override
9699
bool get isInherited => _isInherited;
97100

lib/src/model/model_element.dart

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ const Map<String, int> featureOrder = {
4343
'read / write': 1,
4444
'covariant': 2,
4545
'final': 2,
46+
'late': 2,
4647
'inherited': 3,
4748
'inherited-getter': 3,
4849
'inherited-setter': 3,
@@ -499,6 +500,7 @@ abstract class ModelElement extends Canonicalization
499500
// const and static are not needed here because const/static elements get
500501
// their own sections in the doc.
501502
if (isFinal) allFeatures.add('final');
503+
if (isLate) allFeatures.add('late');
502504
return allFeatures;
503505
}
504506

@@ -897,6 +899,8 @@ abstract class ModelElement extends Canonicalization
897899

898900
bool get isFinal => false;
899901

902+
bool get isLate => false;
903+
900904
bool get isLocalElement => element is LocalElement;
901905

902906
bool get isPropertyAccessor => element is PropertyAccessorElement;

lib/src/model/top_level_variable.dart

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,9 @@ class TopLevelVariable extends ModelElement
6565
return _variable.isFinal;
6666
}
6767

68+
@override
69+
bool get isLate => isFinal && _variable.isLate;
70+
6871
@override
6972
String get kind => isConst ? 'top-level constant' : 'top-level property';
7073

test/model_special_cases_test.dart

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,19 +36,42 @@ void main() {
3636
.firstWhere((lib) => lib.name == 'late_final_without_initializer');
3737
});
3838

39-
test('Late finals test', () {
39+
test('Late final class member test', () {
4040
Class c = lateFinalWithoutInitializer.allClasses
4141
.firstWhere((c) => c.name == 'C');
4242
Field a = c.allFields.firstWhere((f) => f.name == 'a');
4343
Field b = c.allFields.firstWhere((f) => f.name == 'b');
4444
Field cField = c.allFields.firstWhere((f) => f.name == 'cField');
4545
Field dField = c.allFields.firstWhere((f) => f.name == 'dField');
46-
// If nnbd isn't enabled, fields named 'late' come back from the analyzer.
46+
47+
// If nnbd isn't enabled, fields named 'late' come back from the analyzer
48+
// instead of setting up 'isLate'.
4749
expect(c.allFields.any((f) => f.name == 'late'), isFalse);
50+
4851
expect(a.modelType.returnType.name, equals('dynamic'));
52+
expect(a.isLate, isTrue);
53+
expect(a.features, contains('late'));
54+
4955
expect(b.modelType.returnType.name, equals('int'));
56+
expect(b.isLate, isTrue);
57+
expect(b.features, contains('late'));
58+
5059
expect(cField.modelType.returnType.name, equals('dynamic'));
60+
expect(cField.isLate, isTrue);
61+
expect(cField.features, contains('late'));
62+
5163
expect(dField.modelType.returnType.name, equals('double'));
64+
expect(dField.isLate, isTrue);
65+
expect(dField.features, contains('late'));
66+
});
67+
68+
test('Late final top level variables', () {
69+
TopLevelVariable initializeMe = lateFinalWithoutInitializer
70+
.publicProperties
71+
.firstWhere((v) => v.name == 'initializeMe');
72+
expect(initializeMe.modelType.returnType.name, equals('String'));
73+
expect(initializeMe.isLate, isTrue);
74+
expect(initializeMe.features, contains('late'));
5275
});
5376
});
5477

test/model_test.dart

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2513,6 +2513,7 @@ String topLevelFunction(int param1, bool param2, Cool coolBeans,
25132513
Field explicitGetterSetter;
25142514
Field explicitNonDocumentedInBaseClassGetter;
25152515
Field documentedPartialFieldInSubclassOnly;
2516+
Field finalProperty;
25162517
Field ExtraSpecialListLength;
25172518
Field aProperty;
25182519
Field covariantField, covariantSetter;
@@ -2539,6 +2540,8 @@ String topLevelFunction(int param1, bool param2, Cool coolBeans,
25392540
(e) => e.name == 'explicitNonDocumentedInBaseClassGetter');
25402541
documentedPartialFieldInSubclassOnly = UnusualProperties.allModelElements
25412542
.firstWhere((e) => e.name == 'documentedPartialFieldInSubclassOnly');
2543+
finalProperty = UnusualProperties.allModelElements
2544+
.firstWhere((e) => e.name == 'finalProperty');
25422545

25432546
isEmpty =
25442547
CatString.allInstanceFields.firstWhere((p) => p.name == 'isEmpty');
@@ -2774,8 +2777,18 @@ String topLevelFunction(int param1, bool param2, Cool coolBeans,
27742777
expect(constField.isConst, isTrue);
27752778
});
27762779

2777-
test('is not final', () {
2780+
test('is final only when appropriate', () {
27782781
expect(f1.isFinal, isFalse);
2782+
expect(finalProperty.isFinal, isTrue);
2783+
expect(finalProperty.isLate, isFalse);
2784+
expect(finalProperty.features, contains('final'));
2785+
expect(finalProperty.features, isNot(contains('late')));
2786+
expect(onlySetter.isFinal, isFalse);
2787+
expect(onlySetter.features, isNot(contains('final')));
2788+
expect(onlySetter.features, isNot(contains('late')));
2789+
expect(dynamicGetter.isFinal, isFalse);
2790+
expect(dynamicGetter.features, isNot(contains('final')));
2791+
expect(dynamicGetter.features, isNot(contains('late')));
27792792
});
27802793

27812794
test('is not static', () {
@@ -2885,11 +2898,13 @@ String topLevelFunction(int param1, bool param2, Cool coolBeans,
28852898
TopLevelVariable setAndGet, mapWithDynamicKeys;
28862899
TopLevelVariable nodocGetter, nodocSetter;
28872900
TopLevelVariable complicatedReturn;
2888-
TopLevelVariable importantComputations;
2901+
TopLevelVariable meaningOfLife, importantComputations;
28892902

28902903
setUpAll(() {
28912904
v = exLibrary.properties.firstWhere((p) => p.name == 'number');
28922905
v3 = exLibrary.properties.firstWhere((p) => p.name == 'y');
2906+
meaningOfLife =
2907+
fakeLibrary.properties.firstWhere((v) => v.name == 'meaningOfLife');
28932908
importantComputations = fakeLibrary.properties
28942909
.firstWhere((v) => v.name == 'importantComputations');
28952910
complicatedReturn = fakeLibrary.properties
@@ -2908,6 +2923,19 @@ String topLevelFunction(int param1, bool param2, Cool coolBeans,
29082923
.firstWhere((p) => p.name == 'mapWithDynamicKeys');
29092924
});
29102925

2926+
test('Verify that final and late show up (or not) appropriately', () {
2927+
expect(meaningOfLife.isFinal, isTrue);
2928+
expect(meaningOfLife.isLate, isFalse);
2929+
expect(meaningOfLife.features, contains('final'));
2930+
expect(meaningOfLife.features, isNot(contains('late')));
2931+
expect(justGetter.isFinal, isFalse);
2932+
expect(justGetter.features, isNot(contains('final')));
2933+
expect(justGetter.features, isNot(contains('late')));
2934+
expect(justSetter.isFinal, isFalse);
2935+
expect(justSetter.features, isNot(contains('final')));
2936+
expect(justSetter.features, isNot(contains('late')));
2937+
});
2938+
29112939
test(
29122940
'Verify that a map containing anonymous functions as values works correctly',
29132941
() {

testing/test_package_experiments/lib/late_final_without_initializer.dart

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44

55
import 'dart:math';
66

7+
late final String initializeMe;
8+
79
class C {
810
late final a;
911
late final b = 0;

0 commit comments

Comments
 (0)