blob: 8b6f83ca711ed6fd171e9f2cf8e172e773697b87 [file] [log] [blame]
// 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.
package com.google.dart.compiler.resolver;
import com.google.common.annotations.VisibleForTesting;
import com.google.dart.compiler.DartCompilationError;
import com.google.dart.compiler.DartCompilerContext;
import com.google.dart.compiler.ErrorCode;
import com.google.dart.compiler.ErrorSeverity;
import com.google.dart.compiler.PackageLibraryManager;
import com.google.dart.compiler.Source;
import com.google.dart.compiler.SubSystem;
import com.google.dart.compiler.ast.ASTVisitor;
import com.google.dart.compiler.ast.DartFunctionExpression;
import com.google.dart.compiler.ast.DartFunctionTypeAlias;
import com.google.dart.compiler.ast.DartIdentifier;
import com.google.dart.compiler.ast.DartNode;
import com.google.dart.compiler.ast.DartPropertyAccess;
import com.google.dart.compiler.ast.DartSyntheticErrorIdentifier;
import com.google.dart.compiler.ast.DartTypeNode;
import com.google.dart.compiler.ast.LibraryUnit;
import com.google.dart.compiler.ast.Modifiers;
import com.google.dart.compiler.common.HasSourceInfo;
import com.google.dart.compiler.common.SourceInfo;
import com.google.dart.compiler.parser.DartParser;
import com.google.dart.compiler.type.InterfaceType;
import com.google.dart.compiler.type.Type;
import com.google.dart.compiler.type.TypeKind;
import com.google.dart.compiler.type.TypeVariable;
import com.google.dart.compiler.type.Types;
import java.util.Arrays;
import java.util.List;
/**
* Resolution context for resolution of Dart programs. The initial context is
* derived from the library scope, which is then extended with class scope,
* method scope, and block scope as the program is traversed.
*/
@VisibleForTesting
public class ResolutionContext implements ResolutionErrorListener {
private Scope scope;
private final DartCompilerContext context;
private final CoreTypeProvider typeProvider;
private final boolean suppressSdkWarnings;
ResolutionContext(String name, LibraryElement library, DartCompilerContext context,
CoreTypeProvider typeProvider) {
this(new Scope(name, library), context, typeProvider);
}
@VisibleForTesting
public ResolutionContext(Scope scope, DartCompilerContext context,
CoreTypeProvider typeProvider) {
this.scope = scope;
this.context = context;
this.typeProvider = typeProvider;
this.suppressSdkWarnings = context.getCompilerConfiguration().getCompilerOptions()
.suppressSdkWarnings();
}
ResolutionContext(LibraryUnit unit, DartCompilerContext context, CoreTypeProvider typeProvider) {
this(unit.getElement().getScope(), context, typeProvider);
}
@VisibleForTesting
public ResolutionContext extend(ClassElement element) {
return new ResolutionContext(new ClassScope(element, scope), context, typeProvider);
}
ResolutionContext extend(String name) {
return new ResolutionContext(new Scope(name, scope.getLibrary(), scope), context, typeProvider);
}
Scope getScope() {
return scope;
}
void declare(Element element, ErrorCode errorCode) {
String name = element.getName();
Element existingLocalElement = scope.findLocalElement(name);
// Check for duplicate declaration in the enclosing scope.
if (existingLocalElement == null) {
Element existingElement = scope.findElement(scope.getLibrary(), name);
if (existingElement != null) {
SourceInfo nameSourceInfo = element.getNameLocation();
if (existingElement.getKind() == ElementKind.LIBRARY_PREFIX) {
onError(nameSourceInfo, ResolverErrorCode.CANNOT_HIDE_IMPORT_PREFIX, name);
}
}
}
// Check for duplicate declaration in the same scope.
if (existingLocalElement != null && errorCode != null) {
SourceInfo nameSourceInfo = element.getNameLocation();
String existingLocation = Elements.getRelativeElementLocation(element, existingLocalElement);
onError(nameSourceInfo, errorCode, name, existingLocation);
}
// Declare, may be hide existing element.
scope.declareElement(name, element);
}
void pushScope(String name) {
scope = new Scope(name, scope.getLibrary(), scope);
}
void popScope() {
scope = scope.getParent();
}
/**
* Returns <code>true</code> if the type is dynamic or an interface type where
* {@link ClassElement#isInterface()} equals <code>isInterface</code>.
*/
private boolean isInterfaceEquals(Type type, boolean isInterface) {
switch (type.getKind()) {
case DYNAMIC:
// Considered to be a match.
return true;
case INTERFACE:
InterfaceType interfaceType = (InterfaceType) type;
ClassElement element = interfaceType.getElement();
return (element != null && element.isInterface() == isInterface);
default:
break;
}
return false;
}
/**
* Returns <code>true</code> if the type is dynamic or is a class type.
*/
private boolean isClassType(Type type) {
return isInterfaceEquals(type, false);
}
/**
* Returns <code>true</code> if the type is a class or interface type.
*/
private boolean isClassOrInterfaceType(Type type) {
return type.getKind() == TypeKind.INTERFACE
&& ((InterfaceType) type).getElement() != null;
}
/**
* To resolve the class<typeparameters?> specified for extends on a class definition.
*/
InterfaceType resolveClass(DartTypeNode node, boolean isStatic, boolean isFactory) {
if (node == null) {
return null;
}
Type type = resolveType(node, isStatic, isFactory, false, ResolverErrorCode.NO_SUCH_TYPE,
ResolverErrorCode.WRONG_NUMBER_OF_TYPE_ARGUMENTS);
if (!isClassType(type)) {
onError(node.getIdentifier(), ResolverErrorCode.NOT_A_CLASS, type);
type = typeProvider.getDynamicType();
}
node.setType(type);
return (InterfaceType) type;
}
InterfaceType resolveInterface(DartTypeNode node, boolean isStatic, boolean isFactory) {
Type type = resolveType(node, isStatic, isFactory, false,
ResolverErrorCode.NO_SUCH_TYPE, ResolverErrorCode.WRONG_NUMBER_OF_TYPE_ARGUMENTS);
if (type.getKind() != TypeKind.DYNAMIC && !isClassOrInterfaceType(type)) {
onError(node.getIdentifier(), ResolverErrorCode.NOT_A_CLASS_OR_INTERFACE, type);
type = typeProvider.getDynamicType();
}
node.setType(type);
return (InterfaceType) type;
}
Type resolveType(DartTypeNode node, boolean isStatic, boolean isFactory, boolean isAnnotation,
ErrorCode errorCode, ErrorCode wrongNumberErrorCode) {
if (node == null) {
return null;
} else {
Type type = resolveType(node, node.getIdentifier(), node.getTypeArguments(), isStatic,
isFactory, isAnnotation, errorCode, wrongNumberErrorCode);
recordTypeIdentifier(node.getIdentifier(), type.getElement());
return type;
}
}
protected <E extends Element> E recordTypeIdentifier(DartNode node, E element) {
node.getClass();
if (node instanceof DartPropertyAccess) {
recordTypeIdentifier(((DartPropertyAccess)node).getQualifier(),
element.getEnclosingElement());
return recordTypeIdentifier(((DartPropertyAccess)node).getName(), element);
} else if (node instanceof DartIdentifier) {
if (element == null) {
// TypeAnalyzer will diagnose unresolved identifiers.
return null;
}
node.setElement(element);
} else {
throw internalError(node, "Unexpected node: %s", node);
}
return element;
}
Type resolveType(DartNode diagnosticNode, DartNode identifier, List<DartTypeNode> typeArguments,
boolean isStatic, boolean isFactory, boolean isAnnotation, ErrorCode errorCode,
ErrorCode wrongNumberErrorCode) {
// Built-in identifier can not be used as a type annotation.
if (identifier instanceof DartIdentifier) {
String name = ((DartIdentifier) identifier).getName();
if (DartParser.PSEUDO_KEYWORDS_SET.contains(name) && !"dynamic".equals(name)) {
onError(identifier, ResolverErrorCode.BUILT_IN_IDENTIFIER_AS_TYPE, name);
return Types.newDynamicType();
}
}
// OK, valid name for type.
Element element = resolveName(identifier);
ElementKind elementKind = ElementKind.of(element);
switch (elementKind) {
case TYPE_VARIABLE: {
TypeVariableElement typeVariableElement = (TypeVariableElement) element;
if (!isFactory && isStatic &&
typeVariableElement.getDeclaringElement().getKind().equals(ElementKind.CLASS)) {
onError(identifier, ResolverErrorCode.TYPE_VARIABLE_IN_STATIC_CONTEXT, identifier);
return typeProvider.getDynamicType();
}
return makeTypeVariable(typeVariableElement, typeArguments);
}
case CLASS:
case FUNCTION_TYPE_ALIAS:
return instantiateParameterizedType(
(ClassElement) element,
diagnosticNode,
typeArguments,
isStatic,
isFactory,
errorCode,
wrongNumberErrorCode);
case DUPLICATE: {
DuplicateElement duplicateElement = (DuplicateElement) element;
List<String> locations = duplicateElement.getLocations();
ResolverErrorCode duplicateErrorCode;
if (isAnnotation) {
duplicateErrorCode = ResolverErrorCode.DUPLICATE_IMPORTED_NAME_TYPE;
} else {
duplicateErrorCode = ResolverErrorCode.DUPLICATE_IMPORTED_NAME;
}
onError(identifier, duplicateErrorCode, element.getName(), locations.size(), locations);
return typeProvider.getDynamicType();
}
case NONE:
if (Elements.isIdentifierName(identifier, "void")) {
return typeProvider.getVoidType();
}
if (Elements.isIdentifierName(identifier, "dynamic")) {
return typeProvider.getDynamicType();
}
onError(identifier, errorCode, identifier);
return typeProvider.getDynamicType();
default:
if (!(identifier instanceof DartSyntheticErrorIdentifier)) {
if (errorCode.getSubSystem().equals(SubSystem.RESOLVER)) {
onError(identifier, ResolverErrorCode.NOT_A_TYPE, identifier, elementKind);
} else {
onError(identifier, TypeErrorCode.NOT_A_TYPE, identifier, elementKind);
}
}
return typeProvider.getDynamicType();
}
}
InterfaceType instantiateParameterizedType(ClassElement element, DartNode node,
List<DartTypeNode> typeArgumentNodes,
boolean isStatic,
boolean isFactory,
ErrorCode errorCode,
ErrorCode wrongNumberErrorCode) {
List<Type> typeParameters = element.getTypeParameters();
Type[] typeArguments;
if (typeArgumentNodes == null || typeArgumentNodes.size() != typeParameters.size()) {
typeArguments = new Type[typeParameters.size()];
for (int i = 0; i < typeArguments.length; i++) {
typeArguments[i] = typeProvider.getDynamicType();
}
if (typeArgumentNodes != null && typeArgumentNodes.size() > 0) {
onError(node, wrongNumberErrorCode, element.getType(), typeArgumentNodes.size(), typeParameters.size());
}
int index = 0;
if (typeArgumentNodes != null) {
for (DartTypeNode typeNode : typeArgumentNodes) {
Type type = resolveType(typeNode, isStatic, isFactory, false, errorCode,
wrongNumberErrorCode);
typeNode.setType(type);
if (index < typeArguments.length) {
typeArguments[index] = type;
}
index++;
}
}
} else {
typeArguments = new Type[typeArgumentNodes.size()];
for (int i = 0; i < typeArguments.length; i++) {
typeArguments[i] = resolveType(typeArgumentNodes.get(i), isStatic, isFactory, false, errorCode,
wrongNumberErrorCode);
typeArgumentNodes.get(i).setType(typeArguments[i]);
}
}
return element.getType().subst(Arrays.asList(typeArguments), typeParameters);
}
private TypeVariable makeTypeVariable(TypeVariableElement element,
List<DartTypeNode> typeArguments) {
for (DartTypeNode typeArgument : typeArguments) {
onError(typeArgument, ResolverErrorCode.EXTRA_TYPE_ARGUMENT);
}
return element.getTypeVariable();
}
/*
* Interpret this node as a name reference,
*/
Element resolveName(DartNode node) {
return node.accept(new Selector());
}
MethodElement declareFunction(DartFunctionExpression node) {
MethodElement element = Elements.methodFromFunctionExpression(node, Modifiers.NONE);
if (node.getFunctionName() != null) {
declare(
element,
ResolverErrorCode.DUPLICATE_FUNCTION_EXPRESSION);
}
return element;
}
void pushFunctionScope(DartFunctionExpression x) {
pushScope(x.getFunctionName() == null ? "<function>" : x.getFunctionName());
}
void pushFunctionAliasScope(DartFunctionTypeAlias x) {
pushScope(x.getName().getName() == null ? "<function>" : x.getName().getName());
}
AssertionError internalError(HasSourceInfo node, String message, Object... arguments) {
message = String.format(message, arguments);
context.onError(new DartCompilationError(node, ResolverErrorCode.INTERNAL_ERROR,
message));
return new AssertionError("Internal error: " + message);
}
@Override
public void onError(HasSourceInfo hasSourceInfo, ErrorCode errorCode, Object... arguments) {
onError(hasSourceInfo.getSourceInfo(), errorCode, arguments);
}
public void onError(SourceInfo sourceInfo, ErrorCode errorCode, Object... arguments) {
if (suppressSdkWarnings && errorCode.getErrorSeverity() == ErrorSeverity.WARNING) {
Source source = sourceInfo.getSource();
if (source != null && PackageLibraryManager.isDartUri(source.getUri())) {
return;
}
}
context.onError(new DartCompilationError(sourceInfo, errorCode, arguments));
}
class Selector extends ASTVisitor<Element> {
@Override
public Element visitNode(DartNode node) {
throw internalError(node, "Unexpected node: %s", node);
}
@Override
public Element visitPropertyAccess(DartPropertyAccess node) {
Element element = node.getQualifier().accept(this);
if (element != null) {
switch (element.getKind()) {
case LIBRARY_PREFIX :
Scope elementScope = ((LibraryPrefixElement) element).getScope();
return elementScope.findElement(scope.getLibrary(), node.getPropertyName());
case CLASS :
return Elements.findElement((ClassElement) element, node.getPropertyName());
}
}
return null;
}
@Override
public Element visitIdentifier(DartIdentifier node) {
String name = node.getName();
return scope.findElement(scope.getLibrary(), name);
}
@Override
public Element visitSyntheticErrorIdentifier(DartSyntheticErrorIdentifier node) {
return Elements.dynamicElement();
}
}
}