blob: b86593c43ca90603c3187d8b2be82bd1a6cade93 [file] [log] [blame]
// Copyright (c) 2015, 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.
library dart2js.resolution.typedefs;
import '../compiler.dart' show
Compiler;
import '../dart_types.dart';
import '../diagnostics/messages.dart' show
MessageKind;
import '../elements/elements.dart' show
FunctionSignature,
TypedefElement,
TypeVariableElement;
import '../elements/modelx.dart' show
ErroneousElementX,
TypedefElementX;
import '../tree/tree.dart';
import '../util/util.dart' show
Link;
import 'class_hierarchy.dart' show
TypeDefinitionVisitor;
import 'registry.dart' show
ResolutionRegistry;
import 'scope.dart' show
MethodScope,
TypeDeclarationScope;
import 'signatures.dart' show
SignatureResolver;
class TypedefResolverVisitor extends TypeDefinitionVisitor {
TypedefElementX get element => enclosingElement;
TypedefResolverVisitor(Compiler compiler,
TypedefElement typedefElement,
ResolutionRegistry registry)
: super(compiler, typedefElement, registry);
visitTypedef(Typedef node) {
element.computeType(compiler);
scope = new TypeDeclarationScope(scope, element);
resolveTypeVariableBounds(node.typeParameters);
FunctionSignature signature = SignatureResolver.analyze(
compiler, node.formals, node.returnType, element, registry,
defaultValuesError: MessageKind.TYPEDEF_FORMAL_WITH_DEFAULT);
element.functionSignature = signature;
scope = new MethodScope(scope, element);
signature.forEachParameter(addToScope);
element.alias = signature.type;
void checkCyclicReference() {
element.checkCyclicReference(compiler);
}
addDeferredAction(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 BaseDartTypeVisitor {
final Compiler compiler;
final TypedefElementX element;
bool hasCyclicReference = false;
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, _) {
TypedefElementX 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 (seenTypedefsCount == 1) {
// Direct cyclicity.
compiler.reportErrorMessage(
element,
MessageKind.CYCLIC_TYPEDEF,
{'typedefName': element.name});
} else if (seenTypedefsCount == 2) {
// Cyclicity through one other typedef.
compiler.reportErrorMessage(
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.reportErrorMessage(
element,
MessageKind.CYCLIC_TYPEDEF_ONE,
{'typedefName': element.name,
'otherTypedefName': cycle.name});
}
}
}
ErroneousElementX erroneousElement = new ErroneousElementX(
MessageKind.CYCLIC_TYPEDEF,
{'typedefName': element.name},
element.name, element);
element.alias =
new MalformedType(erroneousElement, typedefElement.alias);
element.hasBeenCheckedForCycles = true;
}
} 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);
typeVariableElement.bound.accept(this, null);
seenTypeVariables = seenTypeVariables.tail;
}
}