| // Copyright (c) 2014, 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:_fe_analyzer_shared/src/types/shared_type.dart'; | |
| import 'package:analyzer/dart/constant/value.dart'; | |
| import 'package:analyzer/dart/element/element.dart'; | |
| import 'package:analyzer/dart/element/type.dart'; | |
| import 'package:analyzer/src/dart/element/type.dart'; | |
| import 'package:test/test.dart'; | |
| import 'package:test_reflective_loader/test_reflective_loader.dart'; | |
| import '../../../generated/type_system_base.dart'; | |
| import '../../summary/elements_base.dart'; | |
| import '../resolution/context_collection_resolution.dart'; | |
| main() { | |
| defineReflectiveSuite(() { | |
| defineReflectiveTests(ElementAnnotationImplTest); | |
| defineReflectiveTests(FieldElementImplTest); | |
| defineReflectiveTests(FunctionTypeImplTest); | |
| // TODO(scheglov): implement augmentation | |
| // defineReflectiveTests(MaybeAugmentedInstanceElementMixinTest); | |
| defineReflectiveTests(TypeParameterTypeImplTest); | |
| }); | |
| } | |
| @reflectiveTest | |
| class ElementAnnotationImplTest extends PubPackageResolutionTest { | |
| @override | |
| void setUp() { | |
| super.setUp(); | |
| writeTestPackageConfigWithMeta(); | |
| } | |
| test_computeConstantValue() async { | |
| newFile('$testPackageLibPath/a.dart', r''' | |
| class A { | |
| final String f; | |
| const A(this.f); | |
| } | |
| void f(@A('x') int p) {} | |
| '''); | |
| var result = await resolveTestCodeWithDiagnostics(r''' | |
| import 'a.dart'; | |
| main() { | |
| f(3); | |
| } | |
| '''); | |
| var argument = result.findNode.integerLiteral('3'); | |
| var parameter = argument.correspondingParameter!; | |
| ElementAnnotation annotation = parameter.metadata.annotations[0]; | |
| DartObject value = annotation.computeConstantValue()!; | |
| expect(value.getField('f')!.toStringValue(), 'x'); | |
| } | |
| test_isValidAtElement_declaringFormalParameter() async { | |
| var result = await resolveTestCodeWithDiagnostics(r''' | |
| import 'package:meta/meta_meta.dart'; | |
| @Target({TargetKind.field}) | |
| class FieldOnly { | |
| const FieldOnly(); | |
| } | |
| @Target({TargetKind.parameter}) | |
| class ParameterOnly { | |
| const ParameterOnly(); | |
| } | |
| class A { | |
| @FieldOnly() | |
| final int explicit = 0; | |
| } | |
| class B(@FieldOnly() @ParameterOnly() final int f); | |
| '''); | |
| var explicitField = result.findElement.field('explicit', of: 'A'); | |
| var declaringField = result.findElement.field('f', of: 'B'); | |
| var declaringParameter = result.findElement | |
| .class_('B') | |
| .constructors | |
| .single | |
| .formalParameters | |
| .single; | |
| var explicitFieldOnly = explicitField.metadata.annotations.single; | |
| expect(explicitFieldOnly.isValidAtElement(explicitField), isTrue); | |
| expect(explicitFieldOnly.isValidAtElement(declaringParameter), isFalse); | |
| var declaringFieldOnly = declaringParameter.metadata.annotations[0]; | |
| var declaringParameterOnly = declaringParameter.metadata.annotations[1]; | |
| expect(declaringFieldOnly.isValidAtElement(declaringParameter), isFalse); | |
| expect(declaringFieldOnly.isValidAtElement(declaringField), isTrue); | |
| expect(declaringParameterOnly.isValidAtElement(declaringParameter), isTrue); | |
| expect(declaringParameterOnly.isValidAtElement(declaringField), isFalse); | |
| } | |
| test_isValidAtElement_noTarget() async { | |
| var result = await resolveTestCodeWithDiagnostics(r''' | |
| class A { | |
| const A(); | |
| } | |
| @A() | |
| class B {} | |
| '''); | |
| var class_ = result.findElement.class_('B'); | |
| var annotation = class_.metadata.annotations.single; | |
| expect(annotation.isValidAtElement(class_), isNull); | |
| } | |
| test_isValidAtElement_override() async { | |
| var result = await resolveTestCodeWithDiagnostics(r''' | |
| class A { | |
| void m() {} | |
| } | |
| class B extends A { | |
| @override | |
| void m() {} | |
| void n(int p) {} | |
| } | |
| '''); | |
| var method = result.findElement.method('m', of: 'B'); | |
| var parameter = result.findElement | |
| .method('n', of: 'B') | |
| .formalParameters | |
| .single; | |
| var annotation = method.metadata.annotations.single; | |
| expect(annotation.isValidAtElement(method), isTrue); | |
| expect(annotation.isValidAtElement(parameter), isFalse); | |
| } | |
| } | |
| @reflectiveTest | |
| class FieldElementImplTest extends PubPackageResolutionTest { | |
| test_isEnumConstant() async { | |
| var result = await resolveTestCodeWithDiagnostics(r''' | |
| enum B {B1, B2, B3} | |
| '''); | |
| var B = result.findElement.enum_('B'); | |
| var b2Element = B.getField('B2')!; | |
| expect(b2Element.isEnumConstant, isTrue); | |
| var valuesElement = B.getField('values')!; | |
| expect(valuesElement.isEnumConstant, isFalse); | |
| } | |
| } | |
| @reflectiveTest | |
| class FunctionTypeImplTest extends AbstractTypeSystemTest { | |
| void assertType(DartType type, String expected) { | |
| var typeStr = type.getDisplayString(); | |
| expect(typeStr, expected); | |
| } | |
| void test_getNamedParameterTypes_namedParameters() { | |
| var type = parseFunctionType('void Function(int a, {double b, String c})'); | |
| Map<String, DartType> types = type.namedParameterTypes; | |
| expect(types, hasLength(2)); | |
| expect(types['b'], parseType('double')); | |
| expect(types['c'], parseType('String')); | |
| } | |
| void test_getNamedParameterTypes_noNamedParameters() { | |
| var type = parseFunctionType('void Function(int, double, [String])'); | |
| Map<String, DartType> types = type.namedParameterTypes; | |
| expect(types, hasLength(0)); | |
| } | |
| void test_getNamedParameterTypes_noParameters() { | |
| var type = parseFunctionType('void Function()'); | |
| Map<String, DartType> types = type.namedParameterTypes; | |
| expect(types, hasLength(0)); | |
| } | |
| void test_getNormalParameterTypes_noNormalParameters() { | |
| var type = parseFunctionType('void Function([int, double])'); | |
| List<DartType> types = type.normalParameterTypes; | |
| expect(types, hasLength(0)); | |
| } | |
| void test_getNormalParameterTypes_noParameters() { | |
| var type = parseFunctionType('void Function()'); | |
| List<DartType> types = type.normalParameterTypes; | |
| expect(types, hasLength(0)); | |
| } | |
| void test_getNormalParameterTypes_normalParameters() { | |
| var type = parseFunctionType('void Function(int, double, [String])'); | |
| List<DartType> types = type.normalParameterTypes; | |
| expect(types, hasLength(2)); | |
| expect(types[0], parseType('int')); | |
| expect(types[1], parseType('double')); | |
| } | |
| void test_getOptionalParameterTypes_noOptionalParameters() { | |
| var type = parseFunctionType('void Function(int a, {double b})'); | |
| List<DartType> types = type.optionalParameterTypes; | |
| expect(types, hasLength(0)); | |
| } | |
| void test_getOptionalParameterTypes_noParameters() { | |
| var type = parseFunctionType('void Function()'); | |
| List<DartType> types = type.optionalParameterTypes; | |
| expect(types, hasLength(0)); | |
| } | |
| void test_getOptionalParameterTypes_optionalParameters() { | |
| var type = parseFunctionType('void Function(int, [double, String])'); | |
| List<DartType> types = type.optionalParameterTypes; | |
| expect(types, hasLength(2)); | |
| expect(types[0], parseType('double')); | |
| expect(types[1], parseType('String')); | |
| } | |
| void test_typeParameters_variance() { | |
| var type = parseFunctionType('void Function<in T, inout U, out V>()'); | |
| var typeParameters = type.typeParameters; | |
| expect(typeParameters, hasLength(3)); | |
| expect(typeParameters[0].variance, Variance.contravariant); | |
| expect(typeParameters[1].variance, Variance.invariant); | |
| expect(typeParameters[2].variance, Variance.covariant); | |
| } | |
| } | |
| @reflectiveTest | |
| class MaybeAugmentedInstanceElementMixinTest extends ElementsBaseTest { | |
| @override | |
| bool get keepLinkingLibraries => true; | |
| test_lookUpGetter_declared() async { | |
| var library = await buildLibrary(''' | |
| class A { | |
| int get g {} | |
| } | |
| '''); | |
| var elementA = library.getClass('A')!; | |
| var getter = elementA.getGetter('g'); | |
| expect(elementA.lookUpGetter(name: 'g', library: library), same(getter)); | |
| } | |
| @FailingTest() // TODO(scheglov): implement augmentation | |
| test_lookUpGetter_fromAugmentation() async { | |
| newFile('$testPackageLibPath/a.dart', ''' | |
| part of 'test.dart'; | |
| augment class A { | |
| int get g {} | |
| } | |
| '''); | |
| var library = await buildLibrary(''' | |
| part 'a.dart'; | |
| class A {} | |
| '''); | |
| var elementA = library.getClass('A')!; | |
| var getter = elementA.getGetter('g')!; | |
| expect(elementA.lookUpGetter(name: 'g', library: library), same(getter)); | |
| } | |
| test_lookUpGetter_inherited() async { | |
| var library = await buildLibrary(''' | |
| class A { | |
| int get g {} | |
| } | |
| class B extends A {} | |
| '''); | |
| var classA = library.getClass('A')!; | |
| var getter = classA.getGetter('g'); | |
| var classB = library.getClass('B')!; | |
| expect(classB.lookUpGetter(name: 'g', library: library), same(getter)); | |
| } | |
| test_lookUpGetter_inherited_fromAugmentation() async { | |
| newFile('$testPackageLibPath/a.dart', ''' | |
| part of 'test.dart'; | |
| augment class A { | |
| int get g {} | |
| } | |
| '''); | |
| var library = await buildLibrary(''' | |
| part 'a.dart'; | |
| class A {} | |
| class B extends A {} | |
| '''); | |
| var classA = library.getClass('A')!; | |
| var getter = classA.getGetter('g'); | |
| var classB = library.getClass('B')!; | |
| expect(classB.lookUpGetter(name: 'g', library: library), same(getter)); | |
| } | |
| test_lookUpGetter_inherited_fromMixin() async { | |
| var library = await buildLibrary(''' | |
| mixin A { | |
| int get g {} | |
| } | |
| class B with A {} | |
| '''); | |
| var mixinA = library.getMixin('A')!; | |
| var getter = mixinA.getGetter('g'); | |
| var classB = library.getClass('B')!; | |
| expect(classB.lookUpGetter(name: 'g', library: library), same(getter)); | |
| } | |
| test_lookUpGetter_undeclared() async { | |
| var library = await buildLibrary(''' | |
| class A {} | |
| '''); | |
| var classA = library.getClass('A')!; | |
| expect(classA.lookUpGetter(name: 'g', library: library), isNull); | |
| } | |
| test_lookUpGetter_undeclared_recursive() async { | |
| var library = await buildLibrary(''' | |
| class A extends B {} | |
| class B extends A {} | |
| '''); | |
| var classA = library.getClass('A')!; | |
| expect(classA.lookUpGetter(name: 'g', library: library), isNull); | |
| } | |
| test_lookUpMethod_declared() async { | |
| var library = await buildLibrary(''' | |
| class A { | |
| int m() {} | |
| } | |
| '''); | |
| var classA = library.getClass('A')!; | |
| var method = classA.getMethod('m')!; | |
| expect(classA.lookUpMethod(name: 'm', library: library), same(method)); | |
| } | |
| @FailingTest() // TODO(scheglov): implement augmentation | |
| test_lookUpMethod_fromAugmentation() async { | |
| newFile('$testPackageLibPath/a.dart', ''' | |
| part of 'test.dart'; | |
| augment class A { | |
| int m() {} | |
| } | |
| '''); | |
| var library = await buildLibrary(''' | |
| part 'a.dart'; | |
| class A {} | |
| '''); | |
| var classA = library.getClass('A')!; | |
| var method = classA.getMethod('m')!; | |
| expect(classA.lookUpMethod(name: 'm', library: library), same(method)); | |
| } | |
| test_lookUpMethod_inherited() async { | |
| var library = await buildLibrary(''' | |
| class A { | |
| int m() {} | |
| } | |
| class B extends A {} | |
| '''); | |
| var classA = library.getClass('A')!; | |
| var method = classA.getMethod('m'); | |
| var classB = library.getClass('B')!; | |
| expect(classB.lookUpMethod(name: 'm', library: library), same(method)); | |
| } | |
| test_lookUpMethod_inherited_fromAugmentation() async { | |
| newFile('$testPackageLibPath/a.dart', ''' | |
| part of 'test.dart'; | |
| augment class A { | |
| int m() {} | |
| } | |
| '''); | |
| var library = await buildLibrary(''' | |
| part 'a.dart'; | |
| class A {} | |
| class B extends A {} | |
| '''); | |
| var classA = library.getClass('A')!; | |
| var method = classA.getMethod('m'); | |
| var classB = library.getClass('B')!; | |
| expect(classB.lookUpMethod(name: 'm', library: library), same(method)); | |
| } | |
| test_lookUpMethod_inherited_fromMixin() async { | |
| var library = await buildLibrary(''' | |
| mixin A { | |
| int m() {} | |
| } | |
| class B with A {} | |
| '''); | |
| var mixinA = library.getMixin('A')!; | |
| var method = mixinA.getMethod('m'); | |
| var classB = library.getClass('B')!; | |
| expect(classB.lookUpMethod(name: 'm', library: library), same(method)); | |
| } | |
| test_lookUpMethod_undeclared() async { | |
| var library = await buildLibrary(''' | |
| class A {} | |
| '''); | |
| var classA = library.getClass('A')!; | |
| expect(classA.lookUpMethod(name: 'm', library: library), isNull); | |
| } | |
| test_lookUpMethod_undeclared_recursive() async { | |
| var library = await buildLibrary(''' | |
| class A extends B {} | |
| class B extends A {} | |
| '''); | |
| var classA = library.getClass('A')!; | |
| expect(classA.lookUpMethod(name: 'm', library: library), isNull); | |
| } | |
| test_lookUpSetter_declared() async { | |
| var library = await buildLibrary(''' | |
| class A { | |
| set s(x) {} | |
| } | |
| '''); | |
| var classA = library.getClass('A')!; | |
| var setter = classA.getSetter('s')!; | |
| expect(classA.lookUpSetter(name: 's', library: library), same(setter)); | |
| } | |
| @FailingTest() // TODO(scheglov): implement augmentation | |
| test_lookUpSetter_fromAugmentation() async { | |
| newFile('$testPackageLibPath/a.dart', ''' | |
| part of 'test.dart'; | |
| augment class A { | |
| set s(x) {} | |
| } | |
| '''); | |
| var library = await buildLibrary(''' | |
| part 'a.dart'; | |
| class A {} | |
| '''); | |
| var classA = library.getClass('A')!; | |
| var setter = classA.getSetter('s')!; | |
| expect(classA.lookUpSetter(name: 's', library: library), same(setter)); | |
| } | |
| test_lookUpSetter_inherited() async { | |
| var library = await buildLibrary(''' | |
| class A { | |
| set s(x) {} | |
| } | |
| class B extends A {} | |
| '''); | |
| var classA = library.getClass('A')!; | |
| var setter = classA.getSetter('s')!; | |
| var classB = library.getClass('B')!; | |
| expect(classB.lookUpSetter(name: 's', library: library), same(setter)); | |
| } | |
| @FailingTest() // TODO(scheglov): implement augmentation | |
| test_lookUpSetter_inherited_fromAugmentation() async { | |
| newFile('$testPackageLibPath/a.dart', ''' | |
| part of 'test.dart'; | |
| augment class A { | |
| set s(x) {} | |
| } | |
| '''); | |
| var library = await buildLibrary(''' | |
| part 'a.dart'; | |
| class A {} | |
| class B extends A {} | |
| '''); | |
| var classA = library.getClass('A')!; | |
| var setter = classA.getSetter('s')!; | |
| var classB = library.getClass('B')!; | |
| expect(classB.lookUpSetter(name: 's', library: library), same(setter)); | |
| } | |
| test_lookUpSetter_inherited_fromMixin() async { | |
| var library = await buildLibrary(''' | |
| mixin A { | |
| set s(x) {} | |
| } | |
| class B with A {} | |
| '''); | |
| var mixinA = library.getMixin('A')!; | |
| var setter = mixinA.getSetter('s')!; | |
| var classB = library.getClass('B')!; | |
| expect(classB.lookUpSetter(name: 's', library: library), same(setter)); | |
| } | |
| test_lookUpSetter_undeclared() async { | |
| var library = await buildLibrary(''' | |
| class A {} | |
| '''); | |
| var classA = library.getClass('A')!; | |
| expect(classA.lookUpSetter(name: 's', library: library), isNull); | |
| } | |
| test_lookUpSetter_undeclared_recursive() async { | |
| var library = await buildLibrary(''' | |
| class A extends B {} | |
| class B extends A {} | |
| '''); | |
| var classA = library.getClass('A')!; | |
| expect(classA.lookUpSetter(name: 's', library: library), isNull); | |
| } | |
| } | |
| @reflectiveTest | |
| class TypeParameterTypeImplTest extends AbstractTypeSystemTest { | |
| void test_asInstanceOf_hasBound_element() { | |
| withTypeParameterScope('T extends List<int>', (scope) { | |
| _assert_asInstanceOf( | |
| scope.parseType('T'), | |
| typeProvider.iterableElement, | |
| 'Iterable<int>', | |
| ); | |
| }); | |
| } | |
| void test_asInstanceOf_hasBound_element_noMatch() { | |
| withTypeParameterScope('T extends num', (scope) { | |
| _assert_asInstanceOf( | |
| scope.parseType('T'), | |
| typeProvider.iterableElement, | |
| null, | |
| ); | |
| }); | |
| } | |
| void test_asInstanceOf_hasBound_promoted() { | |
| withTypeParameterScope('T', (scope) { | |
| _assert_asInstanceOf( | |
| scope.parseType('T & List<int>'), | |
| typeProvider.iterableElement, | |
| 'Iterable<int>', | |
| ); | |
| }); | |
| } | |
| void test_asInstanceOf_hasBound_promoted_noMatch() { | |
| withTypeParameterScope('T', (scope) { | |
| _assert_asInstanceOf( | |
| scope.parseType('T & num'), | |
| typeProvider.iterableElement, | |
| null, | |
| ); | |
| }); | |
| } | |
| void test_asInstanceOf_noBound() { | |
| withTypeParameterScope('T', (scope) { | |
| _assert_asInstanceOf( | |
| scope.parseType('T'), | |
| typeProvider.iterableElement, | |
| null, | |
| ); | |
| }); | |
| } | |
| void test_creation() { | |
| withTypeParameterScope('E', (scope) { | |
| var E = scope.typeParameter('E'); | |
| expect(E, isNotNull); | |
| expect(scope.parseTypeParameterType('E'), isNotNull); | |
| }); | |
| } | |
| void test_getElement() { | |
| withTypeParameterScope('E', (scope) { | |
| var E = scope.typeParameter('E'); | |
| TypeParameterTypeImpl type = scope.parseTypeParameterType('E'); | |
| expect(type.element, E); | |
| }); | |
| } | |
| void test_parse_promotedBound() { | |
| withTypeParameterScope('T', (scope) { | |
| var T = scope.typeParameter('T'); | |
| var type = scope.parseTypeParameterType('T & int'); | |
| expect(type.element, same(T)); | |
| expect(type.promotedBound, parseType('int')); | |
| expect(type.getDisplayString(), 'T & int'); | |
| }); | |
| } | |
| void test_parse_promotedBound_question() { | |
| withTypeParameterScope('T', (scope) { | |
| var T = scope.typeParameter('T'); | |
| var type = scope.parseTypeParameterType('(T & int)?'); | |
| expect(type.element, same(T)); | |
| expect(type.promotedBound, parseType('int')); | |
| expect(type.getDisplayString(), '(T & int)?'); | |
| }); | |
| } | |
| void _assert_asInstanceOf( | |
| TypeImpl type, | |
| ClassElement element, | |
| String? expected, | |
| ) { | |
| var result = type.asInstanceOf(element); | |
| expect(result?.getDisplayString(), expected); | |
| } | |
| } |