Resolve mixin declarations.
Errors for onClause are not correct yet, and not tested yet.
R=brianwilkerson@google.com
Change-Id: I9a1f2b4734661baac85a9d1b9311d21104c7c495
Reviewed-on: https://dart-review.googlesource.com/72360
Commit-Queue: Konstantin Shcheglov <scheglov@google.com>
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
diff --git a/pkg/analyzer/lib/dart/ast/ast.dart b/pkg/analyzer/lib/dart/ast/ast.dart
index 84c538a..777bdc3 100644
--- a/pkg/analyzer/lib/dart/ast/ast.dart
+++ b/pkg/analyzer/lib/dart/ast/ast.dart
@@ -5185,6 +5185,9 @@
* Clients may not extend, implement or mix-in this class.
*/
abstract class MixinDeclaration extends NamedCompilationUnitMember {
+ @override
+ ClassElement get declaredElement;
+
/**
* Return the implements clause for the mixin, or `null` if the mixin does not
* implement any interfaces.
diff --git a/pkg/analyzer/lib/src/dart/ast/ast.dart b/pkg/analyzer/lib/src/dart/ast/ast.dart
index c655ac4..573adfe 100644
--- a/pkg/analyzer/lib/src/dart/ast/ast.dart
+++ b/pkg/analyzer/lib/src/dart/ast/ast.dart
@@ -7816,8 +7816,7 @@
..add(rightBracket);
@override
-// MixinElement get declaredElement => _name?.staticElement as MixinElement;
- Element get declaredElement => throw new UnimplementedError();
+ ClassElement get declaredElement => _name?.staticElement as ClassElement;
@deprecated
@override
diff --git a/pkg/analyzer/lib/src/generated/declaration_resolver.dart b/pkg/analyzer/lib/src/generated/declaration_resolver.dart
index e2ee324..4a24786 100644
--- a/pkg/analyzer/lib/src/generated/declaration_resolver.dart
+++ b/pkg/analyzer/lib/src/generated/declaration_resolver.dart
@@ -398,6 +398,16 @@
}
@override
+ Object visitMixinDeclaration(MixinDeclaration node) {
+ ClassElement element = _match(node.name, _walker.getMixin());
+ _walk(new ElementWalker.forClass(element), () {
+ super.visitMixinDeclaration(node);
+ });
+ resolveMetadata(node, node.metadata, element);
+ return null;
+ }
+
+ @override
Object visitPartDirective(PartDirective node) {
super.visitPartDirective(node);
List<ElementAnnotation> annotations =
@@ -839,6 +849,8 @@
int _enumIndex = 0;
List<ExecutableElement> _functions;
int _functionIndex = 0;
+ List<ClassElement> _mixins;
+ int _mixinIndex = 0;
List<ParameterElement> _parameters;
int _parameterIndex = 0;
List<FunctionTypeAliasElement> _typedefs;
@@ -872,6 +884,7 @@
_classes = compilationUnit.types,
_enums = compilationUnit.enums,
_functions = compilationUnit.functions,
+ _mixins = compilationUnit.mixins,
_typedefs = compilationUnit.functionTypeAliases,
_variables =
compilationUnit.topLevelVariables.where(_isNotSynthetic).toList();
@@ -977,6 +990,12 @@
ExecutableElement getFunction() => _functions[_functionIndex++];
/**
+ * Returns the next non-synthetic child of [element] which is a mixin; throws
+ * an [IndexError] if there are no more.
+ */
+ ClassElement getMixin() => _mixins[_mixinIndex++];
+
+ /**
* Returns the next non-synthetic child of [element] which is a parameter;
* throws an [IndexError] if there are no more.
*/
diff --git a/pkg/analyzer/lib/src/generated/element_resolver.dart b/pkg/analyzer/lib/src/generated/element_resolver.dart
index d8b1349..974721e 100644
--- a/pkg/analyzer/lib/src/generated/element_resolver.dart
+++ b/pkg/analyzer/lib/src/generated/element_resolver.dart
@@ -716,6 +716,12 @@
}
@override
+ Object visitMixinDeclaration(MixinDeclaration node) {
+ resolveMetadata(node);
+ return null;
+ }
+
+ @override
Object visitPartDirective(PartDirective node) {
resolveMetadata(node);
return null;
diff --git a/pkg/analyzer/lib/src/generated/parser_fasta.dart b/pkg/analyzer/lib/src/generated/parser_fasta.dart
index 43b0b5f..8cbdd21 100644
--- a/pkg/analyzer/lib/src/generated/parser_fasta.dart
+++ b/pkg/analyzer/lib/src/generated/parser_fasta.dart
@@ -27,6 +27,7 @@
{bool allowNativeClause: false})
: fastaParser = new fasta.Parser(null),
astBuilder = new AstBuilder(errorReporter, fileUri, true) {
+ fastaParser.isMixinSupportEnabled = true;
fastaParser.listener = astBuilder;
astBuilder.parser = fastaParser;
astBuilder.allowNativeClause = allowNativeClause;
diff --git a/pkg/analyzer/lib/src/generated/resolver.dart b/pkg/analyzer/lib/src/generated/resolver.dart
index bbe3d04..8329fb9 100644
--- a/pkg/analyzer/lib/src/generated/resolver.dart
+++ b/pkg/analyzer/lib/src/generated/resolver.dart
@@ -5025,6 +5025,12 @@
*/
ExecutableElement _enclosingFunction = null;
+ /**
+ * The mixin declaration representing the class containing the current node,
+ * or `null` if the current node is not contained in a mixin.
+ */
+ MixinDeclaration _enclosingMixinDeclaration = null;
+
InferenceContext inferenceContext = null;
/**
@@ -5309,7 +5315,8 @@
Object visitAnnotation(Annotation node) {
AstNode parent = node.parent;
if (identical(parent, _enclosingClassDeclaration) ||
- identical(parent, _enclosingFunctionTypeAlias)) {
+ identical(parent, _enclosingFunctionTypeAlias) ||
+ identical(parent, _enclosingMixinDeclaration)) {
return null;
}
node.name?.accept(this);
@@ -6211,6 +6218,31 @@
}
@override
+ Object visitMixinDeclaration(MixinDeclaration node) {
+ //
+ // Resolve the metadata in the library scope.
+ //
+ node.metadata?.accept(this);
+ _enclosingMixinDeclaration = node;
+ //
+ // Continue the class resolution.
+ //
+ ClassElement outerType = enclosingClass;
+ try {
+ enclosingClass = node.declaredElement;
+ typeAnalyzer.thisType = enclosingClass?.type;
+ super.visitMixinDeclaration(node);
+ node.accept(elementResolver);
+ node.accept(typeAnalyzer);
+ } finally {
+ typeAnalyzer.thisType = outerType?.type;
+ enclosingClass = outerType;
+ _enclosingMixinDeclaration = null;
+ }
+ return null;
+ }
+
+ @override
Object visitNamedExpression(NamedExpression node) {
InferenceContext.setTypeFromNode(node.expression, node);
return super.visitNamedExpression(node);
@@ -7645,6 +7677,40 @@
super.visitMethodDeclaration(node);
}
+ @override
+ Object visitMixinDeclaration(MixinDeclaration node) {
+ ClassElement element = node.declaredElement;
+
+ Scope outerScope = nameScope;
+ ClassElement outerClass = enclosingClass;
+ try {
+ enclosingClass = element;
+
+ nameScope = new TypeParameterScope(nameScope, element);
+ visitMixinDeclarationInScope(node);
+
+ nameScope = new ClassScope(nameScope, element);
+ visitMixinMembersInScope(node);
+ } finally {
+ nameScope = outerScope;
+ enclosingClass = outerClass;
+ }
+ return null;
+ }
+
+ void visitMixinDeclarationInScope(MixinDeclaration node) {
+ node.name?.accept(this);
+ node.typeParameters?.accept(this);
+ node.onClause?.accept(this);
+ node.implementsClause?.accept(this);
+ }
+
+ void visitMixinMembersInScope(MixinDeclaration node) {
+ node.documentationComment?.accept(this);
+ node.metadata.accept(this);
+ node.members.accept(this);
+ }
+
/**
* Visit the given statement after it's scope has been created. This is used by ResolverVisitor to
* correctly visit the 'then' and 'else' statements of an 'if' statement.
@@ -9694,7 +9760,8 @@
}
classElement.supertype = superclassType;
}
- _resolve(classElement, withClause, implementsClause);
+ _resolveWithClause(classElement, withClause);
+ _resolveImplementsClause(classElement, implementsClause);
return null;
}
@@ -9736,7 +9803,8 @@
if (classElement != null) {
classElement.supertype = superclassType;
}
- _resolve(classElement, node.withClause, node.implementsClause);
+ _resolveWithClause(classElement, node.withClause);
+ _resolveImplementsClause(classElement, node.implementsClause);
return null;
}
@@ -9915,6 +9983,15 @@
}
@override
+ void visitMixinDeclarationInScope(MixinDeclaration node) {
+ super.visitMixinDeclarationInScope(node);
+ MixinElementImpl element = node.declaredElement;
+ _resolveOnClause(element, node.onClause);
+ _resolveImplementsClause(element, node.implementsClause);
+ return null;
+ }
+
+ @override
Object visitNode(AstNode node) {
// In API mode we need to skip:
// - function bodies;
@@ -10180,24 +10257,10 @@
return null;
}
- /**
- * Resolve the types in the given [withClause] and [implementsClause] and
- * associate those types with the given [classElement].
- */
- void _resolve(ClassElementImpl classElement, WithClause withClause,
- ImplementsClause implementsClause) {
- if (withClause != null) {
- List<InterfaceType> mixinTypes = _resolveTypes(
- withClause.mixinTypes,
- CompileTimeErrorCode.MIXIN_OF_NON_CLASS,
- CompileTimeErrorCode.MIXIN_OF_ENUM,
- CompileTimeErrorCode.MIXIN_OF_NON_CLASS);
- if (classElement != null) {
- classElement.mixins = mixinTypes;
- }
- }
- if (implementsClause != null) {
- NodeList<TypeName> interfaces = implementsClause.interfaces;
+ void _resolveImplementsClause(
+ ClassElementImpl classElement, ImplementsClause clause) {
+ if (clause != null) {
+ NodeList<TypeName> interfaces = clause.interfaces;
List<InterfaceType> interfaceTypes = _resolveTypes(
interfaces,
CompileTimeErrorCode.IMPLEMENTS_NON_CLASS,
@@ -10232,6 +10295,20 @@
}
}
+ void _resolveOnClause(MixinElementImpl classElement, OnClause clause) {
+ List<InterfaceType> types;
+ if (clause != null) {
+ types = _resolveTypes(
+ clause.superclassConstraints,
+ CompileTimeErrorCode.MIXIN_OF_NON_CLASS,
+ CompileTimeErrorCode.MIXIN_OF_ENUM,
+ CompileTimeErrorCode.MIXIN_OF_NON_CLASS);
+ } else {
+ types = [typeProvider.objectType];
+ }
+ classElement.superclassConstraints = types;
+ }
+
/**
* Return the type specified by the given name.
*
@@ -10294,6 +10371,17 @@
return types;
}
+ void _resolveWithClause(ClassElementImpl classElement, WithClause clause) {
+ if (clause != null) {
+ List<InterfaceType> mixinTypes = _resolveTypes(
+ clause.mixinTypes,
+ CompileTimeErrorCode.MIXIN_OF_NON_CLASS,
+ CompileTimeErrorCode.MIXIN_OF_ENUM,
+ CompileTimeErrorCode.MIXIN_OF_NON_CLASS);
+ classElement.mixins = mixinTypes;
+ }
+ }
+
/**
* Given a function typed [parameter] with [FunctionType] based on a
* [GenericFunctionTypeElementImpl], compute and set the return type for the
diff --git a/pkg/analyzer/lib/src/task/dart.dart b/pkg/analyzer/lib/src/task/dart.dart
index 5f96198..ee2ccc0 100644
--- a/pkg/analyzer/lib/src/task/dart.dart
+++ b/pkg/analyzer/lib/src/task/dart.dart
@@ -3761,8 +3761,6 @@
parser.parseFunctionBodies =
options.analyzeFunctionBodiesPredicate(_source);
parser.enableOptionalNewAndConst = true;
- (parser as ParserAdapter).fastaParser.isMixinSupportEnabled =
- (options as AnalysisOptionsImpl).isMixinSupportEnabled;
CompilationUnit unit = parser.parseCompilationUnit(tokenStream);
unit.lineInfo = lineInfo;
diff --git a/pkg/analyzer/test/generated/hint_code_test.dart b/pkg/analyzer/test/generated/hint_code_test.dart
index 6619450..d0ab205 100644
--- a/pkg/analyzer/test/generated/hint_code_test.dart
+++ b/pkg/analyzer/test/generated/hint_code_test.dart
@@ -1660,7 +1660,6 @@
verify([source]);
}
- @failingTest
test_invalidSealedAnnotation_onMixin() async {
Source source = addNamedSource('/lib1.dart', r'''
import 'package:meta/meta.dart';
diff --git a/pkg/analyzer/test/src/dart/resolution/find_element.dart b/pkg/analyzer/test/src/dart/resolution/find_element.dart
index ce924b6..ff87f0e 100644
--- a/pkg/analyzer/test/src/dart/resolution/find_element.dart
+++ b/pkg/analyzer/test/src/dart/resolution/find_element.dart
@@ -1,5 +1,6 @@
import 'package:analyzer/dart/ast/ast.dart';
import 'package:analyzer/dart/element/element.dart';
+import 'package:analyzer/dart/element/type.dart';
import 'package:test/test.dart';
import 'function_ast_visitor.dart';
@@ -85,6 +86,10 @@
fail('Not found import: $targetUri');
}
+ InterfaceType interfaceType(String name) {
+ return class_(name).type;
+ }
+
LocalVariableElement localVar(String name) {
LocalVariableElement result;
unit.accept(new FunctionAstVisitor(
@@ -115,6 +120,15 @@
fail('Not found class method: $name');
}
+ ClassElement mixin(String name) {
+ for (var mixin in unitElement.mixins) {
+ if (mixin.name == name) {
+ return mixin;
+ }
+ }
+ fail('Not found mixin: $name');
+ }
+
ParameterElement parameter(String name) {
ParameterElement parameterElement;
void considerParameter(ParameterElement parameter) {
diff --git a/pkg/analyzer/test/src/dart/resolution/find_node.dart b/pkg/analyzer/test/src/dart/resolution/find_node.dart
index ac80c6c..54b907a 100644
--- a/pkg/analyzer/test/src/dart/resolution/find_node.dart
+++ b/pkg/analyzer/test/src/dart/resolution/find_node.dart
@@ -12,116 +12,138 @@
return unit.directives.singleWhere((d) => d is LibraryDirective);
}
+ Annotation annotation(String search) {
+ return _node(search, (n) => n is Annotation);
+ }
+
AssignmentExpression assignment(String search) {
- return _node(search).getAncestor((n) => n is AssignmentExpression);
+ return _node(search, (n) => n is AssignmentExpression);
}
CascadeExpression cascade(String search) {
- return _node(search).getAncestor((n) => n is CascadeExpression);
+ return _node(search, (n) => n is CascadeExpression);
+ }
+
+ CommentReference commentReference(String search) {
+ return _node(search, (n) => n is CommentReference);
}
ExportDirective export(String search) {
- return _node(search).getAncestor((n) => n is ExportDirective);
+ return _node(search, (n) => n is ExportDirective);
}
FunctionExpression functionExpression(String search) {
- return _node(search).getAncestor((n) => n is FunctionExpression);
+ return _node(search, (n) => n is FunctionExpression);
}
GenericFunctionType genericFunctionType(String search) {
- return _node(search).getAncestor((n) => n is GenericFunctionType);
+ return _node(search, (n) => n is GenericFunctionType);
}
ImportDirective import(String search) {
- return _node(search).getAncestor((n) => n is ImportDirective);
+ return _node(search, (n) => n is ImportDirective);
}
InstanceCreationExpression instanceCreation(String search) {
- return _node(search).getAncestor((n) => n is InstanceCreationExpression);
+ return _node(search, (n) => n is InstanceCreationExpression);
}
ListLiteral listLiteral(String search) {
- return _node(search).getAncestor((n) => n is ListLiteral);
+ return _node(search, (n) => n is ListLiteral);
}
MapLiteral mapLiteral(String search) {
- return _node(search).getAncestor((n) => n is MapLiteral);
+ return _node(search, (n) => n is MapLiteral);
+ }
+
+ MethodDeclaration methodDeclaration(String search) {
+ return _node(search, (n) => n is MethodDeclaration);
}
MethodInvocation methodInvocation(String search) {
- return _node(search).getAncestor((n) => n is MethodInvocation);
+ return _node(search, (n) => n is MethodInvocation);
+ }
+
+ MixinDeclaration mixin(String search) {
+ return _node(search, (n) => n is MixinDeclaration);
}
ParenthesizedExpression parenthesized(String search) {
- return _node(search).getAncestor((n) => n is ParenthesizedExpression);
+ return _node(search, (n) => n is ParenthesizedExpression);
}
PartDirective part(String search) {
- return _node(search).getAncestor((n) => n is PartDirective);
+ return _node(search, (n) => n is PartDirective);
}
PartOfDirective partOf(String search) {
- return _node(search).getAncestor((n) => n is PartOfDirective);
+ return _node(search, (n) => n is PartOfDirective);
}
PostfixExpression postfix(String search) {
- return _node(search).getAncestor((n) => n is PostfixExpression);
+ return _node(search, (n) => n is PostfixExpression);
}
PrefixExpression prefix(String search) {
- return _node(search).getAncestor((n) => n is PrefixExpression);
+ return _node(search, (n) => n is PrefixExpression);
}
PrefixedIdentifier prefixed(String search) {
- return _node(search).getAncestor((n) => n is PrefixedIdentifier);
+ return _node(search, (n) => n is PrefixedIdentifier);
}
RethrowExpression rethrow_(String search) {
- return _node(search).getAncestor((n) => n is RethrowExpression);
+ return _node(search, (n) => n is RethrowExpression);
}
SimpleIdentifier simple(String search) {
- return _node(search);
+ return _node(search, (_) => true);
}
SimpleFormalParameter simpleParameter(String search) {
- return _node(search).getAncestor((n) => n is SimpleFormalParameter);
+ return _node(search, (n) => n is SimpleFormalParameter);
}
StringLiteral stringLiteral(String search) {
- return _node(search).getAncestor((n) => n is StringLiteral);
+ return _node(search, (n) => n is StringLiteral);
}
SuperExpression super_(String search) {
- return _node(search).getAncestor((n) => n is SuperExpression);
+ return _node(search, (n) => n is SuperExpression);
}
ThisExpression this_(String search) {
- return _node(search).getAncestor((n) => n is ThisExpression);
+ return _node(search, (n) => n is ThisExpression);
}
ThrowExpression throw_(String search) {
- return _node(search).getAncestor((n) => n is ThrowExpression);
+ return _node(search, (n) => n is ThrowExpression);
}
TypeName typeName(String search) {
- return _node(search).getAncestor((n) => n is TypeName);
+ return _node(search, (n) => n is TypeName);
}
TypeParameter typeParameter(String search) {
- return _node(search).getAncestor((n) => n is TypeParameter);
+ return _node(search, (n) => n is TypeParameter);
}
VariableDeclaration variableDeclaration(String search) {
- return _node(search).getAncestor((n) => n is VariableDeclaration);
+ return _node(search, (n) => n is VariableDeclaration);
}
- AstNode _node(String search) {
+ AstNode _node(String search, bool Function(AstNode) predicate) {
var index = content.indexOf(search);
if (content.indexOf(search, index + 1) != -1) {
fail('The pattern |$search| is not unique in:\n$content');
}
expect(index, greaterThanOrEqualTo(0));
- return new NodeLocator2(index).searchWithin(unit);
+
+ var node = new NodeLocator2(index).searchWithin(unit);
+ expect(node, isNotNull);
+
+ var result = node.getAncestor(predicate);
+ expect(result, isNotNull);
+ return result;
}
}
diff --git a/pkg/analyzer/test/src/dart/resolution/mixin_test.dart b/pkg/analyzer/test/src/dart/resolution/mixin_test.dart
new file mode 100644
index 0000000..205c71f
--- /dev/null
+++ b/pkg/analyzer/test/src/dart/resolution/mixin_test.dart
@@ -0,0 +1,222 @@
+import 'package:test/test.dart';
+import 'package:test_reflective_loader/test_reflective_loader.dart';
+
+import 'driver_resolution.dart';
+import 'resolution.dart';
+import 'task_resolution.dart';
+
+main() {
+ defineReflectiveSuite(() {
+ defineReflectiveTests(MixinDriverResolutionTest);
+ defineReflectiveTests(MixinTaskResolutionTest);
+ });
+}
+
+abstract class AssignmentResolutionMixin implements ResolutionTest {
+ test_accessor_getter() async {
+ addTestFile(r'''
+mixin M {
+ int get g => 0;
+}
+''');
+ await resolveTestFile();
+
+ var element = findElement.mixin('M');
+
+ var accessors = element.accessors;
+ expect(accessors, hasLength(1));
+
+ var gElement = accessors[0];
+ assertElementName(gElement, 'g', offset: 20);
+
+ var gNode = findNode.methodDeclaration('g =>');
+ assertElement(gNode.name, gElement);
+
+ var fields = element.fields;
+ expect(fields, hasLength(1));
+ assertElementName(fields[0], 'g', isSynthetic: true);
+ }
+
+ test_accessor_method() async {
+ addTestFile(r'''
+mixin M {
+ void foo() {}
+}
+''');
+ await resolveTestFile();
+
+ var element = findElement.mixin('M');
+
+ var methods = element.methods;
+ expect(methods, hasLength(1));
+
+ var fooElement = methods[0];
+ assertElementName(fooElement, 'foo', offset: 17);
+
+ var fooNode = findNode.methodDeclaration('foo()');
+ assertElement(fooNode.name, fooElement);
+ }
+
+ test_accessor_setter() async {
+ addTestFile(r'''
+mixin M {
+ void set s(int _) {}
+}
+''');
+ await resolveTestFile();
+
+ var element = findElement.mixin('M');
+
+ var accessors = element.accessors;
+ expect(accessors, hasLength(1));
+
+ var sElement = accessors[0];
+ assertElementName(sElement, 's=', offset: 21);
+
+ var gNode = findNode.methodDeclaration('s(int _)');
+ assertElement(gNode.name, sElement);
+
+ var fields = element.fields;
+ expect(fields, hasLength(1));
+ assertElementName(fields[0], 's', isSynthetic: true);
+ }
+
+ test_commentReference() async {
+ addTestFile(r'''
+const a = 0;
+
+/// Reference [a] in documentation.
+mixin M {}
+''');
+ await resolveTestFile();
+
+ var aRef = findNode.commentReference('a]').identifier;
+ assertElement(aRef, findElement.topGet('a'));
+ assertTypeNull(aRef);
+ }
+
+ test_element() async {
+ addTestFile(r'''
+mixin M {}
+''');
+ await resolveTestFile();
+
+ var mixin = findNode.mixin('mixin M');
+ var element = findElement.mixin('M');
+ assertElement(mixin, element);
+
+ expect(element.typeParameters, isEmpty);
+ assertElementTypes(element.superclassConstraints, [objectType]);
+ }
+
+ test_field() async {
+ addTestFile(r'''
+mixin M<T> {
+ T f;
+}
+''');
+ await resolveTestFile();
+
+ var element = findElement.mixin('M');
+
+ var typeParameters = element.typeParameters;
+ expect(typeParameters, hasLength(1));
+
+ var tElement = typeParameters.single;
+ assertElementName(tElement, 'T', offset: 8);
+ assertEnclosingElement(tElement, element);
+
+ var tNode = findNode.typeParameter('T> {');
+ assertElement(tNode.name, tElement);
+
+ var fields = element.fields;
+ expect(fields, hasLength(1));
+
+ var fElement = fields[0];
+ assertElementName(fElement, 'f', offset: 17);
+ assertEnclosingElement(fElement, element);
+
+ var fNode = findNode.variableDeclaration('f;');
+ assertElement(fNode.name, fElement);
+
+ assertTypeName(findNode.typeName('T f'), tElement, 'T');
+
+ var accessors = element.accessors;
+ expect(accessors, hasLength(2));
+ assertElementName(accessors[0], 'f', isSynthetic: true);
+ assertElementName(accessors[1], 'f=', isSynthetic: true);
+ }
+
+ test_implementsClause() async {
+ addTestFile(r'''
+class A {}
+class B {}
+
+mixin M implements A, B {} // M
+''');
+ await resolveTestFile();
+
+ var element = findElement.mixin('M');
+ assertElementTypes(element.interfaces, [
+ findElement.interfaceType('A'),
+ findElement.interfaceType('B'),
+ ]);
+
+ var aRef = findNode.typeName('A, ');
+ assertTypeName(aRef, findElement.class_('A'), 'A');
+
+ var bRef = findNode.typeName('B {} // M');
+ assertTypeName(bRef, findElement.class_('B'), 'B');
+ }
+
+ test_metadata() async {
+ addTestFile(r'''
+const a = 0;
+
+@a
+mixin M {}
+''');
+ await resolveTestFile();
+
+ var a = findElement.topGet('a');
+ var element = findElement.mixin('M');
+
+ var metadata = element.metadata;
+ expect(metadata, hasLength(1));
+ expect(metadata[0].element, same(a));
+
+ var annotation = findNode.annotation('@a');
+ assertElement(annotation, a);
+ expect(annotation.elementAnnotation, same(metadata[0]));
+ }
+
+ test_onClause() async {
+ addTestFile(r'''
+class A {}
+class B {}
+
+mixin M on A, B {} // M
+''');
+ await resolveTestFile();
+
+ var element = findElement.mixin('M');
+ assertElementTypes(element.superclassConstraints, [
+ findElement.interfaceType('A'),
+ findElement.interfaceType('B'),
+ ]);
+
+ var aRef = findNode.typeName('A, ');
+ assertTypeName(aRef, findElement.class_('A'), 'A');
+
+ var bRef = findNode.typeName('B {} // M');
+ assertTypeName(bRef, findElement.class_('B'), 'B');
+ }
+}
+
+@reflectiveTest
+class MixinDriverResolutionTest extends DriverResolutionTest
+ with AssignmentResolutionMixin {}
+
+@reflectiveTest
+class MixinTaskResolutionTest extends TaskResolutionTest
+ with AssignmentResolutionMixin {}
diff --git a/pkg/analyzer/test/src/dart/resolution/resolution.dart b/pkg/analyzer/test/src/dart/resolution/resolution.dart
index 9533052..b93718a 100644
--- a/pkg/analyzer/test/src/dart/resolution/resolution.dart
+++ b/pkg/analyzer/test/src/dart/resolution/resolution.dart
@@ -41,6 +41,8 @@
ClassElement get numElement => typeProvider.numType.element;
+ InterfaceType get objectType => typeProvider.objectType;
+
TypeProvider get typeProvider =>
result.unit.declaredElement.context.typeProvider;
@@ -53,11 +55,35 @@
expect(actual, same(expected));
}
+ void assertElementName(Element element, String name,
+ {bool isSynthetic = false, int offset}) {
+ expect(element.name, name);
+ expect(element.isSynthetic, isSynthetic);
+ if (offset != null) {
+ expect(element.nameOffset, offset);
+ }
+ }
+
void assertElementNull(Expression node) {
Element actual = getNodeElement(node);
expect(actual, isNull);
}
+ void assertElementType(DartType type, DartType expected) {
+ expect(type, expected);
+ }
+
+ void assertElementTypes(List<DartType> types, List<DartType> expected) {
+ expect(types, hasLength(expected.length));
+ for (var i = 0; i < types.length; ++i) {
+ assertElementType(types[i], expected[i]);
+ }
+ }
+
+ void assertEnclosingElement(Element element, Element expectedEnclosing) {
+ expect(element.enclosingElement, expectedEnclosing);
+ }
+
void assertIdentifierTopGetRef(SimpleIdentifier ref, String name) {
var getter = findElement.topGet(name);
assertElement(ref, getter);
@@ -129,9 +155,13 @@
expect(node.staticType, isNull);
}
- Element getNodeElement(Expression node) {
- if (node is AssignmentExpression) {
+ Element getNodeElement(AstNode node) {
+ if (node is Annotation) {
+ return node.element;
+ } else if (node is AssignmentExpression) {
return node.staticElement;
+ } else if (node is Declaration) {
+ return node.declaredElement;
} else if (node is Identifier) {
return node.staticElement;
} else if (node is IndexExpression) {