mirror of
https://github.com/dart-lang/sdk
synced 2024-10-14 11:03:19 +00:00
Add support for the new function-type syntax.
Fixes #27967 BUG= http://dartbug.com/27967 R=ahe@google.com, johnniwinther@google.com, sigmund@google.com Committed:85227ba8a6
Reverted:f5fc210f4f
Committed:e4999a6310
Reverted:c64c351539
Review-Url: https://codereview.chromium.org/2567133002 .
This commit is contained in:
parent
3ceb5103cd
commit
c289af39c3
|
@ -34,6 +34,7 @@ dart_style:third_party/pkg_tested/dart_style/lib
|
|||
dartdoc:third_party/pkg/dartdoc/lib
|
||||
dev_compiler:pkg/dev_compiler/lib
|
||||
expect:pkg/expect/lib
|
||||
fasta:pkg/fasta/lib
|
||||
fixnum:third_party/pkg/fixnum/lib
|
||||
front_end:pkg/front_end/lib
|
||||
func:third_party/pkg/func/lib
|
||||
|
|
|
@ -275,6 +275,7 @@ enum MessageKind {
|
|||
INVALID_RECEIVER_IN_INITIALIZER,
|
||||
INVALID_SOURCE_FILE_LOCATION,
|
||||
INVALID_SYMBOL,
|
||||
INVALID_INLINE_FUNCTION_TYPE,
|
||||
INVALID_SYNC_MODIFIER,
|
||||
INVALID_TYPE_VARIABLE_BOUND,
|
||||
INVALID_UNNAMED_CONSTRUCTOR_NAME,
|
||||
|
@ -3319,6 +3320,16 @@ Please include the following information:
|
|||
" require a preamble file located in:\n"
|
||||
" <sdk>/lib/_internal/js_runtime/lib/preambles."),
|
||||
|
||||
MessageKind.INVALID_INLINE_FUNCTION_TYPE: const MessageTemplate(
|
||||
MessageKind.INVALID_INLINE_FUNCTION_TYPE,
|
||||
"Invalid inline function type.",
|
||||
howToFix: "Try changing the inline function type (as in 'int f()') to"
|
||||
" a prefixed function type using the `Function` keyword (as in "
|
||||
"'int Function() f').",
|
||||
examples:
|
||||
const ["typedef F = Function(int f(String x)); main() { F f; }"],
|
||||
),
|
||||
|
||||
MessageKind.INVALID_SYNC_MODIFIER: const MessageTemplate(
|
||||
MessageKind.INVALID_SYNC_MODIFIER, "Invalid modifier 'sync'.",
|
||||
howToFix: "Try replacing 'sync' with 'sync*'.",
|
||||
|
|
|
@ -1208,6 +1208,9 @@ abstract class FormalElement extends Element
|
|||
FunctionTypedElement get functionDeclaration;
|
||||
|
||||
VariableDefinitions get node;
|
||||
|
||||
/// Whether the parameter is unnamed in a function type.
|
||||
bool get isUnnamed;
|
||||
}
|
||||
|
||||
/// A formal parameter of a function or constructor.
|
||||
|
|
|
@ -1334,7 +1334,7 @@ class TypedefElementX extends ElementX
|
|||
ResolutionTypedefType computeType(Resolution resolution) {
|
||||
if (thisTypeCache != null) return thisTypeCache;
|
||||
Typedef node = parseNode(resolution.parsingContext);
|
||||
setThisAndRawTypes(createTypeVariables(node.typeParameters));
|
||||
setThisAndRawTypes(createTypeVariables(node.templateParameters));
|
||||
ensureResolved(resolution);
|
||||
return thisTypeCache;
|
||||
}
|
||||
|
@ -1732,6 +1732,15 @@ class FormalElementX extends ElementX
|
|||
: this.identifier = identifier,
|
||||
super(identifier.source, elementKind, enclosingElement);
|
||||
|
||||
FormalElementX.unnamed(ElementKind elementKind,
|
||||
FunctionTypedElement enclosingElement,
|
||||
this.definitions)
|
||||
: this.identifier = null,
|
||||
super("<unnamed>", elementKind, enclosingElement);
|
||||
|
||||
/// Whether this is an unnamed parameter in a Function type.
|
||||
bool get isUnnamed => identifier == null;
|
||||
|
||||
FunctionTypedElement get functionDeclaration => enclosingElement;
|
||||
|
||||
Modifiers get modifiers => definitions.modifiers;
|
||||
|
|
|
@ -642,6 +642,16 @@ class ResolutionFunctionType extends ResolutionDartType
|
|||
optionalParameterTypes, namedParameters, namedParameterTypes);
|
||||
}
|
||||
|
||||
factory ResolutionFunctionType.generalized(
|
||||
ResolutionDartType returnType,
|
||||
List<ResolutionDartType> parameterTypes,
|
||||
List<ResolutionDartType> optionalParameterTypes,
|
||||
List<String> namedParameters,
|
||||
List<ResolutionDartType> namedParameterTypes) {
|
||||
return new ResolutionFunctionType.internal(null, returnType, parameterTypes,
|
||||
optionalParameterTypes, namedParameters, namedParameterTypes);
|
||||
}
|
||||
|
||||
ResolutionFunctionType.internal(FunctionTypedElement this.element,
|
||||
[ResolutionDartType returnType = const ResolutionDynamicType(),
|
||||
List<ResolutionDartType> parameterTypes = const <ResolutionDartType>[],
|
||||
|
|
|
@ -219,7 +219,8 @@ class NoSuchMethodRegistry {
|
|||
}
|
||||
if (expr is Send && expr.isTypeCast) {
|
||||
Send sendExpr = expr;
|
||||
var typeName = sendExpr.typeAnnotationFromIsCheckOrCast.typeName;
|
||||
var typeAnnotation = sendExpr.typeAnnotationFromIsCheckOrCast;
|
||||
var typeName = typeAnnotation.asNominalTypeAnnotation()?.typeName;
|
||||
if (typeName is Identifier && typeName.source == "dynamic") {
|
||||
expr = sendExpr.receiver;
|
||||
}
|
||||
|
|
|
@ -105,6 +105,7 @@ import '../tree/tree.dart'
|
|||
ForIn,
|
||||
FunctionDeclaration,
|
||||
FunctionExpression,
|
||||
FunctionTypeAnnotation,
|
||||
Identifier,
|
||||
If,
|
||||
Label,
|
||||
|
@ -123,6 +124,7 @@ import '../tree/tree.dart'
|
|||
NewExpression,
|
||||
Node,
|
||||
NodeList,
|
||||
NominalTypeAnnotation,
|
||||
Operator,
|
||||
ParenthesizedExpression,
|
||||
RedirectingFactoryBody,
|
||||
|
@ -1121,14 +1123,28 @@ class KernelVisitor extends Object
|
|||
|
||||
@override
|
||||
visitTypeAnnotation(TypeAnnotation node) {
|
||||
// Shouldn't be called, as the resolver have already resolved types and
|
||||
// Shouldn't be called, as the resolver has already resolved types and
|
||||
// created [DartType] objects.
|
||||
return internalError(node, "TypeAnnotation");
|
||||
}
|
||||
|
||||
@override
|
||||
visitNominalTypeAnnotation(NominalTypeAnnotation node) {
|
||||
// Shouldn't be called, as the resolver has already resolved types and
|
||||
// created [DartType] objects.
|
||||
return internalError(node, "NominalTypeAnnotation");
|
||||
}
|
||||
|
||||
@override
|
||||
visitFunctionTypeAnnotation(FunctionTypeAnnotation node) {
|
||||
// Shouldn't be called, as the resolver has already resolved types and
|
||||
// created [DartType] objects.
|
||||
return internalError(node, "FunctionTypeAnnotation");
|
||||
}
|
||||
|
||||
@override
|
||||
visitTypeVariable(TypeVariable node) {
|
||||
// Shouldn't be called, as the resolver have already resolved types and
|
||||
// Shouldn't be called, as the resolver has already resolved types and
|
||||
// created [DartType] objects.
|
||||
return internalError(node, "TypeVariable");
|
||||
}
|
||||
|
|
|
@ -262,7 +262,7 @@ class NativeBehavior {
|
|||
/// Each tag kind (including the 'type-tag's) can only occur once in the
|
||||
/// sequence.
|
||||
///
|
||||
/// [specString] is the specification string, [resolveType] resolves named
|
||||
/// [specString] is the specification string, [lookupType] resolves named
|
||||
/// types into type values, [typesReturned] and [typesInstantiated] collects
|
||||
/// the types defined by the specification string, and [objectType] and
|
||||
/// [nullType] define the types for `Object` and `Null`, respectively. The
|
||||
|
|
|
@ -17,7 +17,9 @@ import 'package:front_end/src/fasta/parser.dart'
|
|||
class PartialParser extends TopLevelParser {
|
||||
PartialParser(Listener listener) : super(listener);
|
||||
|
||||
Token parseFormalParameters(Token token) => skipFormalParameters(token);
|
||||
Token parseFormalParameters(Token token, {bool inFunctionType: false}) {
|
||||
return skipFormalParameters(token);
|
||||
}
|
||||
}
|
||||
|
||||
class DietParserTask extends CompilerTask {
|
||||
|
|
|
@ -287,10 +287,18 @@ class ElementListener extends Listener {
|
|||
}
|
||||
|
||||
@override
|
||||
void endFunctionTypeAlias(Token typedefKeyword, Token endToken) {
|
||||
popNode(); // TODO(karlklose): do not throw away typeVariables.
|
||||
Identifier name = popNode();
|
||||
popNode(); // returnType
|
||||
void endFunctionTypeAlias(
|
||||
Token typedefKeyword, Token equals, Token endToken) {
|
||||
Identifier name;
|
||||
if (equals == null) {
|
||||
popNode(); // TODO(karlklose): do not throw away typeVariables.
|
||||
name = popNode();
|
||||
popNode(); // returnType
|
||||
} else {
|
||||
popNode(); // Function type.
|
||||
popNode(); // TODO(karlklose): do not throw away typeVariables.
|
||||
name = popNode();
|
||||
}
|
||||
pushElement(new PartialTypedefElement(
|
||||
name.source, compilationUnitElement, typedefKeyword, endToken));
|
||||
rejectBuiltInIdentifier(name);
|
||||
|
@ -324,13 +332,13 @@ class ElementListener extends Listener {
|
|||
@override
|
||||
void endMixinApplication() {
|
||||
NodeList mixins = popNode();
|
||||
TypeAnnotation superclass = popNode();
|
||||
NominalTypeAnnotation superclass = popNode();
|
||||
pushNode(new MixinApplication(superclass, mixins));
|
||||
}
|
||||
|
||||
@override
|
||||
void handleVoidKeyword(Token token) {
|
||||
pushNode(new TypeAnnotation(new Identifier(token), null));
|
||||
pushNode(new NominalTypeAnnotation(new Identifier(token), null));
|
||||
}
|
||||
|
||||
@override
|
||||
|
@ -403,7 +411,7 @@ class ElementListener extends Listener {
|
|||
|
||||
@override
|
||||
void endTypeVariable(Token token, Token extendsOrSuper) {
|
||||
TypeAnnotation bound = popNode();
|
||||
NominalTypeAnnotation bound = popNode();
|
||||
Identifier name = popNode();
|
||||
pushNode(new TypeVariable(name, extendsOrSuper, bound));
|
||||
rejectBuiltInIdentifier(name);
|
||||
|
@ -430,10 +438,21 @@ class ElementListener extends Listener {
|
|||
}
|
||||
|
||||
@override
|
||||
void endType(Token beginToken, Token endToken) {
|
||||
void handleType(Token beginToken, Token endToken) {
|
||||
NodeList typeArguments = popNode();
|
||||
Expression typeName = popNode();
|
||||
pushNode(new TypeAnnotation(typeName, typeArguments));
|
||||
pushNode(new NominalTypeAnnotation(typeName, typeArguments));
|
||||
}
|
||||
|
||||
void handleNoName(Token token) {
|
||||
pushNode(null);
|
||||
}
|
||||
|
||||
@override
|
||||
void handleFunctionType(Token functionToken, Token endToken) {
|
||||
popNode(); // Type parameters.
|
||||
popNode(); // Return type.
|
||||
pushNode(null);
|
||||
}
|
||||
|
||||
@override
|
||||
|
@ -632,6 +651,10 @@ class ElementListener extends Listener {
|
|||
errorCode = MessageKind.BAD_INPUT_CHARACTER;
|
||||
break;
|
||||
|
||||
case ErrorKind.InvalidInlineFunctionType:
|
||||
errorCode = MessageKind.INVALID_INLINE_FUNCTION_TYPE;
|
||||
break;
|
||||
|
||||
case ErrorKind.InvalidSyncModifier:
|
||||
errorCode = MessageKind.INVALID_SYNC_MODIFIER;
|
||||
break;
|
||||
|
|
|
@ -126,13 +126,60 @@ class NodeListener extends ElementListener {
|
|||
}
|
||||
|
||||
@override
|
||||
void endFunctionTypeAlias(Token typedefKeyword, Token endToken) {
|
||||
void endFunctionTypeAlias(
|
||||
Token typedefKeyword, Token equals, Token endToken) {
|
||||
bool isGeneralizedTypeAlias;
|
||||
NodeList templateParameters;
|
||||
TypeAnnotation returnType;
|
||||
Identifier name;
|
||||
NodeList typeParameters;
|
||||
NodeList formals;
|
||||
if (equals == null) {
|
||||
isGeneralizedTypeAlias = false;
|
||||
formals = popNode();
|
||||
templateParameters = popNode();
|
||||
name = popNode();
|
||||
returnType = popNode();
|
||||
} else {
|
||||
// TODO(floitsch): keep using the `FunctionTypeAnnotation' node.
|
||||
isGeneralizedTypeAlias = true;
|
||||
Node type = popNode();
|
||||
if (type.asFunctionTypeAnnotation() == null) {
|
||||
// TODO(floitsch): The parser should diagnose this problem, not
|
||||
// this listener.
|
||||
// However, this problem goes away, when we allow aliases for
|
||||
// non-function types too.
|
||||
reportFatalError(type, 'Expected a function type.');
|
||||
}
|
||||
FunctionTypeAnnotation functionType = type;
|
||||
templateParameters = popNode();
|
||||
name = popNode();
|
||||
returnType = functionType.returnType;
|
||||
typeParameters = functionType.typeParameters;
|
||||
formals = functionType.formals;
|
||||
}
|
||||
pushNode(new Typedef(
|
||||
isGeneralizedTypeAlias,
|
||||
templateParameters,
|
||||
returnType,
|
||||
name,
|
||||
typeParameters,
|
||||
formals,
|
||||
typedefKeyword,
|
||||
endToken));
|
||||
}
|
||||
|
||||
void handleNoName(Token token) {
|
||||
pushNode(null);
|
||||
}
|
||||
|
||||
@override
|
||||
void handleFunctionType(Token functionToken, Token endToken) {
|
||||
NodeList formals = popNode();
|
||||
NodeList typeParameters = popNode();
|
||||
Identifier name = popNode();
|
||||
TypeAnnotation returnType = popNode();
|
||||
pushNode(new Typedef(
|
||||
returnType, name, typeParameters, formals, typedefKeyword, endToken));
|
||||
pushNode(new FunctionTypeAnnotation(
|
||||
returnType, functionToken, typeParameters, formals));
|
||||
}
|
||||
|
||||
@override
|
||||
|
@ -229,7 +276,7 @@ class NodeListener extends ElementListener {
|
|||
NodeList typeArguments = popNode();
|
||||
Node classReference = popNode();
|
||||
if (typeArguments != null) {
|
||||
classReference = new TypeAnnotation(classReference, typeArguments);
|
||||
classReference = new NominalTypeAnnotation(classReference, typeArguments);
|
||||
} else {
|
||||
Identifier identifier = classReference.asIdentifier();
|
||||
Send send = classReference.asSend();
|
||||
|
@ -867,7 +914,7 @@ class NodeListener extends ElementListener {
|
|||
NodeList typeArguments = popNode();
|
||||
Node receiver = popNode();
|
||||
if (typeArguments != null) {
|
||||
receiver = new TypeAnnotation(receiver, typeArguments);
|
||||
receiver = new NominalTypeAnnotation(receiver, typeArguments);
|
||||
recoverableError(typeArguments, 'Type arguments are not allowed here.');
|
||||
} else {
|
||||
Identifier identifier = receiver.asIdentifier();
|
||||
|
|
|
@ -42,7 +42,9 @@ import 'node_listener.dart' show NodeListener;
|
|||
class ClassElementParser extends ClassMemberParser {
|
||||
ClassElementParser(Listener listener) : super(listener);
|
||||
|
||||
Token parseFormalParameters(Token token) => skipFormalParameters(token);
|
||||
Token parseFormalParameters(Token token, {bool inFunctionType: false}) {
|
||||
return skipFormalParameters(token);
|
||||
}
|
||||
}
|
||||
|
||||
abstract class PartialElement implements DeclarationSite {
|
||||
|
|
|
@ -66,8 +66,8 @@ class TypeDefinitionVisitor extends MappingVisitor<ResolutionDartType> {
|
|||
|
||||
TypeVariableElementX variableElement = typeVariable.element;
|
||||
if (typeNode.bound != null) {
|
||||
ResolutionDartType boundType =
|
||||
typeResolver.resolveTypeAnnotation(this, typeNode.bound);
|
||||
ResolutionDartType boundType = typeResolver
|
||||
.resolveNominalTypeAnnotation(this, typeNode.bound, const []);
|
||||
variableElement.boundCache = boundType;
|
||||
|
||||
void checkTypeVariableBound() {
|
||||
|
@ -250,8 +250,8 @@ class ClassResolverVisitor extends TypeDefinitionVisitor {
|
|||
|
||||
/// Resolves the mixed type for [mixinNode] and checks that the mixin type
|
||||
/// is a valid, non-blacklisted interface type. The mixin type is returned.
|
||||
ResolutionDartType checkMixinType(TypeAnnotation mixinNode) {
|
||||
ResolutionDartType mixinType = resolveType(mixinNode);
|
||||
ResolutionDartType checkMixinType(NominalTypeAnnotation mixinNode) {
|
||||
ResolutionDartType mixinType = resolveNominalType(mixinNode);
|
||||
if (isBlackListed(mixinType)) {
|
||||
reporter.reportErrorMessage(
|
||||
mixinNode, MessageKind.CANNOT_MIXIN, {'type': mixinType});
|
||||
|
@ -440,13 +440,13 @@ class ClassResolverVisitor extends TypeDefinitionVisitor {
|
|||
return mixinType;
|
||||
}
|
||||
|
||||
ResolutionDartType resolveType(TypeAnnotation node) {
|
||||
return typeResolver.resolveTypeAnnotation(this, node);
|
||||
ResolutionDartType resolveNominalType(NominalTypeAnnotation node) {
|
||||
return typeResolver.resolveNominalTypeAnnotation(this, node, const []);
|
||||
}
|
||||
|
||||
ResolutionDartType resolveSupertype(
|
||||
ClassElement cls, TypeAnnotation superclass) {
|
||||
ResolutionDartType supertype = resolveType(superclass);
|
||||
ClassElement cls, NominalTypeAnnotation superclass) {
|
||||
ResolutionDartType supertype = resolveNominalType(superclass);
|
||||
if (supertype != null) {
|
||||
if (supertype.isMalformed) {
|
||||
reporter.reportErrorMessage(
|
||||
|
@ -476,7 +476,7 @@ class ClassResolverVisitor extends TypeDefinitionVisitor {
|
|||
Link<ResolutionDartType> result = const Link<ResolutionDartType>();
|
||||
if (interfaces == null) return result;
|
||||
for (Link<Node> link = interfaces.nodes; !link.isEmpty; link = link.tail) {
|
||||
ResolutionDartType interfaceType = resolveType(link.head);
|
||||
ResolutionDartType interfaceType = resolveNominalType(link.head);
|
||||
if (interfaceType != null) {
|
||||
if (interfaceType.isMalformed) {
|
||||
reporter.reportErrorMessage(
|
||||
|
@ -490,7 +490,7 @@ class ClassResolverVisitor extends TypeDefinitionVisitor {
|
|||
{'className': element.name, 'enumType': interfaceType});
|
||||
} else if (!interfaceType.isInterfaceType) {
|
||||
// TODO(johnniwinther): Handle dynamic.
|
||||
TypeAnnotation typeAnnotation = link.head;
|
||||
NominalTypeAnnotation typeAnnotation = link.head;
|
||||
reporter.reportErrorMessage(
|
||||
typeAnnotation.typeName, MessageKind.CLASS_NAME_EXPECTED);
|
||||
} else {
|
||||
|
@ -615,7 +615,7 @@ class ClassSupertypeResolver extends CommonResolverVisitor {
|
|||
visitNodeList(node.interfaces);
|
||||
}
|
||||
|
||||
void visitTypeAnnotation(TypeAnnotation node) {
|
||||
void visitNominalTypeAnnotation(NominalTypeAnnotation node) {
|
||||
node.typeName.accept(this);
|
||||
}
|
||||
|
||||
|
|
|
@ -618,7 +618,7 @@ class ConstructorResolver extends CommonResolverVisitor<ConstructorResult> {
|
|||
return result;
|
||||
}
|
||||
|
||||
ConstructorResult visitTypeAnnotation(TypeAnnotation node) {
|
||||
ConstructorResult visitNominalTypeAnnotation(NominalTypeAnnotation node) {
|
||||
// This is not really resolving a type-annotation, but the name of the
|
||||
// constructor. Therefore we allow deferred types.
|
||||
ResolutionDartType type = resolver.resolveTypeAnnotation(node,
|
||||
|
|
|
@ -56,6 +56,10 @@ abstract class MappingVisitor<T> extends CommonResolverVisitor<T> {
|
|||
|
||||
/// Add [element] to the current scope and check for duplicate definitions.
|
||||
void addToScope(Element element) {
|
||||
if (element is FormalElement && element.isUnnamed) {
|
||||
// No duplicate names possible.
|
||||
return;
|
||||
}
|
||||
Element existing = scope.add(element);
|
||||
if (existing != element) {
|
||||
reportDuplicateDefinition(element.name, element, existing);
|
||||
|
|
|
@ -93,7 +93,9 @@ class SignatureResolver extends MappingVisitor<FormalElementX> {
|
|||
reporter.internalError(node, 'function type parameters not supported');
|
||||
}
|
||||
currentDefinitions = node;
|
||||
FormalElementX element = definition.accept(this);
|
||||
FormalElementX element = definition == null
|
||||
? createUnnamedParameter() // This happens in function types.
|
||||
: definition.accept(this);
|
||||
if (currentDefinitions.metadata != null) {
|
||||
element.metadataInternal =
|
||||
resolution.resolver.resolveMetadata(element, node);
|
||||
|
@ -112,7 +114,8 @@ class SignatureResolver extends MappingVisitor<FormalElementX> {
|
|||
|
||||
void computeParameterType(FormalElementX element,
|
||||
[VariableElement fieldElement]) {
|
||||
void computeFunctionType(FunctionExpression functionExpression) {
|
||||
// Function-type as in `foo(int bar(String x))`
|
||||
void computeInlineFunctionType(FunctionExpression functionExpression) {
|
||||
FunctionSignature functionSignature = SignatureResolver.analyze(
|
||||
resolution,
|
||||
scope,
|
||||
|
@ -134,13 +137,14 @@ class SignatureResolver extends MappingVisitor<FormalElementX> {
|
|||
assert(invariant(currentDefinitions, link.tail.isEmpty));
|
||||
if (link.head.asFunctionExpression() != null) {
|
||||
// Inline function typed parameter, like `void m(int f(String s))`.
|
||||
computeFunctionType(link.head);
|
||||
computeInlineFunctionType(link.head);
|
||||
} else if (link.head.asSend() != null &&
|
||||
link.head.asSend().selector.asFunctionExpression() != null) {
|
||||
// Inline function typed initializing formal or
|
||||
// parameter with default value, like `C(int this.f(String s))` or
|
||||
// `void m([int f(String s) = null])`.
|
||||
computeFunctionType(link.head.asSend().selector.asFunctionExpression());
|
||||
computeInlineFunctionType(
|
||||
link.head.asSend().selector.asFunctionExpression());
|
||||
} else {
|
||||
assert(invariant(currentDefinitions,
|
||||
link.head.asIdentifier() != null || link.head.asSend() != null));
|
||||
|
@ -197,6 +201,15 @@ class SignatureResolver extends MappingVisitor<FormalElementX> {
|
|||
return parameter;
|
||||
}
|
||||
|
||||
FormalElementX createUnnamedParameter() {
|
||||
FormalElementX parameter;
|
||||
assert(!createRealParameters);
|
||||
parameter = new FormalElementX.unnamed(
|
||||
ElementKind.PARAMETER, enclosingElement, currentDefinitions);
|
||||
computeParameterType(parameter);
|
||||
return parameter;
|
||||
}
|
||||
|
||||
InitializingFormalElementX createFieldParameter(
|
||||
Send node, Expression initializer) {
|
||||
InitializingFormalElementX element;
|
||||
|
@ -426,7 +439,7 @@ class SignatureResolver extends MappingVisitor<FormalElementX> {
|
|||
List<Element> orderedOptionalParameters =
|
||||
visitor.optionalParameters.toList();
|
||||
if (visitor.optionalParametersAreNamed) {
|
||||
// TODO(karlklose); replace when [visitor.optinalParameters] is a [List].
|
||||
// TODO(karlklose); replace when [visitor.optionalParameters] is a [List].
|
||||
orderedOptionalParameters.sort((Element a, Element b) {
|
||||
return a.name.compareTo(b.name);
|
||||
});
|
||||
|
@ -441,7 +454,7 @@ class SignatureResolver extends MappingVisitor<FormalElementX> {
|
|||
namedParameterTypes =
|
||||
namedParameterTypesBuilder.toLink().toList(growable: false);
|
||||
} else {
|
||||
// TODO(karlklose); replace when [visitor.optinalParameters] is a [List].
|
||||
// TODO(karlklose); replace when [visitor.optionalParameters] is a [List].
|
||||
LinkBuilder<ResolutionDartType> optionalParameterTypesBuilder =
|
||||
new LinkBuilder<ResolutionDartType>();
|
||||
for (FormalElement parameter in visitor.optionalParameters) {
|
||||
|
|
|
@ -27,6 +27,16 @@ import 'registry.dart' show ResolutionRegistry;
|
|||
import 'resolution_common.dart' show MappingVisitor;
|
||||
import 'scope.dart' show Scope;
|
||||
|
||||
class _FormalsTypeResolutionResult {
|
||||
final List<ResolutionDartType> requiredTypes;
|
||||
final List<ResolutionDartType> orderedTypes;
|
||||
final List<String> names;
|
||||
final List<ResolutionDartType> nameTypes;
|
||||
|
||||
_FormalsTypeResolutionResult(
|
||||
this.requiredTypes, this.orderedTypes, this.names, this.nameTypes);
|
||||
}
|
||||
|
||||
class TypeResolver {
|
||||
final Resolution resolution;
|
||||
|
||||
|
@ -66,6 +76,154 @@ class TypeResolver {
|
|||
ResolutionDartType resolveTypeAnnotation(
|
||||
MappingVisitor visitor, TypeAnnotation node,
|
||||
{bool malformedIsError: false, bool deferredIsMalformed: true}) {
|
||||
return _resolveTypeAnnotation(
|
||||
visitor, node, const [], malformedIsError: malformedIsError,
|
||||
deferredIsMalformed: deferredIsMalformed);
|
||||
}
|
||||
|
||||
// TODO(floitsch): the [visibleTypeParameterNames] is a hack to put
|
||||
// type parameters in scope for the nested types.
|
||||
//
|
||||
// For example, in the following example, the generic type "A" would be stored
|
||||
// in `visibleTypeParameterNames`.
|
||||
// `typedef F = Function(List<A> Function<A>(A x))`.
|
||||
//
|
||||
// They are resolved to `dynamic` until dart2js supports generic methods.
|
||||
ResolutionDartType _resolveTypeAnnotation(
|
||||
MappingVisitor visitor, TypeAnnotation node,
|
||||
List<List<String>> visibleTypeParameterNames,
|
||||
{bool malformedIsError: false, bool deferredIsMalformed: true}) {
|
||||
|
||||
if (node.asNominalTypeAnnotation() != null) {
|
||||
return resolveNominalTypeAnnotation(
|
||||
visitor, node,
|
||||
visibleTypeParameterNames,
|
||||
malformedIsError: malformedIsError,
|
||||
deferredIsMalformed: deferredIsMalformed);
|
||||
}
|
||||
assert(node.asFunctionTypeAnnotation() != null);
|
||||
return _resolveFunctionTypeAnnotation(
|
||||
visitor, node, visibleTypeParameterNames,
|
||||
malformedIsError: malformedIsError,
|
||||
deferredIsMalformed: deferredIsMalformed);
|
||||
}
|
||||
|
||||
/// Resolves the types of a parameter list.
|
||||
///
|
||||
/// This function does not accept "inline" function types. For example
|
||||
/// `foo(int bar(String x))` is not accepted.
|
||||
///
|
||||
/// However, it does work with nested generalized function types:
|
||||
/// `foo(int Function(String) x)`.
|
||||
_FormalsTypeResolutionResult _resolveFormalTypes(
|
||||
MappingVisitor visitor, NodeList formals,
|
||||
List<List<String>> visibleTypeParameterNames) {
|
||||
|
||||
ResolutionDartType resolvePositionalType(VariableDefinitions node) {
|
||||
return _resolveTypeAnnotation(
|
||||
visitor, node.type, visibleTypeParameterNames);
|
||||
}
|
||||
|
||||
void fillNamedTypes(NodeList namedFormals,
|
||||
List<String> names, List<ResolutionDartType> types) {
|
||||
List<Node> nodes = namedFormals.nodes.toList(growable: false);
|
||||
|
||||
// Sort the named arguments first.
|
||||
nodes.sort((node1, node2) {
|
||||
VariableDefinitions a = node1;
|
||||
VariableDefinitions b = node2;
|
||||
assert(a.definitions.nodes.tail.isEmpty);
|
||||
assert(b.definitions.nodes.tail.isEmpty);
|
||||
return a.definitions.nodes.head.asIdentifier().source.compareTo(
|
||||
b.definitions.nodes.head.asIdentifier().source);
|
||||
});
|
||||
|
||||
for (VariableDefinitions node in nodes) {
|
||||
String name = node.definitions.nodes.head.asIdentifier().source;
|
||||
ResolutionDartType type = node.type == null
|
||||
? const ResolutionDynamicType()
|
||||
: _resolveTypeAnnotation(
|
||||
visitor, node.type, visibleTypeParameterNames);
|
||||
names.add(name);
|
||||
types.add(type);
|
||||
}
|
||||
}
|
||||
|
||||
List<ResolutionDartType> requiredTypes = <ResolutionDartType>[];
|
||||
NodeList optionalFormals = null;
|
||||
for (Link<Node> link = formals.nodes; !link.isEmpty; link = link.tail) {
|
||||
if (link.tail.isEmpty && link.head is NodeList) {
|
||||
optionalFormals = link.head;
|
||||
break;
|
||||
}
|
||||
requiredTypes.add(resolvePositionalType(link.head));
|
||||
}
|
||||
|
||||
|
||||
List<ResolutionDartType> orderedTypes = const <ResolutionDartType>[];
|
||||
List<String> names = const <String>[];
|
||||
List<ResolutionDartType> namedTypes = const <ResolutionDartType>[];
|
||||
|
||||
if (optionalFormals != null) {
|
||||
// This must be a list of optional arguments.
|
||||
String value = optionalFormals.beginToken.stringValue;
|
||||
if ((!identical(value, '[')) && (!identical(value, '{'))) {
|
||||
reporter.internalError(optionalFormals, "expected optional parameters");
|
||||
}
|
||||
bool optionalParametersAreNamed = (identical(value, '{'));
|
||||
|
||||
if (optionalParametersAreNamed) {
|
||||
names = <String>[];
|
||||
namedTypes = <ResolutionDartType>[];
|
||||
fillNamedTypes(optionalFormals, names, namedTypes);
|
||||
} else {
|
||||
orderedTypes = <ResolutionDartType>[];
|
||||
for (Link<Node> link = optionalFormals.nodes;
|
||||
!link.isEmpty;
|
||||
link = link.tail) {
|
||||
orderedTypes.add(resolvePositionalType(link.head));
|
||||
}
|
||||
}
|
||||
}
|
||||
return new _FormalsTypeResolutionResult(
|
||||
requiredTypes, orderedTypes, names, namedTypes);
|
||||
}
|
||||
|
||||
ResolutionFunctionType _resolveFunctionTypeAnnotation(MappingVisitor visitor,
|
||||
FunctionTypeAnnotation node,
|
||||
List<List<String>> visibleTypeParameterNames,
|
||||
{bool malformedIsError: false, bool deferredIsMalformed: true}) {
|
||||
|
||||
assert(visibleTypeParameterNames != null);
|
||||
|
||||
if (node.typeParameters != null) {
|
||||
List<String> newTypeNames = node.typeParameters.map((TypeVariable node) {
|
||||
return node.name.asIdentifier().source;
|
||||
}).toList();
|
||||
visibleTypeParameterNames =
|
||||
visibleTypeParameterNames.toList()..add(newTypeNames);
|
||||
}
|
||||
|
||||
ResolutionDartType returnType = node.returnType == null
|
||||
? const ResolutionDynamicType()
|
||||
: _resolveTypeAnnotation(visitor, node.returnType,
|
||||
visibleTypeParameterNames);
|
||||
var formalTypes =
|
||||
_resolveFormalTypes(visitor, node.formals, visibleTypeParameterNames);
|
||||
var result = new ResolutionFunctionType.generalized(
|
||||
returnType,
|
||||
formalTypes.requiredTypes,
|
||||
formalTypes.orderedTypes,
|
||||
formalTypes.names,
|
||||
formalTypes.nameTypes);
|
||||
visitor.registry.useType(node, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
ResolutionDartType resolveNominalTypeAnnotation(MappingVisitor visitor,
|
||||
NominalTypeAnnotation node,
|
||||
List<List<String>> visibleTypeParameterNames,
|
||||
{bool malformedIsError: false, bool deferredIsMalformed: true}) {
|
||||
ResolutionRegistry registry = visitor.registry;
|
||||
|
||||
Identifier typeName;
|
||||
|
@ -74,7 +232,8 @@ class TypeResolver {
|
|||
ResolutionDartType checkNoTypeArguments(ResolutionDartType type) {
|
||||
List<ResolutionDartType> arguments = new List<ResolutionDartType>();
|
||||
bool hasTypeArgumentMismatch = resolveTypeArguments(
|
||||
visitor, node, const <ResolutionDartType>[], arguments);
|
||||
visitor, node, const <ResolutionDartType>[], arguments,
|
||||
visibleTypeParameterNames);
|
||||
if (hasTypeArgumentMismatch) {
|
||||
return new MalformedType(
|
||||
new ErroneousElementX(MessageKind.TYPE_ARGUMENT_COUNT_MISMATCH,
|
||||
|
@ -106,9 +265,6 @@ class TypeResolver {
|
|||
}
|
||||
}
|
||||
|
||||
Element element = resolveTypeName(prefixName, typeName, visitor.scope,
|
||||
deferredIsMalformed: deferredIsMalformed);
|
||||
|
||||
ResolutionDartType reportFailureAndCreateType(
|
||||
MessageKind messageKind, Map messageArguments,
|
||||
{ResolutionDartType userProvidedBadType,
|
||||
|
@ -128,14 +284,33 @@ class TypeResolver {
|
|||
typeName.source, visitor.enclosingElement);
|
||||
}
|
||||
List<ResolutionDartType> arguments = <ResolutionDartType>[];
|
||||
resolveTypeArguments(
|
||||
visitor, node, const <ResolutionDartType>[], arguments);
|
||||
resolveTypeArguments(visitor, node, const <ResolutionDartType>[],
|
||||
arguments, visibleTypeParameterNames);
|
||||
return new MalformedType(
|
||||
erroneousElement, userProvidedBadType, arguments);
|
||||
}
|
||||
|
||||
Element element;
|
||||
// Resolve references to type names as dynamic.
|
||||
// TODO(floitsch): this hackishly resolves generic function type arguments
|
||||
// to dynamic.
|
||||
if (prefixName == null &&
|
||||
visibleTypeParameterNames.any((n) => n.contains(typeName.source))) {
|
||||
type = const ResolutionDynamicType();
|
||||
} else {
|
||||
element = resolveTypeName(prefixName, typeName, visitor.scope,
|
||||
deferredIsMalformed: deferredIsMalformed);
|
||||
}
|
||||
|
||||
// Try to construct the type from the element.
|
||||
if (element == null) {
|
||||
if (type != null) {
|
||||
// Already assigned to through the visibleTypeParameterNames.
|
||||
// Just make sure that it doesn't have type arguments.
|
||||
if (node.typeArguments != null) {
|
||||
reporter.reportWarningMessage(node.typeArguments.nodes.head,
|
||||
MessageKind.ADDITIONAL_TYPE_ARGUMENT);
|
||||
}
|
||||
} else if (element == null) {
|
||||
type = reportFailureAndCreateType(
|
||||
MessageKind.CANNOT_RESOLVE_TYPE, {'typeName': node.typeName});
|
||||
} else if (element.isAmbiguous) {
|
||||
|
@ -170,8 +345,9 @@ class TypeResolver {
|
|||
resolver.ensureClassWillBeResolvedInternal(cls);
|
||||
cls.computeType(resolution);
|
||||
List<ResolutionDartType> arguments = <ResolutionDartType>[];
|
||||
bool hasTypeArgumentMismatch =
|
||||
resolveTypeArguments(visitor, node, cls.typeVariables, arguments);
|
||||
bool hasTypeArgumentMismatch = resolveTypeArguments(
|
||||
visitor, node, cls.typeVariables, arguments,
|
||||
visibleTypeParameterNames);
|
||||
if (hasTypeArgumentMismatch) {
|
||||
type = new BadInterfaceType(
|
||||
cls.declaration,
|
||||
|
@ -194,7 +370,8 @@ class TypeResolver {
|
|||
typdef.computeType(resolution);
|
||||
List<ResolutionDartType> arguments = <ResolutionDartType>[];
|
||||
bool hasTypeArgumentMismatch = resolveTypeArguments(
|
||||
visitor, node, typdef.typeVariables, arguments);
|
||||
visitor, node, typdef.typeVariables, arguments,
|
||||
visibleTypeParameterNames);
|
||||
if (hasTypeArgumentMismatch) {
|
||||
type = new BadTypedefType(
|
||||
typdef,
|
||||
|
@ -241,7 +418,7 @@ class TypeResolver {
|
|||
}
|
||||
|
||||
/// Checks the type arguments of [type] against the type variable bounds.
|
||||
void checkTypeVariableBounds(TypeAnnotation node, GenericType type) {
|
||||
void checkTypeVariableBounds(NominalTypeAnnotation node, GenericType type) {
|
||||
void checkTypeVariableBound(_, ResolutionDartType typeArgument,
|
||||
ResolutionTypeVariableType typeVariable, ResolutionDartType bound) {
|
||||
if (!types.isSubtype(typeArgument, bound)) {
|
||||
|
@ -266,9 +443,10 @@ class TypeResolver {
|
|||
*/
|
||||
bool resolveTypeArguments(
|
||||
MappingVisitor visitor,
|
||||
TypeAnnotation node,
|
||||
NominalTypeAnnotation node,
|
||||
List<ResolutionDartType> typeVariables,
|
||||
List<ResolutionDartType> arguments) {
|
||||
List<ResolutionDartType> arguments,
|
||||
List<List<String>> visibleTypeParameterNames) {
|
||||
if (node.typeArguments == null) {
|
||||
return false;
|
||||
}
|
||||
|
@ -283,8 +461,8 @@ class TypeResolver {
|
|||
typeArguments.head, MessageKind.ADDITIONAL_TYPE_ARGUMENT);
|
||||
typeArgumentCountMismatch = true;
|
||||
}
|
||||
ResolutionDartType argType =
|
||||
resolveTypeAnnotation(visitor, typeArguments.head);
|
||||
ResolutionDartType argType = _resolveTypeAnnotation(
|
||||
visitor, typeArguments.head, visibleTypeParameterNames);
|
||||
// TODO(karlklose): rewrite to not modify [arguments].
|
||||
arguments.add(argType);
|
||||
}
|
||||
|
|
|
@ -27,12 +27,12 @@ class TypedefResolverVisitor extends TypeDefinitionVisitor {
|
|||
visitTypedef(Typedef node) {
|
||||
element.computeType(resolution);
|
||||
scope = new TypeDeclarationScope(scope, element);
|
||||
resolveTypeVariableBounds(node.typeParameters);
|
||||
resolveTypeVariableBounds(node.templateParameters);
|
||||
|
||||
FunctionSignature signature = SignatureResolver.analyze(
|
||||
resolution,
|
||||
scope,
|
||||
null /* typeVariables */,
|
||||
node.typeParameters,
|
||||
node.formals,
|
||||
node.returnType,
|
||||
element,
|
||||
|
|
|
@ -2044,13 +2044,24 @@ class NodeEquivalenceVisitor implements Visitor1<bool, Node> {
|
|||
}
|
||||
|
||||
@override
|
||||
bool visitTypeAnnotation(TypeAnnotation node1, TypeAnnotation node2) {
|
||||
bool visitNominalTypeAnnotation(
|
||||
NominalTypeAnnotation node1, NominalTypeAnnotation node2) {
|
||||
return testNodes(
|
||||
node1, node2, 'typeName', node1.typeName, node2.typeName) &&
|
||||
testNodes(node1, node2, 'typeArguments', node1.typeArguments,
|
||||
node2.typeArguments);
|
||||
}
|
||||
|
||||
@override
|
||||
bool visitFunctionTypeAnnotation(
|
||||
FunctionTypeAnnotation node1, FunctionTypeAnnotation node2) {
|
||||
return testNodes(
|
||||
node1, node2, 'returnType', node1.returnType, node2.returnType) &&
|
||||
testNodes(node1, node2, 'formals', node1.formals, node2.formals) &&
|
||||
testNodes(node1, node2, 'typeParameters', node1.typeParameters,
|
||||
node2.typeParameters);
|
||||
}
|
||||
|
||||
@override
|
||||
bool visitTypeVariable(TypeVariable node1, TypeVariable node2) {
|
||||
return testNodes(node1, node2, 'name', node1.name, node2.name) &&
|
||||
|
@ -2065,8 +2076,8 @@ class NodeEquivalenceVisitor implements Visitor1<bool, Node> {
|
|||
testNodes(
|
||||
node1, node2, 'returnType', node1.returnType, node2.returnType) &&
|
||||
testNodes(node1, node2, 'name', node1.name, node2.name) &&
|
||||
testNodes(node1, node2, 'typeParameters', node1.typeParameters,
|
||||
node2.typeParameters) &&
|
||||
testNodes(node1, node2, 'typeParameters', node1.templateParameters,
|
||||
node2.templateParameters) &&
|
||||
testNodes(node1, node2, 'formals', node1.formals, node2.formals);
|
||||
}
|
||||
|
||||
|
@ -2121,6 +2132,11 @@ class NodeEquivalenceVisitor implements Visitor1<bool, Node> {
|
|||
bool visitStringNode(StringNode node1, StringNode node2) {
|
||||
throw new UnsupportedError('Unexpected nodes: $node1 <> $node2');
|
||||
}
|
||||
|
||||
@override
|
||||
bool visitTypeAnnotation(TypeAnnotation node1, TypeAnnotation node2) {
|
||||
throw new UnsupportedError('Unexpected nodes: $node1 <> $node2');
|
||||
}
|
||||
}
|
||||
|
||||
bool areMetadataAnnotationsEquivalent(
|
||||
|
|
|
@ -2144,6 +2144,9 @@ class LocalParameterElementZ extends ParameterElementZ
|
|||
|
||||
@override
|
||||
ElementKind get kind => ElementKind.PARAMETER;
|
||||
|
||||
@override
|
||||
bool get isUnnamed => false;
|
||||
}
|
||||
|
||||
class InitializingFormalElementZ extends LocalParameterElementZ
|
||||
|
|
|
@ -261,6 +261,14 @@ class ReifiedTypeRepresentationBuilder
|
|||
ResolutionTypeVariableType type, GraphBuilder builder) {
|
||||
ClassElement cls = builder.backend.helpers.RuntimeType;
|
||||
TypeMask instructionType = new TypeMask.subclass(cls, closedWorld);
|
||||
|
||||
// TODO(floitsch): this hack maps type variables of generic function
|
||||
// typedefs to dynamic. For example: `typedef F = Function<T>(T)`.
|
||||
if (type is MethodTypeVariableType) {
|
||||
visitDynamicType(const ResolutionDynamicType(), builder);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!builder.sourceElement.enclosingElement.isClosure &&
|
||||
builder.sourceElement.isInstanceMember) {
|
||||
HInstruction receiver = builder.localsHandler.readThis();
|
||||
|
|
|
@ -51,6 +51,10 @@ abstract class Visitor<R> {
|
|||
R visitForIn(ForIn node) => visitLoop(node);
|
||||
R visitFunctionDeclaration(FunctionDeclaration node) => visitStatement(node);
|
||||
R visitFunctionExpression(FunctionExpression node) => visitExpression(node);
|
||||
R visitFunctionTypeAnnotation(FunctionTypeAnnotation node) {
|
||||
return visitTypeAnnotation(node);
|
||||
}
|
||||
|
||||
R visitGotoStatement(GotoStatement node) => visitStatement(node);
|
||||
R visitIdentifier(Identifier node) => visitExpression(node);
|
||||
R visitImport(Import node) => visitLibraryDependency(node);
|
||||
|
@ -82,6 +86,10 @@ abstract class Visitor<R> {
|
|||
|
||||
R visitNewExpression(NewExpression node) => visitExpression(node);
|
||||
R visitNodeList(NodeList node) => visitNode(node);
|
||||
R visitNominalTypeAnnotation(NominalTypeAnnotation node) {
|
||||
return visitTypeAnnotation(node);
|
||||
}
|
||||
|
||||
R visitOperator(Operator node) => visitIdentifier(node);
|
||||
R visitParenthesizedExpression(ParenthesizedExpression node) {
|
||||
return visitExpression(node);
|
||||
|
@ -179,6 +187,10 @@ abstract class Visitor1<R, A> {
|
|||
return visitExpression(node, arg);
|
||||
}
|
||||
|
||||
R visitFunctionTypeAnnotation(FunctionTypeAnnotation node, A arg) {
|
||||
return visitTypeAnnotation(node, arg);
|
||||
}
|
||||
|
||||
R visitGotoStatement(GotoStatement node, A arg) {
|
||||
return visitStatement(node, arg);
|
||||
}
|
||||
|
@ -228,6 +240,10 @@ abstract class Visitor1<R, A> {
|
|||
|
||||
R visitNewExpression(NewExpression node, A arg) => visitExpression(node, arg);
|
||||
R visitNodeList(NodeList node, A arg) => visitNode(node, arg);
|
||||
R visitNominalTypeAnnotation(NominalTypeAnnotation node, A arg) {
|
||||
visitTypeAnnotation(node, arg);
|
||||
}
|
||||
|
||||
R visitOperator(Operator node, A arg) => visitIdentifier(node, arg);
|
||||
R visitParenthesizedExpression(ParenthesizedExpression node, A arg) {
|
||||
return visitExpression(node, arg);
|
||||
|
@ -263,8 +279,8 @@ abstract class Visitor1<R, A> {
|
|||
R visitLiteralSymbol(LiteralSymbol node, A arg) => visitExpression(node, arg);
|
||||
R visitThrow(Throw node, A arg) => visitExpression(node, arg);
|
||||
R visitTryStatement(TryStatement node, A arg) => visitStatement(node, arg);
|
||||
R visitTypeAnnotation(TypeAnnotation node, A arg) => visitNode(node, arg);
|
||||
R visitTypedef(Typedef node, A arg) => visitNode(node, arg);
|
||||
R visitTypeAnnotation(TypeAnnotation node, A arg) => visitNode(node, arg);
|
||||
R visitTypeVariable(TypeVariable node, A arg) => visitNode(node, arg);
|
||||
R visitVariableDefinitions(VariableDefinitions node, A arg) {
|
||||
return visitStatement(node, arg);
|
||||
|
@ -363,6 +379,7 @@ abstract class Node extends NullTreeElementMixin implements Spannable {
|
|||
ForIn asForIn() => null;
|
||||
FunctionDeclaration asFunctionDeclaration() => null;
|
||||
FunctionExpression asFunctionExpression() => null;
|
||||
FunctionTypeAnnotation asFunctionTypeAnnotation() => null;
|
||||
Identifier asIdentifier() => null;
|
||||
If asIf() => null;
|
||||
Import asImport() => null;
|
||||
|
@ -386,6 +403,7 @@ abstract class Node extends NullTreeElementMixin implements Spannable {
|
|||
NamedMixinApplication asNamedMixinApplication() => null;
|
||||
NewExpression asNewExpression() => null;
|
||||
NodeList asNodeList() => null;
|
||||
NominalTypeAnnotation asNominalTypeAnnotation() => null;
|
||||
Operator asOperator() => null;
|
||||
ParenthesizedExpression asParenthesizedExpression() => null;
|
||||
Part asPart() => null;
|
||||
|
@ -404,7 +422,6 @@ abstract class Node extends NullTreeElementMixin implements Spannable {
|
|||
SwitchStatement asSwitchStatement() => null;
|
||||
Throw asThrow() => null;
|
||||
TryStatement asTryStatement() => null;
|
||||
TypeAnnotation asTypeAnnotation() => null;
|
||||
TypeVariable asTypeVariable() => null;
|
||||
Typedef asTypedef() => null;
|
||||
VariableDefinitions asVariableDefinitions() => null;
|
||||
|
@ -489,7 +506,7 @@ class ClassNode extends Node {
|
|||
}
|
||||
|
||||
class MixinApplication extends Node {
|
||||
final TypeAnnotation superclass;
|
||||
final NominalTypeAnnotation superclass;
|
||||
final NodeList mixins;
|
||||
|
||||
MixinApplication(this.superclass, this.mixins);
|
||||
|
@ -514,8 +531,6 @@ class MixinApplication extends Node {
|
|||
Token getEndToken() => mixins.getEndToken();
|
||||
}
|
||||
|
||||
// TODO(kasperl): Let this share some structure with the typedef for function
|
||||
// type aliases?
|
||||
class NamedMixinApplication extends Node implements MixinApplication {
|
||||
final Identifier name;
|
||||
final NodeList typeParameters;
|
||||
|
@ -530,7 +545,7 @@ class NamedMixinApplication extends Node implements MixinApplication {
|
|||
NamedMixinApplication(this.name, this.typeParameters, this.modifiers,
|
||||
this.mixinApplication, this.interfaces, this.classKeyword, this.endToken);
|
||||
|
||||
TypeAnnotation get superclass => mixinApplication.superclass;
|
||||
NominalTypeAnnotation get superclass => mixinApplication.superclass;
|
||||
NodeList get mixins => mixinApplication.mixins;
|
||||
|
||||
MixinApplication asMixinApplication() => this;
|
||||
|
@ -1723,17 +1738,22 @@ class Rethrow extends Statement {
|
|||
Token getEndToken() => endToken;
|
||||
}
|
||||
|
||||
class TypeAnnotation extends Node {
|
||||
abstract class TypeAnnotation extends Node {
|
||||
}
|
||||
|
||||
class NominalTypeAnnotation extends TypeAnnotation {
|
||||
final Expression typeName;
|
||||
final NodeList typeArguments;
|
||||
|
||||
TypeAnnotation(Expression this.typeName, NodeList this.typeArguments);
|
||||
NominalTypeAnnotation(this.typeName, this.typeArguments);
|
||||
|
||||
TypeAnnotation asTypeAnnotation() => this;
|
||||
NominalTypeAnnotation asNominalTypeAnnotation() => this;
|
||||
|
||||
accept(Visitor visitor) => visitor.visitTypeAnnotation(this);
|
||||
accept(Visitor visitor) => visitor.visitNominalTypeAnnotation(this);
|
||||
|
||||
accept1(Visitor1 visitor, arg) => visitor.visitTypeAnnotation(this, arg);
|
||||
accept1(Visitor1 visitor, arg) {
|
||||
return visitor.visitNominalTypeAnnotation(this, arg);
|
||||
}
|
||||
|
||||
visitChildren(Visitor visitor) {
|
||||
typeName.accept(visitor);
|
||||
|
@ -1829,7 +1849,13 @@ class VariableDefinitions extends Statement {
|
|||
return token;
|
||||
}
|
||||
|
||||
Token getEndToken() => definitions.getEndToken();
|
||||
Token getEndToken() {
|
||||
var result = definitions.getEndToken();
|
||||
if (result != null) return result;
|
||||
assert(definitions.nodes.length == 1);
|
||||
assert(definitions.nodes.last == null);
|
||||
return type.getEndToken();
|
||||
}
|
||||
}
|
||||
|
||||
abstract class Loop extends Statement {
|
||||
|
@ -2857,16 +2883,30 @@ class Combinator extends Node {
|
|||
}
|
||||
|
||||
class Typedef extends Node {
|
||||
final bool isGeneralizedTypeAlias;
|
||||
|
||||
/// Parameters to the template.
|
||||
///
|
||||
/// For example, `T` and `S` are template parameters in the following
|
||||
/// typedef: `typedef F<S, T> = Function(S, T)`, or, in the inlined syntax,
|
||||
/// `typedef F<S, T>(S x, T y)`.
|
||||
final NodeList templateParameters;
|
||||
|
||||
final TypeAnnotation returnType;
|
||||
final Identifier name;
|
||||
/// The generic type parameters to the function type.
|
||||
///
|
||||
/// For example `A` and `B` (but not `T`) are type parameters in
|
||||
/// `typedef F<T> = Function<A, B>(A, B, T)`;
|
||||
final NodeList typeParameters;
|
||||
final NodeList formals;
|
||||
|
||||
final Token typedefKeyword;
|
||||
final Token endToken;
|
||||
|
||||
Typedef(this.returnType, this.name, this.typeParameters, this.formals,
|
||||
this.typedefKeyword, this.endToken);
|
||||
Typedef(this.isGeneralizedTypeAlias, this.templateParameters, this.returnType,
|
||||
this.name, this.typeParameters, this.formals, this.typedefKeyword,
|
||||
this.endToken);
|
||||
|
||||
Typedef asTypedef() => this;
|
||||
|
||||
|
@ -2875,6 +2915,7 @@ class Typedef extends Node {
|
|||
accept1(Visitor1 visitor, arg) => visitor.visitTypedef(this, arg);
|
||||
|
||||
visitChildren(Visitor visitor) {
|
||||
if (templateParameters != null) templateParameters.accept(visitor);
|
||||
if (returnType != null) returnType.accept(visitor);
|
||||
name.accept(visitor);
|
||||
if (typeParameters != null) typeParameters.accept(visitor);
|
||||
|
@ -2882,6 +2923,7 @@ class Typedef extends Node {
|
|||
}
|
||||
|
||||
visitChildren1(Visitor1 visitor, arg) {
|
||||
if (templateParameters != null) templateParameters.accept1(visitor, arg);
|
||||
if (returnType != null) returnType.accept1(visitor, arg);
|
||||
name.accept1(visitor, arg);
|
||||
if (typeParameters != null) typeParameters.accept1(visitor, arg);
|
||||
|
@ -2893,6 +2935,43 @@ class Typedef extends Node {
|
|||
Token getEndToken() => endToken;
|
||||
}
|
||||
|
||||
class FunctionTypeAnnotation extends TypeAnnotation {
|
||||
final TypeAnnotation returnType;
|
||||
final Token functionToken;
|
||||
final NodeList typeParameters;
|
||||
final NodeList formals;
|
||||
|
||||
FunctionTypeAnnotation(
|
||||
this.returnType, this.functionToken, this.typeParameters, this.formals);
|
||||
|
||||
FunctionTypeAnnotation asFunctionTypeAnnotation() => this;
|
||||
|
||||
accept(Visitor visitor) => visitor.visitFunctionTypeAnnotation(this);
|
||||
|
||||
accept1(Visitor1 visitor, arg) {
|
||||
return visitor.visitFunctionTypeAnnotation(this, arg);
|
||||
}
|
||||
|
||||
visitChildren(Visitor visitor) {
|
||||
if (returnType != null) returnType.accept(visitor);
|
||||
if (typeParameters != null) typeParameters.accept(visitor);
|
||||
formals.accept(visitor);
|
||||
}
|
||||
|
||||
visitChildren1(Visitor1 visitor, arg) {
|
||||
if (returnType != null) returnType.accept1(visitor, arg);
|
||||
if (typeParameters != null) typeParameters.accept1(visitor, arg);
|
||||
formals.accept1(visitor, arg);
|
||||
}
|
||||
|
||||
Token getBeginToken() {
|
||||
if (returnType != null) return returnType.getBeginToken();
|
||||
return functionToken;
|
||||
}
|
||||
|
||||
Token getEndToken() => formals.getEndToken();
|
||||
}
|
||||
|
||||
class TryStatement extends Statement {
|
||||
final Block tryBlock;
|
||||
final NodeList catchBlocks;
|
||||
|
@ -3139,6 +3218,8 @@ class ErrorNode extends Node
|
|||
get type => null;
|
||||
|
||||
// Typedef.
|
||||
get isGeneralizedTypeAlias => null;
|
||||
get templateParameters => null;
|
||||
get typeParameters => null;
|
||||
get formals => null;
|
||||
get typedefKeyword => null;
|
||||
|
|
|
@ -153,6 +153,14 @@ class PrettyPrinter extends Indentation with Tagging<Node> implements Visitor {
|
|||
closeNode();
|
||||
}
|
||||
|
||||
visitFunctionTypeAnnotation(FunctionTypeAnnotation node) {
|
||||
openNode(node, "FunctionTypeAnnotation");
|
||||
visitChildNode(node.returnType, "returnType");
|
||||
visitChildNode(node.typeParameters, "typeParameters");
|
||||
visitChildNode(node.formals, "formals");
|
||||
closeNode();
|
||||
}
|
||||
|
||||
visitIdentifier(Identifier node) {
|
||||
openAndCloseNode(node, "Identifier", {"token": node.token});
|
||||
}
|
||||
|
@ -244,6 +252,13 @@ class PrettyPrinter extends Indentation with Tagging<Node> implements Visitor {
|
|||
}
|
||||
}
|
||||
|
||||
visitNominalTypeAnnotation(NominalTypeAnnotation node) {
|
||||
openNode(node, "NominalTypeAnnotation");
|
||||
visitChildNode(node.typeName, "typeName");
|
||||
visitChildNode(node.typeArguments, "typeArguments");
|
||||
closeNode();
|
||||
}
|
||||
|
||||
visitOperator(Operator node) {
|
||||
openAndCloseNode(node, "Operator", {"value": node.token});
|
||||
}
|
||||
|
@ -345,13 +360,6 @@ class PrettyPrinter extends Indentation with Tagging<Node> implements Visitor {
|
|||
visitNodeWithChildren(node, "TryStatement");
|
||||
}
|
||||
|
||||
visitTypeAnnotation(TypeAnnotation node) {
|
||||
openNode(node, "TypeAnnotation");
|
||||
visitChildNode(node.typeName, "typeName");
|
||||
visitChildNode(node.typeArguments, "typeArguments");
|
||||
closeNode();
|
||||
}
|
||||
|
||||
visitTypedef(Typedef node) {
|
||||
visitNodeWithChildren(node, "Typedef");
|
||||
}
|
||||
|
@ -472,6 +480,10 @@ class PrettyPrinter extends Indentation with Tagging<Node> implements Visitor {
|
|||
unimplemented('visitNode', node: node);
|
||||
}
|
||||
|
||||
visitTypeAnnotation(TypeAnnotation node) {
|
||||
unimplemented('visitNode', node: node);
|
||||
}
|
||||
|
||||
unimplemented(String message, {Node node}) {
|
||||
throw message;
|
||||
}
|
||||
|
|
|
@ -548,11 +548,18 @@ class Unparser extends Indentation implements Visitor {
|
|||
visit(node.expression);
|
||||
}
|
||||
|
||||
visitTypeAnnotation(TypeAnnotation node) {
|
||||
visitNominalTypeAnnotation(NominalTypeAnnotation node) {
|
||||
visit(node.typeName);
|
||||
visit(node.typeArguments);
|
||||
}
|
||||
|
||||
visitFunctionTypeAnnotation(FunctionTypeAnnotation node) {
|
||||
visit(node.returnType);
|
||||
write(' Function');
|
||||
visit(node.typeParameters);
|
||||
visit(node.formals);
|
||||
}
|
||||
|
||||
visitTypeVariable(TypeVariable node) {
|
||||
visit(node.name);
|
||||
if (node.bound != null) {
|
||||
|
@ -771,8 +778,8 @@ class Unparser extends Indentation implements Visitor {
|
|||
write(' ');
|
||||
}
|
||||
visit(node.name);
|
||||
if (node.typeParameters != null) {
|
||||
visit(node.typeParameters);
|
||||
if (node.templateParameters != null) {
|
||||
visit(node.templateParameters);
|
||||
}
|
||||
visit(node.formals);
|
||||
write(node.endToken.value);
|
||||
|
@ -904,4 +911,8 @@ class Unparser extends Indentation implements Visitor {
|
|||
visitStringNode(StringNode node) {
|
||||
throw 'internal error'; // Should not be called.
|
||||
}
|
||||
|
||||
visitTypeAnnotation(TypeAnnotation node) {
|
||||
throw 'internal error'; // Should not be called.
|
||||
}
|
||||
}
|
||||
|
|
|
@ -155,7 +155,7 @@ void useNode(tree.Node node) {
|
|||
..asSwitchStatement()
|
||||
..asSyncForIn()
|
||||
..asTryStatement()
|
||||
..asTypeAnnotation()
|
||||
..asNominalTypeAnnotation()
|
||||
..asTypeVariable()
|
||||
..asTypedef()
|
||||
..asWhile()
|
||||
|
|
|
@ -439,7 +439,7 @@ class AstBuilder extends ScopeListener {
|
|||
push(ast.symbolLiteral(toAnalyzerToken(hashToken), components));
|
||||
}
|
||||
|
||||
void endType(Token beginToken, Token endToken) {
|
||||
void handleType(Token beginToken, Token endToken) {
|
||||
debugEvent("Type");
|
||||
TypeArgumentList arguments = pop();
|
||||
Identifier name = pop();
|
||||
|
|
|
@ -1170,7 +1170,7 @@ class BodyBuilder extends ScopeListener<JumpTarget> implements BuilderHelper {
|
|||
}
|
||||
|
||||
@override
|
||||
void endType(Token beginToken, Token endToken) {
|
||||
void handleType(Token beginToken, Token endToken) {
|
||||
// TODO(ahe): The scope is wrong for return types of generic functions.
|
||||
debugEvent("Type");
|
||||
List<DartType> arguments = pop();
|
||||
|
|
|
@ -30,6 +30,7 @@ enum ErrorKind {
|
|||
ExpectedType,
|
||||
ExtraneousModifier,
|
||||
ExtraneousModifierReplace,
|
||||
InvalidInlineFunctionType,
|
||||
InvalidAwaitFor,
|
||||
InvalidSyncModifier,
|
||||
InvalidVoid,
|
||||
|
|
|
@ -233,13 +233,22 @@ class Listener {
|
|||
|
||||
void beginFunctionTypeAlias(Token token) {}
|
||||
|
||||
/// Handle the end of a typedef declaration. Substructures:
|
||||
/// Handle the end of a typedef declaration.
|
||||
///
|
||||
/// If [equals] is null, then we have the following substructures:
|
||||
/// - Metadata
|
||||
/// - Return type
|
||||
/// - Name (identifier)
|
||||
/// - Type variables
|
||||
/// - Template variables (type variables to the template)
|
||||
/// - Formal parameters
|
||||
void endFunctionTypeAlias(Token typedefKeyword, Token endToken) {
|
||||
///
|
||||
/// If [equals] is not null, then the have the following substructures:
|
||||
/// - Metadata
|
||||
/// - Name (identifier)
|
||||
/// - Template variables (type variables to the template)
|
||||
/// - Type (FunctionTypeAnnotation)
|
||||
void endFunctionTypeAlias(
|
||||
Token typedefKeyword, Token equals, Token endToken) {
|
||||
logEvent("FunctionTypeAlias");
|
||||
}
|
||||
|
||||
|
@ -263,7 +272,7 @@ class Listener {
|
|||
/// - mixin application
|
||||
/// - implemented types (TypeList)
|
||||
///
|
||||
/// TODO(paulberry,ahe): it seems incosistent that for a named mixin
|
||||
/// TODO(paulberry,ahe): it seems inconsistent that for a named mixin
|
||||
/// application, the implemented types are a TypeList, whereas for a class
|
||||
/// declaration, each implemented type is listed separately on the stack, and
|
||||
/// the number of implemented types is passed as a parameter.
|
||||
|
@ -634,10 +643,18 @@ class Listener {
|
|||
logEvent("TryStatement");
|
||||
}
|
||||
|
||||
void endType(Token beginToken, Token endToken) {
|
||||
void handleType(Token beginToken, Token endToken) {
|
||||
logEvent("Type");
|
||||
}
|
||||
|
||||
void handleNoName(Token token) {
|
||||
logEvent("NoName");
|
||||
}
|
||||
|
||||
void handleFunctionType(Token functionToken, Token endToken) {
|
||||
logEvent("FunctionType");
|
||||
}
|
||||
|
||||
void beginTypeArguments(Token token) {}
|
||||
|
||||
void endTypeArguments(int count, Token beginToken, Token endToken) {
|
||||
|
|
|
@ -100,7 +100,7 @@ class FormalParameterType {
|
|||
* Subclasses of the class [Listener] are used to listen to events.
|
||||
*
|
||||
* Most methods of this class belong in one of two major categories:
|
||||
* parse metods and peek methods. Parse methods all have the prefix
|
||||
* parse methods and peek methods. Parse methods all have the prefix
|
||||
* parse, and peek methods all have the prefix peek.
|
||||
*
|
||||
* Parse methods generate events (by calling methods on [listener])
|
||||
|
@ -385,11 +385,20 @@ class Parser {
|
|||
Token parseTypedef(Token token) {
|
||||
Token typedefKeyword = token;
|
||||
listener.beginFunctionTypeAlias(token);
|
||||
token = parseReturnTypeOpt(token.next);
|
||||
token = parseIdentifier(token);
|
||||
token = parseTypeVariablesOpt(token);
|
||||
token = parseFormalParameters(token);
|
||||
listener.endFunctionTypeAlias(typedefKeyword, token);
|
||||
Token equals;
|
||||
if (optional('=', peekAfterNominalType(token.next))) {
|
||||
token = parseIdentifier(token.next);
|
||||
token = parseTypeVariablesOpt(token);
|
||||
equals = token;
|
||||
token = expect('=', token);
|
||||
token = parseType(token);
|
||||
} else {
|
||||
token = parseReturnTypeOpt(token.next);
|
||||
token = parseIdentifier(token);
|
||||
token = parseTypeVariablesOpt(token);
|
||||
token = parseFormalParameters(token);
|
||||
}
|
||||
listener.endFunctionTypeAlias(typedefKeyword, equals, token);
|
||||
return expect(';', token);
|
||||
}
|
||||
|
||||
|
@ -404,8 +413,12 @@ class Parser {
|
|||
|
||||
Token parseReturnTypeOpt(Token token) {
|
||||
if (identical(token.stringValue, 'void')) {
|
||||
listener.handleVoidKeyword(token);
|
||||
return token.next;
|
||||
if (isGeneralizedFunctionType(token.next)) {
|
||||
return parseType(token);
|
||||
} else {
|
||||
listener.handleVoidKeyword(token);
|
||||
return token.next;
|
||||
}
|
||||
} else {
|
||||
return parseTypeOpt(token);
|
||||
}
|
||||
|
@ -437,7 +450,11 @@ class Parser {
|
|||
return endToken.next;
|
||||
}
|
||||
|
||||
Token parseFormalParameters(Token token) {
|
||||
/// Parses the formal parameter list of a function.
|
||||
///
|
||||
/// If [inFunctionType] is true, then the names may be omitted (except for
|
||||
/// named arguments). If it is false, then the types may be omitted.
|
||||
Token parseFormalParameters(Token token, {bool inFunctionType: false}) {
|
||||
Token begin = token;
|
||||
listener.beginFormalParameters(begin);
|
||||
expect('(', token);
|
||||
|
@ -450,19 +467,23 @@ class Parser {
|
|||
++parameterCount;
|
||||
String value = token.stringValue;
|
||||
if (identical(value, '[')) {
|
||||
token = parseOptionalFormalParameters(token, false);
|
||||
token = parseOptionalFormalParameters(
|
||||
token, false, inFunctionType: inFunctionType);
|
||||
break;
|
||||
} else if (identical(value, '{')) {
|
||||
token = parseOptionalFormalParameters(token, true);
|
||||
token = parseOptionalFormalParameters(
|
||||
token, true, inFunctionType: inFunctionType);
|
||||
break;
|
||||
}
|
||||
token = parseFormalParameter(token, FormalParameterType.REQUIRED);
|
||||
token = parseFormalParameter(token, FormalParameterType.REQUIRED,
|
||||
inFunctionType: inFunctionType);
|
||||
} while (optional(',', token));
|
||||
listener.endFormalParameters(parameterCount, begin, token);
|
||||
return expect(')', token);
|
||||
}
|
||||
|
||||
Token parseFormalParameter(Token token, FormalParameterType type) {
|
||||
Token parseFormalParameter(Token token, FormalParameterType type,
|
||||
{bool inFunctionType}) {
|
||||
token = parseMetadataStar(token, forParameter: true);
|
||||
listener.beginFormalParameter(token);
|
||||
|
||||
|
@ -475,26 +496,51 @@ class Parser {
|
|||
token = token.next;
|
||||
}
|
||||
token = parseModifiers(token);
|
||||
// TODO(ahe): Validate that there are formal parameters if void.
|
||||
token = parseReturnTypeOpt(token);
|
||||
bool isNamedParameter = type == FormalParameterType.NAMED;
|
||||
|
||||
Token thisKeyword = null;
|
||||
if (optional('this', token)) {
|
||||
thisKeyword = token;
|
||||
// TODO(ahe): Validate field initializers are only used in
|
||||
// constructors, and not for function-typed arguments.
|
||||
token = expect('.', token.next);
|
||||
if (inFunctionType && isNamedParameter) {
|
||||
token = parseType(token);
|
||||
token = parseIdentifier(token);
|
||||
} else if (inFunctionType) {
|
||||
token = parseType(token);
|
||||
if (token.isIdentifier()) {
|
||||
token = parseIdentifier(token);
|
||||
} else {
|
||||
listener.handleNoName(token);
|
||||
}
|
||||
} else {
|
||||
token = parseReturnTypeOpt(token);
|
||||
if (optional('this', token)) {
|
||||
thisKeyword = token;
|
||||
token = expect('.', token.next);
|
||||
}
|
||||
token = parseIdentifier(token);
|
||||
}
|
||||
token = parseIdentifier(token);
|
||||
|
||||
// Generalized function types don't allow inline function types.
|
||||
// The following isn't allowed:
|
||||
// int Function(int bar(String x)).
|
||||
if (optional('(', token)) {
|
||||
Token inlineFunctionTypeStart = token;
|
||||
listener.beginFunctionTypedFormalParameter(token);
|
||||
listener.handleNoTypeVariables(token);
|
||||
token = parseFormalParameters(token);
|
||||
listener.endFunctionTypedFormalParameter(token);
|
||||
if (inFunctionType) {
|
||||
reportRecoverableError(
|
||||
inlineFunctionTypeStart, ErrorKind.InvalidInlineFunctionType);
|
||||
}
|
||||
} else if (optional('<', token)) {
|
||||
Token inlineFunctionTypeStart = token;
|
||||
listener.beginFunctionTypedFormalParameter(token);
|
||||
token = parseTypeVariablesOpt(token);
|
||||
token = parseFormalParameters(token);
|
||||
listener.endFunctionTypedFormalParameter(token);
|
||||
if (inFunctionType) {
|
||||
reportRecoverableError(
|
||||
inlineFunctionTypeStart, ErrorKind.InvalidInlineFunctionType);
|
||||
}
|
||||
}
|
||||
String value = token.stringValue;
|
||||
if ((identical('=', value)) || (identical(':', value))) {
|
||||
|
@ -514,7 +560,8 @@ class Parser {
|
|||
return token;
|
||||
}
|
||||
|
||||
Token parseOptionalFormalParameters(Token token, bool isNamed) {
|
||||
Token parseOptionalFormalParameters(Token token, bool isNamed,
|
||||
{bool inFunctionType}) {
|
||||
Token begin = token;
|
||||
listener.beginOptionalFormalParameters(begin);
|
||||
assert((isNamed && optional('{', token)) || optional('[', token));
|
||||
|
@ -528,7 +575,8 @@ class Parser {
|
|||
}
|
||||
var type =
|
||||
isNamed ? FormalParameterType.NAMED : FormalParameterType.POSITIONAL;
|
||||
token = parseFormalParameter(token, type);
|
||||
token =
|
||||
parseFormalParameter(token, type, inFunctionType: inFunctionType);
|
||||
++parameterCount;
|
||||
} while (optional(',', token));
|
||||
if (parameterCount == 0) {
|
||||
|
@ -547,6 +595,10 @@ class Parser {
|
|||
}
|
||||
|
||||
Token parseTypeOpt(Token token) {
|
||||
if (isGeneralizedFunctionType(token)) {
|
||||
// Function type without return type.
|
||||
return parseType(token);
|
||||
}
|
||||
Token peek = peekAfterIfType(token);
|
||||
if (peek != null && (peek.isIdentifier() || optional('this', peek))) {
|
||||
return parseType(token);
|
||||
|
@ -721,7 +773,7 @@ class Parser {
|
|||
modifierCount++;
|
||||
}
|
||||
listener.handleModifiers(modifierCount);
|
||||
bool isMixinApplication = optional('=', peekAfterType(token));
|
||||
bool isMixinApplication = optional('=', peekAfterNominalType(token));
|
||||
Token name = token.next;
|
||||
token = parseIdentifier(name);
|
||||
|
||||
|
@ -760,7 +812,7 @@ class Parser {
|
|||
Token extendsKeyword;
|
||||
if (optional('extends', token)) {
|
||||
extendsKeyword = token;
|
||||
if (optional('with', peekAfterType(token.next))) {
|
||||
if (optional('with', peekAfterNominalType(token.next))) {
|
||||
token = parseMixinApplication(token.next);
|
||||
} else {
|
||||
token = parseType(token.next);
|
||||
|
@ -852,17 +904,52 @@ class Parser {
|
|||
!identical(value, token.stringValue);
|
||||
}
|
||||
|
||||
bool isGeneralizedFunctionType(Token token) {
|
||||
return optional('Function', token) &&
|
||||
(optional('<', token.next) || optional('(', token.next));
|
||||
}
|
||||
|
||||
Token parseType(Token token) {
|
||||
Token begin = token;
|
||||
if (isValidTypeReference(token)) {
|
||||
token = parseIdentifier(token);
|
||||
token = parseQualifiedRestOpt(token);
|
||||
if (isGeneralizedFunctionType(token)) {
|
||||
// A function type without return type.
|
||||
// Push the non-existing return type first. The loop below will
|
||||
// generate the full type.
|
||||
listener.handleNoType(token);
|
||||
} else if (identical(token.stringValue, 'void') &&
|
||||
isGeneralizedFunctionType(token.next)) {
|
||||
listener.handleVoidKeyword(token);
|
||||
token = token.next;
|
||||
} else {
|
||||
token = reportUnrecoverableError(token, ErrorKind.ExpectedType);
|
||||
listener.handleInvalidTypeReference(token);
|
||||
if (isValidTypeReference(token)) {
|
||||
token = parseIdentifier(token);
|
||||
token = parseQualifiedRestOpt(token);
|
||||
} else {
|
||||
token = reportUnrecoverableError(token, ErrorKind.ExpectedType);
|
||||
listener.handleInvalidTypeReference(token);
|
||||
}
|
||||
token = parseTypeArgumentsOpt(token);
|
||||
listener.handleType(begin, token);
|
||||
}
|
||||
token = parseTypeArgumentsOpt(token);
|
||||
listener.endType(begin, token);
|
||||
|
||||
// While we see a `Function(` treat the pushed type as return type.
|
||||
// For example: `int Function() Function(int) Function(String x)`.
|
||||
while (isGeneralizedFunctionType(token)) {
|
||||
token = parseFunctionType(token);
|
||||
}
|
||||
return token;
|
||||
}
|
||||
|
||||
/// Parses a generalized function type.
|
||||
///
|
||||
/// The return type must already be pushed.
|
||||
Token parseFunctionType(Token token) {
|
||||
assert(optional('Function', token));
|
||||
Token functionToken = token;
|
||||
token = token.next;
|
||||
token = parseTypeVariablesOpt(token);
|
||||
token = parseFormalParameters(token, inFunctionType: true);
|
||||
listener.handleFunctionType(functionToken, token);
|
||||
return token;
|
||||
}
|
||||
|
||||
|
@ -1082,7 +1169,8 @@ class Parser {
|
|||
|
||||
if (!hasType) {
|
||||
listener.handleNoType(name);
|
||||
} else if (optional('void', type)) {
|
||||
} else if (optional('void', type) &&
|
||||
!isGeneralizedFunctionType(type.next)) {
|
||||
listener.handleNoType(name);
|
||||
// TODO(ahe): This error is reported twice, second time is from
|
||||
// [parseVariablesDeclarationMaybeSemicolon] via
|
||||
|
@ -1218,27 +1306,58 @@ class Parser {
|
|||
hasName = true;
|
||||
}
|
||||
identifiers = identifiers.prepend(token);
|
||||
if (isValidTypeReference(token)) {
|
||||
// type ...
|
||||
if (optional('.', token.next)) {
|
||||
// type '.' ...
|
||||
if (token.next.next.isIdentifier()) {
|
||||
// type '.' identifier
|
||||
token = token.next.next;
|
||||
|
||||
if (!isGeneralizedFunctionType(token)) {
|
||||
// Read a potential return type.
|
||||
if (isValidTypeReference(token)) {
|
||||
// type ...
|
||||
if (optional('.', token.next)) {
|
||||
// type '.' ...
|
||||
if (token.next.next.isIdentifier()) {
|
||||
// type '.' identifier
|
||||
token = token.next.next;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (optional('<', token.next)) {
|
||||
if (token.next is BeginGroupToken) {
|
||||
BeginGroupToken beginGroup = token.next;
|
||||
if (beginGroup.endGroup == null) {
|
||||
reportUnrecoverableError(beginGroup, ErrorKind.UnmatchedToken);
|
||||
} else {
|
||||
token = beginGroup.endGroup;
|
||||
if (optional('<', token.next)) {
|
||||
if (token.next is BeginGroupToken) {
|
||||
BeginGroupToken beginGroup = token.next;
|
||||
if (beginGroup.endGroup == null) {
|
||||
reportUnrecoverableError(beginGroup, ErrorKind.UnmatchedToken);
|
||||
} else {
|
||||
token = beginGroup.endGroup;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
token = token.next;
|
||||
}
|
||||
while (isGeneralizedFunctionType(token)) {
|
||||
token = token.next;
|
||||
if (optional('<', token)) {
|
||||
if (token is BeginGroupToken) {
|
||||
BeginGroupToken beginGroup = token;
|
||||
if (beginGroup.endGroup == null) {
|
||||
reportUnrecoverableError(beginGroup, ErrorKind.UnmatchedToken);
|
||||
} else {
|
||||
token = beginGroup.endGroup.next;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!optional('(', token)) {
|
||||
if (optional(';', token)) {
|
||||
reportRecoverableError(token, ErrorKind.ExpectedOpenParens);
|
||||
}
|
||||
token = expect("(", token);
|
||||
}
|
||||
if (token is BeginGroupToken) {
|
||||
BeginGroupToken beginGroup = token;
|
||||
if (beginGroup.endGroup == null) {
|
||||
reportUnrecoverableError(beginGroup, ErrorKind.UnmatchedToken);
|
||||
} else {
|
||||
token = beginGroup.endGroup.next;
|
||||
}
|
||||
}
|
||||
}
|
||||
token = token.next;
|
||||
}
|
||||
return listener.handleMemberName(const Link<Token>());
|
||||
}
|
||||
|
@ -1353,11 +1472,31 @@ class Parser {
|
|||
|
||||
/**
|
||||
* Returns the first token after the type starting at [token].
|
||||
*
|
||||
* This method assumes that [token] is an identifier (or void).
|
||||
* Use [peekAfterIfType] if [token] isn't known to be an identifier.
|
||||
*/
|
||||
Token peekAfterType(Token token) {
|
||||
// We are looking at "identifier ...".
|
||||
Token peek = token;
|
||||
if (!isGeneralizedFunctionType(token)) {
|
||||
peek = peekAfterNominalType(token);
|
||||
}
|
||||
|
||||
// We might have just skipped over the return value of the function type.
|
||||
// Check again, if we are now at a function type position.
|
||||
while (isGeneralizedFunctionType(peek)) {
|
||||
peek = peekAfterFunctionType(peek.next);
|
||||
}
|
||||
return peek;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the first token after the nominal type starting at [token].
|
||||
*
|
||||
* This method assumes that [token] is an identifier (or void).
|
||||
*/
|
||||
Token peekAfterNominalType(Token token) {
|
||||
Token peek = token.next;
|
||||
if (identical(peek.kind, PERIOD_TOKEN)) {
|
||||
if (peek.next.isIdentifier()) {
|
||||
|
@ -1373,12 +1512,57 @@ class Parser {
|
|||
Token gtToken = beginGroupToken.endGroup;
|
||||
if (gtToken != null) {
|
||||
// We are looking at "qualified '<' ... '>' ...".
|
||||
return gtToken.next;
|
||||
peek = gtToken.next;
|
||||
}
|
||||
}
|
||||
return peek;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the first token after the function type starting at [token].
|
||||
*
|
||||
* The token must be at the token *after* the `Function` token position. That
|
||||
* is, the return type and the `Function` token must have already been
|
||||
* skipped.
|
||||
*
|
||||
* This function only skips over one function type syntax.
|
||||
* If necessary, this function must be called multiple times.
|
||||
*
|
||||
* Example:
|
||||
* ```
|
||||
* int Function() Function<T>(int)
|
||||
* ^ ^
|
||||
* A call to this function must be either at `(` or at `<`.
|
||||
* If `token` pointed to the first `(`, then the returned
|
||||
* token points to the second `Function` token.
|
||||
*/
|
||||
Token peekAfterFunctionType(Token token) {
|
||||
// Possible inputs are:
|
||||
// ( ... )
|
||||
// < ... >( ... )
|
||||
|
||||
Token peek = token;
|
||||
// If there is a generic argument to the function, skip over that one first.
|
||||
if (identical(peek.kind, LT_TOKEN)) {
|
||||
BeginGroupToken beginGroupToken = peek;
|
||||
Token closeToken = beginGroupToken.endGroup;
|
||||
if (closeToken != null) {
|
||||
peek = closeToken.next;
|
||||
}
|
||||
}
|
||||
|
||||
// Now we just need to skip over the formals.
|
||||
expect('(', peek);
|
||||
|
||||
BeginGroupToken beginGroupToken = peek;
|
||||
Token closeToken = beginGroupToken.endGroup;
|
||||
if (closeToken != null) {
|
||||
peek = closeToken.next;
|
||||
}
|
||||
|
||||
return peek;
|
||||
}
|
||||
|
||||
/**
|
||||
* If [token] is the start of a type, returns the token after that type.
|
||||
* If [token] is not the start of a type, null is returned.
|
||||
|
|
|
@ -241,7 +241,7 @@ abstract class AbstractScanner implements Scanner {
|
|||
if (($A <= next && next <= $Z) ||
|
||||
identical(next, $_) ||
|
||||
identical(next, $$)) {
|
||||
return tokenizeIdentifier(next, scanOffset, true);
|
||||
return tokenizeKeywordOrIdentifier(next, true);
|
||||
}
|
||||
|
||||
if (identical(next, $LT)) {
|
||||
|
@ -808,6 +808,16 @@ abstract class AbstractScanner implements Scanner {
|
|||
int tokenizeKeywordOrIdentifier(int next, bool allowDollar) {
|
||||
KeywordState state = KeywordState.KEYWORD_STATE;
|
||||
int start = scanOffset;
|
||||
// We allow a leading capital character.
|
||||
if ($A <= next && next <= $Z) {
|
||||
state = state.nextCapital(next);
|
||||
next = advance();
|
||||
} else if ($a <= next && next <= $z){
|
||||
// Do the first next call outside the loop to avoid an additional test
|
||||
// and to make the loop monomorphic.
|
||||
state = state.next(next);
|
||||
next = advance();
|
||||
}
|
||||
while (state != null && $a <= next && next <= $z) {
|
||||
state = state.next(next);
|
||||
next = advance();
|
||||
|
@ -945,12 +955,11 @@ abstract class AbstractScanner implements Scanner {
|
|||
int tokenizeInterpolatedIdentifier(int next) {
|
||||
appendPrecedenceToken(STRING_INTERPOLATION_IDENTIFIER_INFO);
|
||||
|
||||
if ($a <= next && next <= $z) {
|
||||
if ($a <= next && next <= $z ||
|
||||
$A <= next && next <= $Z ||
|
||||
identical(next, $_)) {
|
||||
beginToken(); // The identifier starts here.
|
||||
next = tokenizeKeywordOrIdentifier(next, false);
|
||||
} else if (($A <= next && next <= $Z) || identical(next, $_)) {
|
||||
beginToken(); // The identifier starts here.
|
||||
next = tokenizeIdentifier(next, scanOffset, false);
|
||||
} else {
|
||||
unterminated(r'$', shouldAdvance: false);
|
||||
}
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
library fasta.scanner.keywords;
|
||||
|
||||
import 'characters.dart' show
|
||||
$a;
|
||||
$a, $z, $A, $Z;
|
||||
|
||||
import 'precedence.dart' show
|
||||
PrecedenceInfo;
|
||||
|
@ -77,6 +77,7 @@ class Keyword {
|
|||
const Keyword("async", isPseudo: true),
|
||||
const Keyword("await", isPseudo: true),
|
||||
const Keyword("deferred", isPseudo: true),
|
||||
const Keyword("Function", isPseudo: true),
|
||||
const Keyword("hide", isPseudo: true),
|
||||
const Keyword("native", isPseudo: true),
|
||||
const Keyword("of", isPseudo: true),
|
||||
|
@ -121,10 +122,10 @@ class Keyword {
|
|||
* Abstract state in a state machine for scanning keywords.
|
||||
*/
|
||||
abstract class KeywordState {
|
||||
KeywordState(this.keyword);
|
||||
|
||||
KeywordState next(int c);
|
||||
final Keyword keyword;
|
||||
KeywordState nextCapital(int c);
|
||||
|
||||
Keyword get keyword;
|
||||
|
||||
static KeywordState _KEYWORD_STATE;
|
||||
static KeywordState get KEYWORD_STATE {
|
||||
|
@ -141,7 +142,9 @@ abstract class KeywordState {
|
|||
|
||||
static KeywordState computeKeywordStateTable(
|
||||
int start, List<String> strings, int offset, int length) {
|
||||
List<KeywordState> result = new List<KeywordState>(26);
|
||||
bool isLowercase = true;
|
||||
|
||||
List<KeywordState> table = new List<KeywordState>($z - $A + 1);
|
||||
assert(length != 0);
|
||||
int chunk = 0;
|
||||
int chunkStart = -1;
|
||||
|
@ -152,10 +155,13 @@ abstract class KeywordState {
|
|||
}
|
||||
if (strings[i].length > start) {
|
||||
int c = strings[i].codeUnitAt(start);
|
||||
if ($A <= c && c <= $Z) {
|
||||
isLowercase = false;
|
||||
}
|
||||
if (chunk != c) {
|
||||
if (chunkStart != -1) {
|
||||
assert(result[chunk - $a] == null);
|
||||
result[chunk - $a] = computeKeywordStateTable(
|
||||
assert(table[chunk - $A] == null);
|
||||
table[chunk - $A] = computeKeywordStateTable(
|
||||
start + 1, strings, chunkStart, i - chunkStart);
|
||||
}
|
||||
chunkStart = i;
|
||||
|
@ -164,17 +170,19 @@ abstract class KeywordState {
|
|||
}
|
||||
}
|
||||
if (chunkStart != -1) {
|
||||
assert(result[chunk - $a] == null);
|
||||
result[chunk - $a] = computeKeywordStateTable(
|
||||
assert(table[chunk - $A] == null);
|
||||
table[chunk - $A] = computeKeywordStateTable(
|
||||
start + 1, strings, chunkStart, offset + length - chunkStart);
|
||||
} else {
|
||||
assert(length == 1);
|
||||
return new LeafKeywordState(strings[offset]);
|
||||
}
|
||||
if (isLeaf) {
|
||||
return new ArrayKeywordState(result, strings[offset]);
|
||||
String syntax = isLeaf ? strings[offset] : null;
|
||||
if (isLowercase) {
|
||||
table = table.sublist($a - $A);
|
||||
return new LowerCaseArrayKeywordState(table, syntax);
|
||||
} else {
|
||||
return new ArrayKeywordState(result, null);
|
||||
return new UpperCaseArrayKeywordState(table, syntax);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -182,13 +190,16 @@ abstract class KeywordState {
|
|||
/**
|
||||
* A state with multiple outgoing transitions.
|
||||
*/
|
||||
class ArrayKeywordState extends KeywordState {
|
||||
abstract class ArrayKeywordState implements KeywordState {
|
||||
final List<KeywordState> table;
|
||||
final Keyword keyword;
|
||||
|
||||
ArrayKeywordState(List<KeywordState> this.table, String syntax)
|
||||
: super((syntax == null) ? null : Keyword.keywords[syntax]);
|
||||
: keyword = ((syntax == null) ? null : Keyword.keywords[syntax]);
|
||||
|
||||
KeywordState next(int c) => table[c - $a];
|
||||
KeywordState next(int c);
|
||||
|
||||
KeywordState nextCapital(int c);
|
||||
|
||||
String toString() {
|
||||
StringBuffer sb = new StringBuffer();
|
||||
|
@ -210,13 +221,42 @@ class ArrayKeywordState extends KeywordState {
|
|||
}
|
||||
}
|
||||
|
||||
class LowerCaseArrayKeywordState extends ArrayKeywordState {
|
||||
|
||||
LowerCaseArrayKeywordState(List<KeywordState> table, String syntax)
|
||||
: super(table, syntax) {
|
||||
assert(table.length == $z - $a + 1);
|
||||
}
|
||||
|
||||
KeywordState next(int c) => table[c - $a];
|
||||
|
||||
KeywordState nextCapital(int c) => null;
|
||||
|
||||
}
|
||||
|
||||
class UpperCaseArrayKeywordState extends ArrayKeywordState {
|
||||
|
||||
UpperCaseArrayKeywordState(List<KeywordState> table, String syntax)
|
||||
: super(table, syntax) {
|
||||
assert(table.length == $z - $A + 1);
|
||||
}
|
||||
|
||||
KeywordState next(int c) => table[c - $A];
|
||||
|
||||
KeywordState nextCapital(int c) => table[c - $A];
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* A state that has no outgoing transitions.
|
||||
*/
|
||||
class LeafKeywordState extends KeywordState {
|
||||
LeafKeywordState(String syntax) : super(Keyword.keywords[syntax]);
|
||||
class LeafKeywordState implements KeywordState {
|
||||
final Keyword keyword;
|
||||
|
||||
LeafKeywordState(String syntax) : keyword = Keyword.keywords[syntax];
|
||||
|
||||
KeywordState next(int c) => null;
|
||||
KeywordState nextCapital(int c) => null;
|
||||
|
||||
String toString() => keyword.syntax;
|
||||
}
|
||||
|
|
|
@ -114,7 +114,7 @@ abstract class Token {
|
|||
}
|
||||
|
||||
/**
|
||||
* A [SymbolToken] represents the symbol in its precendence info.
|
||||
* A [SymbolToken] represents the symbol in its precedence info.
|
||||
* Also used for end of file with EOF_INFO.
|
||||
*/
|
||||
class SymbolToken extends Token {
|
||||
|
|
|
@ -133,7 +133,7 @@ class DietListener extends StackListener {
|
|||
}
|
||||
|
||||
@override
|
||||
void endType(Token beginToken, Token endToken) {
|
||||
void handleType(Token beginToken, Token endToken) {
|
||||
debugEvent("Type");
|
||||
discard(1);
|
||||
}
|
||||
|
@ -190,7 +190,8 @@ class DietListener extends StackListener {
|
|||
}
|
||||
|
||||
@override
|
||||
void endFunctionTypeAlias(Token typedefKeyword, Token endToken) {
|
||||
void endFunctionTypeAlias(
|
||||
Token typedefKeyword, Token equals, Token endToken) {
|
||||
debugEvent("FunctionTypeAlias");
|
||||
discard(2); // Name + endToken.
|
||||
checkEmpty(typedefKeyword.charOffset);
|
||||
|
|
|
@ -25,7 +25,9 @@ class DietParser extends ClassMemberParser {
|
|||
DietParser(Listener listener, {bool asyncAwaitKeywordsEnabled: false})
|
||||
: super(listener, asyncAwaitKeywordsEnabled: asyncAwaitKeywordsEnabled);
|
||||
|
||||
Token parseFormalParameters(Token token) => skipFormals(token);
|
||||
Token parseFormalParameters(Token token, {bool inFunctionType: false}) {
|
||||
return skipFormals(token);
|
||||
}
|
||||
|
||||
Token skipFormals(Token token) {
|
||||
listener.beginOptionalFormalParameters(token);
|
||||
|
|
|
@ -346,7 +346,7 @@ class OutlineBuilder extends UnhandledListener {
|
|||
}
|
||||
|
||||
@override
|
||||
void endType(Token beginToken, Token endToken) {
|
||||
void handleType(Token beginToken, Token endToken) {
|
||||
debugEvent("Type");
|
||||
List<TypeBuilder> arguments = pop();
|
||||
String name = pop();
|
||||
|
@ -459,7 +459,8 @@ class OutlineBuilder extends UnhandledListener {
|
|||
}
|
||||
|
||||
@override
|
||||
void endFunctionTypeAlias(Token typedefKeyword, Token endToken) {
|
||||
void endFunctionTypeAlias(
|
||||
Token typedefKeyword, Token equals, Token endToken) {
|
||||
debugEvent("endFunctionTypeAlias");
|
||||
List<FormalParameterBuilder> formals = pop();
|
||||
List<TypeVariableBuilder> typeVariables = pop();
|
||||
|
|
13
pkg/front_end/test/fasta/function_type_recovery_test.dart
Normal file
13
pkg/front_end/test/fasta/function_type_recovery_test.dart
Normal file
|
@ -0,0 +1,13 @@
|
|||
// Copyright (c) 2017, 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.
|
||||
|
||||
// Tests that an inline function type inside a `Function` type isn't a
|
||||
// parser error.
|
||||
|
||||
typedef F = int Function(int f(String x));
|
||||
|
||||
main() {
|
||||
F f = null;
|
||||
String Function(String g(int y)) g = null;
|
||||
}
|
|
@ -149,8 +149,9 @@ Future testTypeVariables() {
|
|||
visitor.visit(definition.type);
|
||||
ResolutionInterfaceType type =
|
||||
visitor.registry.mapping.getType(definition.type);
|
||||
NominalTypeAnnotation annotation = definition.type;
|
||||
Expect.equals(
|
||||
definition.type.typeArguments.slowLength(), type.typeArguments.length);
|
||||
annotation.typeArguments.slowLength(), type.typeArguments.length);
|
||||
int index = 0;
|
||||
for (ResolutionDartType argument in type.typeArguments) {
|
||||
Expect.equals(true, index < expectedElements.length);
|
||||
|
@ -700,7 +701,8 @@ Future testTopLevelFields() {
|
|||
Expect.equals(ElementKind.FIELD, element.kind);
|
||||
VariableDefinitions node =
|
||||
element.variables.parseNode(element, compiler.parsingContext);
|
||||
Identifier typeName = node.type.typeName;
|
||||
NominalTypeAnnotation annotation = node.type;
|
||||
Identifier typeName = annotation.typeName;
|
||||
Expect.equals(typeName.source, 'int');
|
||||
|
||||
compiler.parseScript("var b, c;");
|
||||
|
|
770
tests/language/function_type/test_generator.dart
Normal file
770
tests/language/function_type/test_generator.dart
Normal file
|
@ -0,0 +1,770 @@
|
|||
// 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 'dart:io';
|
||||
|
||||
// By convention:
|
||||
//
|
||||
// T: generic type of typedef.
|
||||
// A: generic type of returned function.
|
||||
// B: generic type of function.
|
||||
//
|
||||
// Example:
|
||||
// typedef F<T>: Function<A> Function<B>();
|
||||
//
|
||||
// We only use: Function, List, int (and function types).
|
||||
// We import 'dart:core' directly and with prefix 'core'.
|
||||
|
||||
abstract class Printable {
|
||||
/// Builds a descriptive string that can be used as an identifier.
|
||||
///
|
||||
/// The string is mainly used for disambiguation, and not for its readability.
|
||||
void writeIdentifier(StringBuffer buffer);
|
||||
}
|
||||
|
||||
abstract class TypeLike implements Printable {
|
||||
/// Prints `this` as valid Dart code for a Type.
|
||||
void writeType(StringBuffer buffer);
|
||||
|
||||
/// Whether this instance uses T in some way.
|
||||
bool get usesT;
|
||||
}
|
||||
|
||||
/// Provides a unique integer for every parameter in a function.
|
||||
int parameterNameCounter = 0;
|
||||
|
||||
/// Whether `T` should be replaced with `int`.
|
||||
bool shouldReplaceTWithInt = false;
|
||||
|
||||
class Parameter implements Printable {
|
||||
final TypeLike type;
|
||||
final String name;
|
||||
|
||||
Parameter(this.type, this.name);
|
||||
|
||||
// Type or name can be null.
|
||||
@override
|
||||
writeIdentifier(buffer) {
|
||||
if (type == null) {
|
||||
buffer.write("null");
|
||||
} else {
|
||||
type.writeIdentifier(buffer);
|
||||
}
|
||||
buffer.write("_");
|
||||
buffer.write(name);
|
||||
}
|
||||
|
||||
void writeType(StringBuffer buffer) {
|
||||
assert(type != null || name != null);
|
||||
if (name == null) {
|
||||
type.writeType(buffer);
|
||||
} else if (type == null) {
|
||||
buffer.write(name);
|
||||
} else {
|
||||
type.writeType(buffer);
|
||||
buffer.write(" ");
|
||||
buffer.write(name);
|
||||
}
|
||||
}
|
||||
|
||||
void writeInFunction(StringBuffer buffer) {
|
||||
assert(type != null || name != null);
|
||||
if (name == null) {
|
||||
type.writeType(buffer);
|
||||
buffer.write(" x");
|
||||
buffer.write(parameterNameCounter++);
|
||||
} else if (type == null) {
|
||||
buffer.write(name);
|
||||
} else {
|
||||
type.writeType(buffer);
|
||||
buffer.write(" ");
|
||||
buffer.write(name);
|
||||
}
|
||||
}
|
||||
|
||||
bool operator ==(other) {
|
||||
return other is Parameter && name == other.name && type == other.type;
|
||||
}
|
||||
|
||||
int get hashCode {
|
||||
return ((name.hashCode * 37) ^ type.hashCode) & 0xFFFFFFFF;
|
||||
}
|
||||
|
||||
bool get usesT => type?.usesT == true;
|
||||
}
|
||||
|
||||
class GenericParameter implements TypeLike {
|
||||
final String name;
|
||||
final TypeLike bound;
|
||||
|
||||
GenericParameter(this.name, [this.bound]);
|
||||
|
||||
// Bound may be null.
|
||||
@override
|
||||
writeIdentifier(buffer) {
|
||||
buffer.write(name);
|
||||
buffer.write("_");
|
||||
if (bound == null) {
|
||||
buffer.write("null");
|
||||
} else {
|
||||
bound.writeIdentifier(buffer);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
writeType(buffer) {
|
||||
buffer.write(name);
|
||||
if (bound != null) {
|
||||
buffer.write(" extends ");
|
||||
bound.writeType(buffer);
|
||||
}
|
||||
}
|
||||
|
||||
bool operator ==(other) {
|
||||
return other is GenericParameter &&
|
||||
name == other.name &&
|
||||
bound == other.bound;
|
||||
}
|
||||
|
||||
int get hashCode {
|
||||
return ((name.hashCode * 23) ^ bound.hashCode) & 0xFFFFFFFF;
|
||||
}
|
||||
|
||||
bool get usesT {
|
||||
return bound?.usesT == true;
|
||||
}
|
||||
}
|
||||
|
||||
void _describeList(StringBuffer buffer, List<Printable> list) {
|
||||
if (list == null) {
|
||||
buffer.write("0");
|
||||
return;
|
||||
}
|
||||
buffer.write(list.length.toString());
|
||||
buffer.write("_");
|
||||
for (int i = 0; i < list.length; i++) {
|
||||
if (i != 0) buffer.write("_");
|
||||
list[i].writeIdentifier(buffer);
|
||||
}
|
||||
}
|
||||
|
||||
void _writeTypes(StringBuffer buffer, List<TypeLike> list,
|
||||
[String prefix = "", String postfix = ""]) {
|
||||
if (list == null || list.isEmpty) return;
|
||||
buffer.write(prefix);
|
||||
for (int i = 0; i < list.length; i++) {
|
||||
if (i != 0) buffer.write(", ");
|
||||
list[i].writeType(buffer);
|
||||
}
|
||||
buffer.write(postfix);
|
||||
}
|
||||
|
||||
void _writeParameters(
|
||||
StringBuffer buffer, List<Parameter> list, bool inFunction,
|
||||
[String prefix = "", String postfix = ""]) {
|
||||
if (list == null || list.isEmpty) return;
|
||||
buffer.write(prefix);
|
||||
for (int i = 0; i < list.length; i++) {
|
||||
if (i != 0) buffer.write(", ");
|
||||
if (inFunction) {
|
||||
list[i].writeInFunction(buffer);
|
||||
} else {
|
||||
list[i].writeType(buffer);
|
||||
}
|
||||
}
|
||||
buffer.write(postfix);
|
||||
}
|
||||
|
||||
bool _listUsesT(List elements) {
|
||||
if (elements == null) return false;
|
||||
return elements.any((p) => p.usesT);
|
||||
}
|
||||
|
||||
bool _listEquals(List list1, List list2) {
|
||||
if (list1 == list2) return true; // Also covers both being null.
|
||||
if (list1 == null || list2 == null) return false;
|
||||
for (int i = 0; i < list1.length; i++) {
|
||||
if (list1[i] != list2[i]) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
int _listHash(List list) {
|
||||
if (list == null) return null.hashCode;
|
||||
int result = 71;
|
||||
for (int i = 0; i < list.length; i++) {
|
||||
result = ((result * 11) ^ list[i].hashCode) & 0xFFFFFFFF;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
class FunctionType implements TypeLike {
|
||||
final TypeLike returnType;
|
||||
final List<GenericParameter> generic;
|
||||
final List<Parameter> required;
|
||||
final List<Parameter> optional;
|
||||
final List<Parameter> named;
|
||||
|
||||
FunctionType(this.returnType, this.generic, this.required,
|
||||
[this.optional, this.named]);
|
||||
|
||||
@override
|
||||
writeIdentifier(buffer) {
|
||||
buffer.write("Fun_");
|
||||
if (returnType == null) {
|
||||
buffer.write("null");
|
||||
} else {
|
||||
returnType.writeIdentifier(buffer);
|
||||
}
|
||||
buffer.write("_");
|
||||
_describeList(buffer, generic);
|
||||
buffer.write("_");
|
||||
_describeList(buffer, required);
|
||||
buffer.write("_");
|
||||
_describeList(buffer, optional);
|
||||
buffer.write("_");
|
||||
_describeList(buffer, named);
|
||||
}
|
||||
|
||||
@override
|
||||
writeType(buffer) {
|
||||
if (returnType != null) {
|
||||
returnType.writeType(buffer);
|
||||
buffer.write(" ");
|
||||
}
|
||||
buffer.write("Function");
|
||||
if (generic != null) _writeTypes(buffer, generic, "<", ">");
|
||||
buffer.write("(");
|
||||
bool notInFunction = true;
|
||||
_writeParameters(buffer, required, notInFunction);
|
||||
if ((optional != null || named != null) &&
|
||||
required != null &&
|
||||
required.isNotEmpty) {
|
||||
buffer.write(", ");
|
||||
}
|
||||
_writeParameters(buffer, optional, notInFunction, "[", "]");
|
||||
_writeParameters(buffer, named, notInFunction, "{", "}");
|
||||
buffer.write(")");
|
||||
}
|
||||
|
||||
/// Writes this type as if it was a function.
|
||||
void writeFunction(StringBuffer buffer, String name, {bool replaceT: true}) {
|
||||
shouldReplaceTWithInt = replaceT;
|
||||
parameterNameCounter = 0;
|
||||
|
||||
if (returnType != null) {
|
||||
returnType.writeType(buffer);
|
||||
buffer.write(" ");
|
||||
}
|
||||
|
||||
buffer.write(name);
|
||||
if (generic != null) _writeTypes(buffer, generic, "<", ">");
|
||||
buffer.write("(");
|
||||
bool inFunction = true;
|
||||
_writeParameters(buffer, required, inFunction);
|
||||
if ((optional != null || named != null) &&
|
||||
required != null &&
|
||||
required.isNotEmpty) {
|
||||
buffer.write(", ");
|
||||
}
|
||||
_writeParameters(buffer, optional, inFunction, "[", "]");
|
||||
_writeParameters(buffer, named, inFunction, "{", "}");
|
||||
buffer.write(") => null;");
|
||||
|
||||
shouldReplaceTWithInt = false;
|
||||
}
|
||||
|
||||
bool operator ==(other) {
|
||||
return returnType == other.returnType &&
|
||||
_listEquals(generic, other.generic) &&
|
||||
_listEquals(required, other.required) &&
|
||||
_listEquals(optional, other.optional) &&
|
||||
_listEquals(named, other.named);
|
||||
}
|
||||
|
||||
int get hashCode {
|
||||
return ((returnType.hashCode * 13) ^
|
||||
(_listHash(generic) * 17) ^
|
||||
(_listHash(required) * 53) ^
|
||||
(_listHash(optional) ^ 31) ^
|
||||
(_listHash(named) * 87)) &
|
||||
0xFFFFFFFF;
|
||||
}
|
||||
|
||||
bool get usesT {
|
||||
return returnType?.usesT == true ||
|
||||
[generic, required, optional, named].any(_listUsesT);
|
||||
}
|
||||
}
|
||||
|
||||
class NominalType implements TypeLike {
|
||||
final String prefix;
|
||||
final String name;
|
||||
final List<TypeLike> generic;
|
||||
|
||||
NominalType(this.name, [this.prefix, this.generic]);
|
||||
|
||||
@override
|
||||
writeIdentifier(buffer) {
|
||||
buffer.write(prefix);
|
||||
buffer.write("_");
|
||||
buffer.write(name);
|
||||
_describeList(buffer, generic);
|
||||
}
|
||||
|
||||
@override
|
||||
writeType(buffer) {
|
||||
if (prefix != null && prefix != "") {
|
||||
buffer.write(prefix);
|
||||
buffer.write(".");
|
||||
}
|
||||
if (shouldReplaceTWithInt && name == "T") {
|
||||
buffer.write("int");
|
||||
} else {
|
||||
buffer.write(name);
|
||||
}
|
||||
_writeTypes(buffer, generic, "<", ">");
|
||||
}
|
||||
|
||||
bool operator ==(other) {
|
||||
return other is NominalType && prefix == other.prefix && name == other.name;
|
||||
}
|
||||
|
||||
int get hashCode {
|
||||
return ((prefix.hashCode * 37) ^ name.hashCode) & 0xFFFFFFFF;
|
||||
}
|
||||
|
||||
bool get usesT => name == "T" || _listUsesT(generic);
|
||||
}
|
||||
|
||||
List<FunctionType> buildFunctionTypes() {
|
||||
List<GenericParameter> as = [
|
||||
new GenericParameter("A"),
|
||||
// new GenericParameter("A", new NominalType("int")),
|
||||
// new GenericParameter("A", new NominalType("int", "core")),
|
||||
];
|
||||
List<GenericParameter> bs = [
|
||||
// new GenericParameter("B"),
|
||||
// new GenericParameter("B", new NominalType("int")),
|
||||
new GenericParameter("B", new NominalType("int", "core")),
|
||||
];
|
||||
List<TypeLike> basicTypes = [
|
||||
new NominalType("int"),
|
||||
// new NominalType("int", "core"),
|
||||
// new NominalType("List"),
|
||||
// new NominalType("List", "core"),
|
||||
new NominalType("Function"),
|
||||
new NominalType("List", "", [new NominalType("Function")]),
|
||||
new NominalType("List", "core", [new NominalType("int", "core")]),
|
||||
new NominalType("List", "", [new NominalType("T")]),
|
||||
// new NominalType("List", "", [new NominalType("Function")]),
|
||||
];
|
||||
|
||||
List<TypeLike> basicsPlusNull = [
|
||||
basicTypes,
|
||||
<TypeLike>[null]
|
||||
].expand((x) => x).toList();
|
||||
|
||||
List<TypeLike> basicsPlusNullPlusVoid = [
|
||||
basicsPlusNull,
|
||||
[new NominalType("void")],
|
||||
].expand((x) => x).toList();
|
||||
|
||||
List<TypeLike> basicsPlusNullPlusB = [
|
||||
basicsPlusNull,
|
||||
[
|
||||
new NominalType("B"),
|
||||
new NominalType("List", "", [new NominalType("B")])
|
||||
]
|
||||
].expand((x) => x).toList();
|
||||
|
||||
List<TypeLike> basicsPlusNullPlusBPlusVoid = [
|
||||
basicsPlusNullPlusB,
|
||||
[new NominalType("void")],
|
||||
].expand((x) => x).toList();
|
||||
|
||||
List<TypeLike> basicsPlusNullPlusA = [
|
||||
basicsPlusNull,
|
||||
[
|
||||
new NominalType("A"),
|
||||
new NominalType("List", "", [new NominalType("A")])
|
||||
]
|
||||
].expand((x) => x).toList();
|
||||
|
||||
List<TypeLike> basicsPlusNullPlusAPlusVoid = [
|
||||
basicsPlusNullPlusA,
|
||||
[new NominalType("void")],
|
||||
].expand((x) => x).toList();
|
||||
|
||||
List<TypeLike> buildFunctionTypes(TypeLike returnType, TypeLike parameterType,
|
||||
[List<GenericParameter> generics,
|
||||
bool generateMoreCombinations = false]) {
|
||||
List<TypeLike> result = [];
|
||||
|
||||
if (parameterType == null) {
|
||||
// int Function().
|
||||
result.add(new FunctionType(returnType, generics, null));
|
||||
return result;
|
||||
}
|
||||
|
||||
// int Function(int x).
|
||||
result.add(new FunctionType(
|
||||
returnType, generics, [new Parameter(parameterType, "x")]));
|
||||
|
||||
if (!generateMoreCombinations) return result;
|
||||
|
||||
// int Function([int x]).
|
||||
result.add(new FunctionType(
|
||||
returnType, generics, null, [new Parameter(parameterType, "x")]));
|
||||
// int Function(int, [int x])
|
||||
result.add(new FunctionType(
|
||||
returnType,
|
||||
generics,
|
||||
[new Parameter(new NominalType("int"), null)],
|
||||
[new Parameter(parameterType, "x")]));
|
||||
// int Function(int x, [int x])
|
||||
result.add(new FunctionType(
|
||||
returnType,
|
||||
generics,
|
||||
[new Parameter(new NominalType("int"), "y")],
|
||||
[new Parameter(parameterType, "x")]));
|
||||
// int Function(int);
|
||||
result.add(new FunctionType(
|
||||
returnType, generics, [new Parameter(parameterType, null)]));
|
||||
// int Function([int]);
|
||||
result.add(new FunctionType(
|
||||
returnType, generics, null, [new Parameter(parameterType, null)]));
|
||||
// int Function(int, [int])
|
||||
result.add(new FunctionType(
|
||||
returnType,
|
||||
generics,
|
||||
[new Parameter(new NominalType("int"), null)],
|
||||
[new Parameter(parameterType, null)]));
|
||||
// int Function(int x, [int])
|
||||
result.add(new FunctionType(
|
||||
returnType,
|
||||
generics,
|
||||
[new Parameter(new NominalType("int"), "x")],
|
||||
[new Parameter(parameterType, null)]));
|
||||
// int Function({int x}).
|
||||
result.add(new FunctionType(returnType, generics, null, null,
|
||||
[new Parameter(parameterType, "x")]));
|
||||
// int Function(int, {int x})
|
||||
result.add(new FunctionType(
|
||||
returnType,
|
||||
generics,
|
||||
[new Parameter(new NominalType("int"), null)],
|
||||
null,
|
||||
[new Parameter(parameterType, "x")]));
|
||||
// int Function(int x, {int x})
|
||||
result.add(new FunctionType(
|
||||
returnType,
|
||||
generics,
|
||||
[new Parameter(new NominalType("int"), "y")],
|
||||
null,
|
||||
[new Parameter(parameterType, "x")]));
|
||||
return result;
|
||||
}
|
||||
|
||||
// The "smaller" function types. May also be used non-nested.
|
||||
List<TypeLike> functionTypes = [];
|
||||
|
||||
for (TypeLike returnType in basicsPlusNullPlusVoid) {
|
||||
for (TypeLike parameterType in basicsPlusNull) {
|
||||
bool generateMoreCombinations = true;
|
||||
functionTypes.addAll(buildFunctionTypes(
|
||||
returnType, parameterType, null, generateMoreCombinations));
|
||||
}
|
||||
}
|
||||
|
||||
// These use `B` from the generic type of the enclosing function.
|
||||
List<TypeLike> returnFunctionTypesB = [];
|
||||
for (TypeLike returnType in basicsPlusNullPlusBPlusVoid) {
|
||||
TypeLike parameterType = new NominalType("B");
|
||||
returnFunctionTypesB.addAll(buildFunctionTypes(returnType, parameterType));
|
||||
}
|
||||
for (TypeLike parameterType in basicsPlusNull) {
|
||||
TypeLike returnType = new NominalType("B");
|
||||
returnFunctionTypesB.addAll(buildFunctionTypes(returnType, parameterType));
|
||||
}
|
||||
|
||||
for (TypeLike returnType in basicsPlusNullPlusAPlusVoid) {
|
||||
for (TypeLike parameterType in basicsPlusNullPlusA) {
|
||||
for (GenericParameter a in as) {
|
||||
functionTypes
|
||||
.addAll(buildFunctionTypes(returnType, parameterType, [a]));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
List<TypeLike> types = [];
|
||||
types.addAll(functionTypes);
|
||||
|
||||
// Now add some higher-order function types.
|
||||
for (TypeLike returnType in functionTypes) {
|
||||
types.addAll(buildFunctionTypes(returnType, null));
|
||||
types.addAll(buildFunctionTypes(returnType, new NominalType("int")));
|
||||
for (var b in bs) {
|
||||
types.addAll(buildFunctionTypes(returnType, null, [b]));
|
||||
types.addAll(buildFunctionTypes(returnType, new NominalType("int"), [b]));
|
||||
}
|
||||
}
|
||||
for (TypeLike returnType in returnFunctionTypesB) {
|
||||
for (var b in bs) {
|
||||
types.addAll(buildFunctionTypes(returnType, null, [b]));
|
||||
types.addAll(buildFunctionTypes(returnType, new NominalType("int"), [b]));
|
||||
}
|
||||
}
|
||||
|
||||
return types;
|
||||
}
|
||||
|
||||
final String HEADER = """
|
||||
// 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.
|
||||
|
||||
// GENERATED - DON'T EDIT.
|
||||
// GENERATED - DON'T EDIT.
|
||||
// GENERATED - DON'T EDIT.
|
||||
// GENERATED - DON'T EDIT.
|
||||
// GENERATED - DON'T EDIT.
|
||||
// GENERATED - DON'T EDIT.
|
||||
// GENERATED - DON'T EDIT.
|
||||
// GENERATED - DON'T EDIT.
|
||||
|
||||
import 'dart:core';
|
||||
import 'dart:core' as core;
|
||||
import 'package:expect/expect.dart';
|
||||
|
||||
@NoInline()
|
||||
@AssumeDynamic()
|
||||
confuse(f) => f;
|
||||
|
||||
final bool inCheckedMode =
|
||||
(() { bool result = false; assert(result = true); return result; })();
|
||||
""";
|
||||
|
||||
|
||||
class Unit {
|
||||
final String name;
|
||||
final StringBuffer typedefs = new StringBuffer();
|
||||
final StringBuffer globals = new StringBuffer();
|
||||
final StringBuffer tests = new StringBuffer();
|
||||
final StringBuffer fields = new StringBuffer();
|
||||
final StringBuffer statics = new StringBuffer();
|
||||
final StringBuffer testMethods = new StringBuffer();
|
||||
final StringBuffer methods = new StringBuffer();
|
||||
|
||||
Unit(this.name);
|
||||
|
||||
void write(StringBuffer buffer) {
|
||||
buffer.write("""
|
||||
$HEADER
|
||||
|
||||
$typedefs
|
||||
|
||||
$globals
|
||||
|
||||
class $name<T> {
|
||||
final bool tIsBool;
|
||||
final bool tIsInt;
|
||||
final bool tIsDynamic;
|
||||
|
||||
$fields
|
||||
|
||||
$name({this.tIsBool: false, this.tIsInt: false})
|
||||
: tIsDynamic = !tIsBool && !tIsInt;
|
||||
|
||||
$methods
|
||||
|
||||
runTests() {\n$tests }
|
||||
|
||||
$testMethods
|
||||
}
|
||||
|
||||
void main() {
|
||||
new $name().runTests();
|
||||
new $name<int>(tIsInt: true).runTests();
|
||||
new $name<bool>(tIsBool: true).runTests();
|
||||
}
|
||||
""");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
final TEST_METHOD_HEADER = """
|
||||
void #testName() {
|
||||
// #typeCode""";
|
||||
|
||||
// Tests that apply for every type.
|
||||
final COMMON_TESTS_TEMPLATE = """
|
||||
Expect.isTrue(#staticFunName is #typeName);
|
||||
Expect.isTrue(confuse(#staticFunName) is #typeName);
|
||||
// In checked mode, verifies the type.
|
||||
#typeCode #localName;
|
||||
// The static function #staticFunName sets `T` to `int`.
|
||||
if (!tIsBool) {
|
||||
#fieldName = #staticFunName as dynamic;
|
||||
#localName = #staticFunName as dynamic;
|
||||
#fieldName = confuse(#staticFunName);
|
||||
#localName = confuse(#staticFunName);
|
||||
}
|
||||
|
||||
Expect.isTrue(#methodFunName is #typeName);
|
||||
Expect.isTrue(#methodFunName is #typeCode);
|
||||
Expect.isTrue(confuse(#methodFunName) is #typeName);
|
||||
// In checked mode, verifies the type.
|
||||
#fieldName = #methodFunName;
|
||||
#localName = #methodFunName;
|
||||
#fieldName = confuse(#methodFunName);
|
||||
#localName = confuse(#methodFunName);""";
|
||||
|
||||
// Tests that depend on the typedef "T" argument.
|
||||
//
|
||||
// These tests are executed when the surrounding class is instantiated with
|
||||
// its generic type set to `int`, `dynamic` or `bool`. In the latter case, the
|
||||
// class field `tIsBool` is set to true.
|
||||
|
||||
// While the types themselves are not affected by the class` `T`, the methods
|
||||
// of the class may use `T`:
|
||||
//
|
||||
// For example:
|
||||
// class A<T> {
|
||||
// f(List<T> x) {}
|
||||
// }
|
||||
final TYPEDEF_T_TESTS_TEMPLATE = """
|
||||
if (!tIsBool) {
|
||||
Expect.isTrue(#staticFunName is #typeName<int>);
|
||||
Expect.isFalse(#staticFunName is #typeName<bool>);
|
||||
Expect.isTrue(confuse(#staticFunName) is #typeName<int>);
|
||||
Expect.isFalse(confuse(#staticFunName) is #typeName<bool>);
|
||||
Expect.equals(tIsDynamic, #methodFunName is #typeName<bool>);
|
||||
Expect.equals(tIsDynamic, confuse(#methodFunName) is #typeName<bool>);
|
||||
} else {
|
||||
if (inCheckedMode) {
|
||||
Expect.throws(() { #fieldName = (#staticFunName as dynamic); });
|
||||
Expect.throws(() { #fieldName = confuse(#staticFunName); });
|
||||
#typeCode #localName;
|
||||
Expect.throws(() { #localName = (#staticFunName as dynamic); });
|
||||
Expect.throws(() { #localName = confuse(#staticFunName); });
|
||||
}
|
||||
#typeCode #localName = #methodFunName;
|
||||
// In checked mode, verifies the type.
|
||||
#fieldName = #methodFunName;
|
||||
#fieldName = confuse(#methodFunName);
|
||||
}""";
|
||||
|
||||
final TEST_METHOD_FOOTER = " }";
|
||||
|
||||
String createTypeName(int id) => "F$id";
|
||||
String createStaticFunName(int id) => "f$id";
|
||||
String createMethodFunName(int id) => "m$id";
|
||||
String createFieldName(int id) => "x$id";
|
||||
String createLocalName(int id) => "l$id";
|
||||
String createTestName(int id) => "test${createTypeName(id)}";
|
||||
|
||||
String createTypeCode(FunctionType type) {
|
||||
StringBuffer typeBuffer = new StringBuffer();
|
||||
type.writeType(typeBuffer);
|
||||
return typeBuffer.toString();
|
||||
}
|
||||
String createStaticFunCode(FunctionType type, int id) {
|
||||
StringBuffer staticFunBuffer = new StringBuffer();
|
||||
type.writeFunction(staticFunBuffer, createStaticFunName(id));
|
||||
return staticFunBuffer.toString();
|
||||
}
|
||||
|
||||
String createMethodFunCode(FunctionType type, int id) {
|
||||
StringBuffer methodFunBuffer = new StringBuffer();
|
||||
type.writeFunction(methodFunBuffer, createMethodFunName(id), replaceT: false);
|
||||
return methodFunBuffer.toString();
|
||||
}
|
||||
|
||||
String createTestMethodFunCode(FunctionType type, String typeCode, int id) {
|
||||
String fillTemplate(String template, int id) {
|
||||
var result = template
|
||||
.replaceAll("#typeName", createTypeName(id))
|
||||
.replaceAll("#staticFunName", createStaticFunName(id))
|
||||
.replaceAll("#methodFunName", createMethodFunName(id))
|
||||
.replaceAll("#fieldName", createFieldName(id))
|
||||
.replaceAll("#localName", createLocalName(id))
|
||||
.replaceAll("#testName", createTestName(id))
|
||||
.replaceAll("#typeCode", typeCode);
|
||||
assert(!result.contains("#"));
|
||||
return result;
|
||||
}
|
||||
|
||||
String commonTests = fillTemplate(COMMON_TESTS_TEMPLATE, id);
|
||||
String genericTTests = "";
|
||||
if (type.usesT) {
|
||||
genericTTests = fillTemplate(TYPEDEF_T_TESTS_TEMPLATE, id);
|
||||
}
|
||||
return """
|
||||
${fillTemplate(TEST_METHOD_HEADER, id)}
|
||||
$commonTests
|
||||
$genericTTests
|
||||
$TEST_METHOD_FOOTER
|
||||
""";
|
||||
}
|
||||
|
||||
void generateTests() {
|
||||
// Keep methods and classes smaller by distributing over several different
|
||||
// classes.
|
||||
List<Unit> units = [];
|
||||
for (int i = 0; i < 100; i++) {
|
||||
units.add(new Unit("U$i"));
|
||||
}
|
||||
|
||||
var types = buildFunctionTypes();
|
||||
|
||||
int typeCounter = 0;
|
||||
types.forEach((FunctionType type) {
|
||||
Unit unit = units[typeCounter % units.length];
|
||||
|
||||
String typeName = createTypeName(typeCounter);
|
||||
String fieldName = createFieldName(typeCounter);
|
||||
String testName = createTestName(typeCounter);
|
||||
|
||||
String typeCode = createTypeCode(type);
|
||||
String staticFunCode = createStaticFunCode(type, typeCounter);
|
||||
String methodFunCode = createMethodFunCode(type, typeCounter);
|
||||
String testMethodCode =
|
||||
createTestMethodFunCode(type, typeCode, typeCounter);
|
||||
|
||||
|
||||
unit.typedefs.writeln("typedef $typeName<T> = $typeCode;");
|
||||
unit.globals.writeln(staticFunCode);
|
||||
unit.fields.writeln(" $typeCode $fieldName;");
|
||||
unit.methods.writeln(" $methodFunCode");
|
||||
unit.testMethods.writeln("$testMethodCode");
|
||||
unit.tests.writeln(" $testName();");
|
||||
|
||||
typeCounter++;
|
||||
});
|
||||
|
||||
for (int i = 0; i < units.length; i++) {
|
||||
var unit = units[i];
|
||||
var buffer = new StringBuffer();
|
||||
unit.write(buffer);
|
||||
var path = Platform.script.resolve("function_type${i}_test.dart").path;
|
||||
new File(path).writeAsStringSync(buffer.toString());
|
||||
}
|
||||
}
|
||||
|
||||
void printUsage() {
|
||||
print("""
|
||||
Generates function type tests.
|
||||
|
||||
All tests are generated in the same directory as this script.
|
||||
""");
|
||||
}
|
||||
|
||||
void main(List<String> arguments) {
|
||||
if (arguments.length != 0) {
|
||||
printUsage();
|
||||
return;
|
||||
}
|
||||
generateTests();
|
||||
}
|
42
tests/language/generic_function_typedef2_test.dart
Normal file
42
tests/language/generic_function_typedef2_test.dart
Normal file
|
@ -0,0 +1,42 @@
|
|||
// Copyright (c) 2012, 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.
|
||||
// Dart test for a function type test that cannot be eliminated at compile time.
|
||||
|
||||
import "package:expect/expect.dart";
|
||||
|
||||
class A {}
|
||||
|
||||
typedef int F();
|
||||
|
||||
typedef G = F; /// 00: compile-time error
|
||||
typedef H = int; /// 01: compile-time error
|
||||
typedef I = A; /// 02: compile-time error
|
||||
typedef J = List<int>; /// 03: compile-time error
|
||||
typedef K = Function(Function<A>(A
|
||||
<int> /// 04: static type warning
|
||||
));
|
||||
typedef L = Function({
|
||||
/* /// 05: compile-time error
|
||||
bool
|
||||
*/ /// 05: compile-time error
|
||||
x});
|
||||
|
||||
typedef M = Function({
|
||||
/* /// 06: compile-time error
|
||||
bool
|
||||
*/ /// 06: compile-time error
|
||||
int});
|
||||
|
||||
foo({bool int}) {}
|
||||
main() {
|
||||
bool b = true;
|
||||
Expect.isFalse(b is G); /// 00: continued
|
||||
Expect.isFalse(b is H); /// 01: continued
|
||||
Expect.isFalse(b is I); /// 02: continued
|
||||
Expect.isFalse(b is J); /// 03: continued
|
||||
Expect.isFalse(b is K); /// 04: continued
|
||||
Expect.isFalse(b is L);
|
||||
Expect.isFalse(b is M);
|
||||
Expect.isTrue(foo is M);
|
||||
}
|
24
tests/language/generic_function_typedef_test.dart
Normal file
24
tests/language/generic_function_typedef_test.dart
Normal file
|
@ -0,0 +1,24 @@
|
|||
// Copyright (c) 2012, 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.
|
||||
// Dart test for a function type test that cannot be eliminated at compile time.
|
||||
|
||||
import "package:expect/expect.dart";
|
||||
|
||||
class A {
|
||||
}
|
||||
|
||||
typedef F<T> = Function<S>(List<S> list, Function<A>(A), T);
|
||||
|
||||
foo(List<dynamic> x, bar(String y), int z) {}
|
||||
foo2(List<int> x, bar(String y), int z) {}
|
||||
|
||||
main() {
|
||||
Expect.isTrue(foo is F);
|
||||
Expect.isTrue(foo is F<int>);
|
||||
Expect.isFalse(foo is F<bool>);
|
||||
|
||||
Expect.isTrue(foo2 is F);
|
||||
Expect.isTrue(foo2 is F<int>);
|
||||
Expect.isFalse(foo2 is F<bool>);
|
||||
}
|
|
@ -66,7 +66,6 @@ vm/debug_break_enabled_vm_test/01: Crash, OK # Expected to hit breakpoint.
|
|||
|
||||
[ ($compiler == none || $compiler == precompiler || $compiler == app_jit) && $checked ]
|
||||
type_variable_bounds4_test/01: Fail # Issue 14006
|
||||
generalized_function_type_test: SkipSlow # Times out in checked mode. Generated test is too large.
|
||||
|
||||
[ ($compiler == none || $compiler == precompiler || $compiler == app_jit) && (($runtime == vm || $runtime == dart_precompiled) || $runtime == drt || $runtime == dartium) ]
|
||||
dynamic_prefix_core_test/none: Fail # Issue 12478
|
||||
|
|
|
@ -50,7 +50,7 @@ const_for_in_variable_test/01: MissingCompileTimeError # Issue 25161
|
|||
|
||||
generic_function_typedef_test: Crash # Issue 27969
|
||||
generic_function_typedef2_test: Crash # Issue 27969
|
||||
generalized_function_type_test: Crash # Issue 27969
|
||||
function_type/function_type*: Crash # Issue 27969
|
||||
|
||||
# Please add new failing tests before this line.
|
||||
# Section below is for invalid tests.
|
||||
|
|
|
@ -37,6 +37,12 @@ accessor_conflict_import_test: RuntimeError # Issue 25626
|
|||
duplicate_part_test/01: MissingCompileTimeError # Issue 27517
|
||||
|
||||
bad_typedef_test/00: Crash # Issue 28214
|
||||
generic_function_typedef2_test/00: Crash # Issue 28214
|
||||
generic_function_typedef2_test/01: Crash # Issue 28214
|
||||
generic_function_typedef2_test/02: Crash # Issue 28214
|
||||
generic_function_typedef2_test/03: Crash # Issue 28214
|
||||
generic_function_typedef2_test/05: Crash # Issue 28214
|
||||
generic_function_typedef2_test/06: Crash # Issue 28214
|
||||
|
||||
covariant_test/02: MissingCompileTimeError, OK # Accepts `covariant` for statics/top-level.
|
||||
covariant_test/08: MissingCompileTimeError, OK # Accepts `covariant` for statics/top-level.
|
||||
|
|
|
@ -45,6 +45,9 @@ vm/debug_break_enabled_vm_test/none: CompileTimeError
|
|||
vm/reflect_core_vm_test: CompileTimeError
|
||||
vm/regress_27201_test: CompileTimeError
|
||||
vm/regress_28325_test: RuntimeError # Issue 28055.
|
||||
generic_function_typedef2_test: DartkCrash # Issue 27969
|
||||
generic_function_typedef_test: DartkCrash # Issue 27969
|
||||
function_type/function_type*: DartkCrash # Issue 27969
|
||||
|
||||
# dartk: JIT failures
|
||||
[ $compiler == dartk && $runtime == vm ]
|
||||
|
|
Loading…
Reference in a new issue