Skip to content

Commit 28962e1

Browse files
bwilkersoncommit-bot@chromium.org
authored andcommitted
Support static fields in extensions
Change-Id: Id7897485e23f39e45918f6029b43a75e5327e0a9 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/108764 Reviewed-by: Phil Quitslund <[email protected]> Reviewed-by: Konstantin Shcheglov <[email protected]> Commit-Queue: Brian Wilkerson <[email protected]>
1 parent 7cadd70 commit 28962e1

File tree

11 files changed

+323
-30
lines changed

11 files changed

+323
-30
lines changed

pkg/analyzer/lib/dart/element/element.dart

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1068,6 +1068,9 @@ abstract class ExtensionElement implements TypeParameterizedElement {
10681068
/// Return the type that is extended by this extension.
10691069
DartType get extendedType;
10701070

1071+
/// Return a list containing all of the fields declared in this extension.
1072+
List<FieldElement> get fields;
1073+
10711074
/// Return a list containing all of the methods declared in this extension.
10721075
List<MethodElement> get methods;
10731076

pkg/analyzer/lib/error/error.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -136,7 +136,7 @@ const List<ErrorCode> errorCodeValues = const [
136136
CompileTimeErrorCode.EXTENDS_DISALLOWED_CLASS,
137137
CompileTimeErrorCode.EXTENDS_NON_CLASS,
138138
CompileTimeErrorCode.EXTENSION_DECLARES_CONSTRUCTOR,
139-
CompileTimeErrorCode.EXTENSION_DECLARES_FIELD,
139+
CompileTimeErrorCode.EXTENSION_DECLARES_INSTANCE_FIELD,
140140
CompileTimeErrorCode.EXTRA_POSITIONAL_ARGUMENTS,
141141
CompileTimeErrorCode.EXTRA_POSITIONAL_ARGUMENTS_COULD_BE_NAMED,
142142
CompileTimeErrorCode.FIELD_INITIALIZED_BY_MULTIPLE_INITIALIZERS,

pkg/analyzer/lib/src/dart/element/element.dart

Lines changed: 223 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -4939,6 +4939,9 @@ class ExtensionElementImpl extends ElementImpl
49394939
/// this extension.
49404940
List<PropertyAccessorElement> _accessors;
49414941

4942+
/// A list containing all of the fields contained in this extension.
4943+
List<FieldElement> _fields;
4944+
49424945
/// A list containing all of the methods contained in this extension.
49434946
List<MethodElement> _methods;
49444947

@@ -4967,16 +4970,14 @@ class ExtensionElementImpl extends ElementImpl
49674970

49684971
if (linkedNode != null) {
49694972
if (linkedNode is ExtensionDeclaration) {
4970-
// TODO(brianwilkerson) Implement this.
4971-
// _createPropertiesAndAccessors();
4972-
// assert(_accessors != null);
4973-
// return _accessors;
4973+
_createPropertiesAndAccessors();
4974+
assert(_accessors != null);
4975+
return _accessors;
49744976
} else {
49754977
return _accessors = const [];
49764978
}
49774979
} else if (_unlinkedExtension != null) {
4978-
// TODO(brianwilkerson) Implement this.
4979-
// _resynthesizePropertyAccessors();
4980+
_resynthesizeFieldsAndPropertyAccessors();
49804981
}
49814982

49824983
return _accessors ??= const <PropertyAccessorElement>[];
@@ -5016,6 +5017,36 @@ class ExtensionElementImpl extends ElementImpl
50165017
_extendedType = extendedType;
50175018
}
50185019

5020+
@override
5021+
List<FieldElement> get fields {
5022+
if (_fields != null) {
5023+
return _fields;
5024+
}
5025+
5026+
if (linkedNode != null) {
5027+
if (linkedNode is ExtensionDeclaration) {
5028+
_createPropertiesAndAccessors();
5029+
assert(_fields != null);
5030+
return _fields;
5031+
} else {
5032+
return _fields = const [];
5033+
}
5034+
} else if (_unlinkedExtension != null) {
5035+
_resynthesizeFieldsAndPropertyAccessors();
5036+
}
5037+
5038+
return _fields ?? const <FieldElement>[];
5039+
}
5040+
5041+
@override
5042+
void set fields(List<FieldElement> fields) {
5043+
_assertNotResynthesized(_unlinkedExtension);
5044+
for (FieldElement field in fields) {
5045+
(field as FieldElementImpl).enclosingElement = this;
5046+
}
5047+
_fields = fields;
5048+
}
5049+
50195050
@override
50205051
bool get isSimplyBounded => true;
50215052

@@ -5155,6 +5186,183 @@ class ExtensionElementImpl extends ElementImpl
51555186
return AbstractClassElementImpl.getSetterFromAccessors(
51565187
setterName, accessors);
51575188
}
5189+
5190+
/// Create the accessors and fields when [linkedNode] is not `null`.
5191+
void _createPropertiesAndAccessors() {
5192+
assert(_accessors == null);
5193+
assert(_fields == null);
5194+
5195+
var context = enclosingUnit.linkedContext;
5196+
var accessorList = <PropertyAccessorElement>[];
5197+
var fieldList = <FieldElement>[];
5198+
5199+
var fields = context.getFields(linkedNode);
5200+
for (var field in fields) {
5201+
var name = field.name.name;
5202+
var fieldElement = FieldElementImpl.forLinkedNodeFactory(
5203+
this,
5204+
reference.getChild('@field').getChild(name),
5205+
field,
5206+
);
5207+
fieldList.add(fieldElement);
5208+
5209+
accessorList.add(fieldElement.getter);
5210+
if (fieldElement.setter != null) {
5211+
accessorList.add(fieldElement.setter);
5212+
}
5213+
}
5214+
5215+
var methods = context.getMethods(linkedNode);
5216+
for (var method in methods) {
5217+
var isGetter = method.isGetter;
5218+
var isSetter = method.isSetter;
5219+
if (!isGetter && !isSetter) continue;
5220+
5221+
var name = method.name.name;
5222+
var containerRef = isGetter
5223+
? reference.getChild('@getter')
5224+
: reference.getChild('@setter');
5225+
5226+
var accessorElement = PropertyAccessorElementImpl.forLinkedNode(
5227+
this,
5228+
containerRef.getChild(name),
5229+
method,
5230+
);
5231+
accessorList.add(accessorElement);
5232+
5233+
var fieldRef = reference.getChild('@field').getChild(name);
5234+
FieldElementImpl field = fieldRef.element;
5235+
if (field == null) {
5236+
field = new FieldElementImpl(name, -1);
5237+
fieldRef.element = field;
5238+
field.enclosingElement = this;
5239+
field.isSynthetic = true;
5240+
field.isFinal = isGetter;
5241+
field.isStatic = accessorElement.isStatic;
5242+
fieldList.add(field);
5243+
} else {
5244+
// TODO(brianwilkerson) Shouldn't this depend on whether there is a
5245+
// setter?
5246+
field.isFinal = false;
5247+
}
5248+
5249+
accessorElement.variable = field;
5250+
if (isGetter) {
5251+
field.getter = accessorElement;
5252+
} else {
5253+
field.setter = accessorElement;
5254+
}
5255+
}
5256+
5257+
_accessors = accessorList;
5258+
_fields = fieldList;
5259+
}
5260+
5261+
/// Resynthesize explicit fields and property accessors and fill [_fields] and
5262+
/// [_accessors] with explicit and implicit elements.
5263+
void _resynthesizeFieldsAndPropertyAccessors() {
5264+
assert(_fields == null);
5265+
assert(_accessors == null);
5266+
5267+
var unlinkedFields = _unlinkedExtension.fields;
5268+
var unlinkedExecutables = _unlinkedExtension.executables;
5269+
5270+
// Build explicit fields and implicit property accessors.
5271+
List<FieldElement> explicitFields;
5272+
List<PropertyAccessorElement> implicitAccessors;
5273+
var unlinkedFieldsLength = unlinkedFields.length;
5274+
if (unlinkedFieldsLength != 0) {
5275+
explicitFields = new List<FieldElement>(unlinkedFieldsLength);
5276+
implicitAccessors = <PropertyAccessorElement>[];
5277+
for (var i = 0; i < unlinkedFieldsLength; i++) {
5278+
var v = unlinkedFields[i];
5279+
FieldElementImpl field =
5280+
new FieldElementImpl.forSerializedFactory(v, this);
5281+
explicitFields[i] = field;
5282+
implicitAccessors.add(
5283+
new PropertyAccessorElementImpl_ImplicitGetter(field)
5284+
..enclosingElement = this);
5285+
if (!field.isConst && !field.isFinal) {
5286+
implicitAccessors.add(
5287+
new PropertyAccessorElementImpl_ImplicitSetter(field)
5288+
..enclosingElement = this);
5289+
}
5290+
}
5291+
} else {
5292+
explicitFields = const <FieldElement>[];
5293+
implicitAccessors = const <PropertyAccessorElement>[];
5294+
}
5295+
5296+
var unlinkedExecutablesLength = unlinkedExecutables.length;
5297+
var getterSetterCount = 0;
5298+
for (var i = 0; i < unlinkedExecutablesLength; i++) {
5299+
var e = unlinkedExecutables[i];
5300+
if (e.kind == UnlinkedExecutableKind.getter ||
5301+
e.kind == UnlinkedExecutableKind.setter) {
5302+
getterSetterCount++;
5303+
}
5304+
}
5305+
5306+
// Build explicit property accessors and implicit fields.
5307+
List<PropertyAccessorElement> explicitAccessors;
5308+
Map<String, FieldElementImpl> implicitFields;
5309+
if (getterSetterCount != 0) {
5310+
explicitAccessors = new List<PropertyAccessorElement>(getterSetterCount);
5311+
implicitFields = <String, FieldElementImpl>{};
5312+
var index = 0;
5313+
for (var i = 0; i < unlinkedExecutablesLength; i++) {
5314+
var e = unlinkedExecutables[i];
5315+
if (e.kind == UnlinkedExecutableKind.getter ||
5316+
e.kind == UnlinkedExecutableKind.setter) {
5317+
PropertyAccessorElementImpl accessor =
5318+
new PropertyAccessorElementImpl.forSerialized(e, this);
5319+
explicitAccessors[index++] = accessor;
5320+
// Create or update the implicit field.
5321+
String fieldName = accessor.displayName;
5322+
FieldElementImpl field = implicitFields[fieldName];
5323+
if (field == null) {
5324+
field = new FieldElementImpl(fieldName, -1);
5325+
implicitFields[fieldName] = field;
5326+
field.enclosingElement = this;
5327+
field.isSynthetic = true;
5328+
field.isFinal = e.kind == UnlinkedExecutableKind.getter;
5329+
field.isStatic = e.isStatic;
5330+
} else {
5331+
field.isFinal = false;
5332+
}
5333+
accessor.variable = field;
5334+
if (e.kind == UnlinkedExecutableKind.getter) {
5335+
field.getter = accessor;
5336+
} else {
5337+
field.setter = accessor;
5338+
}
5339+
}
5340+
}
5341+
} else {
5342+
explicitAccessors = const <PropertyAccessorElement>[];
5343+
implicitFields = const <String, FieldElementImpl>{};
5344+
}
5345+
5346+
// Combine explicit and implicit fields and property accessors.
5347+
if (implicitFields.isEmpty) {
5348+
_fields = explicitFields;
5349+
} else if (explicitFields.isEmpty) {
5350+
_fields = implicitFields.values.toList(growable: false);
5351+
} else {
5352+
_fields = <FieldElement>[]
5353+
..addAll(explicitFields)
5354+
..addAll(implicitFields.values);
5355+
}
5356+
if (explicitAccessors.isEmpty) {
5357+
_accessors = implicitAccessors;
5358+
} else if (implicitAccessors.isEmpty) {
5359+
_accessors = explicitAccessors;
5360+
} else {
5361+
_accessors = <PropertyAccessorElement>[]
5362+
..addAll(explicitAccessors)
5363+
..addAll(implicitAccessors);
5364+
}
5365+
}
51585366
}
51595367

