/*
* Copyright (c) 2013, the Dart project authors.
*
* Licensed under the Eclipse Public License v1.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at
*
* http://www.eclipse.org/legal/epl-v10.html
*
* Unless required by applicable law or agreed to in writing, software distributed under the License
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the License for the specific language governing permissions and limitations under
* the License.
*/
package com.google.dart.engine.internal.resolver;
import com.google.dart.engine.EngineTestCase;
import com.google.dart.engine.ast.AssignmentExpression;
import com.google.dart.engine.ast.AstFactory;
import com.google.dart.engine.ast.BinaryExpression;
import com.google.dart.engine.ast.BlockFunctionBody;
import com.google.dart.engine.ast.DoubleLiteral;
import com.google.dart.engine.ast.Expression;
import com.google.dart.engine.ast.ExpressionFunctionBody;
import com.google.dart.engine.ast.FormalParameter;
import com.google.dart.engine.ast.FormalParameterList;
import com.google.dart.engine.ast.FunctionBody;
import com.google.dart.engine.ast.FunctionExpression;
import com.google.dart.engine.ast.IndexExpression;
import com.google.dart.engine.ast.InstanceCreationExpression;
import com.google.dart.engine.ast.IntegerLiteral;
import com.google.dart.engine.ast.PostfixExpression;
import com.google.dart.engine.ast.PrefixExpression;
import com.google.dart.engine.ast.PrefixedIdentifier;
import com.google.dart.engine.ast.PropertyAccess;
import com.google.dart.engine.ast.SimpleIdentifier;
import com.google.dart.engine.ast.SimpleStringLiteral;
import com.google.dart.engine.ast.TypeName;
import com.google.dart.engine.element.ClassElement;
import com.google.dart.engine.element.ConstructorElement;
import com.google.dart.engine.element.Element;
import com.google.dart.engine.element.MethodElement;
import com.google.dart.engine.element.ParameterElement;
import com.google.dart.engine.element.PropertyAccessorElement;
import com.google.dart.engine.error.GatheringErrorListener;
import com.google.dart.engine.internal.builder.ElementBuilder;
import com.google.dart.engine.internal.context.AnalysisContextImpl;
import com.google.dart.engine.internal.element.ClassElementImpl;
import com.google.dart.engine.internal.element.CompilationUnitElementImpl;
import com.google.dart.engine.internal.element.ConstructorElementImpl;
import com.google.dart.engine.internal.element.FieldElementImpl;
import com.google.dart.engine.internal.element.FunctionElementImpl;
import com.google.dart.engine.internal.element.LibraryElementImpl;
import com.google.dart.engine.internal.element.ParameterElementImpl;
import com.google.dart.engine.internal.element.PropertyAccessorElementImpl;
import com.google.dart.engine.internal.element.VariableElementImpl;
import com.google.dart.engine.internal.element.member.MethodMember;
import com.google.dart.engine.internal.type.FunctionTypeImpl;
import com.google.dart.engine.internal.type.InterfaceTypeImpl;
import com.google.dart.engine.scanner.TokenFactory;
import com.google.dart.engine.scanner.TokenType;
import com.google.dart.engine.sdk.DirectoryBasedDartSdk;
import com.google.dart.engine.source.DartUriResolver;
import com.google.dart.engine.source.FileBasedSource;
import com.google.dart.engine.source.SourceFactory;
import com.google.dart.engine.type.FunctionType;
import com.google.dart.engine.type.InterfaceType;
import com.google.dart.engine.type.Type;
import com.google.dart.engine.utilities.io.FileUtilities2;
import static com.google.dart.engine.ast.AstFactory.adjacentStrings;
import static com.google.dart.engine.ast.AstFactory.asExpression;
import static com.google.dart.engine.ast.AstFactory.assignmentExpression;
import static com.google.dart.engine.ast.AstFactory.binaryExpression;
import static com.google.dart.engine.ast.AstFactory.blockFunctionBody;
import static com.google.dart.engine.ast.AstFactory.booleanLiteral;
import static com.google.dart.engine.ast.AstFactory.cascadeExpression;
import static com.google.dart.engine.ast.AstFactory.conditionalExpression;
import static com.google.dart.engine.ast.AstFactory.doubleLiteral;
import static com.google.dart.engine.ast.AstFactory.expressionFunctionBody;
import static com.google.dart.engine.ast.AstFactory.formalParameterList;
import static com.google.dart.engine.ast.AstFactory.functionExpression;
import static com.google.dart.engine.ast.AstFactory.identifier;
import static com.google.dart.engine.ast.AstFactory.indexExpression;
import static com.google.dart.engine.ast.AstFactory.instanceCreationExpression;
import static com.google.dart.engine.ast.AstFactory.integer;
import static com.google.dart.engine.ast.AstFactory.interpolationExpression;
import static com.google.dart.engine.ast.AstFactory.interpolationString;
import static com.google.dart.engine.ast.AstFactory.isExpression;
import static com.google.dart.engine.ast.AstFactory.listLiteral;
import static com.google.dart.engine.ast.AstFactory.mapLiteral;
import static com.google.dart.engine.ast.AstFactory.mapLiteralEntry;
import static com.google.dart.engine.ast.AstFactory.methodInvocation;
import static com.google.dart.engine.ast.AstFactory.namedExpression;
import static com.google.dart.engine.ast.AstFactory.namedFormalParameter;
import static com.google.dart.engine.ast.AstFactory.nullLiteral;
import static com.google.dart.engine.ast.AstFactory.parenthesizedExpression;
import static com.google.dart.engine.ast.AstFactory.positionalFormalParameter;
import static com.google.dart.engine.ast.AstFactory.postfixExpression;
import static com.google.dart.engine.ast.AstFactory.prefixExpression;
import static com.google.dart.engine.ast.AstFactory.propertyAccess;
import static com.google.dart.engine.ast.AstFactory.simpleFormalParameter;
import static com.google.dart.engine.ast.AstFactory.string;
import static com.google.dart.engine.ast.AstFactory.superExpression;
import static com.google.dart.engine.ast.AstFactory.symbolLiteral;
import static com.google.dart.engine.ast.AstFactory.thisExpression;
import static com.google.dart.engine.ast.AstFactory.throwExpression;
import static com.google.dart.engine.ast.AstFactory.typeName;
import static com.google.dart.engine.element.ElementFactory.classElement;
import static com.google.dart.engine.element.ElementFactory.constructorElement;
import static com.google.dart.engine.element.ElementFactory.fieldElement;
import static com.google.dart.engine.element.ElementFactory.getterElement;
import static com.google.dart.engine.element.ElementFactory.localVariableElement;
import static com.google.dart.engine.element.ElementFactory.methodElement;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
public class StaticTypeAnalyzerTest extends EngineTestCase {
/**
* The error listener to which errors will be reported.
*/
private GatheringErrorListener listener;
/**
* The resolver visitor used to create the analyzer.
*/
private ResolverVisitor visitor;
/**
* The analyzer being used to analyze the test cases.
*/
private StaticTypeAnalyzer analyzer;
/**
* The type provider used to access the types.
*/
private TestTypeProvider typeProvider;
public void fail_visitAwaitExpression_flattened() throws Exception {
// TODO(paulberry): Some async/await type checking has not been fully backported from dart. In
// particular, this test can't be implemented until Future is available in the type provider.
// See dartbug.com/22252.
fail("Test not implemented yet");
}
public void fail_visitAwaitExpression_simple() throws Exception {
// TODO(paulberry): Some async/await type checking has not been fully backported from dart. In
// particular, this test can't be implemented until Future is available in the type provider.
// See dartbug.com/22252.
fail("Test not implemented yet");
}
public void fail_visitFunctionExpression_async_expression_flatten() throws Exception {
// TODO(paulberry): Some async/await type checking has not been fully backported from dart. In
// particular, this test can't be implemented until Future is available in the type provider.
// See dartbug.com/22252.
fail("Test not implemented yet");
}
public void fail_visitFunctionExpression_async_expression_flatten_twice() throws Exception {
// TODO(paulberry): Some async/await type checking has not been fully backported from dart. In
// particular, this test can't be implemented until Future is available in the type provider.
// See dartbug.com/22252.
fail("Test not implemented yet");
}
public void fail_visitFunctionExpressionInvocation() throws Exception {
fail("Not yet tested");
listener.assertNoErrors();
}
public void fail_visitMethodInvocation() throws Exception {
fail("Not yet tested");
listener.assertNoErrors();
}
public void fail_visitSimpleIdentifier() throws Exception {
fail("Not yet tested");
listener.assertNoErrors();
}
@Override
public void setUp() {
listener = new GatheringErrorListener();
typeProvider = new TestTypeProvider();
analyzer = createAnalyzer();
}
public void test_visitAdjacentStrings() throws Exception {
// "a" "b"
Expression node = adjacentStrings(resolvedString("a"), resolvedString("b"));
assertSame(typeProvider.getStringType(), analyze(node));
listener.assertNoErrors();
}
public void test_visitAsExpression() throws Exception {
// class A { ... this as B ... }
// class B extends A {}
ClassElement superclass = classElement("A");
InterfaceType superclassType = superclass.getType();
ClassElement subclass = classElement("B", superclassType);
Expression node = asExpression(thisExpression(), typeName(subclass));
assertSame(subclass.getType(), analyze(node, superclassType));
listener.assertNoErrors();
}
public void test_visitAssignmentExpression_compound() throws Exception {
// i += 1
InterfaceType numType = typeProvider.getNumType();
SimpleIdentifier identifier = resolvedVariable(typeProvider.getIntType(), "i");
AssignmentExpression node = assignmentExpression(
identifier,
TokenType.PLUS_EQ,
resolvedInteger(1));
MethodElement plusMethod = getMethod(numType, "+");
node.setStaticElement(plusMethod);
assertSame(numType, analyze(node));
listener.assertNoErrors();
}
public void test_visitAssignmentExpression_simple() throws Exception {
// i = 0
InterfaceType intType = typeProvider.getIntType();
Expression node = assignmentExpression(
resolvedVariable(intType, "i"),
TokenType.EQ,
resolvedInteger(0));
assertSame(intType, analyze(node));
listener.assertNoErrors();
}
public void test_visitBinaryExpression_equals() throws Exception {
// 2 == 3
Expression node = binaryExpression(resolvedInteger(2), TokenType.EQ_EQ, resolvedInteger(3));
assertSame(typeProvider.getBoolType(), analyze(node));
listener.assertNoErrors();
}
public void test_visitBinaryExpression_logicalAnd() throws Exception {
// false && true
Expression node = binaryExpression(
booleanLiteral(false),
TokenType.AMPERSAND_AMPERSAND,
booleanLiteral(true));
assertSame(typeProvider.getBoolType(), analyze(node));
listener.assertNoErrors();
}
public void test_visitBinaryExpression_logicalOr() throws Exception {
// false || true
Expression node = binaryExpression(
booleanLiteral(false),
TokenType.BAR_BAR,
booleanLiteral(true));
assertSame(typeProvider.getBoolType(), analyze(node));
listener.assertNoErrors();
}
public void test_visitBinaryExpression_notEquals() throws Exception {
// 2 != 3
Expression node = binaryExpression(resolvedInteger(2), TokenType.BANG_EQ, resolvedInteger(3));
assertSame(typeProvider.getBoolType(), analyze(node));
listener.assertNoErrors();
}
public void test_visitBinaryExpression_plusID() throws Exception {
// 1 + 2.0
BinaryExpression node = binaryExpression(
resolvedInteger(1),
TokenType.PLUS,
resolvedDouble(2.0));
node.setStaticElement(getMethod(typeProvider.getNumType(), "+"));
assertSame(typeProvider.getDoubleType(), analyze(node));
listener.assertNoErrors();
}
public void test_visitBinaryExpression_plusII() throws Exception {
// 1 + 2
BinaryExpression node = binaryExpression(resolvedInteger(1), TokenType.PLUS, resolvedInteger(2));
node.setStaticElement(getMethod(typeProvider.getNumType(), "+"));
assertSame(typeProvider.getIntType(), analyze(node));
listener.assertNoErrors();
}
public void test_visitBinaryExpression_slash() throws Exception {
// 2 / 2
BinaryExpression node = binaryExpression(
resolvedInteger(2),
TokenType.SLASH,
resolvedInteger(2));
node.setStaticElement(getMethod(typeProvider.getNumType(), "/"));
assertSame(typeProvider.getDoubleType(), analyze(node));
listener.assertNoErrors();
}
public void test_visitBinaryExpression_star_notSpecial() throws Exception {
// class A {
// A operator *(double value);
// }
// (a as A) * 2.0
ClassElementImpl classA = classElement("A");
InterfaceType typeA = classA.getType();
MethodElement operator = methodElement("*", typeA, typeProvider.getDoubleType());
classA.setMethods(new MethodElement[] {operator});
BinaryExpression node = binaryExpression(
asExpression(identifier("a"), typeName(classA)),
TokenType.PLUS,
resolvedDouble(2.0));
node.setStaticElement(operator);
assertSame(typeA, analyze(node));
listener.assertNoErrors();
}
public void test_visitBinaryExpression_starID() throws Exception {
// 1 * 2.0
BinaryExpression node = binaryExpression(
resolvedInteger(1),
TokenType.PLUS,
resolvedDouble(2.0));
node.setStaticElement(getMethod(typeProvider.getNumType(), "*"));
assertSame(typeProvider.getDoubleType(), analyze(node));
listener.assertNoErrors();
}
public void test_visitBooleanLiteral_false() throws Exception {
// false
Expression node = booleanLiteral(false);
assertSame(typeProvider.getBoolType(), analyze(node));
listener.assertNoErrors();
}
public void test_visitBooleanLiteral_true() throws Exception {
// true
Expression node = booleanLiteral(true);
assertSame(typeProvider.getBoolType(), analyze(node));
listener.assertNoErrors();
}
public void test_visitCascadeExpression() throws Exception {
// a..length
Expression node = cascadeExpression(resolvedString("a"), propertyAccess(null, "length"));
assertSame(typeProvider.getStringType(), analyze(node));
listener.assertNoErrors();
}
public void test_visitConditionalExpression_differentTypes() throws Exception {
// true ? 1.0 : 0
Expression node = conditionalExpression(
booleanLiteral(true),
resolvedDouble(1.0),
resolvedInteger(0));
assertSame(typeProvider.getNumType(), analyze(node));
listener.assertNoErrors();
}
public void test_visitConditionalExpression_sameTypes() throws Exception {
// true ? 1 : 0
Expression node = conditionalExpression(
booleanLiteral(true),
resolvedInteger(1),
resolvedInteger(0));
assertSame(typeProvider.getIntType(), analyze(node));
listener.assertNoErrors();
}
public void test_visitDoubleLiteral() throws Exception {
// 4.33
Expression node = doubleLiteral(4.33);
assertSame(typeProvider.getDoubleType(), analyze(node));
listener.assertNoErrors();
}
public void test_visitFunctionExpression_async_block() throws Exception {
// () async {}
BlockFunctionBody body = AstFactory.blockFunctionBody();
body.setKeyword(TokenFactory.tokenFromString("async"));
FunctionExpression node = resolvedFunctionExpression(AstFactory.formalParameterList(), body);
@SuppressWarnings("unused")
Type resultType = analyze(node);
// TODO(paulberry): We should check that resultType is Future<dynamic>. But we can't because
// the Future type isn't available in the type provider.
listener.assertNoErrors();
}
public void test_visitFunctionExpression_async_expression() throws Exception {
// () async => e, where e has type int
InterfaceType intType = typeProvider.getIntType();
Expression expression = resolvedVariable(intType, "e");
ExpressionFunctionBody body = AstFactory.expressionFunctionBody(expression);
body.setKeyword(TokenFactory.tokenFromString("async"));
FunctionExpression node = resolvedFunctionExpression(AstFactory.formalParameterList(), body);
@SuppressWarnings("unused")
Type resultType = analyze(node);
// TODO(paulberry): We should check that resultType is Future<int>. But we can't because the
// Future type isn't available in the type provider.
listener.assertNoErrors();
}
public void test_visitFunctionExpression_generator_async() throws Exception {
// () async* {}
BlockFunctionBody body = AstFactory.blockFunctionBody();
body.setKeyword(TokenFactory.tokenFromString("async"));
body.setStar(TokenFactory.tokenFromType(TokenType.STAR));
FunctionExpression node = resolvedFunctionExpression(AstFactory.formalParameterList(), body);
@SuppressWarnings("unused")
Type resultType = analyze(node);
// TODO(paulberry): we should check that resultType is Stream<dynamic>. But we can't because
// the Stream type isn't available in the type provider.
listener.assertNoErrors();
}
public void test_visitFunctionExpression_generator_sync() throws Exception {
// () sync* {}
BlockFunctionBody body = AstFactory.blockFunctionBody();
body.setKeyword(TokenFactory.tokenFromString("sync"));
body.setStar(TokenFactory.tokenFromType(TokenType.STAR));
FunctionExpression node = resolvedFunctionExpression(AstFactory.formalParameterList(), body);
Type resultType = analyze(node);
assertFunctionType(typeProvider.getIterableDynamicType(), null, null, null, resultType);
listener.assertNoErrors();
}
public void test_visitFunctionExpression_named_block() throws Exception {
// ({p1 : 0, p2 : 0}) {}
Type dynamicType = typeProvider.getDynamicType();
FormalParameter p1 = namedFormalParameter(simpleFormalParameter("p1"), resolvedInteger(0));
setType(p1, dynamicType);
FormalParameter p2 = namedFormalParameter(simpleFormalParameter("p2"), resolvedInteger(0));
setType(p2, dynamicType);
FunctionExpression node = resolvedFunctionExpression(
formalParameterList(p1, p2),
blockFunctionBody());
analyze(p1);
analyze(p2);
Type resultType = analyze(node);
Map<String, Type> expectedNamedTypes = new HashMap<String, Type>();
expectedNamedTypes.put("p1", dynamicType);
expectedNamedTypes.put("p2", dynamicType);
assertFunctionType(dynamicType, null, null, expectedNamedTypes, resultType);
listener.assertNoErrors();
}
public void test_visitFunctionExpression_named_expression() throws Exception {
// ({p : 0}) -> 0;
Type dynamicType = typeProvider.getDynamicType();
FormalParameter p = namedFormalParameter(simpleFormalParameter("p"), resolvedInteger(0));
setType(p, dynamicType);
FunctionExpression node = resolvedFunctionExpression(
formalParameterList(p),
expressionFunctionBody(resolvedInteger(0)));
analyze(p);
Type resultType = analyze(node);
Map<String, Type> expectedNamedTypes = new HashMap<String, Type>();
expectedNamedTypes.put("p", dynamicType);
assertFunctionType(typeProvider.getIntType(), null, null, expectedNamedTypes, resultType);
listener.assertNoErrors();
}
public void test_visitFunctionExpression_normal_block() throws Exception {
// (p1, p2) {}
Type dynamicType = typeProvider.getDynamicType();
FormalParameter p1 = simpleFormalParameter("p1");
setType(p1, dynamicType);
FormalParameter p2 = simpleFormalParameter("p2");
setType(p2, dynamicType);
FunctionExpression node = resolvedFunctionExpression(
formalParameterList(p1, p2),
blockFunctionBody());
analyze(p1);
analyze(p2);
Type resultType = analyze(node);
assertFunctionType(dynamicType, new Type[] {dynamicType, dynamicType}, null, null, resultType);
listener.assertNoErrors();
}
public void test_visitFunctionExpression_normal_expression() throws Exception {
// (p1, p2) -> 0
Type dynamicType = typeProvider.getDynamicType();
FormalParameter p = simpleFormalParameter("p");
setType(p, dynamicType);
FunctionExpression node = resolvedFunctionExpression(
formalParameterList(p),
expressionFunctionBody(resolvedInteger(0)));
analyze(p);
Type resultType = analyze(node);
assertFunctionType(typeProvider.getIntType(), new Type[] {dynamicType}, null, null, resultType);
listener.assertNoErrors();
}
public void test_visitFunctionExpression_normalAndNamed_block() throws Exception {
// (p1, {p2 : 0}) {}
Type dynamicType = typeProvider.getDynamicType();
FormalParameter p1 = simpleFormalParameter("p1");
setType(p1, dynamicType);
FormalParameter p2 = namedFormalParameter(simpleFormalParameter("p2"), resolvedInteger(0));
setType(p2, dynamicType);
FunctionExpression node = resolvedFunctionExpression(
formalParameterList(p1, p2),
blockFunctionBody());
analyze(p2);
Type resultType = analyze(node);
Map<String, Type> expectedNamedTypes = new HashMap<String, Type>();
expectedNamedTypes.put("p2", dynamicType);
assertFunctionType(dynamicType, new Type[] {dynamicType}, null, expectedNamedTypes, resultType);
listener.assertNoErrors();
}
public void test_visitFunctionExpression_normalAndNamed_expression() throws Exception {
// (p1, {p2 : 0}) -> 0
Type dynamicType = typeProvider.getDynamicType();
FormalParameter p1 = simpleFormalParameter("p1");
setType(p1, dynamicType);
FormalParameter p2 = namedFormalParameter(simpleFormalParameter("p2"), resolvedInteger(0));
setType(p2, dynamicType);
FunctionExpression node = resolvedFunctionExpression(
formalParameterList(p1, p2),
expressionFunctionBody(resolvedInteger(0)));
analyze(p2);
Type resultType = analyze(node);
Map<String, Type> expectedNamedTypes = new HashMap<String, Type>();
expectedNamedTypes.put("p2", dynamicType);
assertFunctionType(
typeProvider.getIntType(),
new Type[] {dynamicType},
null,
expectedNamedTypes,
resultType);
listener.assertNoErrors();
}
public void test_visitFunctionExpression_normalAndPositional_block() throws Exception {
// (p1, [p2 = 0]) {}
Type dynamicType = typeProvider.getDynamicType();
FormalParameter p1 = simpleFormalParameter("p1");
setType(p1, dynamicType);
FormalParameter p2 = positionalFormalParameter(simpleFormalParameter("p2"), resolvedInteger(0));
setType(p2, dynamicType);
FunctionExpression node = resolvedFunctionExpression(
formalParameterList(p1, p2),
blockFunctionBody());
analyze(p1);
analyze(p2);
Type resultType = analyze(node);
assertFunctionType(
dynamicType,
new Type[] {dynamicType},
new Type[] {dynamicType},
null,
resultType);
listener.assertNoErrors();
}
public void test_visitFunctionExpression_normalAndPositional_expression() throws Exception {
// (p1, [p2 = 0]) -> 0
Type dynamicType = typeProvider.getDynamicType();
FormalParameter p1 = simpleFormalParameter("p1");
setType(p1, dynamicType);
FormalParameter p2 = positionalFormalParameter(simpleFormalParameter("p2"), resolvedInteger(0));
setType(p2, dynamicType);
FunctionExpression node = resolvedFunctionExpression(
formalParameterList(p1, p2),
expressionFunctionBody(resolvedInteger(0)));
analyze(p1);
analyze(p2);
Type resultType = analyze(node);
assertFunctionType(
typeProvider.getIntType(),
new Type[] {dynamicType},
new Type[] {dynamicType},
null,
resultType);
listener.assertNoErrors();
}
public void test_visitFunctionExpression_positional_block() throws Exception {
// ([p1 = 0, p2 = 0]) {}
Type dynamicType = typeProvider.getDynamicType();
FormalParameter p1 = positionalFormalParameter(simpleFormalParameter("p1"), resolvedInteger(0));
setType(p1, dynamicType);
FormalParameter p2 = positionalFormalParameter(simpleFormalParameter("p2"), resolvedInteger(0));
setType(p2, dynamicType);
FunctionExpression node = resolvedFunctionExpression(
formalParameterList(p1, p2),
blockFunctionBody());
analyze(p1);
analyze(p2);
Type resultType = analyze(node);
assertFunctionType(dynamicType, null, new Type[] {dynamicType, dynamicType}, null, resultType);
listener.assertNoErrors();
}
public void test_visitFunctionExpression_positional_expression() throws Exception {
// ([p1 = 0, p2 = 0]) -> 0
Type dynamicType = typeProvider.getDynamicType();
FormalParameter p = positionalFormalParameter(simpleFormalParameter("p"), resolvedInteger(0));
setType(p, dynamicType);
FunctionExpression node = resolvedFunctionExpression(
formalParameterList(p),
expressionFunctionBody(resolvedInteger(0)));
analyze(p);
Type resultType = analyze(node);
assertFunctionType(typeProvider.getIntType(), null, new Type[] {dynamicType}, null, resultType);
listener.assertNoErrors();
}
public void test_visitIndexExpression_getter() throws Exception {
// List a;
// a[2]
InterfaceType listType = typeProvider.getListType();
SimpleIdentifier identifier = resolvedVariable(listType, "a");
IndexExpression node = indexExpression(identifier, resolvedInteger(2));
MethodElement indexMethod = listType.getElement().getMethods()[0];
node.setStaticElement(indexMethod);
assertSame(listType.getTypeArguments()[0], analyze(node));
listener.assertNoErrors();
}
public void test_visitIndexExpression_setter() throws Exception {
// List a;
// a[2] = 0
InterfaceType listType = typeProvider.getListType();
SimpleIdentifier identifier = resolvedVariable(listType, "a");
IndexExpression node = indexExpression(identifier, resolvedInteger(2));
MethodElement indexMethod = listType.getElement().getMethods()[1];
node.setStaticElement(indexMethod);
assignmentExpression(node, TokenType.EQ, integer(0));
assertSame(listType.getTypeArguments()[0], analyze(node));
listener.assertNoErrors();
}
public void test_visitIndexExpression_typeParameters() throws Exception {
// List<int> list = ...
// list[0]
InterfaceType intType = typeProvider.getIntType();
InterfaceType listType = typeProvider.getListType();
// (int) -> E
MethodElement methodElement = getMethod(listType, "[]");
// "list" has type List<int>
SimpleIdentifier identifier = identifier("list");
InterfaceType listOfIntType = listType.substitute(new Type[] {intType});
identifier.setStaticType(listOfIntType);
// list[0] has MethodElement element (int) -> E
IndexExpression indexExpression = indexExpression(identifier, integer(0));
MethodElement indexMethod = MethodMember.from(methodElement, listOfIntType);
indexExpression.setStaticElement(indexMethod);
// analyze and assert result of the index expression
assertSame(intType, analyze(indexExpression));
listener.assertNoErrors();
}
public void test_visitIndexExpression_typeParameters_inSetterContext() throws Exception {
// List<int> list = ...
// list[0] = 0;
InterfaceType intType = typeProvider.getIntType();
InterfaceType listType = typeProvider.getListType();
// (int, E) -> void
MethodElement methodElement = getMethod(listType, "[]=");
// "list" has type List<int>
SimpleIdentifier identifier = identifier("list");
InterfaceType listOfIntType = listType.substitute(new Type[] {intType});
identifier.setStaticType(listOfIntType);
// list[0] has MethodElement element (int) -> E
IndexExpression indexExpression = indexExpression(identifier, integer(0));
MethodElement indexMethod = MethodMember.from(methodElement, listOfIntType);
indexExpression.setStaticElement(indexMethod);
// list[0] should be in a setter context
assignmentExpression(indexExpression, TokenType.EQ, integer(0));
// analyze and assert result of the index expression
assertSame(intType, analyze(indexExpression));
listener.assertNoErrors();
}
public void test_visitInstanceCreationExpression_named() throws Exception {
// new C.m()
ClassElementImpl classElement = classElement("C");
String constructorName = "m";
ConstructorElementImpl constructor = constructorElement(classElement, constructorName);
constructor.setReturnType(classElement.getType());
FunctionTypeImpl constructorType = new FunctionTypeImpl(constructor);
constructor.setType(constructorType);
classElement.setConstructors(new ConstructorElement[] {constructor});
InstanceCreationExpression node = instanceCreationExpression(
null,
typeName(classElement),
identifier(constructorName));
node.setStaticElement(constructor);
assertSame(classElement.getType(), analyze(node));
listener.assertNoErrors();
}
public void test_visitInstanceCreationExpression_typeParameters() throws Exception {
// new C<I>()
ClassElementImpl elementC = classElement("C", "E");
ClassElementImpl elementI = classElement("I");
ConstructorElementImpl constructor = constructorElement(elementC, null);
elementC.setConstructors(new ConstructorElement[] {constructor});
constructor.setReturnType(elementC.getType());
FunctionTypeImpl constructorType = new FunctionTypeImpl(constructor);
constructor.setType(constructorType);
TypeName typeName = typeName(elementC, typeName(elementI));
typeName.setType(elementC.getType().substitute(new Type[] {elementI.getType()}));
InstanceCreationExpression node = instanceCreationExpression(null, typeName);
node.setStaticElement(constructor);
InterfaceType interfaceType = (InterfaceType) analyze(node);
Type[] typeArgs = interfaceType.getTypeArguments();
assertEquals(1, typeArgs.length);
assertEquals(elementI.getType(), typeArgs[0]);
listener.assertNoErrors();
}
public void test_visitInstanceCreationExpression_unnamed() throws Exception {
// new C()
ClassElementImpl classElement = classElement("C");
ConstructorElementImpl constructor = constructorElement(classElement, null);
constructor.setReturnType(classElement.getType());
FunctionTypeImpl constructorType = new FunctionTypeImpl(constructor);
constructor.setType(constructorType);
classElement.setConstructors(new ConstructorElement[] {constructor});
InstanceCreationExpression node = instanceCreationExpression(null, typeName(classElement));
node.setStaticElement(constructor);
assertSame(classElement.getType(), analyze(node));
listener.assertNoErrors();
}
public void test_visitIntegerLiteral() throws Exception {
// 42
Expression node = resolvedInteger(42);
assertSame(typeProvider.getIntType(), analyze(node));
listener.assertNoErrors();
}
public void test_visitIsExpression_negated() throws Exception {
// a is! String
Expression node = isExpression(resolvedString("a"), true, typeName("String"));
assertSame(typeProvider.getBoolType(), analyze(node));
listener.assertNoErrors();
}
public void test_visitIsExpression_notNegated() throws Exception {
// a is String
Expression node = isExpression(resolvedString("a"), false, typeName("String"));
assertSame(typeProvider.getBoolType(), analyze(node));
listener.assertNoErrors();
}
public void test_visitListLiteral_empty() throws Exception {
// []
Expression node = listLiteral();
Type resultType = analyze(node);
assertType(
typeProvider.getListType().substitute(new Type[] {typeProvider.getDynamicType()}),
resultType);
listener.assertNoErrors();
}
public void test_visitListLiteral_nonEmpty() throws Exception {
// [0]
Expression node = listLiteral(resolvedInteger(0));
Type resultType = analyze(node);
assertType(
typeProvider.getListType().substitute(new Type[] {typeProvider.getDynamicType()}),
resultType);
listener.assertNoErrors();
}
public void test_visitMapLiteral_empty() throws Exception {
// {}
Expression node = mapLiteral();
Type resultType = analyze(node);
assertType(
typeProvider.getMapType().substitute(
new Type[] {typeProvider.getDynamicType(), typeProvider.getDynamicType()}),
resultType);
listener.assertNoErrors();
}
public void test_visitMapLiteral_nonEmpty() throws Exception {
// {"k" : 0}
Expression node = mapLiteral(mapLiteralEntry("k", resolvedInteger(0)));
Type resultType = analyze(node);
assertType(
typeProvider.getMapType().substitute(
new Type[] {typeProvider.getDynamicType(), typeProvider.getDynamicType()}),
resultType);
listener.assertNoErrors();
}
public void test_visitMethodInvocation_then() throws Exception {
// then()
Expression node = methodInvocation(null, "then");
analyze(node);
listener.assertNoErrors();
}
public void test_visitNamedExpression() throws Exception {
// n: a
Expression node = namedExpression("n", resolvedString("a"));
assertSame(typeProvider.getStringType(), analyze(node));
listener.assertNoErrors();
}
public void test_visitNullLiteral() throws Exception {
// null
Expression node = nullLiteral();
assertSame(typeProvider.getBottomType(), analyze(node));
listener.assertNoErrors();
}
public void test_visitParenthesizedExpression() throws Exception {
// (0)
Expression node = parenthesizedExpression(resolvedInteger(0));
assertSame(typeProvider.getIntType(), analyze(node));
listener.assertNoErrors();
}
public void test_visitPostfixExpression_minusMinus() throws Exception {
// 0--
PostfixExpression node = postfixExpression(resolvedInteger(0), TokenType.MINUS_MINUS);
assertSame(typeProvider.getIntType(), analyze(node));
listener.assertNoErrors();
}
public void test_visitPostfixExpression_plusPlus() throws Exception {
// 0++
PostfixExpression node = postfixExpression(resolvedInteger(0), TokenType.PLUS_PLUS);
assertSame(typeProvider.getIntType(), analyze(node));
listener.assertNoErrors();
}
public void test_visitPrefixedIdentifier_getter() throws Exception {
Type boolType = typeProvider.getBoolType();
PropertyAccessorElementImpl getter = getterElement("b", false, boolType);
PrefixedIdentifier node = identifier("a", "b");
node.getIdentifier().setStaticElement(getter);
assertSame(boolType, analyze(node));
listener.assertNoErrors();
}
public void test_visitPrefixedIdentifier_setter() throws Exception {
Type boolType = typeProvider.getBoolType();
FieldElementImpl field = fieldElement("b", false, false, false, boolType);
PropertyAccessorElement setter = field.getSetter();
PrefixedIdentifier node = identifier("a", "b");
node.getIdentifier().setStaticElement(setter);
assertSame(boolType, analyze(node));
listener.assertNoErrors();
}
public void test_visitPrefixedIdentifier_variable() throws Exception {
VariableElementImpl variable = localVariableElement("b");
variable.setType(typeProvider.getBoolType());
PrefixedIdentifier node = identifier("a", "b");
node.getIdentifier().setStaticElement(variable);
assertSame(typeProvider.getBoolType(), analyze(node));
listener.assertNoErrors();
}
public void test_visitPrefixExpression_bang() throws Exception {
// !0
PrefixExpression node = prefixExpression(TokenType.BANG, resolvedInteger(0));
assertSame(typeProvider.getBoolType(), analyze(node));
listener.assertNoErrors();
}
public void test_visitPrefixExpression_minus() throws Exception {
// -0
PrefixExpression node = prefixExpression(TokenType.MINUS, resolvedInteger(0));
MethodElement minusMethod = getMethod(typeProvider.getNumType(), "-");
node.setStaticElement(minusMethod);
assertSame(typeProvider.getNumType(), analyze(node));
listener.assertNoErrors();
}
public void test_visitPrefixExpression_minusMinus() throws Exception {
// --0
PrefixExpression node = prefixExpression(TokenType.MINUS_MINUS, resolvedInteger(0));
MethodElement minusMethod = getMethod(typeProvider.getNumType(), "-");
node.setStaticElement(minusMethod);
assertSame(typeProvider.getIntType(), analyze(node));
listener.assertNoErrors();
}
public void test_visitPrefixExpression_not() throws Exception {
// !true
Expression node = prefixExpression(TokenType.BANG, booleanLiteral(true));
assertSame(typeProvider.getBoolType(), analyze(node));
listener.assertNoErrors();
}
public void test_visitPrefixExpression_plusPlus() throws Exception {
// ++0
PrefixExpression node = prefixExpression(TokenType.PLUS_PLUS, resolvedInteger(0));
MethodElement plusMethod = getMethod(typeProvider.getNumType(), "+");
node.setStaticElement(plusMethod);
assertSame(typeProvider.getIntType(), analyze(node));
listener.assertNoErrors();
}
public void test_visitPrefixExpression_tilde() throws Exception {
// ~0
PrefixExpression node = prefixExpression(TokenType.TILDE, resolvedInteger(0));
MethodElement tildeMethod = getMethod(typeProvider.getIntType(), "~");
node.setStaticElement(tildeMethod);
assertSame(typeProvider.getIntType(), analyze(node));
listener.assertNoErrors();
}
public void test_visitPropertyAccess_propagated_getter() throws Exception {
Type boolType = typeProvider.getBoolType();
PropertyAccessorElementImpl getter = getterElement("b", false, boolType);
PropertyAccess node = propertyAccess(identifier("a"), "b");
node.getPropertyName().setPropagatedElement(getter);
assertSame(boolType, analyze(node, false));
listener.assertNoErrors();
}
public void test_visitPropertyAccess_propagated_setter() throws Exception {
Type boolType = typeProvider.getBoolType();
FieldElementImpl field = fieldElement("b", false, false, false, boolType);
PropertyAccessorElement setter = field.getSetter();
PropertyAccess node = propertyAccess(identifier("a"), "b");
node.getPropertyName().setPropagatedElement(setter);
assertSame(boolType, analyze(node, false));
listener.assertNoErrors();
}
public void test_visitPropertyAccess_static_getter() throws Exception {
Type boolType = typeProvider.getBoolType();
PropertyAccessorElementImpl getter = getterElement("b", false, boolType);
PropertyAccess node = propertyAccess(identifier("a"), "b");
node.getPropertyName().setStaticElement(getter);
assertSame(boolType, analyze(node));
listener.assertNoErrors();
}
public void test_visitPropertyAccess_static_setter() throws Exception {
Type boolType = typeProvider.getBoolType();
FieldElementImpl field = fieldElement("b", false, false, false, boolType);
PropertyAccessorElement setter = field.getSetter();
PropertyAccess node = propertyAccess(identifier("a"), "b");
node.getPropertyName().setStaticElement(setter);
assertSame(boolType, analyze(node));
listener.assertNoErrors();
}
public void test_visitSimpleStringLiteral() throws Exception {
// "a"
Expression node = resolvedString("a");
assertSame(typeProvider.getStringType(), analyze(node));
listener.assertNoErrors();
}
public void test_visitStringInterpolation() throws Exception {
// "a${'b'}c"
Expression node = string(
interpolationString("a", "a"),
interpolationExpression(resolvedString("b")),
interpolationString("c", "c"));
assertSame(typeProvider.getStringType(), analyze(node));
listener.assertNoErrors();
}
public void test_visitSuperExpression() throws Exception {
// super
InterfaceType superType = classElement("A").getType();
InterfaceType thisType = classElement("B", superType).getType();
Expression node = superExpression();
assertSame(thisType, analyze(node, thisType));
listener.assertNoErrors();
}
public void test_visitSymbolLiteral() throws Exception {
assertSame(typeProvider.getSymbolType(), analyze(symbolLiteral("a")));
}
public void test_visitThisExpression() throws Exception {
// this
InterfaceType thisType = classElement("B", classElement("A").getType()).getType();
Expression node = thisExpression();
assertSame(thisType, analyze(node, thisType));
listener.assertNoErrors();
}
public void test_visitThrowExpression_withoutValue() throws Exception {
// throw
Expression node = throwExpression();
assertSame(typeProvider.getBottomType(), analyze(node));
listener.assertNoErrors();
}
public void test_visitThrowExpression_withValue() throws Exception {
// throw 0
Expression node = throwExpression(resolvedInteger(0));
assertSame(typeProvider.getBottomType(), analyze(node));
listener.assertNoErrors();
}
/**
* Return the type associated with the given expression after the static type analyzer has
* computed a type for it.
*
* @param node the expression with which the type is associated
* @return the type associated with the expression
*/
private Type analyze(Expression node) {
return analyze(node, null, true);
}
/**
* Return the type associated with the given expression after the static or propagated type
* analyzer has computed a type for it.
*
* @param node the expression with which the type is associated
* @param useStaticType {@code true} if the static type is being requested, and {@code false} if
* the propagated type is being requested
* @return the type associated with the expression
*/
private Type analyze(Expression node, boolean useStaticType) {
return analyze(node, null, useStaticType);
}
/**
* Return the type associated with the given expression after the static type analyzer has
* computed a type for it.
*
* @param node the expression with which the type is associated
* @param thisType the type of 'this'
* @return the type associated with the expression
*/
private Type analyze(Expression node, InterfaceType thisType) {
return analyze(node, thisType, true);
}
/**
* Return the type associated with the given expression after the static type analyzer has
* computed a type for it.
*
* @param node the expression with which the type is associated
* @param thisType the type of 'this'
* @param useStaticType {@code true} if the static type is being requested, and {@code false} if
* the propagated type is being requested
* @return the type associated with the expression
*/
private Type analyze(Expression node, InterfaceType thisType, boolean useStaticType) {
try {
Field typeField = analyzer.getClass().getDeclaredField("thisType");
typeField.setAccessible(true);
typeField.set(analyzer, thisType);
} catch (Exception exception) {
throw new IllegalArgumentException("Could not set type of 'this'", exception);
}
node.accept(analyzer);
if (useStaticType) {
return node.getStaticType();
} else {
return node.getPropagatedType();
}
}
/**
* Return the type associated with the given parameter after the static type analyzer has computed
* a type for it.
*
* @param node the parameter with which the type is associated
* @return the type associated with the parameter
*/
private Type analyze(FormalParameter node) {
node.accept(analyzer);
return ((ParameterElement) node.getIdentifier().getStaticElement()).getType();
}
/**
* Assert that the actual type is a function type with the expected characteristics.
*
* @param expectedReturnType the expected return type of the function
* @param expectedNormalTypes the expected types of the normal parameters
* @param expectedOptionalTypes the expected types of the optional parameters
* @param expectedNamedTypes the expected types of the named parameters
* @param actualType the type being tested
*/
private void assertFunctionType(Type expectedReturnType, Type[] expectedNormalTypes,
Type[] expectedOptionalTypes, Map<String, Type> expectedNamedTypes, Type actualType) {
assertInstanceOf(FunctionType.class, actualType);
FunctionType functionType = (FunctionType) actualType;
Type[] normalTypes = functionType.getNormalParameterTypes();
if (expectedNormalTypes == null) {
assertLength(0, normalTypes);
} else {
int expectedCount = expectedNormalTypes.length;
assertLength(expectedCount, normalTypes);
for (int i = 0; i < expectedCount; i++) {
assertSame(expectedNormalTypes[i], normalTypes[i]);
}
}
Type[] optionalTypes = functionType.getOptionalParameterTypes();
if (expectedOptionalTypes == null) {
assertLength(0, optionalTypes);
} else {
int expectedCount = expectedOptionalTypes.length;
assertLength(expectedCount, optionalTypes);
for (int i = 0; i < expectedCount; i++) {
assertSame(expectedOptionalTypes[i], optionalTypes[i]);
}
}
Map<String, Type> namedTypes = functionType.getNamedParameterTypes();
if (expectedNamedTypes == null) {
assertSizeOfMap(0, namedTypes);
} else {
assertSizeOfMap(expectedNamedTypes.size(), namedTypes);
for (Map.Entry<String, Type> entry : expectedNamedTypes.entrySet()) {
assertSame(entry.getValue(), namedTypes.get(entry.getKey()));
}
}
assertEquals(expectedReturnType, functionType.getReturnType());
}
private void assertType(InterfaceTypeImpl expectedType, InterfaceTypeImpl actualType) {
assertEquals(expectedType.getDisplayName(), actualType.getDisplayName());
assertEquals(expectedType.getElement(), actualType.getElement());
Type[] expectedArguments = expectedType.getTypeArguments();
int length = expectedArguments.length;
Type[] actualArguments = actualType.getTypeArguments();
assertLength(length, actualArguments);
for (int i = 0; i < length; i++) {
assertType(expectedArguments[i], actualArguments[i]);
}
}
private void assertType(Type expectedType, Type actualType) {
if (expectedType instanceof InterfaceTypeImpl) {
assertInstanceOf(InterfaceTypeImpl.class, actualType);
assertType((InterfaceTypeImpl) expectedType, (InterfaceTypeImpl) actualType);
}
// TODO(brianwilkerson) Compare other kinds of types then make this a shared utility method
}
/**
* Create the analyzer used by the tests.
*
* @return the analyzer to be used by the tests
*/
private StaticTypeAnalyzer createAnalyzer() {
AnalysisContextImpl context = new AnalysisContextImpl();
SourceFactory sourceFactory = new SourceFactory(new DartUriResolver(
DirectoryBasedDartSdk.getDefaultSdk()));
context.setSourceFactory(sourceFactory);
FileBasedSource source = new FileBasedSource(FileUtilities2.createFile("/lib.dart"));
CompilationUnitElementImpl definingCompilationUnit = new CompilationUnitElementImpl("lib.dart");
definingCompilationUnit.setSource(source);
LibraryElementImpl definingLibrary = new LibraryElementImpl(context, null);
definingLibrary.setDefiningCompilationUnit(definingCompilationUnit);
Library library = new Library(context, listener, source);
library.setLibraryElement(definingLibrary);
visitor = new ResolverVisitor(library, source, typeProvider);
visitor.getOverrideManager().enterScope();
try {
Field analyzerField = visitor.getClass().getDeclaredField("typeAnalyzer");
analyzerField.setAccessible(true);
return (StaticTypeAnalyzer) analyzerField.get(visitor);
} catch (Exception exception) {
throw new IllegalArgumentException("Could not create analyzer", exception);
}
}
/**
* Return an integer literal that has been resolved to the correct type.
*
* @param value the value of the literal
* @return an integer literal that has been resolved to the correct type
*/
private DoubleLiteral resolvedDouble(double value) {
DoubleLiteral literal = doubleLiteral(value);
literal.setStaticType(typeProvider.getDoubleType());
return literal;
}
/**
* Create a function expression that has an element associated with it, where the element has an
* incomplete type associated with it (just like the one
* {@link ElementBuilder#visitFunctionExpression(FunctionExpression)} would have built if we had
* run it).
*
* @param parameters the parameters to the function
* @param body the body of the function
* @return a resolved function expression
*/
private FunctionExpression resolvedFunctionExpression(FormalParameterList parameters,
FunctionBody body) {
ArrayList<ParameterElement> parameterElements = new ArrayList<ParameterElement>();
for (FormalParameter parameter : parameters.getParameters()) {
ParameterElementImpl element = new ParameterElementImpl(parameter.getIdentifier());
element.setParameterKind(parameter.getKind());
element.setType(typeProvider.getDynamicType());
parameter.getIdentifier().setStaticElement(element);
parameterElements.add(element);
}
FunctionExpression node = functionExpression(parameters, body);
FunctionElementImpl element = new FunctionElementImpl(null);
element.setParameters(parameterElements.toArray(new ParameterElement[parameterElements.size()]));
element.setType(new FunctionTypeImpl(element));
node.setElement(element);
return node;
}
/**
* Return an integer literal that has been resolved to the correct type.
*
* @param value the value of the literal
* @return an integer literal that has been resolved to the correct type
*/
private IntegerLiteral resolvedInteger(int value) {
IntegerLiteral literal = integer(value);
literal.setStaticType(typeProvider.getIntType());
return literal;
}
/**
* Return a string literal that has been resolved to the correct type.
*
* @param value the value of the literal
* @return a string literal that has been resolved to the correct type
*/
private SimpleStringLiteral resolvedString(String value) {
SimpleStringLiteral string = string(value);
string.setStaticType(typeProvider.getStringType());
return string;
}
/**
* Return a simple identifier that has been resolved to a variable element with the given type.
*
* @param type the type of the variable being represented
* @param variableName the name of the variable
* @return a simple identifier that has been resolved to a variable element with the given type
*/
private SimpleIdentifier resolvedVariable(InterfaceType type, String variableName) {
SimpleIdentifier identifier = identifier(variableName);
VariableElementImpl element = localVariableElement(identifier);
element.setType(type);
identifier.setStaticElement(element);
identifier.setStaticType(type);
return identifier;
}
/**
* Set the type of the given parameter to the given type.
*
* @param parameter the parameter whose type is to be set
* @param type the new type of the given parameter
*/
private void setType(FormalParameter parameter, Type type) {
SimpleIdentifier identifier = parameter.getIdentifier();
Element element = identifier.getStaticElement();
if (!(element instanceof ParameterElement)) {
element = new ParameterElementImpl(identifier);
identifier.setStaticElement(element);
}
((ParameterElementImpl) element).setType(type);
}
}