blob: d34a268a8c493d596989f80df829536b71347be5 [file] [log] [blame]
// Copyright (c) 2016, 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/ast/ast.dart';
import 'package:analyzer/dart/element/element.dart';
import 'package:analyzer/src/dart/error/hint_codes.dart';
import 'package:test/test.dart';
import 'package:test_reflective_loader/test_reflective_loader.dart';
import '../src/dart/resolution/context_collection_resolution.dart';
import '../util/element_type_matchers.dart';
main() {
defineReflectiveSuite(() {
defineReflectiveTests(AnnotationElementResolverTest);
defineReflectiveTests(ElementResolverTest);
});
}
@reflectiveTest
class AnnotationElementResolverTest extends PubPackageResolutionTest {
test_class_namedConstructor() async {
newFile('$testPackageLibPath/a.dart', r'''
class A {
const A.named();
}
''');
await _validateAnnotation('', '@A.named()',
(name1, name2, name3, annotationElement) {
expect(name1!.staticElement, isClassElement);
expect(name1.staticElement!.displayName, 'A');
expect(name2!.staticElement, isConstructorElement);
expect(name2.staticElement!.displayName, 'A.named');
expect(name3, isNull);
if (annotationElement is ConstructorElement) {
expect(annotationElement, same(name2.staticElement));
expect(annotationElement.enclosingElement, name1.staticElement);
expect(annotationElement.displayName, 'A.named');
expect(annotationElement.parameters, isEmpty);
} else {
fail('Expected "annotationElement" is ConstructorElement, '
'but (${annotationElement.runtimeType}) $annotationElement found.');
}
});
}
test_class_prefixed_namedConstructor() async {
newFile('$testPackageLibPath/a.dart', r'''
class A {
const A.named();
}
''');
await _validateAnnotation('as p', '@p.A.named()',
(name1, name2, name3, annotationElement) {
expect(name1!.staticElement, isPrefixElement);
expect(name1.staticElement!.displayName, 'p');
expect(name2!.staticElement, isClassElement);
expect(name2.staticElement!.displayName, 'A');
expect(name3!.staticElement, isConstructorElement);
expect(name3.staticElement!.displayName, 'A.named');
if (annotationElement is ConstructorElement) {
expect(annotationElement, same(name3.staticElement));
expect(annotationElement.enclosingElement, name2.staticElement);
expect(annotationElement.displayName, 'A.named');
expect(annotationElement.parameters, isEmpty);
} else {
fail('Expected "annotationElement" is ConstructorElement, '
'but (${annotationElement.runtimeType}) $annotationElement found.');
}
});
}
test_class_prefixed_staticConstField() async {
newFile('$testPackageLibPath/a.dart', r'''
class A {
static const V = 0;
}
''');
await _validateAnnotation('as p', '@p.A.V',
(name1, name2, name3, annotationElement) {
expect(name1!.staticElement, isPrefixElement);
expect(name1.staticElement!.displayName, 'p');
expect(name2!.staticElement, isClassElement);
expect(name2.staticElement!.displayName, 'A');
expect(name3!.staticElement, isPropertyAccessorElement);
expect(name3.staticElement!.displayName, 'V');
if (annotationElement is PropertyAccessorElement) {
expect(annotationElement, same(name3.staticElement));
expect(annotationElement.enclosingElement, name2.staticElement);
expect(annotationElement.displayName, 'V');
} else {
fail('Expected "annotationElement" is PropertyAccessorElement, '
'but (${annotationElement.runtimeType}) $annotationElement found.');
}
});
}
test_class_prefixed_unnamedConstructor() async {
newFile('$testPackageLibPath/a.dart', r'''
class A {
const A();
}
''');
await _validateAnnotation('as p', '@p.A',
(name1, name2, name3, annotationElement) {
expect(name1!.staticElement, isPrefixElement);
expect(name1.staticElement!.displayName, 'p');
expect(name2!.staticElement, isClassElement);
expect(name2.staticElement!.displayName, 'A');
expect(name3, isNull);
if (annotationElement is ConstructorElement) {
expect(annotationElement.enclosingElement, name2.staticElement);
expect(annotationElement.displayName, 'A');
expect(annotationElement.parameters, isEmpty);
} else {
fail('Expected "annotationElement" is ConstructorElement, '
'but (${annotationElement.runtimeType}) $annotationElement found.');
}
});
}
test_class_staticConstField() async {
newFile('$testPackageLibPath/a.dart', r'''
class A {
static const V = 0;
}
''');
await _validateAnnotation('', '@A.V',
(name1, name2, name3, annotationElement) {
expect(name1!.staticElement, isClassElement);
expect(name1.staticElement!.displayName, 'A');
expect(name2!.staticElement, isPropertyAccessorElement);
expect(name2.staticElement!.displayName, 'V');
expect(name3, isNull);
if (annotationElement is PropertyAccessorElement) {
expect(annotationElement, same(name2.staticElement));
expect(annotationElement.enclosingElement, name1.staticElement);
expect(annotationElement.displayName, 'V');
} else {
fail('Expected "annotationElement" is PropertyAccessorElement, '
'but (${annotationElement.runtimeType}) $annotationElement found.');
}
});
}
test_class_unnamedConstructor() async {
newFile('$testPackageLibPath/a.dart', r'''
class A {
const A();
}
''');
await _validateAnnotation('', '@A',
(name1, name2, name3, annotationElement) {
expect(name1!.staticElement, isClassElement);
expect(name1.staticElement!.displayName, 'A');
expect(name2, isNull);
expect(name3, isNull);
if (annotationElement is ConstructorElement) {
expect(annotationElement.enclosingElement, name1.staticElement);
expect(annotationElement.displayName, 'A');
expect(annotationElement.parameters, isEmpty);
} else {
fail('Expected "annotationElement" is ConstructorElement, '
'but (${annotationElement.runtimeType}) $annotationElement found.');
}
});
}
test_topLevelVariable() async {
newFile('$testPackageLibPath/a.dart', r'''
const V = 0;
''');
await _validateAnnotation('', '@V',
(name1, name2, name3, annotationElement) {
expect(name1!.staticElement, isPropertyAccessorElement);
expect(name1.staticElement!.displayName, 'V');
expect(name2, isNull);
expect(name3, isNull);
if (annotationElement is PropertyAccessorElement) {
expect(annotationElement, same(name1.staticElement));
expect(annotationElement.enclosingElement, isCompilationUnitElement);
expect(annotationElement.displayName, 'V');
} else {
fail('Expected "annotationElement" is PropertyAccessorElement, '
'but (${annotationElement.runtimeType}) $annotationElement found.');
}
});
}
test_topLevelVariable_prefixed() async {
newFile('$testPackageLibPath/a.dart', r'''
const V = 0;
''');
await _validateAnnotation('as p', '@p.V',
(name1, name2, name3, annotationElement) {
expect(name1!.staticElement, isPrefixElement);
expect(name1.staticElement!.displayName, 'p');
expect(name2!.staticElement, isPropertyAccessorElement);
expect(name2.staticElement!.displayName, 'V');
expect(name3, isNull);
if (annotationElement is PropertyAccessorElement) {
expect(annotationElement, same(name2.staticElement));
expect(annotationElement.enclosingElement, isCompilationUnitElement);
expect(annotationElement.displayName, 'V');
} else {
fail('Expected "annotationElement" is PropertyAccessorElement, '
'but (${annotationElement.runtimeType}) $annotationElement found.');
}
});
}
Future<void> _validateAnnotation(
String annotationPrefix,
String annotationText,
Function(SimpleIdentifier? name, SimpleIdentifier? name2,
SimpleIdentifier? name3, Element annotationElement)
validator) async {
await resolveTestCode('''
import 'a.dart' $annotationPrefix;
$annotationText
class C {}
''');
var clazz = findNode.classDeclaration('C');
Annotation annotation = clazz.metadata.single;
Identifier name = annotation.name;
Element annotationElement = annotation.element!;
if (name is SimpleIdentifier) {
validator(name, null, annotation.constructorName, annotationElement);
} else if (name is PrefixedIdentifier) {
validator(name.prefix, name.identifier, annotation.constructorName,
annotationElement);
} else {
fail('Unknown "name": ${name.runtimeType} $name');
}
}
}
@reflectiveTest
class ElementResolverTest extends PubPackageResolutionTest {
test_visitBreakStatement_withLabel() async {
await assertNoErrorsInCode('''
test() {
loop: while (true) {
break loop;
}
}
''');
var breakStatement = findNode.breakStatement('break loop');
expect(breakStatement.label!.staticElement, findElement.label('loop'));
expect(breakStatement.target, findNode.whileStatement('while (true)'));
}
test_visitBreakStatement_withoutLabel() async {
await assertNoErrorsInCode('''
test() {
while (true) {
break;
}
}
''');
var breakStatement = findNode.breakStatement('break');
expect(breakStatement.target, findNode.whileStatement('while (true)'));
}
test_visitCommentReference_prefixedIdentifier_class_getter() async {
await assertNoErrorsInCode('''
class A {
int get p => 0;
set p(int value) {}
}
/// [A.p]
test() {}
''');
var prefixed = findNode.prefixed('A.p');
expect(prefixed.prefix.staticElement, findElement.class_('A'));
expect(prefixed.identifier.staticElement, findElement.getter('p'));
}
test_visitCommentReference_prefixedIdentifier_class_method() async {
await assertNoErrorsInCode('''
class A {
m() {}
}
/// [A.m]
test() {}
''');
var prefixed = findNode.prefixed('A.m');
expect(prefixed.prefix.staticElement, findElement.class_('A'));
expect(prefixed.identifier.staticElement, findElement.method('m'));
}
test_visitCommentReference_prefixedIdentifier_class_operator() async {
await assertNoErrorsInCode('''
class A {
operator ==(other) => true;
}
/// [A.==]
test() {}
''');
var prefixed = findNode.prefixed('A.==');
expect(prefixed.prefix.staticElement, findElement.class_('A'));
expect(prefixed.identifier.staticElement, findElement.method('=='));
}
test_visitConstructorName_named() async {
await assertNoErrorsInCode('''
class A implements B {
A.a();
}
class B {
factory B() = A.a/*reference*/;
}
''');
expect(findNode.constructorName('A.a/*reference*/').staticElement,
same(findElement.constructor('a')));
}
test_visitConstructorName_unnamed() async {
await assertNoErrorsInCode('''
class A implements B {
A();
}
class B {
factory B() = A/*reference*/;
}
''');
expect(findNode.constructorName('A/*reference*/').staticElement,
same(findElement.unnamedConstructor('A')));
}
test_visitContinueStatement_withLabel() async {
await assertNoErrorsInCode('''
test() {
loop: while (true) {
continue loop;
}
}
''');
var continueStatement = findNode.continueStatement('continue loop');
expect(continueStatement.label!.staticElement, findElement.label('loop'));
expect(continueStatement.target, findNode.whileStatement('while (true)'));
}
test_visitContinueStatement_withoutLabel() async {
await assertNoErrorsInCode('''
test() {
while (true) {
continue;
}
}
''');
var continueStatement = findNode.continueStatement('continue');
expect(continueStatement.target, findNode.whileStatement('while (true)'));
}
test_visitExportDirective_combinators() async {
await assertNoErrorsInCode('''
export 'dart:math' hide pi;
''');
var pi = findElement
.export('dart:math')
.exportedLibrary!
.exportNamespace
.get('pi') as PropertyAccessorElement;
expect(findNode.simple('pi').staticElement, pi.variable);
}
test_visitExportDirective_noCombinators() async {
await assertNoErrorsInCode('''
export 'dart:math';
''');
expect(findNode.export('dart:math').element2!.exportedLibrary!.name,
'dart.math');
}
test_visitFieldFormalParameter() async {
await assertNoErrorsInCode('''
class A {
int f;
A(this.f);
}
''');
expect(
findNode.fieldFormalParameter('this.f').declaredElement!.type, intType);
}
test_visitImportDirective_combinators_noPrefix() async {
await assertErrorsInCode('''
import 'dart:math' show pi;
''', [
error(HintCode.UNUSED_IMPORT, 7, 11),
]);
var pi = findElement
.import('dart:math')
.importedLibrary!
.exportNamespace
.get('pi') as PropertyAccessorElement;
expect(findNode.simple('pi').staticElement, pi.variable);
}
test_visitImportDirective_combinators_prefix() async {
await assertErrorsInCode('''
import 'dart:math' as p show pi hide ln10;
''', [
error(HintCode.UNUSED_IMPORT, 7, 11),
]);
var mathNamespace =
findElement.import('dart:math').importedLibrary!.exportNamespace;
var pi = mathNamespace.get('pi') as PropertyAccessorElement;
expect(findNode.simple('pi').staticElement, pi.variable);
var ln10 = mathNamespace.get('ln10') as PropertyAccessorElement;
expect(findNode.simple('ln10').staticElement, ln10.variable);
}
test_visitImportDirective_noCombinators_noPrefix() async {
await assertErrorsInCode('''
import 'dart:math';
''', [
error(HintCode.UNUSED_IMPORT, 7, 11),
]);
expect(findNode.import('dart:math').element2!.importedLibrary!.name,
'dart.math');
}
test_visitImportDirective_noCombinators_prefix() async {
await assertErrorsInCode('''
import 'dart:math' as p;
''', [
error(HintCode.UNUSED_IMPORT, 7, 11),
]);
expect(findNode.import('dart:math').element2!.importedLibrary!.name,
'dart.math');
}
test_visitImportDirective_withCombinators() async {
newFile('$testPackageLibPath/lib1.dart', r'''
int v1 = 0;
final int v2 = 0;
''');
await assertErrorsInCode('''
import 'lib1.dart' show v1, v2;
''', [
error(HintCode.UNUSED_IMPORT, 7, 11),
]);
var importedVariables = findNode
.import('lib1.dart')
.element2!
.importedLibrary!
.definingCompilationUnit
.topLevelVariables;
var v1 = importedVariables.where((v) => v.name == 'v1').single;
var v2 = importedVariables.where((v) => v.name == 'v2').single;
expect(findNode.simple('v1').staticElement, same(v1));
expect(findNode.simple('v2').staticElement, same(v2));
}
test_visitInstanceCreationExpression_named() async {
await assertNoErrorsInCode('''
class A {
A.a();
}
test() => new A.a();
''');
expect(findNode.instanceCreation('new A.a').constructorName.staticElement,
findElement.constructor('a'));
}
test_visitInstanceCreationExpression_named_namedParameter() async {
await assertNoErrorsInCode('''
class A {
A.named({int a = 0});
}
test() => new A.named(a: 0);
''');
expect(
findNode.simple('a:').staticElement, same(findElement.parameter('a')));
}
test_visitInstanceCreationExpression_unnamed() async {
await assertNoErrorsInCode('''
class A {}
test() => new A();
''');
expect(findNode.instanceCreation('new A').constructorName.staticElement,
findElement.unnamedConstructor('A'));
}
test_visitMethodInvocation() async {
await assertNoErrorsInCode('''
num get i => 0;
test() => i.abs();
''');
expect(
findNode
.methodInvocation('i.abs()')
.methodName
.staticElement!
.declaration,
same(typeProvider.numType.getMethod('abs')));
}
test_visitPrefixedIdentifier_dynamic() async {
await assertNoErrorsInCode('''
test(dynamic a) => a.b;
''');
var identifier = findNode.prefixed('a.b');
expect(identifier.staticElement, isNull);
expect(identifier.identifier.staticElement, isNull);
}
test_visitRedirectingConstructorInvocation_named() async {
await assertNoErrorsInCode('''
class C {
C(int x) : this.named(x /*usage*/);
C.named(int y);
}
''');
var invocation = findNode.redirectingConstructorInvocation('this');
var namedConstructor = findElement.constructor('named', of: 'C');
expect(invocation.staticElement, namedConstructor);
expect(invocation.constructorName!.staticElement, namedConstructor);
expect(findNode.simple('x /*usage*/').staticParameterElement,
findElement.parameter('y'));
}
test_visitRedirectingConstructorInvocation_unnamed() async {
await assertNoErrorsInCode('''
class C {
C.named(int x) : this(x /*usage*/);
C(int y);
}
''');
expect(findNode.redirectingConstructorInvocation('this').staticElement,
findElement.unnamedConstructor('C'));
expect(findNode.simple('x /*usage*/').staticParameterElement,
findElement.parameter('y'));
}
test_visitSuperConstructorInvocation() async {
await assertNoErrorsInCode('''
class A {}
class B extends A {
B() : super();
}
''');
expect(findNode.superConstructorInvocation('super').staticElement,
findElement.unnamedConstructor('A'));
}
test_visitSuperConstructorInvocation_namedParameter() async {
await assertNoErrorsInCode('''
class A {
A({dynamic p});
}
class B extends A {
B() : super(p: 0);
}
''');
expect(findNode.superConstructorInvocation('super').staticElement,
findElement.unnamedConstructor('A'));
expect(
findNode.simple('p:').staticElement, same(findElement.parameter('p')));
}
}