51605368
/// A concrete implementation of a [FieldElement].
@@ -5185,7 +5393,7 @@ class FieldElementImpl extends PropertyInducingElementImpl
51855393
}
51865394

51875395
factory FieldElementImpl.forLinkedNodeFactory(
5188-
ClassElementImpl enclosing, Reference reference, AstNode linkedNode) {
5396+
ElementImpl enclosing, Reference reference, AstNode linkedNode) {
51895397
var context = enclosing.enclosingUnit.linkedContext;
51905398
if (context.shouldBeConstFieldElement(linkedNode)) {
51915399
return ConstFieldElementImpl.forLinkedNode(
@@ -5207,23 +5415,21 @@ class FieldElementImpl extends PropertyInducingElementImpl
52075415

52085416
/// Initialize using the given serialized information.
52095417
factory FieldElementImpl.forSerializedFactory(
5210-
UnlinkedVariable unlinkedVariable, ClassElementImpl enclosingClass) {
5418+
UnlinkedVariable unlinkedVariable, ElementImpl enclosingElement) {
52115419
if (unlinkedVariable.initializer?.bodyExpr != null &&
52125420
(unlinkedVariable.isConst ||
52135421
unlinkedVariable.isFinal &&
52145422
!unlinkedVariable.isStatic &&
5215-
enclosingClass._hasConstConstructor)) {
5423+
enclosingElement is ClassElementImpl &&
5424+
enclosingElement._hasConstConstructor)) {
52165425
return new ConstFieldElementImpl.forSerialized(
5217-
unlinkedVariable, enclosingClass);
5426+
unlinkedVariable, enclosingElement);
52185427
} else {
52195428
return new FieldElementImpl.forSerialized(
5220-
unlinkedVariable, enclosingClass);
5429+
unlinkedVariable, enclosingElement);
52215430
}
52225431
}
52235432

