blob: b60d8b454ec4a7ef7c6089fcd62b5208fe5bf772 [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.collect.Lists;
import com.google.common.io.CharStreams;
import com.google.dart.compiler.CompilerTestCase;
import com.google.dart.compiler.Source;
import com.google.dart.compiler.ast.ASTVisitor;
import com.google.dart.compiler.ast.DartClass;
import com.google.dart.compiler.ast.DartDeclaration;
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.DartIdentifier;
import com.google.dart.compiler.ast.DartLabel;
import com.google.dart.compiler.ast.DartMethodDefinition;
import com.google.dart.compiler.ast.DartNewExpression;
import com.google.dart.compiler.ast.DartNode;
import com.google.dart.compiler.ast.DartParameter;
import com.google.dart.compiler.ast.DartStatement;
import com.google.dart.compiler.ast.DartTypeNode;
import com.google.dart.compiler.ast.DartTypeParameter;
import com.google.dart.compiler.ast.DartUnit;
import com.google.dart.compiler.ast.DartVariableStatement;
import com.google.dart.compiler.common.SourceInfo;
import com.google.dart.compiler.type.FunctionAliasType;
import com.google.dart.compiler.type.Type;
import com.google.dart.compiler.type.TypeVariable;
import static com.google.dart.compiler.common.ErrorExpectation.assertErrors;
import static com.google.dart.compiler.common.ErrorExpectation.errEx;
import java.io.Reader;
import java.util.LinkedList;
import java.util.List;
/**
* Variant of {@link ResolverTest}, which is based on {@link CompilerTestCase}. It is probably
* slower, not actually unit test, but easier to use if you need access to DartNode's.
*/
public class ResolverCompilerTest extends CompilerTestCase {
public void test_parameters_withFunctionAlias() throws Exception {
AnalyzeLibraryResult libraryResult = analyzeLibrary(
"typedef List<T> TypeAlias<T, U extends List<T>>(List<T> arg, U u);");
assertErrors(libraryResult.getCompilationErrors());
DartUnit unit = libraryResult.getLibraryUnitResult().getUnits().iterator().next();
DartFunctionTypeAlias typeAlias = findTypedef(unit, "TypeAlias");
assertNotNull(typeAlias);
FunctionAliasElement element = typeAlias.getElement();
FunctionAliasType ftype = element.getType();
Type returnType = ftype.getElement().getFunctionType().getReturnType();
assertEquals("List<TypeAlias.T>", returnType.toString());
List<? extends Type> arguments = ftype.getArguments();
assertEquals(2, arguments.size());
TypeVariable arg0 = (TypeVariable) arguments.get(0);
assertEquals("T", arg0.getTypeVariableElement().getName());
Type bound0 = arg0.getTypeVariableElement().getBound();
assertEquals("Object", bound0.toString());
TypeVariable arg1 = (TypeVariable) arguments.get(1);
assertEquals("U", arg1.getTypeVariableElement().getName());
Type bound1 = arg1.getTypeVariableElement().getBound();
assertEquals("List<TypeAlias.T>", bound1.toString());
}
/**
* This test succeeds if no exceptions are thrown.
*/
public void test_recursiveTypes() throws Exception {
analyzeLibrary(
"class A extends A implements A {}",
"class B extends C {}",
"class C extends B {}");
}
/**
* This test checks the class declarations to make sure that elements are set for all identifiers.
* This is useful to the editor and other consumers of the AST.
*/
public void test_resolution_on_class_decls() throws Exception {
AnalyzeLibraryResult libraryResult = analyzeLibrary(
"class A {}",
"abstract class B<T> {}",
"class C<T> extends A implements B<T> {}",
"class D extends C<int> {}",
"class E implements C<int> {}",
"class F<T extends A> {}",
"class G extends F<C<int>> {}");
assertErrors(libraryResult.getCompilationErrors());
DartUnit unit = libraryResult.getLibraryUnitResult().getUnits().iterator().next();
List<DartNode> nodes = unit.getTopLevelNodes();
DartClass A = (DartClass) nodes.get(0);
assertEquals("A", A.getClassName());
DartClass B = (DartClass) nodes.get(1);
assertEquals("B", B.getClassName());
DartClass C = (DartClass) nodes.get(2);
assertEquals("C", C.getClassName());
DartClass D = (DartClass) nodes.get(3);
assertEquals("D", D.getClassName());
DartClass E = (DartClass) nodes.get(4);
assertEquals("E", E.getClassName());
DartClass F = (DartClass) nodes.get(5);
assertEquals("F", F.getClassName());
DartClass G = (DartClass) nodes.get(6);
assertEquals("G", G.getClassName());
// class A
assertNotNull(A.getName().getElement());
assertSame(A.getElement(), A.getName().getElement());
// interface B<T> default C
assertNotNull(B.getName().getElement());
assertSame(B.getName().getElement(), B.getElement());
assertEquals(1, B.getTypeParameters().size());
DartTypeParameter T;
T = B.getTypeParameters().get(0);
assertNotNull(T);
assertNotNull(T.getName().getElement());
assertTrue(T.getName().getElement() instanceof TypeVariableElement);
assertEquals("T", T.getName().getName());
// class C<T> extends A implements B<T> {}
assertNotNull(C.getName().getElement());
assertSame(C.getElement(), C.getName().getElement());
assertEquals(1, C.getTypeParameters().size());
T = C.getTypeParameters().get(0);
assertNotNull(T);
assertNotNull(T.getName().getElement());
assertTrue(T.getName().getElement() instanceof TypeVariableElement);
assertEquals("T", T.getName().getName());
assertSame(A.getElement(), C.getSuperclass().getIdentifier().getElement());
assertEquals(1, C.getInterfaces().size());
DartTypeNode iface = C.getInterfaces().get(0);
assertNotNull(iface);
assertSame(B.getElement(), iface.getIdentifier().getElement());
assertSame(
T.getName().getElement(),
iface.getTypeArguments().get(0).getIdentifier().getElement());
// class D extends C<int> {}
assertNotNull(D.getName().getElement());
assertSame(D.getElement(), D.getName().getElement());
assertEquals(0, D.getTypeParameters().size());
assertSame(C.getElement(), D.getSuperclass().getIdentifier().getElement());
DartTypeNode typeArg;
typeArg = D.getSuperclass().getTypeArguments().get(0);
assertNotNull(typeArg.getIdentifier());
assertEquals("int", typeArg.getIdentifier().getElement().getOriginalName());
// class E implements C<int> {}
assertNotNull(E.getName().getElement());
assertSame(E.getElement(), E.getName().getElement());
assertEquals(0, E.getTypeParameters().size());
assertSame(C.getElement(), E.getInterfaces().get(0).getIdentifier().getElement());
typeArg = E.getInterfaces().get(0).getTypeArguments().get(0);
assertNotNull(typeArg.getIdentifier());
assertEquals("int", typeArg.getIdentifier().getElement().getOriginalName());
// class F<T extends A> {}",
assertNotNull(F.getName().getElement());
assertSame(F.getElement(), F.getName().getElement());
assertEquals(1, F.getTypeParameters().size());
T = F.getTypeParameters().get(0);
assertNotNull(T);
assertNotNull(T.getName().getElement());
assertTrue(T.getName().getElement() instanceof TypeVariableElement);
assertEquals("T", T.getName().getName());
assertSame(A.getElement(), T.getBound().getIdentifier().getElement());
// class G extends F<C<int>> {}
assertNotNull(G.getName().getElement());
assertSame(G.getElement(), G.getName().getElement());
assertEquals(0, G.getTypeParameters().size());
assertNotNull(G.getSuperclass());
assertSame(F.getElement(), G.getSuperclass().getIdentifier().getElement());
typeArg = G.getSuperclass().getTypeArguments().get(0);
assertSame(C.getElement(), typeArg.getIdentifier().getElement());
assertEquals(
"int",
typeArg.getTypeArguments().get(0).getIdentifier().getElement().getOriginalName());
}
/**
* We should be able to resolve implicit default constructor.
*/
public void test_resolveConstructor_implicit() throws Exception {
AnalyzeLibraryResult libraryResult = analyzeLibrary(
"class F {",
"}",
"class Test {",
" foo() {",
" new F();",
" }",
"}");
assertErrors(libraryResult.getCompilationErrors());
DartUnit unit = libraryResult.getLibraryUnitResult().getUnits().iterator().next();
DartNewExpression newExpression = findNodeBySource(unit, "new F()");
ConstructorElement constructorElement = newExpression.getElement();
assertNotNull(constructorElement);
assertEquals("", getElementSource(constructorElement));
}
public void test_resolveConstructor_noSuchConstructor() throws Exception {
AnalyzeLibraryResult libraryResult = analyzeLibrary(
"class A {",
"}",
"class Test {",
" foo() {",
" new A.foo();",
" }",
"}");
assertErrors(
libraryResult.getErrors(),
errEx(TypeErrorCode.NEW_EXPRESSION_NOT_CONSTRUCTOR, 5, 11, 3));
DartUnit unit = libraryResult.getLibraryUnitResult().getUnits().iterator().next();
DartNewExpression newExpression = findNodeBySource(unit, "new A.foo()");
ConstructorElement constructorElement = newExpression.getElement();
assertNull(constructorElement);
}
public void test_resolveConstructor_super_implicitDefault() throws Exception {
AnalyzeLibraryResult libraryResult = analyzeLibrary(
"// filler filler filler filler filler filler filler filler filler filler",
"class A {",
"}",
"class B extends A {",
" B() : super() {}",
"}",
"");
assertErrors(libraryResult.getErrors());
}
public void test_superMethodInvocation_inConstructorInitializer() throws Exception {
AnalyzeLibraryResult libraryResult = analyzeLibrary(
"// filler filler filler filler filler filler filler filler filler filler",
"class A {",
" foo() {}",
"}",
"class B extends A {",
" var x;",
" B() : x = super.foo() {}",
"}",
"");
assertErrors(
libraryResult.getErrors(),
errEx(ResolverErrorCode.SUPER_METHOD_INVOCATION_IN_CONSTRUCTOR_INITIALIZER, 7, 13, 11));
}
private static String getElementSource(Element element) throws Exception {
SourceInfo sourceInfo = element.getSourceInfo();
// TODO(scheglov) When we will remove Source.getNode(), this null check may be removed
Source source = sourceInfo.getSource();
if (source == null) {
return "";
}
Reader reader = sourceInfo.getSource().getSourceReader();
try {
String code = CharStreams.toString(reader);
int offset = sourceInfo.getOffset();
return code.substring(offset, offset + sourceInfo.getLength());
} finally {
reader.close();
}
}
/**
* Each name in {@link DartNode}, such as all names in {@link DartDeclaration}s should have same
* {@link Element} as the {@link Element} of enclosing {@link DartNode}.
*/
public void test_setElement_forName_inDeclarations() throws Exception {
AnalyzeLibraryResult libraryResult = analyzeLibrary(
"// filler filler filler filler filler filler filler filler filler filler",
"class A<B extends A> {",
" var a1;",
" get a2 {}",
" A() {}",
"}",
"var c;",
"d(e) {",
" var f;",
" g() {};",
" () {} ();",
" h: d(0);",
"}",
"typedef i();",
"");
assertErrors(libraryResult.getErrors());
DartUnit unit = libraryResult.getLibraryUnitResult().getUnits().iterator().next();
// in class A
{
DartClass classA = (DartClass) unit.getTopLevelNodes().get(0);
assertDeclarationNameElement(classA, "A");
{
DartTypeParameter typeParameter = classA.getTypeParameters().get(0);
assertDeclarationNameElement(typeParameter, "B");
}
{
DartFieldDefinition fieldDef = (DartFieldDefinition) classA.getMembers().get(0);
assertDeclarationNameElement(fieldDef.getFields().get(0), "a1");
}
{
DartFieldDefinition fieldDef = (DartFieldDefinition) classA.getMembers().get(1);
// since this is a getter, its actually a method element different from the original.
DartField f = fieldDef.getFields().get(0);
assertNotNull(f);
Element e = f.getElement();
assertNotNull(e);
assertTrue(f.getName().getName().equals("a2"));
assertTrue(e.getName().equals("a2"));
}
{
DartMethodDefinition constructor = (DartMethodDefinition) classA.getMembers().get(2);
assertDeclarationNameElement(constructor, "");
}
}
// top level "c"
{
DartFieldDefinition fieldDef = (DartFieldDefinition) unit.getTopLevelNodes().get(1);
assertDeclarationNameElement(fieldDef.getFields().get(0), "c");
}
// top level "d"
{
DartMethodDefinition method = (DartMethodDefinition) unit.getTopLevelNodes().get(2);
assertDeclarationNameElement(method, "d");
{
DartParameter parameter = method.getFunction().getParameters().get(0);
assertDeclarationNameElement(parameter, "e");
}
{
List<DartStatement> statements = method.getFunction().getBody().getStatements();
{
DartVariableStatement variableStatement = (DartVariableStatement) statements.get(0);
assertDeclarationNameElement(variableStatement.getVariables().get(0), "f");
}
{
DartExprStmt statement = (DartExprStmt) statements.get(1);
DartFunctionExpression functionExpression = (DartFunctionExpression) statement.getExpression();
assertNameHasSameElement(functionExpression, functionExpression.getName(), "g");
}
{
DartLabel label = (DartLabel) statements.get(4);
assertNameHasSameElement(label, label.getLabel(), "h");
}
}
}
// top level "i"
{
DartFunctionTypeAlias functionType = (DartFunctionTypeAlias) unit.getTopLevelNodes().get(3);
assertDeclarationNameElement(functionType, "i");
}
// assert that all DartIdentifiers are visited
final LinkedList<String> visitedIdentifiers = Lists.newLinkedList();
unit.accept(new ASTVisitor<Void>() {
@Override
public Void visitIdentifier(DartIdentifier node) {
visitedIdentifiers.addLast(node.getName());
return null;
}
});
assertEquals("A", visitedIdentifiers.removeFirst());
assertEquals("B", visitedIdentifiers.removeFirst());
assertEquals("A", visitedIdentifiers.removeFirst());
assertEquals("a1", visitedIdentifiers.removeFirst());
assertEquals("a2", visitedIdentifiers.removeFirst());
assertEquals("a2", visitedIdentifiers.removeFirst());
assertEquals("A", visitedIdentifiers.removeFirst());
assertEquals("c", visitedIdentifiers.removeFirst());
assertEquals("d", visitedIdentifiers.removeFirst());
assertEquals("e", visitedIdentifiers.removeFirst());
assertEquals("f", visitedIdentifiers.removeFirst());
assertEquals("g", visitedIdentifiers.removeFirst());
assertEquals("h", visitedIdentifiers.removeFirst());
assertEquals("d", visitedIdentifiers.removeFirst());
assertEquals("i", visitedIdentifiers.removeFirst());
}
/**
* Asserts that given nodes have same not <code>null</code> {@link Element}.
*/
private static void assertDeclarationNameElement(
DartDeclaration<? extends DartExpression> declaration,
String name) {
assertNameHasSameElement(declaration, declaration.getName(), name);
}
/**
* Asserts that given nodes have same not <code>null</code> {@link Element}.
*/
private static void assertNameHasSameElement(DartNode node, DartExpression nameNode, String name) {
Element expectedElement = node.getElement();
assertNotNull(expectedElement);
assertEquals(name, expectedElement.getName());
assertSame(expectedElement, nameNode.getElement());
}
}