[cfe] Add InternalMethodInvocation, InternalPropertyGet and InternalPropertySet
- to replace internal use of the old method invocation encoding.
Change-Id: I7ca876d28beec265f1a143ce5f29deb9e61616d7
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/208184
Reviewed-by: Dmitry Stefantsov <dmitryas@google.com>
Commit-Queue: Johnni Winther <johnniwinther@google.com>
diff --git a/pkg/front_end/lib/src/fasta/kernel/body_builder.dart b/pkg/front_end/lib/src/fasta/kernel/body_builder.dart
index 3555b29..771e607 100644
--- a/pkg/front_end/lib/src/fasta/kernel/body_builder.dart
+++ b/pkg/front_end/lib/src/fasta/kernel/body_builder.dart
@@ -6583,9 +6583,7 @@
arguments))
..fileOffset = receiver.fileOffset;
} else {
- MethodInvocation node =
- forest.createMethodInvocation(offset, receiver, name, arguments);
- return node;
+ return forest.createMethodInvocation(offset, receiver, name, arguments);
}
}
diff --git a/pkg/front_end/lib/src/fasta/kernel/expression_generator.dart b/pkg/front_end/lib/src/fasta/kernel/expression_generator.dart
index 5333b0c..82dbe5c 100644
--- a/pkg/front_end/lib/src/fasta/kernel/expression_generator.dart
+++ b/pkg/front_end/lib/src/fasta/kernel/expression_generator.dart
@@ -752,7 +752,7 @@
Expression buildSimpleRead() {
VariableDeclarationImpl variable =
_helper.createVariableDeclarationForValue(receiverExpression);
- PropertyGet read = _forest.createPropertyGet(
+ Expression read = _forest.createPropertyGet(
fileOffset,
_helper.createVariableGet(variable, receiverExpression.fileOffset,
forNullGuardedAccess: true),
@@ -765,7 +765,7 @@
Expression buildAssignment(Expression value, {bool voidContext = false}) {
VariableDeclarationImpl variable =
_helper.createVariableDeclarationForValue(receiverExpression);
- PropertySet read = _helper.forest.createPropertySet(
+ Expression read = _helper.forest.createPropertySet(
fileOffset,
_helper.createVariableGet(variable, receiverExpression.fileOffset,
forNullGuardedAccess: true),
diff --git a/pkg/front_end/lib/src/fasta/kernel/forest.dart b/pkg/front_end/lib/src/fasta/kernel/forest.dart
index 8ba13f0..9dc0296 100644
--- a/pkg/front_end/lib/src/fasta/kernel/forest.dart
+++ b/pkg/front_end/lib/src/fasta/kernel/forest.dart
@@ -722,11 +722,11 @@
..fileOffset = fileOffset;
}
- MethodInvocation createMethodInvocation(
+ Expression createMethodInvocation(
int fileOffset, Expression expression, Name name, Arguments arguments) {
// ignore: unnecessary_null_comparison
assert(fileOffset != null);
- return new MethodInvocation(expression, name, arguments)
+ return new InternalMethodInvocation(expression, name, arguments)
..fileOffset = fileOffset;
}
@@ -758,19 +758,18 @@
return new NullCheck(expression)..fileOffset = fileOffset;
}
- PropertyGet createPropertyGet(
- int fileOffset, Expression receiver, Name name) {
+ Expression createPropertyGet(int fileOffset, Expression receiver, Name name) {
// ignore: unnecessary_null_comparison
assert(fileOffset != null);
- return new PropertyGet(receiver, name)..fileOffset = fileOffset;
+ return new InternalPropertyGet(receiver, name)..fileOffset = fileOffset;
}
- PropertySet createPropertySet(
+ Expression createPropertySet(
int fileOffset, Expression receiver, Name name, Expression value,
{required bool forEffect, bool readOnlyReceiver: false}) {
// ignore: unnecessary_null_comparison
assert(fileOffset != null);
- return new PropertySetImpl(receiver, name, value,
+ return new InternalPropertySet(receiver, name, value,
forEffect: forEffect, readOnlyReceiver: readOnlyReceiver)
..fileOffset = fileOffset;
}
diff --git a/pkg/front_end/lib/src/fasta/kernel/inference_visitor.dart b/pkg/front_end/lib/src/fasta/kernel/inference_visitor.dart
index 946ce03..719356e 100644
--- a/pkg/front_end/lib/src/fasta/kernel/inference_visitor.dart
+++ b/pkg/front_end/lib/src/fasta/kernel/inference_visitor.dart
@@ -322,7 +322,7 @@
}
@override
- void visitInvalidInitializer(Initializer node) {
+ void visitInvalidInitializer(InvalidInitializer node) {
_unhandledInitializer(node);
}
@@ -1112,6 +1112,8 @@
return new LocalForInVariable(syntheticAssignment);
} else if (syntheticAssignment is PropertySet) {
return new PropertyForInVariable(syntheticAssignment);
+ } else if (syntheticAssignment is InternalPropertySet) {
+ return new InternalPropertyForInVariable(syntheticAssignment);
} else if (syntheticAssignment is SuperPropertySet) {
return new SuperPropertyForInVariable(syntheticAssignment);
} else if (syntheticAssignment is StaticSet) {
@@ -2820,6 +2822,19 @@
isExpressionInvocation: false, isImplicitCall: false);
}
+ ExpressionInferenceResult visitInternalMethodInvocation(
+ InternalMethodInvocation node, DartType typeContext) {
+ assert(node.name != unaryMinusName);
+ ExpressionInferenceResult result = inferrer.inferNullAwareExpression(
+ node.receiver, const UnknownType(), true);
+ Link<NullAwareGuard> nullAwareGuards = result.nullAwareGuards;
+ Expression receiver = result.nullAwareAction;
+ DartType receiverType = result.nullAwareActionType;
+ return inferrer.inferMethodInvocation(node.fileOffset, nullAwareGuards,
+ receiver, receiverType, node.name, node.arguments, typeContext,
+ isExpressionInvocation: false, isImplicitCall: false);
+ }
+
ExpressionInferenceResult visitExpressionInvocation(
ExpressionInvocation node, DartType typeContext) {
ExpressionInferenceResult result = inferrer.inferNullAwareExpression(
@@ -5900,6 +5915,44 @@
rhsType, replacement, nullAwareGuards);
}
+ ExpressionInferenceResult visitInternalPropertySet(
+ InternalPropertySet node, DartType typeContext) {
+ ExpressionInferenceResult receiverResult = inferrer
+ .inferNullAwareExpression(node.receiver, const UnknownType(), true,
+ isVoidAllowed: false);
+
+ Link<NullAwareGuard> nullAwareGuards = receiverResult.nullAwareGuards;
+ Expression receiver = receiverResult.nullAwareAction;
+ DartType receiverType = receiverResult.nullAwareActionType;
+
+ ObjectAccessTarget target = inferrer.findInterfaceMember(
+ receiverType, node.name, node.fileOffset,
+ setter: true, instrumented: true, includeExtensionMethods: true);
+ if (target.isInstanceMember || target.isObjectMember) {
+ if (inferrer.instrumentation != null &&
+ receiverType == const DynamicType()) {
+ inferrer.instrumentation!.record(
+ inferrer.uriForInstrumentation,
+ node.fileOffset,
+ 'target',
+ new InstrumentationValueForMember(target.member!));
+ }
+ }
+ DartType writeContext = inferrer.getSetterType(target, receiverType);
+ ExpressionInferenceResult rhsResult = inferrer
+ .inferExpression(node.value, writeContext, true, isVoidAllowed: true);
+ DartType rhsType = rhsResult.inferredType;
+ Expression rhs = inferrer.ensureAssignableResult(writeContext, rhsResult,
+ fileOffset: node.fileOffset, isVoidAllowed: writeContext is VoidType);
+
+ Expression replacement = _computePropertySet(
+ node.fileOffset, receiver, receiverType, node.name, target, rhs,
+ valueType: rhsType, forEffect: node.forEffect);
+
+ return inferrer.createNullAwareExpressionInferenceResult(
+ rhsType, replacement, nullAwareGuards);
+ }
+
ExpressionInferenceResult visitNullAwareIfNullSet(
NullAwareIfNullSet node, DartType typeContext) {
ExpressionInferenceResult receiverResult = inferrer
@@ -6030,6 +6083,31 @@
return expressionInferenceResult;
}
+ ExpressionInferenceResult visitInternalPropertyGet(
+ InternalPropertyGet node, DartType typeContext) {
+ ExpressionInferenceResult result = inferrer.inferNullAwareExpression(
+ node.receiver, const UnknownType(), true);
+
+ Link<NullAwareGuard> nullAwareGuards = result.nullAwareGuards;
+ Expression receiver = result.nullAwareAction;
+ DartType receiverType = result.nullAwareActionType;
+
+ node.receiver = receiver..parent = node;
+ PropertyGetInferenceResult propertyGetInferenceResult = _computePropertyGet(
+ node.fileOffset, receiver, receiverType, node.name, typeContext,
+ isThisReceiver: node.receiver is ThisExpression);
+ ExpressionInferenceResult readResult =
+ propertyGetInferenceResult.expressionInferenceResult;
+ inferrer.flowAnalysis.propertyGet(node, node.receiver, node.name.text,
+ propertyGetInferenceResult.member, readResult.inferredType);
+ ExpressionInferenceResult expressionInferenceResult =
+ inferrer.createNullAwareExpressionInferenceResult(
+ readResult.inferredType, readResult.expression, nullAwareGuards);
+ inferrer.flowAnalysis
+ .forwardExpression(expressionInferenceResult.nullAwareAction, node);
+ return expressionInferenceResult;
+ }
+
@override
void visitRedirectingInitializer(RedirectingInitializer node) {
inferrer.inferConstructorParameterTypes(node.target);
@@ -7316,6 +7394,69 @@
}
}
+class InternalPropertyForInVariable implements ForInVariable {
+ final InternalPropertySet propertySet;
+
+ DartType? _writeType;
+
+ Expression? _rhs;
+
+ InternalPropertyForInVariable(this.propertySet);
+
+ @override
+ DartType computeElementType(TypeInferrerImpl inferrer) {
+ ExpressionInferenceResult receiverResult = inferrer.inferExpression(
+ propertySet.receiver, const UnknownType(), true);
+ propertySet.receiver = receiverResult.expression..parent = propertySet;
+ DartType receiverType = receiverResult.inferredType;
+ ObjectAccessTarget writeTarget = inferrer.findInterfaceMember(
+ receiverType, propertySet.name, propertySet.fileOffset,
+ setter: true, instrumented: true, includeExtensionMethods: true);
+ DartType elementType =
+ _writeType = inferrer.getSetterType(writeTarget, receiverType);
+ Expression? error = inferrer.reportMissingInterfaceMember(
+ writeTarget,
+ receiverType,
+ propertySet.name,
+ propertySet.fileOffset,
+ templateUndefinedSetter);
+ if (error != null) {
+ _rhs = error;
+ } else {
+ if (writeTarget.isInstanceMember || writeTarget.isObjectMember) {
+ if (inferrer.instrumentation != null &&
+ receiverType == const DynamicType()) {
+ inferrer.instrumentation!.record(
+ inferrer.uriForInstrumentation,
+ propertySet.fileOffset,
+ 'target',
+ new InstrumentationValueForMember(writeTarget.member!));
+ }
+ }
+ _rhs = propertySet.value;
+ }
+ return elementType;
+ }
+
+ @override
+ Expression inferAssignment(TypeInferrerImpl inferrer, DartType rhsType) {
+ Expression rhs = inferrer.ensureAssignable(
+ inferrer.computeGreatestClosure(_writeType!), rhsType, _rhs!,
+ errorTemplate: templateForInLoopElementTypeNotAssignable,
+ nullabilityErrorTemplate:
+ templateForInLoopElementTypeNotAssignableNullability,
+ nullabilityPartErrorTemplate:
+ templateForInLoopElementTypeNotAssignablePartNullability,
+ isVoidAllowed: true);
+
+ propertySet.value = rhs..parent = propertySet;
+ ExpressionInferenceResult result = inferrer.inferExpression(
+ propertySet, const UnknownType(), !inferrer.isTopLevel,
+ isVoidAllowed: true);
+ return result.expression;
+ }
+}
+
class SuperPropertyForInVariable implements ForInVariable {
final SuperPropertySet superPropertySet;
diff --git a/pkg/front_end/lib/src/fasta/kernel/internal_ast.dart b/pkg/front_end/lib/src/fasta/kernel/internal_ast.dart
index 749419c..9389905 100644
--- a/pkg/front_end/lib/src/fasta/kernel/internal_ast.dart
+++ b/pkg/front_end/lib/src/fasta/kernel/internal_ast.dart
@@ -422,6 +422,7 @@
IndexSet,
LoadLibraryTearOff,
LocalPostIncDec,
+ MethodInvocation,
NullAwareCompoundSet,
NullAwareExtension,
NullAwareIfNullSet,
@@ -429,7 +430,9 @@
NullAwarePropertyGet,
NullAwarePropertySet,
Parenthesized,
+ PropertyGet,
PropertyPostIncDec,
+ PropertySet,
StaticPostIncDec,
SuperIndexSet,
SuperPostIncDec,
@@ -442,16 +445,19 @@
@override
R accept<R>(ExpressionVisitor<R> visitor) {
- if (visitor is Printer || visitor is Precedence) {
- // Allow visitors needed for toString.
+ if (visitor is Printer || visitor is Precedence || visitor is Transformer) {
+ // Allow visitors needed for toString and replaceWith.
return visitor.defaultExpression(this);
}
- return unsupported("${runtimeType}.accept", -1, null);
+ return unsupported(
+ "${runtimeType}.accept on ${visitor.runtimeType}", -1, null);
}
@override
- R accept1<R, A>(ExpressionVisitor1<R, A> visitor, A arg) =>
- unsupported("${runtimeType}.accept1", -1, null);
+ R accept1<R, A>(ExpressionVisitor1<R, A> visitor, A arg) {
+ return unsupported(
+ "${runtimeType}.accept1 on ${visitor.runtimeType}", -1, null);
+ }
@override
DartType getStaticType(StaticTypeContext context) =>
@@ -1341,14 +1347,13 @@
@override
void toTextInternal(AstPrinter printer) {
Expression propertyGet = read;
- if (propertyGet is PropertyGet) {
+ if (propertyGet is InternalPropertyGet) {
Expression receiver = propertyGet.receiver;
if (receiver is VariableGet && receiver.variable == variable) {
// Special-case the usual use of this node.
printer.writeExpression(variable.initializer!);
printer.write('?.');
- printer.writeInterfaceMemberName(
- propertyGet.interfaceTargetReference, propertyGet.name);
+ printer.writeName(propertyGet.name);
return;
}
}
@@ -4524,3 +4529,244 @@
}
throw new UnsupportedError("Clone not supported for ${node.runtimeType}.");
}
+
+/// A dynamically bound method invocation of the form `o.foo()`.
+///
+/// This will be transformed into an [InstanceInvocation], [DynamicInvocation],
+/// [FunctionInvocation] or [StaticInvocation] (for implicit extension method
+/// invocation) after type inference.
+// TODO(johnniwinther): Rename to `MethodInvocation` when [MethodInvocation]
+// has been removed.
+class InternalMethodInvocation extends InternalExpression {
+ Expression receiver;
+
+ Name name;
+
+ Arguments arguments;
+
+ InternalMethodInvocation(this.receiver, this.name, this.arguments)
+ // ignore: unnecessary_null_comparison
+ : assert(receiver != null),
+ // ignore: unnecessary_null_comparison
+ assert(arguments != null) {
+ receiver.parent = this;
+ arguments.parent = this;
+ }
+
+ @override
+ ExpressionInferenceResult acceptInference(
+ InferenceVisitor visitor, DartType typeContext) {
+ return visitor.visitInternalMethodInvocation(this, typeContext);
+ }
+
+ @override
+ InternalExpressionKind get kind => InternalExpressionKind.MethodInvocation;
+
+ @override
+ void visitChildren(Visitor<dynamic> v) {
+ receiver.accept(v);
+ arguments.accept(v);
+ }
+
+ @override
+ void transformChildren(Transformer v) {
+ // ignore: unnecessary_null_comparison
+ if (receiver != null) {
+ receiver = v.transform(receiver);
+ receiver.parent = this;
+ }
+ // ignore: unnecessary_null_comparison
+ if (arguments != null) {
+ arguments = v.transform(arguments);
+ arguments.parent = this;
+ }
+ }
+
+ @override
+ void transformOrRemoveChildren(RemovingTransformer v) {
+ // ignore: unnecessary_null_comparison
+ if (receiver != null) {
+ receiver = v.transform(receiver);
+ receiver.parent = this;
+ }
+ // ignore: unnecessary_null_comparison
+ if (arguments != null) {
+ arguments = v.transform(arguments);
+ arguments.parent = this;
+ }
+ }
+
+ @override
+ String toString() {
+ return "InternalMethodInvocation(${toStringInternal()})";
+ }
+
+ @override
+ int get precedence => Precedence.PRIMARY;
+
+ @override
+ void toTextInternal(AstPrinter printer) {
+ printer.writeExpression(receiver, minimumPrecedence: Precedence.PRIMARY);
+ printer.write('.');
+ printer.writeName(name);
+ printer.writeArguments(arguments);
+ }
+}
+
+/// A dynamically bound property read of the form `o.foo`.
+///
+/// This will be transformed into an [InstanceGet], [InstanceTearOff],
+/// [DynamicGet], [FunctionTearOff] or [StaticInvocation] (for implicit
+/// extension member access) after type inference.
+// TODO(johnniwinther): Rename to `PropertyGet` when [PropertyGet]
+// has been removed.
+class InternalPropertyGet extends InternalExpression {
+ Expression receiver;
+
+ Name name;
+
+ InternalPropertyGet(this.receiver, this.name)
+ // ignore: unnecessary_null_comparison
+ : assert(receiver != null) {
+ receiver.parent = this;
+ }
+
+ @override
+ ExpressionInferenceResult acceptInference(
+ InferenceVisitor visitor, DartType typeContext) {
+ return visitor.visitInternalPropertyGet(this, typeContext);
+ }
+
+ @override
+ InternalExpressionKind get kind => InternalExpressionKind.PropertyGet;
+
+ @override
+ void visitChildren(Visitor<dynamic> v) {
+ receiver.accept(v);
+ }
+
+ @override
+ void transformChildren(Transformer v) {
+ // ignore: unnecessary_null_comparison
+ if (receiver != null) {
+ receiver = v.transform(receiver);
+ receiver.parent = this;
+ }
+ }
+
+ @override
+ void transformOrRemoveChildren(RemovingTransformer v) {
+ // ignore: unnecessary_null_comparison
+ if (receiver != null) {
+ receiver = v.transform(receiver);
+ receiver.parent = this;
+ }
+ }
+
+ @override
+ String toString() {
+ return "InternalPropertyGet(${toStringInternal()})";
+ }
+
+ @override
+ int get precedence => Precedence.PRIMARY;
+
+ @override
+ void toTextInternal(AstPrinter printer) {
+ printer.writeExpression(receiver, minimumPrecedence: Precedence.PRIMARY);
+ printer.write('.');
+ printer.writeName(name);
+ }
+}
+
+/// A dynamically bound property write of the form `o.foo = e`.
+///
+/// This will be transformed into an [InstanceSet], [DynamicSet], or
+/// [StaticInvocation] (for implicit extension member access) after type
+/// inference.
+// TODO(johnniwinther): Rename to `PropertySet` when [PropertySet]
+// has been removed.
+class InternalPropertySet extends InternalExpression {
+ Expression receiver;
+ Name name;
+ Expression value;
+
+ /// If `true` the assignment is need for its effect and not for its value.
+ final bool forEffect;
+
+ /// If `true` the receiver can be cloned and doesn't need a temporary variable
+ /// for multiple reads.
+ final bool readOnlyReceiver;
+
+ InternalPropertySet(this.receiver, this.name, this.value,
+ {required this.forEffect, required this.readOnlyReceiver})
+ // ignore: unnecessary_null_comparison
+ : assert(receiver != null),
+ // ignore: unnecessary_null_comparison
+ assert(value != null),
+ // ignore: unnecessary_null_comparison
+ assert(forEffect != null),
+ // ignore: unnecessary_null_comparison
+ assert(readOnlyReceiver != null) {
+ receiver.parent = this;
+ value.parent = this;
+ }
+
+ @override
+ ExpressionInferenceResult acceptInference(
+ InferenceVisitor visitor, DartType typeContext) {
+ return visitor.visitInternalPropertySet(this, typeContext);
+ }
+
+ @override
+ InternalExpressionKind get kind => InternalExpressionKind.PropertySet;
+
+ @override
+ void visitChildren(Visitor v) {
+ receiver.accept(v);
+ name.accept(v);
+ value.accept(v);
+ }
+
+ @override
+ void transformChildren(Transformer v) {
+ // ignore: unnecessary_null_comparison
+ if (receiver != null) {
+ receiver = v.transform(receiver);
+ receiver.parent = this;
+ }
+ // ignore: unnecessary_null_comparison
+ if (value != null) {
+ value = v.transform(value);
+ value.parent = this;
+ }
+ }
+
+ @override
+ void transformOrRemoveChildren(RemovingTransformer v) {
+ // ignore: unnecessary_null_comparison
+ if (receiver != null) {
+ receiver = v.transform(receiver);
+ receiver.parent = this;
+ }
+ // ignore: unnecessary_null_comparison
+ if (value != null) {
+ value = v.transform(value);
+ value.parent = this;
+ }
+ }
+
+ @override
+ String toString() {
+ return "InternalPropertySet(${toStringInternal()})";
+ }
+
+ @override
+ void toTextInternal(AstPrinter printer) {
+ printer.writeExpression(receiver, minimumPrecedence: Precedence.PRIMARY);
+ printer.write('.');
+ printer.writeName(name);
+ printer.write(' = ');
+ printer.writeExpression(value);
+ }
+}
diff --git a/pkg/front_end/test/spell_checking_list_code.txt b/pkg/front_end/test/spell_checking_list_code.txt
index e8b4e79..ae9d883 100644
--- a/pkg/front_end/test/spell_checking_list_code.txt
+++ b/pkg/front_end/test/spell_checking_list_code.txt
@@ -358,6 +358,7 @@
dummy
dupdate
dyn
+dynamically
e
easy
ecma
diff --git a/pkg/front_end/test/text_representation/internal_ast_text_representation_test.dart b/pkg/front_end/test/text_representation/internal_ast_text_representation_test.dart
index e1f4a5b..a88f79a 100644
--- a/pkg/front_end/test/text_representation/internal_ast_text_representation_test.dart
+++ b/pkg/front_end/test/text_representation/internal_ast_text_representation_test.dart
@@ -44,6 +44,8 @@
_testFunctionDeclarationImpl();
_testIfNullExpression();
_testIntLiterals();
+ _testInternalMethodInvocation();
+ _testInternalPropertyGet();
_testExpressionInvocation();
_testNamedFunctionExpressionJudgment();
_testNullAwareMethodInvocation();
@@ -372,6 +374,35 @@
testExpression(new ShadowLargeIntLiteral('bar', TreeNode.noOffset), 'bar');
}
+void _testInternalMethodInvocation() {
+ testExpression(
+ new InternalMethodInvocation(
+ new IntLiteral(0), new Name('boz'), new ArgumentsImpl([])),
+ '''
+0.boz()''');
+ testExpression(
+ new InternalMethodInvocation(
+ new IntLiteral(0),
+ new Name('boz'),
+ new ArgumentsImpl([
+ new IntLiteral(1)
+ ], types: [
+ const VoidType(),
+ const DynamicType()
+ ], named: [
+ new NamedExpression('foo', new IntLiteral(2)),
+ new NamedExpression('bar', new IntLiteral(3))
+ ])),
+ '''
+0.boz<void, dynamic>(1, foo: 2, bar: 3)''');
+}
+
+void _testInternalPropertyGet() {
+ testExpression(
+ new InternalPropertyGet(new IntLiteral(0), new Name('boz')), '''
+0.boz''');
+}
+
void _testExpressionInvocation() {
testExpression(
new ExpressionInvocation(new IntLiteral(0), new ArgumentsImpl([])), '''
@@ -417,7 +448,7 @@
// An unusual use of this node.
testExpression(
new NullAwareMethodInvocation(variable,
- new PropertyGet(new VariableGet(variable), new Name('foo'))),
+ new InternalPropertyGet(new VariableGet(variable), new Name('foo'))),
'''
let final dynamic #0 = 0 in null-aware #0.foo''');
}
@@ -429,7 +460,7 @@
// The usual use of this node.
testExpression(
new NullAwarePropertyGet(variable,
- new PropertyGet(new VariableGet(variable), new Name('foo'))),
+ new InternalPropertyGet(new VariableGet(variable), new Name('foo'))),
'''
0?.foo''');
diff --git a/pkg/front_end/testcases/general/invalid_super_initializer.dart b/pkg/front_end/testcases/general/invalid_super_initializer.dart
new file mode 100644
index 0000000..1e3f1aa
--- /dev/null
+++ b/pkg/front_end/testcases/general/invalid_super_initializer.dart
@@ -0,0 +1,15 @@
+// Copyright (c) 2021, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+abstract class A {}
+
+class B extends A {
+ B(): super()?.foo() {}
+}
+
+bad() {
+ new B();
+}
+
+main() {}
\ No newline at end of file
diff --git a/pkg/front_end/testcases/general/invalid_super_initializer.dart.textual_outline.expect b/pkg/front_end/testcases/general/invalid_super_initializer.dart.textual_outline.expect
new file mode 100644
index 0000000..946f9ea
--- /dev/null
+++ b/pkg/front_end/testcases/general/invalid_super_initializer.dart.textual_outline.expect
@@ -0,0 +1,6 @@
+abstract class A {}
+class B extends A {
+ B(): super()?.foo() {}
+}
+bad() {}
+main() {}
diff --git a/pkg/front_end/testcases/general/invalid_super_initializer.dart.weak.expect b/pkg/front_end/testcases/general/invalid_super_initializer.dart.weak.expect
new file mode 100644
index 0000000..d8ef127
--- /dev/null
+++ b/pkg/front_end/testcases/general/invalid_super_initializer.dart.weak.expect
@@ -0,0 +1,32 @@
+library /*isNonNullableByDefault*/;
+//
+// Problems in library:
+//
+// pkg/front_end/testcases/general/invalid_super_initializer.dart:8:8: Error: Can't use 'super' as an expression.
+// To delegate a constructor to a super constructor, put the super call as an initializer.
+// B(): super()?.foo() {}
+// ^
+//
+// pkg/front_end/testcases/general/invalid_super_initializer.dart:8:8: Error: Expected an initializer.
+// B(): super()?.foo() {}
+// ^
+//
+import self as self;
+import "dart:core" as core;
+
+abstract class A extends core::Object {
+ synthetic constructor •() → self::A
+ : super core::Object::•()
+ ;
+}
+class B extends self::A {
+ constructor •() → self::B
+ : final dynamic #t1 = let final dynamic #t2 = invalid-expression "pkg/front_end/testcases/general/invalid_super_initializer.dart:8:8: Error: Can't use 'super' as an expression.
+To delegate a constructor to a super constructor, put the super call as an initializer.
+ B(): super()?.foo() {}
+ ^" in #t2 == null ?{dynamic} null : #t2{dynamic}.foo() {}
+}
+static method bad() → dynamic {
+ new self::B::•();
+}
+static method main() → dynamic {}
diff --git a/pkg/front_end/testcases/general/invalid_super_initializer.dart.weak.outline.expect b/pkg/front_end/testcases/general/invalid_super_initializer.dart.weak.outline.expect
new file mode 100644
index 0000000..13e0b1e
--- /dev/null
+++ b/pkg/front_end/testcases/general/invalid_super_initializer.dart.weak.outline.expect
@@ -0,0 +1,16 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+
+abstract class A extends core::Object {
+ synthetic constructor •() → self::A
+ ;
+}
+class B extends self::A {
+ constructor •() → self::B
+ ;
+}
+static method bad() → dynamic
+ ;
+static method main() → dynamic
+ ;
diff --git a/pkg/front_end/testcases/general/invalid_super_initializer.dart.weak.transformed.expect b/pkg/front_end/testcases/general/invalid_super_initializer.dart.weak.transformed.expect
new file mode 100644
index 0000000..d8ef127
--- /dev/null
+++ b/pkg/front_end/testcases/general/invalid_super_initializer.dart.weak.transformed.expect
@@ -0,0 +1,32 @@
+library /*isNonNullableByDefault*/;
+//
+// Problems in library:
+//
+// pkg/front_end/testcases/general/invalid_super_initializer.dart:8:8: Error: Can't use 'super' as an expression.
+// To delegate a constructor to a super constructor, put the super call as an initializer.
+// B(): super()?.foo() {}
+// ^
+//
+// pkg/front_end/testcases/general/invalid_super_initializer.dart:8:8: Error: Expected an initializer.
+// B(): super()?.foo() {}
+// ^
+//
+import self as self;
+import "dart:core" as core;
+
+abstract class A extends core::Object {
+ synthetic constructor •() → self::A
+ : super core::Object::•()
+ ;
+}
+class B extends self::A {
+ constructor •() → self::B
+ : final dynamic #t1 = let final dynamic #t2 = invalid-expression "pkg/front_end/testcases/general/invalid_super_initializer.dart:8:8: Error: Can't use 'super' as an expression.
+To delegate a constructor to a super constructor, put the super call as an initializer.
+ B(): super()?.foo() {}
+ ^" in #t2 == null ?{dynamic} null : #t2{dynamic}.foo() {}
+}
+static method bad() → dynamic {
+ new self::B::•();
+}
+static method main() → dynamic {}
diff --git a/pkg/front_end/testcases/general/redirecting_factory_invocation_in_invalid.dart b/pkg/front_end/testcases/general/redirecting_factory_invocation_in_invalid.dart
new file mode 100644
index 0000000..d12b28b
--- /dev/null
+++ b/pkg/front_end/testcases/general/redirecting_factory_invocation_in_invalid.dart
@@ -0,0 +1,63 @@
+// Copyright (c) 2021, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE.md file.
+
+class Class1 {
+ int field = 0;
+
+ factory Class1() = Class1._;
+
+ Class1._();
+
+ int get getter => 0;
+}
+
+class Class2 extends Class1 {
+ final Class2 _c2;
+
+ Class2(this._c2) : super._() {
+ // Invocation inside an invalid unary expression.
+ -new Class1();
+ // Invocation inside an invalid binary expression.
+ ('' + '') - new Class1();
+ // Invocation inside an invalid index set.
+ (0 + 1)[0] = new Class1();
+ _c2[0] = new Class1();
+ // Invocation inside an invalid index get.
+ (0 + 1)[new Class1()];
+ // Invocation inside an invalid property get.
+ new Class1().foo;
+ // Invocation inside an invalid property set.
+ (0 + 1).foo = new Class1();
+ // Invocation inside an invalid invocation.
+ new Class1().foo();
+ // Invocation inside an invalid implicit call invocation.
+ new Class1()();
+ // Invocation inside an invalid implicit field invocation.
+ new Class1().field();
+ // Invocation inside an invalid implicit getter invocation.
+ new Class1().getter();
+ // Invocation inside an invalid implicit call-getter invocation.
+ _c2(new Class1());
+ // Duplicate named arguments
+ method(a: 0, a: new Class1());
+ // Triple named arguments
+ method(a: 0, a: 1, a: new Class1());
+ // Invocation inside an invalid super index get.
+ super[new Class1()];
+ // Invocation inside an invalid super index set.
+ super[0] = new Class1();
+ // Invocation inside an invalid super set.
+ super.foo = new Class1();
+ // Invocation inside an invalid super invocation.
+ super.foo(new Class1());
+ // Invocation inside an invalid super binary.
+ super + new Class1();
+ }
+
+ method({a}) {}
+
+ int get call => 0;
+}
+
+main() {}
\ No newline at end of file
diff --git a/pkg/front_end/testcases/general/redirecting_factory_invocation_in_invalid.dart.textual_outline.expect b/pkg/front_end/testcases/general/redirecting_factory_invocation_in_invalid.dart.textual_outline.expect
new file mode 100644
index 0000000..607b231
--- /dev/null
+++ b/pkg/front_end/testcases/general/redirecting_factory_invocation_in_invalid.dart.textual_outline.expect
@@ -0,0 +1,15 @@
+class Class1 {
+ int field = 0;
+ factory Class1() = Class1._;
+ Class1._();
+ int get getter => 0;
+}
+
+class Class2 extends Class1 {
+ final Class2 _c2;
+ Class2(this._c2) : super._() {}
+ method({a}) {}
+ int get call => 0;
+}
+
+main() {}
diff --git a/pkg/front_end/testcases/general/redirecting_factory_invocation_in_invalid.dart.textual_outline_modelled.expect b/pkg/front_end/testcases/general/redirecting_factory_invocation_in_invalid.dart.textual_outline_modelled.expect
new file mode 100644
index 0000000..ca3ab16
--- /dev/null
+++ b/pkg/front_end/testcases/general/redirecting_factory_invocation_in_invalid.dart.textual_outline_modelled.expect
@@ -0,0 +1,15 @@
+class Class1 {
+ Class1._();
+ factory Class1() = Class1._;
+ int field = 0;
+ int get getter => 0;
+}
+
+class Class2 extends Class1 {
+ Class2(this._c2) : super._() {}
+ final Class2 _c2;
+ int get call => 0;
+ method({a}) {}
+}
+
+main() {}
diff --git a/pkg/front_end/testcases/general/redirecting_factory_invocation_in_invalid.dart.weak.expect b/pkg/front_end/testcases/general/redirecting_factory_invocation_in_invalid.dart.weak.expect
new file mode 100644
index 0000000..232d5ac
--- /dev/null
+++ b/pkg/front_end/testcases/general/redirecting_factory_invocation_in_invalid.dart.weak.expect
@@ -0,0 +1,187 @@
+library /*isNonNullableByDefault*/;
+//
+// Problems in library:
+//
+// pkg/front_end/testcases/general/redirecting_factory_invocation_in_invalid.dart:47:10: Error: Superclass has no method named '[]'.
+// super[new Class1()];
+// ^
+//
+// pkg/front_end/testcases/general/redirecting_factory_invocation_in_invalid.dart:49:10: Error: Superclass has no method named '[]='.
+// super[0] = new Class1();
+// ^
+//
+// pkg/front_end/testcases/general/redirecting_factory_invocation_in_invalid.dart:51:11: Error: Superclass has no setter named 'foo'.
+// super.foo = new Class1();
+// ^^^
+//
+// pkg/front_end/testcases/general/redirecting_factory_invocation_in_invalid.dart:53:11: Error: Superclass has no method named 'foo'.
+// super.foo(new Class1());
+// ^^^
+//
+// pkg/front_end/testcases/general/redirecting_factory_invocation_in_invalid.dart:55:11: Error: Superclass has no method named '+'.
+// super + new Class1();
+// ^
+//
+// pkg/front_end/testcases/general/redirecting_factory_invocation_in_invalid.dart:20:5: Error: The operator 'unary-' isn't defined for the class 'Class1'.
+// - 'Class1' is from 'pkg/front_end/testcases/general/redirecting_factory_invocation_in_invalid.dart'.
+// Try correcting the operator to an existing operator, or defining a 'unary-' operator.
+// -new Class1();
+// ^
+//
+// pkg/front_end/testcases/general/redirecting_factory_invocation_in_invalid.dart:22:15: Error: The operator '-' isn't defined for the class 'String'.
+// Try correcting the operator to an existing operator, or defining a '-' operator.
+// ('' + '') - new Class1();
+// ^
+//
+// pkg/front_end/testcases/general/redirecting_factory_invocation_in_invalid.dart:24:12: Error: The operator '[]=' isn't defined for the class 'int'.
+// Try correcting the operator to an existing operator, or defining a '[]=' operator.
+// (0 + 1)[0] = new Class1();
+// ^
+//
+// pkg/front_end/testcases/general/redirecting_factory_invocation_in_invalid.dart:25:8: Error: The operator '[]=' isn't defined for the class 'Class2'.
+// - 'Class2' is from 'pkg/front_end/testcases/general/redirecting_factory_invocation_in_invalid.dart'.
+// Try correcting the operator to an existing operator, or defining a '[]=' operator.
+// _c2[0] = new Class1();
+// ^
+//
+// pkg/front_end/testcases/general/redirecting_factory_invocation_in_invalid.dart:27:12: Error: The operator '[]' isn't defined for the class 'int'.
+// Try correcting the operator to an existing operator, or defining a '[]' operator.
+// (0 + 1)[new Class1()];
+// ^
+//
+// pkg/front_end/testcases/general/redirecting_factory_invocation_in_invalid.dart:29:18: Error: The getter 'foo' isn't defined for the class 'Class1'.
+// - 'Class1' is from 'pkg/front_end/testcases/general/redirecting_factory_invocation_in_invalid.dart'.
+// Try correcting the name to the name of an existing getter, or defining a getter or field named 'foo'.
+// new Class1().foo;
+// ^^^
+//
+// pkg/front_end/testcases/general/redirecting_factory_invocation_in_invalid.dart:31:13: Error: The setter 'foo' isn't defined for the class 'int'.
+// Try correcting the name to the name of an existing setter, or defining a setter or field named 'foo'.
+// (0 + 1).foo = new Class1();
+// ^^^
+//
+// pkg/front_end/testcases/general/redirecting_factory_invocation_in_invalid.dart:33:18: Error: The method 'foo' isn't defined for the class 'Class1'.
+// - 'Class1' is from 'pkg/front_end/testcases/general/redirecting_factory_invocation_in_invalid.dart'.
+// Try correcting the name to the name of an existing method, or defining a method named 'foo'.
+// new Class1().foo();
+// ^^^
+//
+// pkg/front_end/testcases/general/redirecting_factory_invocation_in_invalid.dart:35:17: Error: The method 'call' isn't defined for the class 'Class1'.
+// - 'Class1' is from 'pkg/front_end/testcases/general/redirecting_factory_invocation_in_invalid.dart'.
+// Try correcting the name to the name of an existing method, or defining a method named 'call'.
+// new Class1()();
+// ^
+//
+// pkg/front_end/testcases/general/redirecting_factory_invocation_in_invalid.dart:37:23: Error: 'field' isn't a function or method and can't be invoked.
+// new Class1().field();
+// ^^^^...
+//
+// pkg/front_end/testcases/general/redirecting_factory_invocation_in_invalid.dart:39:24: Error: 'getter' isn't a function or method and can't be invoked.
+// new Class1().getter();
+// ^^^^...
+//
+// pkg/front_end/testcases/general/redirecting_factory_invocation_in_invalid.dart:41:8: Error: 'call' isn't a function or method and can't be invoked.
+// _c2(new Class1());
+// ^^^^
+//
+// pkg/front_end/testcases/general/redirecting_factory_invocation_in_invalid.dart:43:18: Error: Duplicated named argument 'a'.
+// method(a: 0, a: new Class1());
+// ^
+//
+// pkg/front_end/testcases/general/redirecting_factory_invocation_in_invalid.dart:45:18: Error: Duplicated named argument 'a'.
+// method(a: 0, a: 1, a: new Class1());
+// ^
+//
+// pkg/front_end/testcases/general/redirecting_factory_invocation_in_invalid.dart:45:24: Error: Duplicated named argument 'a'.
+// method(a: 0, a: 1, a: new Class1());
+// ^
+//
+import self as self;
+import "dart:core" as core;
+
+class Class1 extends core::Object {
+ field core::int field = 0;
+ static final field dynamic _redirecting# = <dynamic>[self::Class1::•]/*isLegacy*/;
+ constructor _() → self::Class1
+ : super core::Object::•()
+ ;
+ static factory •() → self::Class1
+ let dynamic #redirecting_factory = self::Class1::_ in invalid-expression;
+ get getter() → core::int
+ return 0;
+}
+class Class2 extends self::Class1 {
+ final field self::Class2 _c2;
+ constructor •(self::Class2 _c2) → self::Class2
+ : self::Class2::_c2 = _c2, super self::Class1::_() {
+ invalid-expression "pkg/front_end/testcases/general/redirecting_factory_invocation_in_invalid.dart:20:5: Error: The operator 'unary-' isn't defined for the class 'Class1'.
+ - 'Class1' is from 'pkg/front_end/testcases/general/redirecting_factory_invocation_in_invalid.dart'.
+Try correcting the operator to an existing operator, or defining a 'unary-' operator.
+ -new Class1();
+ ^";
+ invalid-expression "pkg/front_end/testcases/general/redirecting_factory_invocation_in_invalid.dart:22:15: Error: The operator '-' isn't defined for the class 'String'.
+Try correcting the operator to an existing operator, or defining a '-' operator.
+ ('' + '') - new Class1();
+ ^";
+ invalid-expression "pkg/front_end/testcases/general/redirecting_factory_invocation_in_invalid.dart:24:12: Error: The operator '[]=' isn't defined for the class 'int'.
+Try correcting the operator to an existing operator, or defining a '[]=' operator.
+ (0 + 1)[0] = new Class1();
+ ^";
+ invalid-expression "pkg/front_end/testcases/general/redirecting_factory_invocation_in_invalid.dart:25:8: Error: The operator '[]=' isn't defined for the class 'Class2'.
+ - 'Class2' is from 'pkg/front_end/testcases/general/redirecting_factory_invocation_in_invalid.dart'.
+Try correcting the operator to an existing operator, or defining a '[]=' operator.
+ _c2[0] = new Class1();
+ ^";
+ invalid-expression "pkg/front_end/testcases/general/redirecting_factory_invocation_in_invalid.dart:27:12: Error: The operator '[]' isn't defined for the class 'int'.
+Try correcting the operator to an existing operator, or defining a '[]' operator.
+ (0 + 1)[new Class1()];
+ ^";
+ invalid-expression "pkg/front_end/testcases/general/redirecting_factory_invocation_in_invalid.dart:29:18: Error: The getter 'foo' isn't defined for the class 'Class1'.
+ - 'Class1' is from 'pkg/front_end/testcases/general/redirecting_factory_invocation_in_invalid.dart'.
+Try correcting the name to the name of an existing getter, or defining a getter or field named 'foo'.
+ new Class1().foo;
+ ^^^";
+ invalid-expression "pkg/front_end/testcases/general/redirecting_factory_invocation_in_invalid.dart:31:13: Error: The setter 'foo' isn't defined for the class 'int'.
+Try correcting the name to the name of an existing setter, or defining a setter or field named 'foo'.
+ (0 + 1).foo = new Class1();
+ ^^^";
+ invalid-expression "pkg/front_end/testcases/general/redirecting_factory_invocation_in_invalid.dart:33:18: Error: The method 'foo' isn't defined for the class 'Class1'.
+ - 'Class1' is from 'pkg/front_end/testcases/general/redirecting_factory_invocation_in_invalid.dart'.
+Try correcting the name to the name of an existing method, or defining a method named 'foo'.
+ new Class1().foo();
+ ^^^";
+ invalid-expression "pkg/front_end/testcases/general/redirecting_factory_invocation_in_invalid.dart:35:17: Error: The method 'call' isn't defined for the class 'Class1'.
+ - 'Class1' is from 'pkg/front_end/testcases/general/redirecting_factory_invocation_in_invalid.dart'.
+Try correcting the name to the name of an existing method, or defining a method named 'call'.
+ new Class1()();
+ ^";
+ invalid-expression "pkg/front_end/testcases/general/redirecting_factory_invocation_in_invalid.dart:37:23: Error: 'field' isn't a function or method and can't be invoked.
+ new Class1().field();
+ ^^^^...";
+ invalid-expression "pkg/front_end/testcases/general/redirecting_factory_invocation_in_invalid.dart:39:24: Error: 'getter' isn't a function or method and can't be invoked.
+ new Class1().getter();
+ ^^^^...";
+ let final self::Class2 #t1 = this.{self::Class2::_c2}{self::Class2} in let final self::Class1 #t2 = new self::Class1::_() in invalid-expression "pkg/front_end/testcases/general/redirecting_factory_invocation_in_invalid.dart:41:8: Error: 'call' isn't a function or method and can't be invoked.
+ _c2(new Class1());
+ ^^^^";
+ this.{self::Class2::method}(a: invalid-expression "pkg/front_end/testcases/general/redirecting_factory_invocation_in_invalid.dart:43:18: Error: Duplicated named argument 'a'.
+ method(a: 0, a: new Class1());
+ ^"){({a: dynamic}) → dynamic};
+ this.{self::Class2::method}(a: invalid-expression "pkg/front_end/testcases/general/redirecting_factory_invocation_in_invalid.dart:45:24: Error: Duplicated named argument 'a'.
+ method(a: 0, a: 1, a: new Class1());
+ ^"){({a: dynamic}) → dynamic};
+ super.[](new self::Class1::_());
+ super.[]=(0, new self::Class1::_());
+ super.foo = new self::Class1::_();
+ super.foo(new self::Class1::_());
+ super.+(new self::Class1::_());
+ }
+ method method({dynamic a = #C1}) → dynamic {}
+ get call() → core::int
+ return 0;
+}
+static method main() → dynamic {}
+
+constants {
+ #C1 = null
+}
diff --git a/pkg/front_end/testcases/general/redirecting_factory_invocation_in_invalid.dart.weak.outline.expect b/pkg/front_end/testcases/general/redirecting_factory_invocation_in_invalid.dart.weak.outline.expect
new file mode 100644
index 0000000..83aea2e
--- /dev/null
+++ b/pkg/front_end/testcases/general/redirecting_factory_invocation_in_invalid.dart.weak.outline.expect
@@ -0,0 +1,25 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+
+class Class1 extends core::Object {
+ field core::int field;
+ static final field dynamic _redirecting# = <dynamic>[self::Class1::•]/*isLegacy*/;
+ constructor _() → self::Class1
+ ;
+ static factory •() → self::Class1
+ let dynamic #redirecting_factory = self::Class1::_ in invalid-expression;
+ get getter() → core::int
+ ;
+}
+class Class2 extends self::Class1 {
+ final field self::Class2 _c2;
+ constructor •(self::Class2 _c2) → self::Class2
+ ;
+ method method({dynamic a}) → dynamic
+ ;
+ get call() → core::int
+ ;
+}
+static method main() → dynamic
+ ;
diff --git a/pkg/front_end/testcases/general/redirection_chain_type_arguments.dart b/pkg/front_end/testcases/general/redirection_chain_type_arguments.dart
index 7b4cd53..7d7ffec 100644
--- a/pkg/front_end/testcases/general/redirection_chain_type_arguments.dart
+++ b/pkg/front_end/testcases/general/redirection_chain_type_arguments.dart
@@ -1,7 +1,9 @@
// Copyright (c) 2018, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE.md file.
+
// @dart=2.9
+
// The test checks that type arguments of the target of redirection factory
// constructors are preserved throughout the chain of redirections.
diff --git a/pkg/front_end/testcases/text_serialization.status b/pkg/front_end/testcases/text_serialization.status
index 6aacb2f..6d07e17 100644
--- a/pkg/front_end/testcases/text_serialization.status
+++ b/pkg/front_end/testcases/text_serialization.status
@@ -100,6 +100,7 @@
general/override_check_basic: TypeCheckError # Issue #31620
general/override_check_with_covariant_modifier: TypeCheckError # Issue #31620
general/override_setter_with_field: TypeCheckError
+general/redirecting_factory_invocation_in_invalid: TypeCheckError
general/spread_collection: RuntimeError # Should be fixed as part of implementing spread collection support
general/type_parameter_type_named_int: RuntimeError
general/type_variable_as_super: RuntimeError
diff --git a/pkg/front_end/testcases/textual_outline.status b/pkg/front_end/testcases/textual_outline.status
index e8cae1b..f4830a4 100644
--- a/pkg/front_end/testcases/textual_outline.status
+++ b/pkg/front_end/testcases/textual_outline.status
@@ -86,6 +86,7 @@
general/incomplete_field_formal_parameter: FormatterCrash
general/invalid_operator2: FormatterCrash
general/invalid_operator: FormatterCrash
+general/invalid_super_initializer: FormatterCrash
general/issue42997: FormatterCrash
general/issue43363: FormatterCrash
general/issue45490: FormatterCrash
diff --git a/pkg/front_end/testcases/weak.status b/pkg/front_end/testcases/weak.status
index d88eb7b16..9f50ba9 100644
--- a/pkg/front_end/testcases/weak.status
+++ b/pkg/front_end/testcases/weak.status
@@ -106,6 +106,7 @@
general/override_check_basic: TypeCheckError # Issue #31620
general/override_check_with_covariant_modifier: TypeCheckError # Issue #31620
general/override_setter_with_field: TypeCheckError
+general/redirecting_factory_invocation_in_invalid: TypeCheckError
general/spread_collection: RuntimeError
general/type_parameter_type_named_int: RuntimeError # Expected
general/type_variable_as_super: RuntimeError
diff --git a/pkg/kernel/lib/ast.dart b/pkg/kernel/lib/ast.dart
index 0009adc..d7c72c7 100644
--- a/pkg/kernel/lib/ast.dart
+++ b/pkg/kernel/lib/ast.dart
@@ -3082,7 +3082,12 @@
@override
void toTextInternal(AstPrinter printer) {
- // TODO(johnniwinther): Implement this.
+ printer.write('super');
+ if (target.name.text.isNotEmpty) {
+ printer.write('.');
+ printer.write(target.name.text);
+ }
+ printer.writeArguments(arguments, includeTypeArguments: false);
}
}