blob: f8901a310762e8d2646070abf97b73e080e6fef8 [file] [log] [blame]
// Copyright (c) 2019, 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:analyzer/dart/element/element.dart';
import 'package:analyzer/dart/element/nullability_suffix.dart';
import 'package:analyzer/dart/element/type.dart';
import 'package:analyzer/src/dart/element/inheritance_manager3.dart';
import 'package:meta/meta.dart';
import 'package:test/test.dart';
import 'package:test_reflective_loader/test_reflective_loader.dart';
import '../resolution/driver_resolution.dart';
main() {
defineReflectiveSuite(() {
defineReflectiveTests(InheritanceManager3Test);
});
}
@reflectiveTest
class InheritanceManager3Test extends DriverResolutionTest {
InheritanceManager3 manager;
@override
Future<void> resolveTestFile() async {
await super.resolveTestFile();
manager = new InheritanceManager3(
result.unit.declaredElement.context.typeSystem,
);
}
test_getInherited_closestSuper() async {
await resolveTestCode('''
class A {
void foo() {}
}
class B extends A {
void foo() {}
}
class X extends B {
void foo() {}
}
''');
_assertGetInherited(
className: 'X',
name: 'foo',
expected: 'B.foo: void Function()',
);
}
test_getInherited_interfaces() async {
await resolveTestCode('''
abstract class I {
void foo();
}
abstrac class J {
void foo();
}
class X implements I, J {
void foo() {}
}
''');
_assertGetInherited(
className: 'X',
name: 'foo',
expected: 'J.foo: void Function()',
);
}
test_getInherited_mixin() async {
await resolveTestCode('''
class A {
void foo() {}
}
mixin M {
void foo() {}
}
class X extends A with M {
void foo() {}
}
''');
_assertGetInherited(
className: 'X',
name: 'foo',
expected: 'M.foo: void Function()',
);
}
test_getInherited_preferImplemented() async {
await resolveTestCode('''
class A {
void foo() {}
}
class I {
void foo() {}
}
class X extends A implements I {
void foo() {}
}
''');
_assertGetInherited(
className: 'X',
name: 'foo',
expected: 'A.foo: void Function()',
);
}
test_getInheritedConcreteMap_accessor_extends() async {
await resolveTestCode('''
class A {
int get foo => 0;
}
class B extends A {}
''');
_assertInheritedConcreteMap('B', r'''
A.foo: int Function()
''');
}
test_getInheritedConcreteMap_accessor_implements() async {
await resolveTestCode('''
class A {
int get foo => 0;
}
abstract class B implements A {}
''');
_assertInheritedConcreteMap('B', '');
}
test_getInheritedConcreteMap_accessor_with() async {
await resolveTestCode('''
mixin A {
int get foo => 0;
}
class B extends Object with A {}
''');
_assertInheritedConcreteMap('B', r'''
A.foo: int Function()
''');
}
test_getInheritedConcreteMap_implicitExtends() async {
await resolveTestCode('''
class A {}
''');
_assertInheritedConcreteMap('A', '');
}
test_getInheritedConcreteMap_method_extends() async {
await resolveTestCode('''
class A {
void foo() {}
}
class B extends A {}
''');
_assertInheritedConcreteMap('B', r'''
A.foo: void Function()
''');
}
test_getInheritedConcreteMap_method_extends_abstract() async {
await resolveTestCode('''
abstract class A {
void foo();
}
class B extends A {}
''');
_assertInheritedConcreteMap('B', '');
}
test_getInheritedConcreteMap_method_extends_invalidForImplements() async {
await resolveTestCode('''
abstract class I {
void foo(int x, {int y});
void bar(String s);
}
class A {
void foo(int x) {}
}
class C extends A implements I {}
''');
_assertInheritedConcreteMap('C', r'''
A.foo: void Function(int)
''');
}
test_getInheritedConcreteMap_method_implements() async {
await resolveTestCode('''
class A {
void foo() {}
}
abstract class B implements A {}
''');
_assertInheritedConcreteMap('B', '');
}
test_getInheritedConcreteMap_method_with() async {
await resolveTestCode('''
mixin A {
void foo() {}
}
class B extends Object with A {}
''');
_assertInheritedConcreteMap('B', r'''
A.foo: void Function()
''');
}
test_getInheritedConcreteMap_method_with2() async {
await resolveTestCode('''
mixin A {
void foo() {}
}
mixin B {
void bar() {}
}
class C extends Object with A, B {}
''');
_assertInheritedConcreteMap('C', r'''
A.foo: void Function()
B.bar: void Function()
''');
}
test_getInheritedMap_accessor_extends() async {
await resolveTestCode('''
class A {
int get foo => 0;
}
class B extends A {}
''');
_assertInheritedMap('B', r'''
A.foo: int Function()
''');
}
test_getInheritedMap_accessor_implements() async {
await resolveTestCode('''
class A {
int get foo => 0;
}
abstract class B implements A {}
''');
_assertInheritedMap('B', r'''
A.foo: int Function()
''');
}
test_getInheritedMap_accessor_with() async {
await resolveTestCode('''
mixin A {
int get foo => 0;
}
class B extends Object with A {}
''');
_assertInheritedMap('B', r'''
A.foo: int Function()
''');
}
test_getInheritedMap_closestSuper() async {
await resolveTestCode('''
class A {
void foo() {}
}
class B extends A {
void foo() {}
}
class X extends B {}
''');
_assertInheritedMap('X', r'''
B.foo: void Function()
''');
}
test_getInheritedMap_field_extends() async {
await resolveTestCode('''
class A {
int foo;
}
class B extends A {}
''');
_assertInheritedMap('B', r'''
A.foo: int Function()
A.foo=: void Function(int)
''');
}
test_getInheritedMap_field_implements() async {
await resolveTestCode('''
class A {
int foo;
}
abstract class B implements A {}
''');
_assertInheritedMap('B', r'''
A.foo: int Function()
A.foo=: void Function(int)
''');
}
test_getInheritedMap_field_with() async {
await resolveTestCode('''
mixin A {
int foo;
}
class B extends Object with A {}
''');
_assertInheritedMap('B', r'''
A.foo: int Function()
A.foo=: void Function(int)
''');
}
test_getInheritedMap_implicitExtendsObject() async {
await resolveTestCode('''
class A {}
''');
_assertInheritedMap('A', '');
}
test_getInheritedMap_method_extends() async {
await resolveTestCode('''
class A {
void foo() {}
}
class B extends A {}
''');
_assertInheritedMap('B', r'''
A.foo: void Function()
''');
}
test_getInheritedMap_method_implements() async {
await resolveTestCode('''
class A {
void foo() {}
}
abstract class B implements A {}
''');
_assertInheritedMap('B', r'''
A.foo: void Function()
''');
}
test_getInheritedMap_method_with() async {
await resolveTestCode('''
mixin A {
void foo() {}
}
class B extends Object with A {}
''');
_assertInheritedMap('B', r'''
A.foo: void Function()
''');
}
test_getInheritedMap_preferImplemented() async {
await resolveTestCode('''
class A {
void foo() {}
}
class I {
void foo() {}
}
class X extends A implements I {
void foo() {}
}
''');
_assertInheritedMap('X', r'''
A.foo: void Function()
''');
}
test_getInheritedMap_union_conflict() async {
await resolveTestCode('''
abstract class I {
int foo();
void bar();
}
abstract class J {
double foo();
void bar();
}
abstract class A implements I, J {}
''');
_assertInheritedMap('A', r'''
J.bar: void Function()
''');
}
test_getInheritedMap_union_differentNames() async {
await resolveTestCode('''
abstract class I {
int foo();
}
abstract class J {
double bar();
}
abstract class A implements I, J {}
''');
_assertInheritedMap('A', r'''
I.foo: int Function()
J.bar: double Function()
''');
}
test_getInheritedMap_union_multipleSubtypes_2_getters() async {
await resolveTestCode('''
abstract class I {
int get foo;
}
abstract class J {
int get foo;
}
abstract class A implements I, J {}
''');
_assertInheritedMap('A', r'''
J.foo: int Function()
''');
}
test_getInheritedMap_union_multipleSubtypes_2_methods() async {
await resolveTestCode('''
abstract class I {
void foo();
}
abstract class J {
void foo();
}
abstract class A implements I, J {}
''');
_assertInheritedMap('A', r'''
J.foo: void Function()
''');
}
test_getInheritedMap_union_multipleSubtypes_2_setters() async {
await resolveTestCode('''
abstract class I {
void set foo(num _);
}
abstract class J {
void set foo(int _);
}
abstract class A implements I, J {}
abstract class B implements J, I {}
''');
_assertInheritedMap('A', r'''
I.foo=: void Function(num)
''');
_assertInheritedMap('B', r'''
I.foo=: void Function(num)
''');
}
test_getInheritedMap_union_multipleSubtypes_3_getters() async {
await resolveTestCode('''
class A {}
class B extends A {}
class C extends B {}
abstract class I1 {
A get foo;
}
abstract class I2 {
B get foo;
}
abstract class I3 {
C get foo;
}
abstract class D implements I1, I2, I3 {}
abstract class E implements I3, I2, I1 {}
''');
_assertInheritedMap('D', r'''
I3.foo: C Function()
''');
_assertInheritedMap('E', r'''
I3.foo: C Function()
''');
}
test_getInheritedMap_union_multipleSubtypes_3_methods() async {
await resolveTestCode('''
class A {}
class B extends A {}
class C extends B {}
abstract class I1 {
void foo(A _);
}
abstract class I2 {
void foo(B _);
}
abstract class I3 {
void foo(C _);
}
abstract class D implements I1, I2, I3 {}
abstract class E implements I3, I2, I1 {}
''');
_assertInheritedMap('D', r'''
I1.foo: void Function(A)
''');
}
test_getInheritedMap_union_multipleSubtypes_3_setters() async {
await resolveTestCode('''
class A {}
class B extends A {}
class C extends B {}
abstract class I1 {
set foo(A _);
}
abstract class I2 {
set foo(B _);
}
abstract class I3 {
set foo(C _);
}
abstract class D implements I1, I2, I3 {}
abstract class E implements I3, I2, I1 {}
''');
_assertInheritedMap('D', r'''
I1.foo=: void Function(A)
''');
_assertInheritedMap('E', r'''
I1.foo=: void Function(A)
''');
}
test_getInheritedMap_union_oneSubtype_2_methods() async {
await resolveTestCode('''
abstract class I1 {
int foo();
}
abstract class I2 {
int foo([int _]);
}
abstract class A implements I1, I2 {}
abstract class B implements I2, I1 {}
''');
_assertInheritedMap('A', r'''
I2.foo: int Function([int])
''');
_assertInheritedMap('B', r'''
I2.foo: int Function([int])
''');
}
test_getInheritedMap_union_oneSubtype_3_methods() async {
await resolveTestCode('''
abstract class I1 {
int foo();
}
abstract class I2 {
int foo([int _]);
}
abstract class I3 {
int foo([int _, int __]);
}
abstract class A implements I1, I2, I3 {}
abstract class B implements I3, I2, I1 {}
''');
_assertInheritedMap('A', r'''
I3.foo: int Function([int, int])
''');
_assertInheritedMap('B', r'''
I3.foo: int Function([int, int])
''');
}
test_getMember() async {
await resolveTestCode('''
abstract class I1 {
void f(int i);
}
abstract class I2 {
void f(Object o);
}
abstract class C implements I1, I2 {}
''');
_assertGetMember(
className: 'C',
name: 'f',
expected: 'I2.f: void Function(Object)',
);
}
test_getMember_concrete() async {
await resolveTestCode('''
class A {
void foo() {}
}
''');
_assertGetMember(
className: 'A',
name: 'foo',
concrete: true,
expected: 'A.foo: void Function()',
);
}
test_getMember_concrete_abstract() async {
await resolveTestCode('''
abstract class A {
void foo();
}
''');
_assertGetMember(
className: 'A',
name: 'foo',
concrete: true,
);
}
test_getMember_concrete_fromMixedClass() async {
await resolveTestCode('''
class A {
void foo() {}
}
class X with A {}
''');
_assertGetMember(
className: 'X',
name: 'foo',
concrete: true,
expected: 'A.foo: void Function()',
);
}
test_getMember_concrete_fromMixedClass2() async {
await resolveTestCode('''
class A {
void foo() {}
}
class B = Object with A;
class X with B {}
''');
_assertGetMember(
className: 'X',
name: 'foo',
concrete: true,
expected: 'A.foo: void Function()',
);
}
test_getMember_concrete_fromMixedClass_skipObject() async {
await resolveTestCode('''
class A {
String toString() => 'A';
}
class B {}
class X extends A with B {}
''');
_assertGetMember(
className: 'X',
name: 'toString',
concrete: true,
expected: 'A.toString: String Function()',
);
}
test_getMember_concrete_fromMixin() async {
await resolveTestCode('''
mixin M {
void foo() {}
}
class X with M {}
''');
_assertGetMember(
className: 'X',
name: 'foo',
concrete: true,
expected: 'M.foo: void Function()',
);
}
test_getMember_concrete_fromSuper() async {
await resolveTestCode('''
class A {
void foo() {}
}
class B extends A {}
abstract class C extends B {}
''');
_assertGetMember(
className: 'B',
name: 'foo',
concrete: true,
expected: 'A.foo: void Function()',
);
_assertGetMember(
className: 'C',
name: 'foo',
concrete: true,
expected: 'A.foo: void Function()',
);
}
test_getMember_concrete_missing() async {
await resolveTestCode('''
abstract class A {}
''');
_assertGetMember(
className: 'A',
name: 'foo',
concrete: true,
);
}
test_getMember_concrete_noSuchMethod() async {
await resolveTestCode('''
class A {
void foo() {}
}
class B implements A {
noSuchMethod(_) {}
}
abstract class C extends B {}
''');
_assertGetMember(
className: 'B',
name: 'foo',
concrete: true,
expected: 'A.foo: void Function()',
);
_assertGetMember(
className: 'C',
name: 'foo',
concrete: true,
expected: 'A.foo: void Function()',
);
}
test_getMember_concrete_noSuchMethod_mixin() async {
await resolveTestCode('''
class A {
void foo();
noSuchMethod(_) {}
}
abstract class B extends Object with A {}
''');
// noSuchMethod forwarders are not mixed-in.
// https://github.com/dart-lang/sdk/issues/33553#issuecomment-424638320
_assertGetMember(
className: 'B',
name: 'foo',
concrete: true,
);
}
test_getMember_concrete_noSuchMethod_moreSpecificSignature() async {
await resolveTestCode('''
class A {
void foo() {}
}
class B implements A {
noSuchMethod(_) {}
}
class C extends B {
void foo([int a]);
}
''');
_assertGetMember(
className: 'C',
name: 'foo',
concrete: true,
expected: 'C.foo: void Function([int])',
);
}
test_getMember_preferLatest_mixin() async {
await resolveTestCode('''
class A {
void foo() {}
}
mixin M1 {
void foo() {}
}
mixin M2 {
void foo() {}
}
abstract class I {
void foo();
}
class X extends A with M1, M2 implements I {}
''');
_assertGetMember(
className: 'X',
name: 'foo',
expected: 'M2.foo: void Function()',
);
}
test_getMember_preferLatest_superclass() async {
await resolveTestCode('''
class A {
void foo() {}
}
class B extends A {
void foo() {}
}
abstract class I {
void foo();
}
class X extends B implements I {}
''');
_assertGetMember(
className: 'X',
name: 'foo',
expected: 'B.foo: void Function()',
);
}
test_getMember_preferLatest_this() async {
await resolveTestCode('''
class A {
void foo() {}
}
abstract class I {
void foo();
}
class X extends A implements I {
void foo() {}
}
''');
_assertGetMember(
className: 'X',
name: 'foo',
expected: 'X.foo: void Function()',
);
}
test_getMember_super_abstract() async {
await resolveTestCode('''
abstract class A {
void foo();
}
class B extends A {
noSuchMethod(_) {}
}
''');
_assertGetMember(
className: 'B',
name: 'foo',
forSuper: true,
);
}
test_getMember_super_forMixin_interface() async {
await resolveTestCode('''
abstract class A {
void foo();
}
mixin M implements A {}
''');
_assertGetMember(
className: 'M',
name: 'foo',
forSuper: true,
);
}
test_getMember_super_forMixin_superclassConstraint() async {
await resolveTestCode('''
abstract class A {
void foo();
}
mixin M on A {}
''');
_assertGetMember(
className: 'M',
name: 'foo',
forSuper: true,
expected: 'A.foo: void Function()',
);
}
test_getMember_super_fromMixin() async {
await resolveTestCode('''
mixin M {
void foo() {}
}
class X extends Object with M {
void foo() {}
}
''');
_assertGetMember(
className: 'X',
name: 'foo',
forSuper: true,
expected: 'M.foo: void Function()',
);
}
test_getMember_super_fromSuper() async {
await resolveTestCode('''
class A {
void foo() {}
}
class B extends A {
void foo() {}
}
''');
_assertGetMember(
className: 'B',
name: 'foo',
forSuper: true,
expected: 'A.foo: void Function()',
);
}
test_getMember_super_missing() async {
await resolveTestCode('''
class A {}
class B extends A {}
''');
_assertGetMember(
className: 'B',
name: 'foo',
forSuper: true,
);
}
test_getMember_super_noSuchMember() async {
await resolveTestCode('''
class A {
void foo();
noSuchMethod(_) {}
}
class B extends A {
void foo() {}
}
''');
_assertGetMember(
className: 'B',
name: 'foo',
forSuper: true,
expected: 'A.foo: void Function()',
);
}
void _assertExecutable(ExecutableElement element, String expected) {
if (expected != null) {
var type = element.type;
var enclosingElement = element.enclosingElement;
var actual = '${enclosingElement.name}.${element.name}: $type';
expect(actual, expected);
} else {
expect(element, isNull);
}
}
void _assertGetInherited({
@required String className,
@required String name,
String expected,
}) {
var interfaceType = _classInterfaceType(className);
var member = manager.getInherited(
interfaceType,
new Name(null, name),
);
_assertExecutable(member, expected);
}
void _assertGetMember({
@required String className,
@required String name,
String expected,
bool concrete = false,
bool forSuper = false,
}) {
var interfaceType = _classInterfaceType(className);
var memberType = manager.getMember(
interfaceType,
new Name(null, name),
concrete: concrete,
forSuper: forSuper,
);
_assertExecutable(memberType, expected);
}
void _assertInheritedConcreteMap(String className, String expected) {
var type = _classInterfaceType(className);
var map = manager.getInheritedConcreteMap(type);
_assertNameToExecutableMap(map, expected);
}
void _assertInheritedMap(String className, String expected) {
var type = _classInterfaceType(className);
var map = manager.getInheritedMap(type);
_assertNameToExecutableMap(map, expected);
}
void _assertNameToExecutableMap(
Map<Name, ExecutableElement> map, String expected) {
var lines = <String>[];
for (var name in map.keys) {
var element = map[name];
var type = element.type;
var enclosingElement = element.enclosingElement;
if (enclosingElement.name == 'Object') continue;
lines.add('${enclosingElement.name}.${element.name}: $type');
}
lines.sort();
var actual = lines.isNotEmpty ? lines.join('\n') + '\n' : '';
if (actual != expected) {
print(actual);
}
expect(actual, expected);
}
InterfaceType _classInterfaceType(String className) {
var element = findElement.classOrMixin(className);
return element.instantiate(
typeArguments: const [],
nullabilitySuffix: NullabilitySuffix.star,
);
}
}