Skip to content

Commit 23a903e

Browse files
scheglovCommit Queue
authored andcommitted
DeCo. Build extension type primary constructors like regular ones.
Previously, the AST builder enforced strict constraints on extension type primary constructors during parsing, often discarding or transforming invalid parameters (e.g., multiple, optional, or named parameters) to force a valid representation field structure. This limited recovery and resulted in inaccurate element models for invalid code. This change updates the AST builder to parse and build elements for the primary constructor's formal parameters exactly as written. The validation logic ensuring a single, positional representation field has been moved to `ErrorVerifier`. To maintain the invariant that an extension type has a representation, a synthetic "recovery" representation field is now added to the element model if the parsed parameters do not provide a valid one. Bug: #61701 Change-Id: I37b7eaf2c085db6317b2c2b0bf35ff31423f9738 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/465180 Reviewed-by: Paul Berry <[email protected]> Reviewed-by: Brian Wilkerson <[email protected]> Reviewed-by: Johnni Winther <[email protected]> Commit-Queue: Konstantin Shcheglov <[email protected]>
1 parent 823ffa0 commit 23a903e

File tree

19 files changed

+8329
-973
lines changed

19 files changed

+8329
-973
lines changed

pkg/analysis_server/test/services/completion/dart/location/extension_type_declaration_test.dart

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,20 @@ class C0 {}
7272
suggestions
7373
C0
7474
kind: class
75+
covariant
76+
kind: keyword
77+
dynamic
78+
kind: keyword
79+
final
80+
kind: keyword
81+
super
82+
kind: keyword
83+
this
84+
kind: keyword
85+
var
86+
kind: keyword
87+
void
88+
kind: keyword
7589
''');
7690
}
7791

pkg/analysis_server/test/src/services/correction/fix/remove_comma_test.dart

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,9 +62,11 @@ f() {
6262

6363
Future<void> test_representationFieldTrailingComma() async {
6464
await resolveTestCode('''
65+
// @dart = 3.10
6566
extension type A(int i,) {}
6667
''');
6768
await assertHasFix('''
69+
// @dart = 3.10
6870
extension type A(int i) {}
6971
''');
7072
}

pkg/analyzer/api.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3652,6 +3652,7 @@ package:analyzer/dart/element/element.dart:
36523652
isExternal (getter: bool)
36533653
isOriginDeclaringFormalParameter (getter: bool)
36543654
isOriginEnumValues (getter: bool)
3655+
isOriginExtensionTypeRecoveryRepresentation (getter: bool)
36553656
isPromotable (getter: bool)
36563657
FieldFormalParameterElement (class extends Object implements FormalParameterElement):
36573658
new (constructor: FieldFormalParameterElement Function())

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1289,6 +1289,10 @@ abstract class FieldElement implements PropertyInducingElement {
12891289
/// and [isOriginDeclaringFormalParameter] are `false`.
12901290
bool get isOriginEnumValues;
12911291

1292+
/// Whether the field is an extension type representation field, created only
1293+
/// for recovery purposes, and does not correspond to a formal parameter.
1294+
bool get isOriginExtensionTypeRecoveryRepresentation;
1295+
12921296
/// Whether the field can be type promoted.
12931297
bool get isPromotable;
12941298
}

pkg/analyzer/lib/src/dart/analysis/driver.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,7 @@ testFineAfterLibraryAnalyzerHook;
108108
// TODO(scheglov): Clean up the list of implicitly analyzed files.
109109
class AnalysisDriver {
110110
/// The version of data format, should be incremented on every format change.
111-
static const int DATA_VERSION = 597;
111+
static const int DATA_VERSION = 598;
112112

113113
/// The number of exception contexts allowed to write. Once this field is
114114
/// zero, we stop writing any new exception contexts in this process.

pkg/analyzer/lib/src/dart/ast/ast.dart

Lines changed: 50 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -9015,12 +9015,51 @@ final class ExtensionTypeDeclarationImpl
90159015
@Deprecated('Use primaryConstructor instead')
90169016
@override
90179017
RepresentationDeclarationImpl get representation {
9018-
var formal = primaryConstructor.formalParameters.parameters.first;
9019-
formal as SimpleFormalParameterImpl;
9018+
Token syntheticName() {
9019+
return StringToken(TokenType.IDENTIFIER, '', 0);
9020+
}
9021+
9022+
TypeAnnotationImpl syntheticType() {
9023+
return NamedTypeImpl(
9024+
importPrefix: null,
9025+
name: StringToken(TokenType.IDENTIFIER, 'InvalidType', 0),
9026+
question: null,
9027+
typeArguments: null,
9028+
);
9029+
}
9030+
9031+
var parameters = primaryConstructor.formalParameters.parameters;
9032+
var formal = parameters.firstOrNull;
9033+
if (formal is DefaultFormalParameterImpl) {
9034+
formal = formal.parameter;
9035+
}
90209036

90219037
var representation = _representation;
90229038
if (representation == null) {
90239039
var constructorName = primaryConstructor.constructorName;
9040+
9041+
if (formal is DefaultFormalParameterImpl) {
9042+
formal = formal.parameter;
9043+
}
9044+
9045+
TypeAnnotationImpl fieldType;
9046+
Token fieldName;
9047+
List<AnnotationImpl> fieldMetadata;
9048+
9049+
if (formal is SimpleFormalParameterImpl) {
9050+
fieldType = formal.type ?? syntheticType();
9051+
fieldName = formal.name ?? syntheticName();
9052+
fieldMetadata = formal.metadata;
9053+
} else if (formal is FieldFormalParameterImpl) {
9054+
fieldType = formal.type ?? syntheticType();
9055+
fieldName = formal.name;
9056+
fieldMetadata = formal.metadata;
9057+
} else {
9058+
fieldType = syntheticType();
9059+
fieldName = formal?.name ?? syntheticName();
9060+
fieldMetadata = formal?.metadata ?? const [];
9061+
}
9062+
90249063
representation = RepresentationDeclarationImpl(
90259064
constructorName: constructorName != null
90269065
? RepresentationConstructorNameImpl(
@@ -9029,19 +9068,19 @@ final class ExtensionTypeDeclarationImpl
90299068
)
90309069
: null,
90319070
leftParenthesis: primaryConstructor.formalParameters.leftParenthesis,
9032-
fieldMetadata: formal.metadata,
9033-
fieldType: formal.type!,
9034-
fieldName: formal.name!,
9071+
fieldMetadata: fieldMetadata,
9072+
fieldType: fieldType,
9073+
fieldName: fieldName,
90359074
rightParenthesis: primaryConstructor.formalParameters.rightParenthesis,
90369075
);
90379076
_representation = _becomeParentOf(representation);
90389077
}
9039-
representation.fieldFragment =
9040-
(formal.declaredFragment as FieldFormalParameterFragmentImpl?)
9041-
?.element
9042-
.field
9043-
?.firstFragment;
9044-
representation.constructorFragment = declaredFragment?.constructors.first;
9078+
representation.constructorFragment = primaryConstructor.declaredFragment;
9079+
if (declaredFragment case var declaredFragment?) {
9080+
representation.fieldFragment = declaredFragment.fields
9081+
.where((f) => !f.isStatic)
9082+
.firstOrNull;
9083+
}
90459084
return representation;
90469085
}
90479086

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

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2874,6 +2874,12 @@ class FieldElementImpl extends PropertyInducingElementImpl
28742874
return _firstFragment.isOriginEnumValues;
28752875
}
28762876

2877+
@override
2878+
@trackedIncludedInId
2879+
bool get isOriginExtensionTypeRecoveryRepresentation {
2880+
return _firstFragment.isOriginExtensionTypeRecoveryRepresentation;
2881+
}
2882+
28772883
@override
28782884
@trackedIncludedInId
28792885
bool get isPromotable => _firstFragment.isPromotable;
@@ -8186,6 +8192,12 @@ enum Modifier {
81868192
/// Whether the field is the `values` field of an enum.
81878193
ORIGIN_ENUM_VALUES,
81888194

8195+
/// Indicates that the element is an extension type representation field,
8196+
/// created only for recovery purposes, and does not correspond to a formal
8197+
/// parameter.
8198+
ORIGIN_EXTENSION_TYPE_RECOVERY_REPRESENTATION,
8199+
8200+
81898201
/// Whether the property inducing element is from a getter or setter.
81908202
ORIGIN_GETTER_SETTER,
81918203

@@ -10612,6 +10624,7 @@ enum _FieldFragmentImplModifiers {
1061210624
isEnumConstant,
1061310625
isOriginDeclaringFormalParameter,
1061410626
isOriginEnumValues,
10627+
isOriginExtensionTypeRecoveryRepresentation,
1061510628
isPromotable,
1061610629
}
1061710630

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

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -260,6 +260,14 @@ mixin _FieldFragmentImplMixin {
260260
setModifier(Modifier.ORIGIN_ENUM_VALUES, value);
261261
}
262262

263+
bool get isOriginExtensionTypeRecoveryRepresentation {
264+
return hasModifier(Modifier.ORIGIN_EXTENSION_TYPE_RECOVERY_REPRESENTATION);
265+
}
266+
267+
set isOriginExtensionTypeRecoveryRepresentation(bool value) {
268+
setModifier(Modifier.ORIGIN_EXTENSION_TYPE_RECOVERY_REPRESENTATION, value);
269+
}
270+
263271
bool get isPromotable {
264272
return hasModifier(Modifier.PROMOTABLE);
265273
}

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -471,6 +471,10 @@ class SubstitutedFieldElementImpl extends SubstitutedVariableElementImpl
471471
@override
472472
bool get isOriginEnumValues => baseElement.isOriginEnumValues;
473473

474+
@override
475+
bool get isOriginExtensionTypeRecoveryRepresentation =>
476+
baseElement.isOriginExtensionTypeRecoveryRepresentation;
477+
474478
@override
475479
bool get isOriginGetterSetter => baseElement.isOriginGetterSetter;
476480

0 commit comments

Comments
 (0)