Skip to content
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

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

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

@override
bool get isLate {
if (isFinal) return field.isLate;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider return isFinal && field.isLate;

Copy link
Contributor Author

@jcollins-g jcollins-g Nov 14, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

changed to bool get isLate => isFinal && field.isLate;

return false;
}

@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
6 changes: 6 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,12 @@ class TopLevelVariable extends ModelElement
return _variable.isFinal;
}

@override
bool get isLate {
if (isFinal) return _variable.isLate;
return false;
}

@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