aa5c2b3da6
ReferenceName is used in equivalence testing to match reference reguardless of whether the reference have associated nodes or canonical names. The implementation didn't previously account for fields, getters and setters with the same simple name, and would wrongfully equate them. Change-Id: Iacc8e0eb2c5c04a3cf11c3b5104a8f0441cf3ab6 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/221621 Reviewed-by: Chloe Stefantsova <cstefantsova@google.com> Commit-Queue: Johnni Winther <johnniwinther@google.com>
334 lines
12 KiB
Dart
334 lines
12 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/src/equivalence.dart';
|
|
|
|
void testReferenceNames(Map<ReferenceNameKind, List<ReferenceNameObject>> map1,
|
|
Map<ReferenceNameKind, List<ReferenceNameObject>> map2) {
|
|
Expect.setEquals(map1.keys, map2.keys);
|
|
map1.forEach((ReferenceNameKind kind1, List<ReferenceNameObject> list1) {
|
|
map1.forEach((ReferenceNameKind kind2, List<ReferenceNameObject> list2) {
|
|
for (int index1 = 0; index1 < list1.length; index1++) {
|
|
for (int index2 = 0; index2 < list2.length; index2++) {
|
|
ReferenceName name1 = list1[index1].referenceName;
|
|
Object object1 = list1[index1].object;
|
|
ReferenceName name2 = list2[index2].referenceName;
|
|
Object object2 = list2[index2].object;
|
|
if (kind1 == kind2 && index1 == index2) {
|
|
Expect.equals(
|
|
name1,
|
|
name2,
|
|
"Expected $name1 for ${object1} (${object1.runtimeType}) and "
|
|
"$name2 for $object2 (${object2.runtimeType}) to be equal.");
|
|
} else {
|
|
Expect.notEquals(
|
|
name1,
|
|
name2,
|
|
"Expected $name1 for ${object1} (${object1.runtimeType}) and "
|
|
"$name2 for $object2 (${object2.runtimeType}) to be unequal.");
|
|
}
|
|
}
|
|
}
|
|
});
|
|
});
|
|
}
|
|
|
|
void main() {
|
|
Component component1 = createComponent();
|
|
Map<ReferenceNameKind, List<ReferenceNameObject>> referenceNames1 =
|
|
computeReferenceNamesFromComponent(component1);
|
|
|
|
Component component2 = createComponent();
|
|
Map<ReferenceNameKind, List<ReferenceNameObject>> referenceNames2 =
|
|
computeReferenceNamesFromComponent(component2);
|
|
|
|
Component component3 = createComponent();
|
|
component3.computeCanonicalNames();
|
|
CanonicalName root3 = component3.root;
|
|
Map<ReferenceNameKind, List<ReferenceNameObject>> referenceNames3 =
|
|
computeReferenceNamesFromCanonicalName(root3);
|
|
|
|
Component component4 = createComponent();
|
|
component4.computeCanonicalNames();
|
|
CanonicalName root4 = component3.root;
|
|
Map<ReferenceNameKind, List<ReferenceNameObject>> referenceNames4 =
|
|
computeReferenceNamesFromCanonicalName(root4);
|
|
|
|
testReferenceNames(referenceNames1, referenceNames2);
|
|
testReferenceNames(referenceNames1, referenceNames3);
|
|
testReferenceNames(referenceNames3, referenceNames4);
|
|
}
|
|
|
|
Component createComponent() {
|
|
Component component = new Component();
|
|
Library library1 = new Library(Uri.parse('test:library1'), fileUri: dummyUri);
|
|
component.libraries.add(library1);
|
|
Library library2 = new Library(Uri.parse('test:library2'), fileUri: dummyUri);
|
|
component.libraries.add(library2);
|
|
|
|
library1.addProcedure(new Procedure(
|
|
new Name('foo'), ProcedureKind.Method, new FunctionNode(null),
|
|
fileUri: dummyUri));
|
|
library1.addProcedure(new Procedure(
|
|
new Name('bar'), ProcedureKind.Operator, new FunctionNode(null),
|
|
fileUri: dummyUri));
|
|
library1.addProcedure(new Procedure(
|
|
new Name('baz'), ProcedureKind.Factory, new FunctionNode(null),
|
|
fileUri: dummyUri));
|
|
library1.addProcedure(new Procedure(
|
|
new Name('boz'), ProcedureKind.Getter, new FunctionNode(null),
|
|
fileUri: dummyUri));
|
|
// The setter should be distinct from the getter even when they have the same
|
|
// name.
|
|
library1.addProcedure(new Procedure(
|
|
new Name('boz'), ProcedureKind.Setter, new FunctionNode(null),
|
|
fileUri: dummyUri));
|
|
|
|
library1.addProcedure(new Procedure(
|
|
new Name('_boz', library2), ProcedureKind.Getter, new FunctionNode(null),
|
|
fileUri: dummyUri));
|
|
// The setter should be distinct from the getter even when they have the same
|
|
// name.
|
|
library1.addProcedure(new Procedure(
|
|
new Name('_boz', library2), ProcedureKind.Setter, new FunctionNode(null),
|
|
fileUri: dummyUri));
|
|
|
|
library1.addField(
|
|
new Field.immutable(new Name('_foo', library1), fileUri: dummyUri));
|
|
library1.addField(
|
|
new Field.mutable(new Name('_bar', library2), fileUri: dummyUri));
|
|
|
|
Class class1 = new Class(name: 'Foo', fileUri: dummyUri);
|
|
library2.addClass(class1);
|
|
Class class2 = new Class(name: 'Bar', fileUri: dummyUri);
|
|
library2.addClass(class2);
|
|
|
|
class2.addConstructor(new Constructor(new FunctionNode(null),
|
|
name: new Name(''), fileUri: dummyUri));
|
|
class2.addConstructor(new Constructor(new FunctionNode(null),
|
|
name: new Name('_', library1), fileUri: dummyUri));
|
|
|
|
class2.addProcedure(new Procedure(
|
|
new Name('foo'), ProcedureKind.Method, new FunctionNode(null),
|
|
fileUri: dummyUri));
|
|
class2.addProcedure(new Procedure(
|
|
new Name('bar'), ProcedureKind.Operator, new FunctionNode(null),
|
|
fileUri: dummyUri));
|
|
class2.addProcedure(new Procedure(
|
|
new Name('baz'), ProcedureKind.Factory, new FunctionNode(null),
|
|
fileUri: dummyUri));
|
|
class2.addProcedure(new Procedure(
|
|
new Name('boz'), ProcedureKind.Getter, new FunctionNode(null),
|
|
fileUri: dummyUri));
|
|
// The setter should be distinct from the getter even when they have the same
|
|
// name.
|
|
class2.addProcedure(new Procedure(
|
|
new Name('boz'), ProcedureKind.Setter, new FunctionNode(null),
|
|
fileUri: dummyUri));
|
|
|
|
class2.addProcedure(new Procedure(
|
|
new Name('_boz', library2), ProcedureKind.Getter, new FunctionNode(null),
|
|
fileUri: dummyUri));
|
|
// The setter should be distinct from the getter even when they have the same
|
|
// name.
|
|
class2.addProcedure(new Procedure(
|
|
new Name('_boz', library2), ProcedureKind.Setter, new FunctionNode(null),
|
|
fileUri: dummyUri));
|
|
|
|
class2.addField(
|
|
new Field.immutable(new Name('_foo', library1), fileUri: dummyUri));
|
|
class2.addField(
|
|
new Field.mutable(new Name('_bar', library2), fileUri: dummyUri));
|
|
|
|
class2.addRedirectingFactory(new RedirectingFactory(null,
|
|
name: new Name('_boz', library1),
|
|
function: new FunctionNode(null),
|
|
fileUri: dummyUri));
|
|
|
|
library1.addExtension(new Extension(name: 'Baz', fileUri: dummyUri));
|
|
|
|
library1.addTypedef(new Typedef('Boz', dummyDartType, fileUri: dummyUri));
|
|
|
|
return component;
|
|
}
|
|
|
|
void sortReferenceNames(Map<ReferenceNameKind, List<ReferenceNameObject>> map) {
|
|
map.forEach((key, value) {
|
|
value.sort(
|
|
(n1, n2) => n1.referenceName.name!.compareTo(n2.referenceName.name!));
|
|
});
|
|
}
|
|
|
|
Map<ReferenceNameKind, List<ReferenceNameObject>>
|
|
computeReferenceNamesFromComponent(Component component) {
|
|
Map<ReferenceNameKind, List<ReferenceNameObject>> map = {};
|
|
void add(ReferenceNameKind kind, ReferenceNameObject object) {
|
|
(map[kind] ??= []).add(object);
|
|
}
|
|
|
|
for (Library library in component.libraries) {
|
|
add(ReferenceNameKind.Library,
|
|
new ReferenceNameObject(ReferenceName.fromNamedNode(library), library));
|
|
for (Typedef typedef in library.typedefs) {
|
|
add(
|
|
ReferenceNameKind.Typedef,
|
|
new ReferenceNameObject(
|
|
ReferenceName.fromNamedNode(typedef), typedef));
|
|
}
|
|
for (Field field in library.fields) {
|
|
add(
|
|
ReferenceNameKind.Field,
|
|
new ReferenceNameObject(
|
|
ReferenceName.fromNamedNode(field, ReferenceNameKind.Field),
|
|
field));
|
|
add(
|
|
ReferenceNameKind.Getter,
|
|
new ReferenceNameObject(
|
|
ReferenceName.fromNamedNode(field, ReferenceNameKind.Getter),
|
|
field));
|
|
if (field.hasSetter) {
|
|
add(
|
|
ReferenceNameKind.Setter,
|
|
new ReferenceNameObject(
|
|
ReferenceName.fromNamedNode(field, ReferenceNameKind.Setter),
|
|
field));
|
|
}
|
|
}
|
|
for (Procedure procedure in library.procedures) {
|
|
ReferenceNameKind kind;
|
|
if (procedure.isGetter) {
|
|
kind = ReferenceNameKind.Getter;
|
|
} else if (procedure.isSetter) {
|
|
kind = ReferenceNameKind.Setter;
|
|
} else {
|
|
kind = ReferenceNameKind.Function;
|
|
}
|
|
add(
|
|
kind,
|
|
new ReferenceNameObject(
|
|
ReferenceName.fromNamedNode(procedure), procedure));
|
|
}
|
|
for (Class cls in library.classes) {
|
|
add(ReferenceNameKind.Declaration,
|
|
new ReferenceNameObject(ReferenceName.fromNamedNode(cls), cls));
|
|
|
|
for (Constructor constructor in cls.constructors) {
|
|
add(
|
|
ReferenceNameKind.Function,
|
|
new ReferenceNameObject(
|
|
ReferenceName.fromNamedNode(constructor), constructor));
|
|
}
|
|
for (Procedure procedure in cls.procedures) {
|
|
ReferenceNameKind kind;
|
|
if (procedure.isGetter) {
|
|
kind = ReferenceNameKind.Getter;
|
|
} else if (procedure.isSetter) {
|
|
kind = ReferenceNameKind.Setter;
|
|
} else {
|
|
kind = ReferenceNameKind.Function;
|
|
}
|
|
add(
|
|
kind,
|
|
new ReferenceNameObject(
|
|
ReferenceName.fromNamedNode(procedure), procedure));
|
|
}
|
|
for (Field field in cls.fields) {
|
|
add(ReferenceNameKind.Field,
|
|
new ReferenceNameObject(ReferenceName.fromNamedNode(field), field));
|
|
add(
|
|
ReferenceNameKind.Getter,
|
|
new ReferenceNameObject(
|
|
ReferenceName.fromNamedNode(field, ReferenceNameKind.Getter),
|
|
field));
|
|
if (field.hasSetter) {
|
|
add(
|
|
ReferenceNameKind.Setter,
|
|
new ReferenceNameObject(
|
|
ReferenceName.fromNamedNode(field, ReferenceNameKind.Setter),
|
|
field));
|
|
}
|
|
}
|
|
for (RedirectingFactory redirectingFactory in cls.redirectingFactories) {
|
|
add(
|
|
ReferenceNameKind.Function,
|
|
new ReferenceNameObject(
|
|
ReferenceName.fromNamedNode(redirectingFactory),
|
|
redirectingFactory));
|
|
}
|
|
}
|
|
for (Extension extension in library.extensions) {
|
|
add(
|
|
ReferenceNameKind.Declaration,
|
|
new ReferenceNameObject(
|
|
ReferenceName.fromNamedNode(extension), extension));
|
|
}
|
|
}
|
|
sortReferenceNames(map);
|
|
return map;
|
|
}
|
|
|
|
Map<ReferenceNameKind, List<ReferenceNameObject>>
|
|
computeReferenceNamesFromCanonicalName(CanonicalName root) {
|
|
Map<ReferenceNameKind, List<ReferenceNameObject>> map = {};
|
|
|
|
void visit(CanonicalName canonicalName, ReferenceNameKind kind) {
|
|
void addObject() {
|
|
(map[kind] ??= []).add(new ReferenceNameObject(
|
|
ReferenceName.fromCanonicalName(canonicalName), canonicalName));
|
|
}
|
|
|
|
switch (kind) {
|
|
case ReferenceNameKind.Unknown:
|
|
for (CanonicalName child in canonicalName.children) {
|
|
visit(child, ReferenceNameKind.Library);
|
|
}
|
|
break;
|
|
case ReferenceNameKind.Library:
|
|
addObject();
|
|
for (CanonicalName child in canonicalName.children) {
|
|
ReferenceNameKind childKind = ReferenceNameKind.Declaration;
|
|
if (CanonicalName.isSymbolicName(child.name)) {
|
|
childKind = ReferenceName.kindFromSymbolicName(child.name);
|
|
}
|
|
visit(child, childKind);
|
|
}
|
|
break;
|
|
case ReferenceNameKind.Declaration:
|
|
addObject();
|
|
for (CanonicalName child in canonicalName.children) {
|
|
visit(child, ReferenceName.kindFromSymbolicName(child.name));
|
|
}
|
|
break;
|
|
case ReferenceNameKind.Typedef:
|
|
case ReferenceNameKind.Function:
|
|
case ReferenceNameKind.Field:
|
|
case ReferenceNameKind.Getter:
|
|
case ReferenceNameKind.Setter:
|
|
if (canonicalName.childrenOrNull != null) {
|
|
// Private name
|
|
for (CanonicalName child in canonicalName.children) {
|
|
visit(child, kind);
|
|
}
|
|
} else {
|
|
addObject();
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
visit(root, ReferenceNameKind.Unknown);
|
|
|
|
sortReferenceNames(map);
|
|
return map;
|
|
}
|
|
|
|
class ReferenceNameObject {
|
|
final ReferenceName referenceName;
|
|
final Object object;
|
|
|
|
ReferenceNameObject(this.referenceName, this.object);
|
|
}
|