| Index: sdk/lib/_internal/compiler/implementation/resolution/members.dart
|
| diff --git a/sdk/lib/_internal/compiler/implementation/resolution/members.dart b/sdk/lib/_internal/compiler/implementation/resolution/members.dart
|
| index 130943c3f752e220ae367a76ff733065f9c28743..d908d0a0e5084b5a3396504db8e89d19ce810b0e 100644
|
| --- a/sdk/lib/_internal/compiler/implementation/resolution/members.dart
|
| +++ b/sdk/lib/_internal/compiler/implementation/resolution/members.dart
|
| @@ -3340,7 +3340,101 @@ class TypedefResolverVisitor extends TypeDefinitionVisitor {
|
| element.alias = compiler.computeFunctionType(
|
| element, element.functionSignature);
|
|
|
| - // TODO(johnniwinther): Check for cyclic references in the typedef alias.
|
| + void checkCyclicReference() {
|
| + var visitor = new TypedefCyclicVisitor(compiler, element);
|
| + type.accept(visitor, null);
|
| + }
|
| + compiler.enqueuer.resolution.addPostProcessAction(element,
|
| + checkCyclicReference);
|
| + }
|
| +}
|
| +
|
| +// TODO(johnniwinther): Replace with a traversal on the AST when the type
|
| +// annotations in typedef alias are stored in a [TreeElements] mapping.
|
| +class TypedefCyclicVisitor extends DartTypeVisitor {
|
| + final Compiler compiler;
|
| + final TypedefElement element;
|
| + bool hasCyclicReference = false;
|
| +
|
| + /// Counter for how many bounds the visitor currently has on the call-stack.
|
| + /// Used to detect when to report [Messagekind.CYCLIC_TYPEDEF_TYPEVAR].
|
| + int seenBoundsCount = 0;
|
| +
|
| + Link<TypedefElement> seenTypedefs = const Link<TypedefElement>();
|
| +
|
| + int seenTypedefsCount = 0;
|
| +
|
| + Link<TypeVariableElement> seenTypeVariables =
|
| + const Link<TypeVariableElement>();
|
| +
|
| + TypedefCyclicVisitor(Compiler this.compiler, TypedefElement this.element);
|
| +
|
| + visitType(DartType type, _) {
|
| + // Do nothing.
|
| + }
|
| +
|
| + visitTypedefType(TypedefType type, _) {
|
| + TypedefElement typedefElement = type.element;
|
| + if (seenTypedefs.contains(typedefElement)) {
|
| + if (!hasCyclicReference && identical(element, typedefElement)) {
|
| + // Only report an error on the checked typedef to avoid generating
|
| + // multiple errors for the same cyclicity.
|
| + hasCyclicReference = true;
|
| + if (seenBoundsCount > 0) {
|
| + compiler.reportError(element, MessageKind.CYCLIC_TYPEDEF_TYPEVAR);
|
| + } else if (seenTypedefsCount == 1) {
|
| + // Direct cyclicity.
|
| + compiler.reportError(element,
|
| + MessageKind.CYCLIC_TYPEDEF,
|
| + {'typedefName': element.name});
|
| + } else if (seenTypedefsCount == 2) {
|
| + // Cyclicity through one other typedef.
|
| + compiler.reportError(element,
|
| + MessageKind.CYCLIC_TYPEDEF_ONE,
|
| + {'typedefName': element.name,
|
| + 'otherTypedefName': seenTypedefs.head.name});
|
| + } else {
|
| + // Cyclicity through more than one other typedef.
|
| + for (TypedefElement cycle in seenTypedefs) {
|
| + if (!identical(typedefElement, cycle)) {
|
| + compiler.reportError(element,
|
| + MessageKind.CYCLIC_TYPEDEF_ONE,
|
| + {'typedefName': element.name,
|
| + 'otherTypedefName': cycle.name});
|
| + }
|
| + }
|
| + }
|
| + }
|
| + } else {
|
| + seenTypedefs = seenTypedefs.prepend(typedefElement);
|
| + seenTypedefsCount++;
|
| + type.visitChildren(this, null);
|
| + typedefElement.alias.accept(this, null);
|
| + seenTypedefs = seenTypedefs.tail;
|
| + seenTypedefsCount--;
|
| + }
|
| + }
|
| +
|
| + visitFunctionType(FunctionType type, _) {
|
| + type.visitChildren(this, null);
|
| + }
|
| +
|
| + visitInterfaceType(InterfaceType type, _) {
|
| + type.visitChildren(this, null);
|
| + }
|
| +
|
| + visitTypeVariableType(TypeVariableType type, _) {
|
| + TypeVariableElement typeVariableElement = type.element;
|
| + if (seenTypeVariables.contains(typeVariableElement)) {
|
| + // Avoid running in cycles on cyclic type variable bounds.
|
| + // Cyclicity is reported elsewhere.
|
| + return;
|
| + }
|
| + seenTypeVariables = seenTypeVariables.prepend(typeVariableElement);
|
| + seenBoundsCount++;
|
| + typeVariableElement.bound.accept(this, null);
|
| + seenBoundsCount--;
|
| + seenTypeVariables = seenTypeVariables.tail;
|
| }
|
| }
|
|
|
|
|