mirror of
https://github.com/dart-lang/sdk
synced 2024-10-14 10:18:13 +00:00
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:
parent
303dfdf9b5
commit
99acf35ede
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in a new issue