Skip to content

Add 'late' as a feature for final variables #2071

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 9 commits into from
Nov 14, 2019
Merged
3 changes: 3 additions & 0 deletions lib/src/model/field.dart
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,9 @@ class Field extends ModelElement
return field.isFinal;
}

@override
bool get isLate => isFinal && field.isLate;

@override
bool get isInherited => _isInherited;

Expand Down
4 changes: 4 additions & 0 deletions lib/src/model/model_element.dart
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ const Map<String, int> featureOrder = {
'read / write': 1,
'covariant': 2,
'final': 2,
'late': 2,
'inherited': 3,
'inherited-getter': 3,
'inherited-setter': 3,
Expand Down Expand Up @@ -499,6 +500,7 @@ abstract class ModelElement extends Canonicalization
// const and static are not needed here because const/static elements get
// their own sections in the doc.
if (isFinal) allFeatures.add('final');
if (isLate) allFeatures.add('late');
return allFeatures;
}

Expand Down Expand Up @@ -897,6 +899,8 @@ abstract class ModelElement extends Canonicalization

bool get isFinal => false;

bool get isLate => false;

bool get isLocalElement => element is LocalElement;

bool get isPropertyAccessor => element is PropertyAccessorElement;
Expand Down
3 changes: 3 additions & 0 deletions lib/src/model/top_level_variable.dart
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,9 @@ class TopLevelVariable extends ModelElement
return _variable.isFinal;
}

@override
bool get isLate => isFinal && _variable.isLate;

@override
String get kind => isConst ? 'top-level constant' : 'top-level property';

Expand Down
27 changes: 25 additions & 2 deletions test/model_special_cases_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -36,19 +36,42 @@ void main() {
.firstWhere((lib) => lib.name == 'late_final_without_initializer');
});

test('Late finals test', () {
test('Late final class member test', () {
Class c = lateFinalWithoutInitializer.allClasses
.firstWhere((c) => c.name == 'C');
Field a = c.allFields.firstWhere((f) => f.name == 'a');
Field b = c.allFields.firstWhere((f) => f.name == 'b');
Field cField = c.allFields.firstWhere((f) => f.name == 'cField');
Field dField = c.allFields.firstWhere((f) => f.name == 'dField');
// If nnbd isn't enabled, fields named 'late' come back from the analyzer.

// If nnbd isn't enabled, fields named 'late' come back from the analyzer
// instead of setting up 'isLate'.
expect(c.allFields.any((f) => f.name == 'late'), isFalse);

expect(a.modelType.returnType.name, equals('dynamic'));
expect(a.isLate, isTrue);
expect(a.features, contains('late'));

expect(b.modelType.returnType.name, equals('int'));
expect(b.isLate, isTrue);
expect(b.features, contains('late'));

expect(cField.modelType.returnType.name, equals('dynamic'));
expect(cField.isLate, isTrue);
expect(cField.features, contains('late'));

expect(dField.modelType.returnType.name, equals('double'));
expect(dField.isLate, isTrue);
expect(dField.features, contains('late'));
});

test('Late final top level variables', () {
TopLevelVariable initializeMe = lateFinalWithoutInitializer
.publicProperties
.firstWhere((v) => v.name == 'initializeMe');
expect(initializeMe.modelType.returnType.name, equals('String'));
expect(initializeMe.isLate, isTrue);
expect(initializeMe.features, contains('late'));
});
});

Expand Down
32 changes: 30 additions & 2 deletions test/model_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2513,6 +2513,7 @@ String topLevelFunction(int param1, bool param2, Cool coolBeans,
Field explicitGetterSetter;
Field explicitNonDocumentedInBaseClassGetter;
Field documentedPartialFieldInSubclassOnly;
Field finalProperty;
Field ExtraSpecialListLength;
Field aProperty;
Field covariantField, covariantSetter;
Expand All @@ -2539,6 +2540,8 @@ String topLevelFunction(int param1, bool param2, Cool coolBeans,
(e) => e.name == 'explicitNonDocumentedInBaseClassGetter');
documentedPartialFieldInSubclassOnly = UnusualProperties.allModelElements
.firstWhere((e) => e.name == 'documentedPartialFieldInSubclassOnly');
finalProperty = UnusualProperties.allModelElements
.firstWhere((e) => e.name == 'finalProperty');

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

test('is not final', () {
test('is final only when appropriate', () {
expect(f1.isFinal, isFalse);
expect(finalProperty.isFinal, isTrue);
expect(finalProperty.isLate, isFalse);
expect(finalProperty.features, contains('final'));
expect(finalProperty.features, isNot(contains('late')));
expect(onlySetter.isFinal, isFalse);
expect(onlySetter.features, isNot(contains('final')));
expect(onlySetter.features, isNot(contains('late')));
expect(dynamicGetter.isFinal, isFalse);
expect(dynamicGetter.features, isNot(contains('final')));
expect(dynamicGetter.features, isNot(contains('late')));
});

test('is not static', () {
Expand Down Expand Up @@ -2885,11 +2898,13 @@ String topLevelFunction(int param1, bool param2, Cool coolBeans,
TopLevelVariable setAndGet, mapWithDynamicKeys;
TopLevelVariable nodocGetter, nodocSetter;
TopLevelVariable complicatedReturn;
TopLevelVariable importantComputations;
TopLevelVariable meaningOfLife, importantComputations;

setUpAll(() {
v = exLibrary.properties.firstWhere((p) => p.name == 'number');
v3 = exLibrary.properties.firstWhere((p) => p.name == 'y');
meaningOfLife =
fakeLibrary.properties.firstWhere((v) => v.name == 'meaningOfLife');
importantComputations = fakeLibrary.properties
.firstWhere((v) => v.name == 'importantComputations');
complicatedReturn = fakeLibrary.properties
Expand All @@ -2908,6 +2923,19 @@ String topLevelFunction(int param1, bool param2, Cool coolBeans,
.firstWhere((p) => p.name == 'mapWithDynamicKeys');
});

test('Verify that final and late show up (or not) appropriately', () {
expect(meaningOfLife.isFinal, isTrue);
expect(meaningOfLife.isLate, isFalse);
expect(meaningOfLife.features, contains('final'));
expect(meaningOfLife.features, isNot(contains('late')));
expect(justGetter.isFinal, isFalse);
expect(justGetter.features, isNot(contains('final')));
expect(justGetter.features, isNot(contains('late')));
expect(justSetter.isFinal, isFalse);
expect(justSetter.features, isNot(contains('final')));
expect(justSetter.features, isNot(contains('late')));
});

test(
'Verify that a map containing anonymous functions as values works correctly',
() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@

import 'dart:math';

late final String initializeMe;

class C {
late final a;
late final b = 0;
Expand Down