blob: c2aab5636b881440fd0f0044c2a785b4bc398c67 [file] [log] [blame]
// Copyright (c) 2014, 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.
part of resolution;
/**
* [SignatureResolver] resolves function signatures.
*/
class SignatureResolver extends CommonResolverVisitor<Element> {
final Element enclosingElement;
final MessageKind defaultValuesError;
Link<Element> optionalParameters = const Link<Element>();
int optionalParameterCount = 0;
bool isOptionalParameter = false;
bool optionalParametersAreNamed = false;
VariableDefinitions currentDefinitions;
SignatureResolver(Compiler compiler,
this.enclosingElement,
{this.defaultValuesError})
: super(compiler);
bool get defaultValuesAllowed => defaultValuesError == null;
Element visitNodeList(NodeList node) {
// This must be a list of optional arguments.
String value = node.beginToken.stringValue;
if ((!identical(value, '[')) && (!identical(value, '{'))) {
internalError(node, "expected optional parameters");
}
optionalParametersAreNamed = (identical(value, '{'));
isOptionalParameter = true;
LinkBuilder<Element> elements = analyzeNodes(node.nodes);
optionalParameterCount = elements.length;
optionalParameters = elements.toLink();
return null;
}
Element visitVariableDefinitions(VariableDefinitions node) {
Link<Node> definitions = node.definitions.nodes;
if (definitions.isEmpty) {
cancel(node, 'internal error: no parameter definition');
return null;
}
if (!definitions.tail.isEmpty) {
cancel(definitions.tail.head, 'internal error: extra definition');
return null;
}
Node definition = definitions.head;
if (definition is NodeList) {
cancel(node, 'optional parameters are not implemented');
}
if (node.modifiers.isConst()) {
error(node, MessageKind.FORMAL_DECLARED_CONST);
}
if (node.modifiers.isStatic()) {
error(node, MessageKind.FORMAL_DECLARED_STATIC);
}
if (currentDefinitions != null) {
cancel(node, 'function type parameters not supported');
}
currentDefinitions = node;
Element element = definition.accept(this);
currentDefinitions = null;
return element;
}
void validateName(Identifier node) {
String name = node.source;
if (isOptionalParameter &&
optionalParametersAreNamed &&
isPrivateName(node.source)) {
compiler.reportError(node, MessageKind.PRIVATE_NAMED_PARAMETER);
}
}
Element visitIdentifier(Identifier node) {
validateName(node);
Element variables = new VariableListElementX.node(currentDefinitions,
ElementKind.VARIABLE_LIST, enclosingElement);
// Ensure a parameter is not typed 'void'.
variables.computeType(compiler);
return new VariableElementX(node.source, variables,
ElementKind.PARAMETER, node);
}
String getParameterName(Send node) {
var identifier = node.selector.asIdentifier();
if (identifier != null) {
// Normal parameter: [:Type name:].
validateName(identifier);
return identifier.source;
} else {
// Function type parameter: [:void name(DartType arg):].
var functionExpression = node.selector.asFunctionExpression();
if (functionExpression != null &&
functionExpression.name.asIdentifier() != null) {
validateName(functionExpression.name);
return functionExpression.name.asIdentifier().source;
} else {
cancel(node,
'internal error: unimplemented receiver on parameter send');
}
}
}
// The only valid [Send] can be in constructors and must be of the form
// [:this.x:] (where [:x:] represents an instance field).
FieldParameterElement visitSend(Send node) {
FieldParameterElement element;
if (node.receiver.asIdentifier() == null ||
!node.receiver.asIdentifier().isThis()) {
error(node, MessageKind.INVALID_PARAMETER);
} else if (!identical(enclosingElement.kind,
ElementKind.GENERATIVE_CONSTRUCTOR)) {
error(node, MessageKind.INITIALIZING_FORMAL_NOT_ALLOWED);
} else {
String name = getParameterName(node);
Element fieldElement = currentClass.lookupLocalMember(name);
if (fieldElement == null ||
!identical(fieldElement.kind, ElementKind.FIELD)) {
error(node, MessageKind.NOT_A_FIELD, {'fieldName': name});
} else if (!fieldElement.isInstanceMember()) {
error(node, MessageKind.NOT_INSTANCE_FIELD, {'fieldName': name});
}
Element variables = new VariableListElementX.node(currentDefinitions,
ElementKind.VARIABLE_LIST, enclosingElement);
element = new FieldParameterElementX(name, fieldElement, variables, node);
}
return element;
}
/// A [SendSet] node is an optional parameter with a default value.
Element visitSendSet(SendSet node) {
Element element;
if (node.receiver != null) {
element = visitSend(node);
} else if (node.selector.asIdentifier() != null ||
node.selector.asFunctionExpression() != null) {
Element variables = new VariableListElementX.node(currentDefinitions,
ElementKind.VARIABLE_LIST, enclosingElement);
Identifier identifier = node.selector.asIdentifier() != null ?
node.selector.asIdentifier() :
node.selector.asFunctionExpression().name.asIdentifier();
validateName(identifier);
String source = identifier.source;
element = new VariableElementX(source, variables,
ElementKind.PARAMETER, node);
}
Node defaultValue = node.arguments.head;
if (!defaultValuesAllowed) {
error(defaultValue, defaultValuesError);
}
// Visit the value. The compile time constant handler will
// make sure it's a compile time constant.
resolveExpression(defaultValue);
return element;
}
Element visitFunctionExpression(FunctionExpression node) {
// This is a function typed parameter.
Modifiers modifiers = currentDefinitions.modifiers;
if (modifiers.isFinal()) {
compiler.reportError(modifiers,
MessageKind.FINAL_FUNCTION_TYPE_PARAMETER);
}
if (modifiers.isVar()) {
compiler.reportError(modifiers, MessageKind.VAR_FUNCTION_TYPE_PARAMETER);
}
Element variable = visit(node.name);
SignatureResolver.analyze(compiler, node.parameters, node.returnType,
variable,
defaultValuesError: MessageKind.FUNCTION_TYPE_FORMAL_WITH_DEFAULT);
// TODO(ahe): Resolve and record the function type in the correct
// [TreeElements].
return variable;
}
LinkBuilder<Element> analyzeNodes(Link<Node> link) {
LinkBuilder<Element> elements = new LinkBuilder<Element>();
for (; !link.isEmpty; link = link.tail) {
Element element = link.head.accept(this);
if (element != null) {
elements.addLast(element);
} else {
// If parameter is null, the current node should be the last,
// and a list of optional named parameters.
if (!link.tail.isEmpty || (link.head is !NodeList)) {
internalError(link.head, "expected optional parameters");
}
}
}
return elements;
}
/**
* Resolves formal parameters and return type to a [FunctionSignature].
*/
static FunctionSignature analyze(Compiler compiler,
NodeList formalParameters,
Node returnNode,
Element element,
{MessageKind defaultValuesError}) {
SignatureResolver visitor = new SignatureResolver(compiler, element,
defaultValuesError: defaultValuesError);
Link<Element> parameters = const Link<Element>();
int requiredParameterCount = 0;
if (formalParameters == null) {
if (!element.isGetter()) {
compiler.reportError(element, MessageKind.MISSING_FORMALS);
}
} else {
if (element.isGetter()) {
if (!identical(formalParameters.getEndToken().next.stringValue,
// TODO(ahe): Remove the check for native keyword.
'native')) {
compiler.reportError(formalParameters,
MessageKind.EXTRA_FORMALS);
}
}
LinkBuilder<Element> parametersBuilder =
visitor.analyzeNodes(formalParameters.nodes);
requiredParameterCount = parametersBuilder.length;
parameters = parametersBuilder.toLink();
}
DartType returnType;
if (element.isFactoryConstructor()) {
returnType = element.getEnclosingClass().computeType(compiler);
// Because there is no type annotation for the return type of
// this element, we explicitly add one.
if (compiler.enableTypeAssertions) {
compiler.enqueuer.resolution.registerIsCheck(
returnType, new TreeElementMapping(element));
}
} else {
returnType = compiler.resolveReturnType(element, returnNode);
}
if (element.isSetter() && (requiredParameterCount != 1 ||
visitor.optionalParameterCount != 0)) {
// If there are no formal parameters, we already reported an error above.
if (formalParameters != null) {
compiler.reportError(formalParameters,
MessageKind.ILLEGAL_SETTER_FORMALS);
}
}
return new FunctionSignatureX(parameters,
visitor.optionalParameters,
requiredParameterCount,
visitor.optionalParameterCount,
visitor.optionalParametersAreNamed,
returnType);
}
// TODO(ahe): This is temporary.
void resolveExpression(Node node) {
if (node == null) return;
node.accept(new ResolverVisitor(compiler, enclosingElement,
new TreeElementMapping(enclosingElement)));
}
// TODO(ahe): This is temporary.
ClassElement get currentClass {
return enclosingElement.isMember()
? enclosingElement.getEnclosingClass() : null;
}
}