5224-
@override
5225-
ClassElement get enclosingElement => super.enclosingElement as ClassElement;
5226-
52275433
@override
52285434
bool get isCovariant {
52295435
if (linkedNode != null) {
@@ -5245,7 +5451,9 @@ class FieldElementImpl extends PropertyInducingElementImpl
52455451

52465452
@override
52475453
bool get isEnumConstant =>
5248-
enclosingElement != null && enclosingElement.isEnum && !isSynthetic;
5454+
enclosingElement is ClassElement &&
5455+
(enclosingElement as ClassElement).isEnum &&
5456+
!isSynthetic;
52495457

52505458
@override
52515459
bool get isStatic {

pkg/analyzer/lib/src/error/codes.dart

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1185,10 +1185,11 @@ class CompileTimeErrorCode extends ErrorCode {
11851185
/**
11861186
* No parameters.
11871187
*/
1188-
static const CompileTimeErrorCode EXTENSION_DECLARES_FIELD =
1189-
const CompileTimeErrorCode(
1190-
'EXTENSION_DECLARES_FIELD', "Extensions can't declare fields.",
1191-
correction: "Try removing the field declaration.");
1188+
static const CompileTimeErrorCode EXTENSION_DECLARES_INSTANCE_FIELD =
1189+
const CompileTimeErrorCode('EXTENSION_DECLARES_INSTANCE_FIELD',
1190+
"Extensions can't declare instance fields.",
1191+
correction:
1192+
"Try removing the field declaration or making it a static field.");
11921193

11931194
/**
11941195
* 12.14.2 Binding Actuals to Formals: It is a static warning if <i>m &lt;

pkg/analyzer/lib/src/fasta/ast_builder.dart

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -921,13 +921,14 @@ class AstBuilder extends StackListener {
921921
Token covariantKeyword = covariantToken;
922922
List<Annotation> metadata = pop();
923923
Comment comment = _findComment(metadata, beginToken);
924-
if (extensionDeclaration != null) {
924+
if (extensionDeclaration != null && staticToken == null) {
925925
// TODO(brianwilkerson) Decide how to handle constructor and field
926926
// declarations within extensions. They are invalid, but we might want to
927927
// resolve them in order to get navigation, search, etc.
928928
for (VariableDeclaration variable in variables) {
929929
errorReporter.errorReporter.reportErrorForNode(
930-
CompileTimeErrorCode.EXTENSION_DECLARES_FIELD, variable.name);
930+
CompileTimeErrorCode.EXTENSION_DECLARES_INSTANCE_FIELD,
931+
variable.name);
931932
}
932933
return;
933934
}

0 commit comments

Comments
 (0)