/*
* 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.AstNode;
import com.google.dart.engine.ast.CatchClause;
import com.google.dart.engine.ast.ClassDeclaration;
import com.google.dart.engine.ast.ClassTypeAlias;
import com.google.dart.engine.ast.ExtendsClause;
import com.google.dart.engine.ast.FormalParameter;
import com.google.dart.engine.ast.ImplementsClause;
import com.google.dart.engine.ast.SimpleFormalParameter;
import com.google.dart.engine.ast.SimpleIdentifier;
import com.google.dart.engine.ast.TypeName;
import com.google.dart.engine.ast.VariableDeclaration;
import com.google.dart.engine.ast.WithClause;
import com.google.dart.engine.element.ClassElement;
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.error.GatheringErrorListener;
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.LibraryElementImpl;
import com.google.dart.engine.internal.element.LocalVariableElementImpl;
import com.google.dart.engine.internal.element.ParameterElementImpl;
import com.google.dart.engine.internal.type.DynamicTypeImpl;
import com.google.dart.engine.internal.type.VoidTypeImpl;
import com.google.dart.engine.scanner.Keyword;
import com.google.dart.engine.source.FileBasedSource;
import com.google.dart.engine.source.FileUriResolver;
import com.google.dart.engine.source.Source;
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 static com.google.dart.engine.ast.AstFactory.catchClause;
import static com.google.dart.engine.ast.AstFactory.classDeclaration;
import static com.google.dart.engine.ast.AstFactory.classTypeAlias;
import static com.google.dart.engine.ast.AstFactory.extendsClause;
import static com.google.dart.engine.ast.AstFactory.fieldFormalParameter;
import static com.google.dart.engine.ast.AstFactory.formalParameterList;
import static com.google.dart.engine.ast.AstFactory.identifier;
import static com.google.dart.engine.ast.AstFactory.implementsClause;
import static com.google.dart.engine.ast.AstFactory.libraryIdentifier;
import static com.google.dart.engine.ast.AstFactory.simpleFormalParameter;
import static com.google.dart.engine.ast.AstFactory.typeName;
import static com.google.dart.engine.ast.AstFactory.variableDeclaration;
import static com.google.dart.engine.ast.AstFactory.variableDeclarationList;
import static com.google.dart.engine.ast.AstFactory.withClause;
import static com.google.dart.engine.element.ElementFactory.classElement;
import static com.google.dart.engine.element.ElementFactory.methodElement;
import static com.google.dart.engine.element.ElementFactory.requiredParameter;
import static com.google.dart.engine.utilities.io.FileUtilities2.createFile;
public class TypeResolverVisitorTest extends EngineTestCase {
/**
* The error listener to which errors will be reported.
*/
private GatheringErrorListener listener;
/**
* The object representing the information about the library in which the types are being
* resolved.
*/
private Library library;
/**
* The type provider used to access the types.
*/
private TestTypeProvider typeProvider;
/**
* The visitor used to resolve types needed to form the type hierarchy.
*/
private TypeResolverVisitor visitor;
/**
* The visitor used to resolve types needed to form the type hierarchy.
*/
private ImplicitConstructorBuilder implicitConstructorBuilder;
public void fail_visitConstructorDeclaration() throws Exception {
fail("Not yet tested");
listener.assertNoErrors();
}
public void fail_visitFunctionDeclaration() throws Exception {
fail("Not yet tested");
listener.assertNoErrors();
}
public void fail_visitFunctionTypeAlias() throws Exception {
fail("Not yet tested");
listener.assertNoErrors();
}
public void fail_visitFunctionTypedFormalParameter() throws Exception {
fail("Not yet tested");
listener.assertNoErrors();
}
public void fail_visitMethodDeclaration() throws Exception {
fail("Not yet tested");
listener.assertNoErrors();
}
public void fail_visitVariableDeclaration() throws Exception {
fail("Not yet tested");
ClassElement type = classElement("A");
VariableDeclaration node = variableDeclaration("a");
variableDeclarationList(null, typeName(type), node);
//resolve(node);
assertSame(type.getType(), node.getName().getStaticType());
listener.assertNoErrors();
}
@Override
public void setUp() {
listener = new GatheringErrorListener();
SourceFactory factory = new SourceFactory(new FileUriResolver());
AnalysisContextImpl context = new AnalysisContextImpl();
context.setSourceFactory(factory);
Source librarySource = new FileBasedSource(createFile("/lib.dart"));
library = new Library(context, listener, librarySource);
LibraryElementImpl element = new LibraryElementImpl(context, libraryIdentifier("lib"));
element.setDefiningCompilationUnit(new CompilationUnitElementImpl("lib.dart"));
library.setLibraryElement(element);
typeProvider = new TestTypeProvider();
visitor = new TypeResolverVisitor(library, librarySource, typeProvider);
implicitConstructorBuilder = new ImplicitConstructorBuilder(
library,
librarySource,
typeProvider);
}
public void test_visitCatchClause_exception() throws Exception {
// catch (e)
CatchClause clause = catchClause("e");
SimpleIdentifier exceptionParameter = clause.getExceptionParameter();
exceptionParameter.setStaticElement(new LocalVariableElementImpl(exceptionParameter));
resolveCatchClause(clause, typeProvider.getDynamicType(), null);
listener.assertNoErrors();
}
public void test_visitCatchClause_exception_stackTrace() throws Exception {
// catch (e, s)
CatchClause clause = catchClause("e", "s");
SimpleIdentifier exceptionParameter = clause.getExceptionParameter();
exceptionParameter.setStaticElement(new LocalVariableElementImpl(exceptionParameter));
SimpleIdentifier stackTraceParameter = clause.getStackTraceParameter();
stackTraceParameter.setStaticElement(new LocalVariableElementImpl(stackTraceParameter));
resolveCatchClause(clause, typeProvider.getDynamicType(), typeProvider.getStackTraceType());
listener.assertNoErrors();
}
public void test_visitCatchClause_on_exception() throws Exception {
// on E catch (e)
ClassElement exceptionElement = classElement("E");
TypeName exceptionType = typeName(exceptionElement);
CatchClause clause = catchClause(exceptionType, "e");
SimpleIdentifier exceptionParameter = clause.getExceptionParameter();
exceptionParameter.setStaticElement(new LocalVariableElementImpl(exceptionParameter));
resolveCatchClause(clause, exceptionElement.getType(), null, exceptionElement);
listener.assertNoErrors();
}
public void test_visitCatchClause_on_exception_stackTrace() throws Exception {
// on E catch (e, s)
ClassElement exceptionElement = classElement("E");
TypeName exceptionType = typeName(exceptionElement);
((SimpleIdentifier) exceptionType.getName()).setStaticElement(exceptionElement);
CatchClause clause = catchClause(exceptionType, "e", "s");
SimpleIdentifier exceptionParameter = clause.getExceptionParameter();
exceptionParameter.setStaticElement(new LocalVariableElementImpl(exceptionParameter));
SimpleIdentifier stackTraceParameter = clause.getStackTraceParameter();
stackTraceParameter.setStaticElement(new LocalVariableElementImpl(stackTraceParameter));
resolveCatchClause(
clause,
exceptionElement.getType(),
typeProvider.getStackTraceType(),
exceptionElement);
listener.assertNoErrors();
}
public void test_visitClassDeclaration() {
// class A extends B with C implements D {}
// class B {}
// class C {}
// class D {}
ClassElement elementA = classElement("A");
ClassElement elementB = classElement("B");
ClassElement elementC = classElement("C");
ClassElement elementD = classElement("D");
ExtendsClause extendsClause = extendsClause(typeName(elementB));
WithClause withClause = withClause(typeName(elementC));
ImplementsClause implementsClause = implementsClause(typeName(elementD));
ClassDeclaration declaration = classDeclaration(
null,
"A",
null,
extendsClause,
withClause,
implementsClause);
declaration.getName().setStaticElement(elementA);
resolveNode(declaration, elementA, elementB, elementC, elementD);
assertSame(elementB.getType(), elementA.getSupertype());
InterfaceType[] mixins = elementA.getMixins();
assertLength(1, mixins);
assertSame(elementC.getType(), mixins[0]);
InterfaceType[] interfaces = elementA.getInterfaces();
assertLength(1, interfaces);
assertSame(elementD.getType(), interfaces[0]);
listener.assertNoErrors();
}
public void test_visitClassDeclaration_instanceMemberCollidesWithClass() {
// class A {}
// class B extends A {
// void A() {}
// }
ClassElementImpl elementA = classElement("A");
ClassElementImpl elementB = classElement("B");
elementB.setMethods(new MethodElement[] {methodElement("A", VoidTypeImpl.getInstance())});
ExtendsClause extendsClause = extendsClause(typeName(elementA));
ClassDeclaration declaration = classDeclaration(null, "B", null, extendsClause, null, null);
declaration.getName().setStaticElement(elementB);
resolveNode(declaration, elementA, elementB);
assertSame(elementA.getType(), elementB.getSupertype());
listener.assertNoErrors();
}
public void test_visitClassTypeAlias() {
// class A = B with C implements D;
ClassElement elementA = classElement("A");
ClassElement elementB = classElement("B");
ClassElement elementC = classElement("C");
ClassElement elementD = classElement("D");
WithClause withClause = withClause(typeName(elementC));
ImplementsClause implementsClause = implementsClause(typeName(elementD));
ClassTypeAlias alias = classTypeAlias(
"A",
null,
null,
typeName(elementB),
withClause,
implementsClause);
alias.getName().setStaticElement(elementA);
resolveNode(alias, elementA, elementB, elementC, elementD);
assertSame(elementB.getType(), elementA.getSupertype());
InterfaceType[] mixins = elementA.getMixins();
assertLength(1, mixins);
assertSame(elementC.getType(), mixins[0]);
InterfaceType[] interfaces = elementA.getInterfaces();
assertLength(1, interfaces);
assertSame(elementD.getType(), interfaces[0]);
listener.assertNoErrors();
}
public void test_visitFieldFormalParameter_functionType() throws Exception {
InterfaceType intType = typeProvider.getIntType();
TypeName intTypeName = typeName("int");
String innerParameterName = "a";
SimpleFormalParameter parameter = simpleFormalParameter(innerParameterName);
parameter.getIdentifier().setStaticElement(requiredParameter(innerParameterName));
String outerParameterName = "p";
FormalParameter node = fieldFormalParameter(
null,
intTypeName,
outerParameterName,
formalParameterList(parameter));
node.getIdentifier().setStaticElement(requiredParameter(outerParameterName));
Type parameterType = resolveFormalParameter(node, intType.getElement());
assertInstanceOf(FunctionType.class, parameterType);
FunctionType functionType = (FunctionType) parameterType;
assertSame(intType, functionType.getReturnType());
assertLength(1, functionType.getParameters());
listener.assertNoErrors();
}
public void test_visitFieldFormalParameter_noType() throws Exception {
String parameterName = "p";
FormalParameter node = fieldFormalParameter(Keyword.VAR, null, parameterName);
node.getIdentifier().setStaticElement(requiredParameter(parameterName));
assertSame(typeProvider.getDynamicType(), resolveFormalParameter(node));
listener.assertNoErrors();
}
public void test_visitFieldFormalParameter_type() throws Exception {
InterfaceType intType = typeProvider.getIntType();
TypeName intTypeName = typeName("int");
String parameterName = "p";
FormalParameter node = fieldFormalParameter(null, intTypeName, parameterName);
node.getIdentifier().setStaticElement(requiredParameter(parameterName));
assertSame(intType, resolveFormalParameter(node, intType.getElement()));
listener.assertNoErrors();
}
public void test_visitSimpleFormalParameter_noType() throws Exception {
// p
FormalParameter node = simpleFormalParameter("p");
node.getIdentifier().setStaticElement(new ParameterElementImpl(identifier("p")));
assertSame(typeProvider.getDynamicType(), resolveFormalParameter(node));
listener.assertNoErrors();
}
public void test_visitSimpleFormalParameter_type() throws Exception {
// int p
InterfaceType intType = typeProvider.getIntType();
ClassElement intElement = intType.getElement();
FormalParameter node = simpleFormalParameter(typeName(intElement), "p");
SimpleIdentifier identifier = node.getIdentifier();
ParameterElementImpl element = new ParameterElementImpl(identifier);
identifier.setStaticElement(element);
assertSame(intType, resolveFormalParameter(node, intElement));
listener.assertNoErrors();
}
public void test_visitTypeName_noParameters_noArguments() throws Exception {
ClassElement classA = classElement("A");
TypeName typeName = typeName(classA);
typeName.setType(null); // The factory method sets the type, but we want the resolver to do so.
resolveNode(typeName, classA);
assertSame(classA.getType(), typeName.getType());
listener.assertNoErrors();
}
public void test_visitTypeName_parameters_arguments() throws Exception {
ClassElement classA = classElement("A", "E");
ClassElement classB = classElement("B");
TypeName typeName = typeName(classA, typeName(classB));
typeName.setType(null); // The factory method sets the type, but we want the resolver to do so.
resolveNode(typeName, classA, classB);
InterfaceType resultType = (InterfaceType) typeName.getType();
assertSame(classA, resultType.getElement());
Type[] resultArguments = resultType.getTypeArguments();
assertLength(1, resultArguments);
assertSame(classB.getType(), resultArguments[0]);
listener.assertNoErrors();
}
public void test_visitTypeName_parameters_noArguments() throws Exception {
ClassElement classA = classElement("A", "E");
TypeName typeName = typeName(classA);
typeName.setType(null); // The factory method sets the type, but we want the resolver to do so.
resolveNode(typeName, classA);
InterfaceType resultType = (InterfaceType) typeName.getType();
assertSame(classA, resultType.getElement());
Type[] resultArguments = resultType.getTypeArguments();
assertLength(1, resultArguments);
assertSame(DynamicTypeImpl.getInstance(), resultArguments[0]);
listener.assertNoErrors();
}
public void test_visitTypeName_void() throws Exception {
ClassElement classA = classElement("A");
TypeName typeName = typeName("void");
resolveNode(typeName, classA);
assertSame(VoidTypeImpl.getInstance(), typeName.getType());
listener.assertNoErrors();
}
/**
* Analyze the given catch clause and assert that the types of the parameters have been set to the
* given types. The types can be null if the catch clause does not have the corresponding
* parameter.
*
* @param node the catch clause to be analyzed
* @param exceptionType the expected type of the exception parameter
* @param stackTraceType the expected type of the stack trace parameter
* @param definedElements the elements that are to be defined in the scope in which the element is
* being resolved
*/
private void resolveCatchClause(CatchClause node, Type exceptionType,
InterfaceType stackTraceType, Element... definedElements) {
resolveNode(node, definedElements);
SimpleIdentifier exceptionParameter = node.getExceptionParameter();
if (exceptionParameter != null) {
assertSame(exceptionType, exceptionParameter.getStaticType());
}
SimpleIdentifier stackTraceParameter = node.getStackTraceParameter();
if (stackTraceParameter != null) {
assertSame(stackTraceType, stackTraceParameter.getStaticType());
}
}
/**
* 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
* @param definedElements the elements that are to be defined in the scope in which the element is
* being resolved
* @return the type associated with the parameter
*/
private Type resolveFormalParameter(FormalParameter node, Element... definedElements) {
resolveNode(node, definedElements);
return ((ParameterElement) node.getIdentifier().getStaticElement()).getType();
}
/**
* Return the element associated with the given identifier after the resolver has resolved the
* identifier.
*
* @param node the expression to be resolved
* @param definedElements the elements that are to be defined in the scope in which the element is
* being resolved
* @return the element to which the expression was resolved
*/
private void resolveNode(AstNode node, Element... definedElements) {
for (Element element : definedElements) {
library.getLibraryScope().define(element);
}
node.accept(visitor);
node.accept(implicitConstructorBuilder);
}
}