blob: 34ad25cb0135afa3e7a62d0aa22eddf2b687aca7 [file] [edit]
// 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);
}
}