fix indexed super constructor
This fixes a bug where the AstBuilder fails
given a constructor of the form:
class C {
C() : super()[];
}
In the process, 2 buildInitializer methods were extracted from
the AstBuilder.endInitializers method.
Fix https://github.com/dart-lang/sdk/issues/37285
Change-Id: Icacf28b2ed0eff9b7168c97ee0c03d78e5fcd68b
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/106500
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
Commit-Queue: Dan Rubel <danrubel@google.com>
diff --git a/pkg/analyzer/lib/src/fasta/ast_builder.dart b/pkg/analyzer/lib/src/fasta/ast_builder.dart
index abfe057..955d11e 100644
--- a/pkg/analyzer/lib/src/fasta/ast_builder.dart
+++ b/pkg/analyzer/lib/src/fasta/ast_builder.dart
@@ -327,6 +327,116 @@
}
}
+ ConstructorInitializer buildInitializer(Object initializerObject) {
+ if (initializerObject is FunctionExpressionInvocation) {
+ Expression function = initializerObject.function;
+ if (function is SuperExpression) {
+ return ast.superConstructorInvocation(
+ function.superKeyword, null, null, initializerObject.argumentList);
+ } else {
+ return ast.redirectingConstructorInvocation(
+ (function as ThisExpression).thisKeyword,
+ null,
+ null,
+ initializerObject.argumentList);
+ }
+ }
+
+ if (initializerObject is MethodInvocation) {
+ Expression target = initializerObject.target;
+ if (target is SuperExpression) {
+ return ast.superConstructorInvocation(
+ target.superKeyword,
+ initializerObject.operator,
+ initializerObject.methodName,
+ initializerObject.argumentList);
+ }
+ if (target is ThisExpression) {
+ return ast.redirectingConstructorInvocation(
+ target.thisKeyword,
+ initializerObject.operator,
+ initializerObject.methodName,
+ initializerObject.argumentList);
+ }
+ return buildInitializerTargetExpressionRecovery(
+ target, initializerObject);
+ }
+
+ if (initializerObject is PropertyAccess) {
+ return buildInitializerTargetExpressionRecovery(
+ initializerObject.target, initializerObject);
+ }
+
+ if (initializerObject is AssignmentExpression) {
+ Token thisKeyword;
+ Token period;
+ SimpleIdentifier fieldName;
+ Expression left = initializerObject.leftHandSide;
+ if (left is PropertyAccess) {
+ Expression target = left.target;
+ if (target is ThisExpression) {
+ thisKeyword = target.thisKeyword;
+ period = left.operator;
+ } else {
+ assert(target is SuperExpression);
+ // Recovery:
+ // Parser has reported FieldInitializedOutsideDeclaringClass.
+ }
+ fieldName = left.propertyName;
+ } else if (left is SimpleIdentifier) {
+ fieldName = left;
+ } else {
+ // Recovery:
+ // Parser has reported invalid assignment.
+ SuperExpression superExpression = left;
+ fieldName = ast.simpleIdentifier(superExpression.superKeyword);
+ }
+ return ast.constructorFieldInitializer(thisKeyword, period, fieldName,
+ initializerObject.operator, initializerObject.rightHandSide);
+ }
+
+ if (initializerObject is AssertInitializer) {
+ return initializerObject;
+ }
+
+ if (initializerObject is IndexExpression) {
+ return buildInitializerTargetExpressionRecovery(
+ initializerObject.target, initializerObject);
+ }
+
+ throw new UnsupportedError('unsupported initializer:'
+ ' ${initializerObject.runtimeType} :: $initializerObject');
+ }
+
+ AstNode buildInitializerTargetExpressionRecovery(
+ Expression target, Object initializerObject) {
+ if (target is FunctionExpressionInvocation) {
+ Expression targetFunct = target.function;
+ if (targetFunct is SuperExpression) {
+ // TODO(danrubel): Consider generating this error in the parser
+ // This error is also reported in the body builder
+ handleRecoverableError(messageInvalidSuperInInitializer,
+ targetFunct.superKeyword, targetFunct.superKeyword);
+ return ast.superConstructorInvocation(
+ targetFunct.superKeyword, null, null, target.argumentList);
+ }
+ if (targetFunct is ThisExpression) {
+ // TODO(danrubel): Consider generating this error in the parser
+ // This error is also reported in the body builder
+ handleRecoverableError(messageInvalidThisInInitializer,
+ targetFunct.thisKeyword, targetFunct.thisKeyword);
+ return ast.redirectingConstructorInvocation(
+ targetFunct.thisKeyword, null, null, target.argumentList);
+ }
+ throw new UnsupportedError('unsupported initializer:'
+ ' ${initializerObject.runtimeType} :: $initializerObject'
+ ' %% targetFunct : ${targetFunct.runtimeType} :: $targetFunct');
+ }
+ throw new UnsupportedError('unsupported initializer:'
+ ' ${initializerObject.runtimeType} :: $initializerObject'
+ ' %% target : ${target.runtimeType} :: $target');
+ }
+
void checkFieldFormalParameters(FormalParameterList parameters) {
if (parameters?.parameters != null) {
parameters.parameters.forEach((FormalParameter param) {
@@ -1221,122 +1331,12 @@
var initializers = <ConstructorInitializer>[];
for (Object initializerObject in initializerObjects) {
- if (initializerObject is FunctionExpressionInvocation) {
- Expression function = initializerObject.function;
- if (function is SuperExpression) {
- initializers.add(ast.superConstructorInvocation(function.superKeyword,
- null, null, initializerObject.argumentList));
- } else {
- initializers.add(ast.redirectingConstructorInvocation(
- (function as ThisExpression).thisKeyword,
- null,
- null,
- initializerObject.argumentList));
- }
- } else if (initializerObject is MethodInvocation) {
- Expression target = initializerObject.target;
- if (target is SuperExpression) {
- initializers.add(ast.superConstructorInvocation(
- target.superKeyword,
- initializerObject.operator,
- initializerObject.methodName,
- initializerObject.argumentList));
- } else if (target is ThisExpression) {
- initializers.add(ast.redirectingConstructorInvocation(
- target.thisKeyword,
- initializerObject.operator,
- initializerObject.methodName,
- initializerObject.argumentList));
- } else {
- // Recovery: Invalid initializer
- if (target is FunctionExpressionInvocation) {
- var targetFunct = target.function;
- if (targetFunct is SuperExpression) {
- initializers.add(ast.superConstructorInvocation(
- targetFunct.superKeyword, null, null, target.argumentList));
- // TODO(danrubel): Consider generating this error in the parser
- // This error is also reported in the body builder
- handleRecoverableError(messageInvalidSuperInInitializer,
- targetFunct.superKeyword, targetFunct.superKeyword);
- } else if (targetFunct is ThisExpression) {
- initializers.add(ast.redirectingConstructorInvocation(
- targetFunct.thisKeyword, null, null, target.argumentList));
- // TODO(danrubel): Consider generating this error in the parser
- // This error is also reported in the body builder
- handleRecoverableError(messageInvalidThisInInitializer,
- targetFunct.thisKeyword, targetFunct.thisKeyword);
- } else {
- throw new UnsupportedError(
- 'unsupported initializer $initializerObject');
- }
- } else {
- throw new UnsupportedError(
- 'unsupported initializer $initializerObject');
- }
- }
- } else if (initializerObject is AssignmentExpression) {
- Token thisKeyword;
- Token period;
- SimpleIdentifier fieldName;
- Expression left = initializerObject.leftHandSide;
- if (left is PropertyAccess) {
- Expression target = left.target;
- if (target is ThisExpression) {
- thisKeyword = target.thisKeyword;
- period = left.operator;
- } else {
- assert(target is SuperExpression);
- // Recovery:
- // Parser has reported FieldInitializedOutsideDeclaringClass.
- }
- fieldName = left.propertyName;
- } else if (left is SimpleIdentifier) {
- fieldName = left;
- } else {
- // Recovery:
- // Parser has reported invalid assignment.
- SuperExpression superExpression = left;
- fieldName = ast.simpleIdentifier(superExpression.superKeyword);
- }
- initializers.add(ast.constructorFieldInitializer(
- thisKeyword,
- period,
- fieldName,
- initializerObject.operator,
- initializerObject.rightHandSide));
- } else if (initializerObject is AssertInitializer) {
- initializers.add(initializerObject);
- } else if (initializerObject is PropertyAccess) {
- // Recovery: Invalid initializer
- Expression target = initializerObject.target;
- if (target is FunctionExpressionInvocation) {
- var targetFunct = target.function;
- if (targetFunct is SuperExpression) {
- initializers.add(ast.superConstructorInvocation(
- targetFunct.superKeyword, null, null, target.argumentList));
- // TODO(danrubel): Consider generating this error in the parser
- // This error is also reported in the body builder
- handleRecoverableError(messageInvalidSuperInInitializer,
- targetFunct.superKeyword, targetFunct.superKeyword);
- } else if (targetFunct is ThisExpression) {
- initializers.add(ast.redirectingConstructorInvocation(
- targetFunct.thisKeyword, null, null, target.argumentList));
- // TODO(danrubel): Consider generating this error in the parser
- // This error is also reported in the body builder
- handleRecoverableError(messageInvalidThisInInitializer,
- targetFunct.thisKeyword, targetFunct.thisKeyword);
- } else {
- throw new UnsupportedError(
- 'unsupported initializer $initializerObject');
- }
- } else {
- throw new UnsupportedError(
- 'unsupported initializer $initializerObject');
- }
- } else {
+ ConstructorInitializer initializer = buildInitializer(initializerObject);
+ if (initializer == null) {
throw new UnsupportedError('unsupported initializer:'
' ${initializerObject.runtimeType} :: $initializerObject');
}
+ initializers.add(initializer);
}
push(initializers);
diff --git a/pkg/analyzer/test/generated/parser_test.dart b/pkg/analyzer/test/generated/parser_test.dart
index 4de8709..e038ca8 100644
--- a/pkg/analyzer/test/generated/parser_test.dart
+++ b/pkg/analyzer/test/generated/parser_test.dart
@@ -1566,6 +1566,54 @@
expect(constructor.body, isEmptyFunctionBody);
}
+ void test_parseConstructor_superIndexed() {
+ createParser('C() : super()[];');
+ var constructor = parser.parseClassMember('C') as ConstructorDeclaration;
+ listener.assertErrors([
+ expectedError(ParserErrorCode.INVALID_SUPER_IN_INITIALIZER, 6, 5),
+ expectedError(ParserErrorCode.MISSING_IDENTIFIER, 14, 1),
+ ]);
+ expect(constructor, isNotNull);
+ expect(constructor.externalKeyword, isNull);
+ expect(constructor.constKeyword, isNull);
+ expect(constructor.factoryKeyword, isNull);
+ expect(constructor.returnType.name, 'C');
+ _assertIsDeclarationName(constructor.returnType, false);
+ expect(constructor.name, isNull);
+ expect(constructor.parameters, isNotNull);
+ expect(constructor.parameters.parameters, isEmpty);
+ expect(constructor.separator.lexeme, ':');
+ expect(constructor.initializers, hasLength(1));
+ SuperConstructorInvocation initializer = constructor.initializers[0];
+ expect(initializer.argumentList.arguments, isEmpty);
+ expect(constructor.redirectedConstructor, isNull);
+ expect(constructor.body, isEmptyFunctionBody);
+ }
+
+ void test_parseConstructor_thisIndexed() {
+ createParser('C() : this()[];');
+ var constructor = parser.parseClassMember('C') as ConstructorDeclaration;
+ listener.assertErrors([
+ expectedError(ParserErrorCode.INVALID_THIS_IN_INITIALIZER, 6, 4),
+ expectedError(ParserErrorCode.MISSING_IDENTIFIER, 13, 1),
+ ]);
+ expect(constructor, isNotNull);
+ expect(constructor.externalKeyword, isNull);
+ expect(constructor.constKeyword, isNull);
+ expect(constructor.factoryKeyword, isNull);
+ expect(constructor.returnType.name, 'C');
+ _assertIsDeclarationName(constructor.returnType, false);
+ expect(constructor.name, isNull);
+ expect(constructor.parameters, isNotNull);
+ expect(constructor.parameters.parameters, isEmpty);
+ expect(constructor.separator.lexeme, ':');
+ expect(constructor.initializers, hasLength(1));
+ RedirectingConstructorInvocation initializer = constructor.initializers[0];
+ expect(initializer.argumentList.arguments, isEmpty);
+ expect(constructor.redirectedConstructor, isNull);
+ expect(constructor.body, isEmptyFunctionBody);
+ }
+
void test_parseConstructor_unnamed() {
createParser('C();');
var constructor = parser.parseClassMember('C') as ConstructorDeclaration;