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>
This commit is contained in:
Dan Rubel 2019-06-18 17:59:35 +00:00 committed by commit-bot@chromium.org
parent 303dfdf9b5
commit 99acf35ede
2 changed files with 161 additions and 113 deletions

View file

@ -327,6 +327,116 @@ class AstBuilder extends StackListener {
}
}
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 @@ class AstBuilder extends StackListener {
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);

View file

@ -1566,6 +1566,54 @@ void Function<A>(core.List<core.int> x) m() => null;
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;