d7f200b97d
(Part of https://github.com/dart-lang/sdk/issues/63288) This change migrates the kernel package to use the new constructor declaration syntax, described in https://github.com/dart-lang/language/blob/main/accepted/future-releases/primary-constructors/feature-specification.md#abbreviations-of-in-body-constructor-declarations. This change was performed in an automated fashion, by (a) bumping the package's SDK constraint to `3.13.0-0`, (b) enabling the lints `unnecessary_type_name_in_constructor` and `unnecessary_const_in_enum_constructor`, (c) fixing the resulting lint failures using `dart fix`, and then (d) reformatting the affected files. To ease code review, I've reverted unrelated formatting changes. Change-Id: I8d32a0b26450a44dfa2ebd9fe2dff9df6a6a6964 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/508426 Reviewed-by: Konstantin Shcheglov <scheglov@google.com> Commit-Queue: Paul Berry <paulberry@google.com>
254 lines
6.9 KiB
Dart
254 lines
6.9 KiB
Dart
// 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.
|
|
|
|
import 'package:expect/expect.dart';
|
|
import 'package:kernel/ast.dart';
|
|
import 'package:kernel/clone.dart';
|
|
import 'package:kernel/src/coverage.dart';
|
|
import 'package:kernel/src/equivalence.dart';
|
|
import 'package:kernel/src/node_creator.dart';
|
|
|
|
void main() {
|
|
testBodyCloning();
|
|
testBodyCloningInContext();
|
|
testMemberCloning();
|
|
}
|
|
|
|
void testBodyCloning() {
|
|
// TODO(johnniwinther): Add a test for cloning in context.
|
|
NodeCreator creator = new NodeCreator(
|
|
initializers: [],
|
|
members: [],
|
|
nodes: inBodyNodeKinds,
|
|
);
|
|
List<TreeNode> nodes = creator.generateBodies();
|
|
|
|
CoverageVisitor coverageVisitor = new CoverageVisitor();
|
|
for (TreeNode node in nodes) {
|
|
node.accept(coverageVisitor);
|
|
CloneVisitorNotMembers cloner = new CloneVisitorNotMembers();
|
|
TreeNode clone = cloner.clone(node);
|
|
EquivalenceResult result = checkEquivalence(
|
|
node,
|
|
clone,
|
|
strategy: const NoFileOffsetEquivalenceStrategy(),
|
|
);
|
|
if (!result.isEquivalent) {
|
|
print(result);
|
|
}
|
|
Expect.isTrue(result.isEquivalent, "$node");
|
|
}
|
|
Expect.isEmpty(
|
|
creator.createdKinds.toSet()..removeAll(coverageVisitor.visited),
|
|
'Nodes not covered in testing.',
|
|
);
|
|
}
|
|
|
|
void testBodyCloningInContext() {
|
|
NodeCreator creator = new NodeCreator(
|
|
initializers: [],
|
|
members: [],
|
|
nodes: inBodyNodeKinds,
|
|
);
|
|
List<Statement> nodes = creator.generateBodies();
|
|
|
|
CoverageVisitor coverageVisitor = new CoverageVisitor();
|
|
for (Statement node in nodes) {
|
|
node.accept(coverageVisitor);
|
|
CloneVisitorNotMembers cloner = new CloneVisitorNotMembers();
|
|
// Set up context for [statement].
|
|
new Procedure(
|
|
Name('foo'),
|
|
ProcedureKind.Method,
|
|
FunctionNode(node),
|
|
fileUri: dummyUri,
|
|
);
|
|
TreeNode clone = cloner.cloneInContext(node);
|
|
EquivalenceResult result = checkEquivalence(node, clone);
|
|
if (!result.isEquivalent) {
|
|
print(result);
|
|
}
|
|
Expect.isTrue(result.isEquivalent, "$node");
|
|
}
|
|
Expect.isEmpty(
|
|
creator.createdKinds.toSet()..removeAll(coverageVisitor.visited),
|
|
'Nodes not covered in testing.',
|
|
);
|
|
}
|
|
|
|
void testMemberCloning() {
|
|
NodeCreator creator = new NodeCreator(nodes: inBodyNodeKinds);
|
|
Component component = creator.generateComponent();
|
|
|
|
CoverageVisitor coverageVisitor = new CoverageVisitor();
|
|
|
|
void testMembers<M extends Member>(
|
|
Iterable<M> members,
|
|
M Function(CloneVisitorWithMembers, M) cloneFunction,
|
|
String Function(M) toStringFunction,
|
|
) {
|
|
for (M member in members) {
|
|
member.accept(coverageVisitor);
|
|
CloneVisitorWithMembers cloner = new CloneVisitorWithMembers();
|
|
M clone = cloneFunction(cloner, member);
|
|
EquivalenceResult result = checkEquivalence(
|
|
member,
|
|
clone,
|
|
strategy: const MemberEquivalenceStrategy(),
|
|
);
|
|
if (!result.isEquivalent) {
|
|
print(result);
|
|
}
|
|
Expect.isTrue(result.isEquivalent, toStringFunction(member));
|
|
}
|
|
}
|
|
|
|
void testProcedures(Iterable<Procedure> procedures) {
|
|
testMembers<Procedure>(
|
|
procedures,
|
|
(cloner, procedure) => cloner.cloneProcedure(procedure, null),
|
|
(procedure) =>
|
|
"${procedure.runtimeType}(${procedure.name}):"
|
|
"${procedure.function.body}",
|
|
);
|
|
}
|
|
|
|
void testFields(Iterable<Field> fields) {
|
|
testMembers<Field>(
|
|
fields,
|
|
(cloner, field) => cloner.cloneField(field, null, null, null),
|
|
(field) =>
|
|
"${field.runtimeType}(${field.name}):"
|
|
"${field.initializer}",
|
|
);
|
|
}
|
|
|
|
void testConstructors(Iterable<Constructor> constructors) {
|
|
testMembers<Constructor>(
|
|
constructors,
|
|
(cloner, constructor) => cloner.cloneConstructor(constructor, null),
|
|
(constructor) =>
|
|
"${constructor.runtimeType}(${constructor.name}):"
|
|
"${constructor.initializers}:"
|
|
"${constructor.function.body}",
|
|
);
|
|
}
|
|
|
|
for (Library library in component.libraries) {
|
|
testProcedures(library.procedures);
|
|
testFields(library.fields);
|
|
for (Class cls in library.classes) {
|
|
testProcedures(cls.procedures);
|
|
testFields(cls.fields);
|
|
testConstructors(cls.constructors);
|
|
}
|
|
}
|
|
Expect.isEmpty(
|
|
creator.createdKinds.toSet()..removeAll(coverageVisitor.visited),
|
|
'Nodes not covered in testing.',
|
|
);
|
|
}
|
|
|
|
class NoFileOffsetEquivalenceStrategy extends EquivalenceStrategy {
|
|
const new();
|
|
|
|
@override
|
|
bool checkTreeNode_fileOffset(
|
|
EquivalenceVisitor visitor,
|
|
TreeNode node,
|
|
TreeNode other,
|
|
) {
|
|
if (other.fileOffset == TreeNode.noOffset) return true;
|
|
return super.checkTreeNode_fileOffset(visitor, node, other);
|
|
}
|
|
}
|
|
|
|
class MemberEquivalenceStrategy extends EquivalenceStrategy {
|
|
const new();
|
|
|
|
void assumeClonedReferences(
|
|
EquivalenceVisitor visitor,
|
|
Member member1,
|
|
Reference? reference1,
|
|
Member member2,
|
|
Reference? reference2,
|
|
) {
|
|
if (reference1 != null && reference2 != null) {
|
|
ReferenceName referenceName1 = ReferenceName.fromNamedNode(member1);
|
|
ReferenceName referenceName2 = ReferenceName.fromNamedNode(member2);
|
|
if (referenceName1.kind == referenceName2.kind &&
|
|
referenceName1.memberName == referenceName2.memberName &&
|
|
referenceName1.memberUri == referenceName2.memberUri &&
|
|
referenceName2.declarationName == null ||
|
|
referenceName2.libraryUri == null) {
|
|
visitor.assumeReferences(reference1, reference2);
|
|
}
|
|
}
|
|
}
|
|
|
|
@override
|
|
bool checkProcedure(
|
|
EquivalenceVisitor visitor,
|
|
Procedure? node,
|
|
Object? other,
|
|
) {
|
|
if (node is Procedure && other is Procedure) {
|
|
assumeClonedReferences(
|
|
visitor,
|
|
node,
|
|
node.reference,
|
|
other,
|
|
other.reference,
|
|
);
|
|
}
|
|
return super.checkProcedure(visitor, node, other);
|
|
}
|
|
|
|
@override
|
|
bool checkConstructor(
|
|
EquivalenceVisitor visitor,
|
|
Constructor? node,
|
|
Object? other,
|
|
) {
|
|
if (node is Constructor && other is Constructor) {
|
|
assumeClonedReferences(
|
|
visitor,
|
|
node,
|
|
node.reference,
|
|
other,
|
|
other.reference,
|
|
);
|
|
}
|
|
return super.checkConstructor(visitor, node, other);
|
|
}
|
|
|
|
@override
|
|
bool checkField(EquivalenceVisitor visitor, Field? node, Object? other) {
|
|
if (node is Field && other is Field) {
|
|
assumeClonedReferences(
|
|
visitor,
|
|
node,
|
|
node.fieldReference,
|
|
other,
|
|
other.fieldReference,
|
|
);
|
|
assumeClonedReferences(
|
|
visitor,
|
|
node,
|
|
node.getterReference,
|
|
other,
|
|
other.getterReference,
|
|
);
|
|
assumeClonedReferences(
|
|
visitor,
|
|
node,
|
|
node.setterReference,
|
|
other,
|
|
other.setterReference,
|
|
);
|
|
}
|
|
return super.checkField(visitor, node, other);
|
|
}
|
|
}
|