blob: ab903c7ea3866827f0111df750b335ad3512e60d [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.type;
import com.google.common.base.Joiner;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import com.google.common.io.CharStreams;
import com.google.dart.compiler.ErrorCode;
import com.google.dart.compiler.ast.DartClass;
import com.google.dart.compiler.ast.DartExprStmt;
import com.google.dart.compiler.ast.DartExpression;
import com.google.dart.compiler.ast.DartField;
import com.google.dart.compiler.ast.DartFieldDefinition;
import com.google.dart.compiler.ast.DartFunctionExpression;
import com.google.dart.compiler.ast.DartFunctionTypeAlias;
import com.google.dart.compiler.ast.DartNode;
import com.google.dart.compiler.ast.DartStatement;
import com.google.dart.compiler.ast.DartUnit;
import com.google.dart.compiler.ast.LibraryUnit;
import com.google.dart.compiler.parser.DartParser;
import com.google.dart.compiler.resolver.ClassElement;
import com.google.dart.compiler.resolver.ClassNodeElement;
import com.google.dart.compiler.resolver.CoreTypeProvider;
import com.google.dart.compiler.resolver.Element;
import com.google.dart.compiler.resolver.Elements;
import com.google.dart.compiler.resolver.FunctionAliasElement;
import com.google.dart.compiler.resolver.LibraryElement;
import com.google.dart.compiler.resolver.MemberBuilder;
import com.google.dart.compiler.resolver.MockLibraryUnit;
import com.google.dart.compiler.resolver.ResolutionContext;
import com.google.dart.compiler.resolver.Resolver;
import com.google.dart.compiler.resolver.Resolver.ResolveElementsVisitor;
import com.google.dart.compiler.resolver.Scope;
import com.google.dart.compiler.resolver.SupertypeResolver;
import com.google.dart.compiler.resolver.TopLevelElementBuilder;
import com.google.dart.compiler.util.DartSourceString;
import java.io.IOError;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
/**
* Base class for static type analysis tests.
*/
public abstract class TypeAnalyzerTestCase extends TypeTestCase {
private class MockCoreTypeProvider implements CoreTypeProvider {
private final Type voidType = Types.newVoidType();
private final DynamicType dynamicType = Types.newDynamicType();
@Override
public InterfaceType getArrayType(Type elementType) {
return list.getType().subst(Arrays.asList(elementType), list.getTypeParameters());
}
@Override
public InterfaceType getBoolType() {
return bool.getType();
}
@Override
public InterfaceType getDoubleType() {
return doubleElement.getType();
}
@Override
public DynamicType getDynamicType() {
return dynamicType;
}
@Override
public InterfaceType getFunctionType() {
return function.getType();
}
@Override
public InterfaceType getIntType() {
return intElement.getType();
}
@Override
public InterfaceType getIteratorType(Type elementType) {
InterfaceType iteratorType = iterElement.getType();
return iteratorType.subst(Arrays.asList(elementType), iterElement.getTypeParameters());
}
@Override
public InterfaceType getMapType(Type key, Type value) {
InterfaceType mapType = map.getType();
return mapType.subst(Arrays.asList(key, value),
mapType.getElement().getTypeParameters());
}
@Override
public Type getNullType() {
return getDynamicType();
}
@Override
public InterfaceType getNumType() {
return number.getType();
}
@Override
public InterfaceType getObjectType() {
return object.getType();
}
@Override
public InterfaceType getStringType() {
return string.getType();
}
@Override
public Type getVoidType() {
return voidType;
}
@Override
public InterfaceType getTypeType() {
return type.getType();
}
}
private class MockScope extends Scope {
private MockScope() {
super("test mock scope", null);
}
@Override
public Element findLocalElement(String name) {
return coreElements.get(name);
}
}
protected final CoreTypeProvider typeProvider = new MockCoreTypeProvider();
private Resolver resolver = new Resolver(context, getMockScope("<test toplevel>"), typeProvider);
private final Types types = Types.getInstance(typeProvider);
private HashSet<ClassElement> diagnosedAbstractClasses = new HashSet<ClassElement>();
protected DartStatement analyze(String statement) {
DartStatement node = parseStatement(statement);
analyzeNode(node);
return node;
}
protected ClassElement analyzeClass(ClassNodeElement cls, int expectedErrorCount) {
setExpectedTypeErrorCount(expectedErrorCount);
analyzeToplevel(cls.getNode());
checkExpectedTypeErrorCount(cls.getName());
return cls;
}
protected Map<String, ClassNodeElement> analyzeClasses(Map<String, ClassNodeElement> classes,
ErrorCode... codes) {
setExpectedTypeErrorCount(codes.length);
for (ClassNodeElement cls : classes.values()) {
analyzeToplevel(cls.getNode());
}
List<ErrorCode> errorCodes = context.getErrorCodes();
assertEquals(Arrays.toString(codes), errorCodes.toString());
errorCodes.clear();
checkExpectedTypeErrorCount();
return classes;
}
protected void analyzeFail(String statement, ErrorCode errorCode) {
try {
analyze(statement);
fail("Test unexpectedly passed. Expected ErrorCode: " + errorCode);
} catch (TestTypeError error) {
assertEquals(errorCode, error.getErrorCode());
}
}
protected Type analyzeIn(ClassElement element, String expression, int expectedErrorCount) {
DartExpression node = parseExpression(expression);
ResolutionContext resolutionContext =
new ResolutionContext(getMockScope("<test expression>"), context,
typeProvider).extend(element);
ResolveElementsVisitor visitor =
resolver.new ResolveElementsVisitor(resolutionContext, element,
Elements.methodElement(null, null));
setExpectedTypeErrorCount(expectedErrorCount);
node.accept(visitor);
Type type = node.accept(makeTypeAnalyzer(element));
checkExpectedTypeErrorCount(expression);
return type;
}
private Type analyzeNode(DartNode node) {
ResolutionContext resolutionContext =
new ResolutionContext(getMockScope("<test node>"), context, typeProvider);
ResolveElementsVisitor visitor =
resolver.new ResolveElementsVisitor(resolutionContext, null,
Elements.methodElement(null, null));
node.accept(visitor);
return node.accept(makeTypeAnalyzer(Elements.dynamicElement()));
}
private Type analyzeToplevel(DartNode node) {
return node.accept(makeTypeAnalyzer(Elements.dynamicElement()));
}
private String assign(String type, String expression) {
return String.format("() { %s x = %s; }", type, expression);
}
protected Type checkAssignIn(ClassElement element, String type, String expression, int errorCount) {
return analyzeIn(element, assign(type, expression), errorCount);
}
protected void checkFunctionStatement(String statement, String printString) {
DartExprStmt node = (DartExprStmt) analyze(statement);
DartFunctionExpression expression = (DartFunctionExpression) node.getExpression();
Element element = expression.getElement();
FunctionType type = (FunctionType) element.getType();
assertEquals(printString, type.toString());
}
protected void checkSimpleType(Type type, String expression) {
assertSame(type, typeOf(expression));
setExpectedTypeErrorCount(1); // x is unresolved.
assertSame(type, typeOf("x = " + expression));
checkExpectedTypeErrorCount();
}
protected void checkType(Type type, String expression) {
assertEquals(type, typeOf(expression));
assertEquals(type, typeOf("x = " + expression));
}
private Scope getMockScope(String name) {
LibraryUnit libraryUnit = MockLibraryUnit.create();
return new Scope(name, libraryUnit.getElement(), new MockScope());
}
private DartParser getParser(String string) {
DartSourceString source = new DartSourceString("<source string>", string);
return new DartParser(source, string, false, Sets.<String>newHashSet(), listener, null);
}
private String getResource(String name) {
String packageName = getClass().getPackage().getName().replace('.', '/');
String resouceName = packageName + "/" + name;
InputStream stream = getClass().getClassLoader().getResourceAsStream(resouceName);
if (stream == null) {
throw new AssertionError("Missing resource: " + resouceName);
}
InputStreamReader reader = new InputStreamReader(stream);
try {
return CharStreams.toString(reader); // Also closes the reader.
} catch (IOException e) {
throw new IOError(e);
}
}
@Override
Types getTypes() {
return types;
}
protected ClassElement loadClass(String file, String name) {
ClassElement cls = loadFile(file).get(name);
assertNotNull("unable to locate " + name, cls);
return cls;
}
protected Map<String, ClassNodeElement> loadFile(final String name) {
String source = getResource(name);
return loadSource(source);
}
protected Map<String, ClassNodeElement> loadSource(String source) {
Map<String, ClassNodeElement> classes = Maps.newLinkedHashMap();
DartUnit unit = parseUnit(source);
Scope scope = getMockScope("<test toplevel>");
LibraryElement libraryElement = scope.getLibrary();
libraryElement.getScope().declareElement("Object", object);
unit.setLibrary(libraryElement.getLibraryUnit());
TopLevelElementBuilder elementBuilder = new TopLevelElementBuilder();
elementBuilder.exec(unit.getLibrary(), unit, context);
for (DartNode node : unit.getTopLevelNodes()) {
if (node instanceof DartClass) {
DartClass classNode = (DartClass) node;
ClassNodeElement classElement = classNode.getElement();
String className = classElement.getName();
coreElements.put(className, classElement);
classes.put(className, classElement);
} else if (node instanceof DartFieldDefinition) {
DartFieldDefinition fieldNode = (DartFieldDefinition) node;
for (DartField field : fieldNode.getFields()) {
Element fieldElement = field.getElement();
coreElements.put(fieldElement.getName(), fieldElement);
}
} else {
DartFunctionTypeAlias alias = (DartFunctionTypeAlias) node;
FunctionAliasElement element = alias.getElement();
coreElements.put(element.getName(), element);
}
}
SupertypeResolver supertypeResolver = new SupertypeResolver();
supertypeResolver.exec(unit, context, scope, typeProvider);
MemberBuilder memberBuilder = new MemberBuilder();
memberBuilder.exec(unit, context, scope, typeProvider);
resolver.exec(unit);
return classes;
}
protected Map<String, ClassNodeElement> loadSource(String firstLine, String secondLine,
String... rest) {
return loadSource(Joiner.on('\n').join(firstLine, secondLine, (Object[]) rest).toString());
}
private TypeAnalyzer.Analyzer makeTypeAnalyzer(ClassElement element) {
TypeAnalyzer.Analyzer analyzer =
new TypeAnalyzer.Analyzer(context, typeProvider, diagnosedAbstractClasses);
analyzer.setCurrentClass(element.getType());
analyzer.pushBasicBlockContext();
return analyzer;
}
private DartExpression parseExpression(String source) {
return getParser(source).parseExpression();
}
private DartStatement parseStatement(String source) {
return getParser(source).parseStatement();
}
private DartUnit parseUnit(String string) {
// DartSourceString source = new DartSourceString("<source string>", string);
return getParser(string).parseUnit();
}
protected String returnWithType(String type, Object expression) {
return String.format("%s foo() { return %s; }", type, String.valueOf(expression));
}
@Override
protected void tearDown() {
resolver = null;
diagnosedAbstractClasses = null;
}
private Type typeOf(String expression) {
return analyzeNode(parseExpression(expression));
}
}