/* * 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.resolver; import com.google.dart.engine.ast.Block; import com.google.dart.engine.ast.BlockFunctionBody; import com.google.dart.engine.ast.ClassDeclaration; import com.google.dart.engine.ast.CompilationUnit; import com.google.dart.engine.ast.ConditionalExpression; import com.google.dart.engine.ast.Expression; import com.google.dart.engine.ast.ExpressionStatement; import com.google.dart.engine.ast.FormalParameter; import com.google.dart.engine.ast.FunctionDeclaration; import com.google.dart.engine.ast.FunctionExpression; import com.google.dart.engine.ast.IfStatement; import com.google.dart.engine.ast.IndexExpression; import com.google.dart.engine.ast.ListLiteral; import com.google.dart.engine.ast.MethodInvocation; import com.google.dart.engine.ast.NodeList; import com.google.dart.engine.ast.PrefixedIdentifier; import com.google.dart.engine.ast.ReturnStatement; import com.google.dart.engine.ast.SimpleFormalParameter; import com.google.dart.engine.ast.SimpleIdentifier; import com.google.dart.engine.ast.Statement; import com.google.dart.engine.ast.VariableDeclarationStatement; import com.google.dart.engine.ast.WhileStatement; import com.google.dart.engine.element.LibraryElement; import com.google.dart.engine.error.StaticWarningCode; import com.google.dart.engine.source.Source; import com.google.dart.engine.type.FunctionType; import com.google.dart.engine.type.InterfaceType; import com.google.dart.engine.type.Type; import junit.framework.AssertionFailedError; public class TypePropagationTest extends ResolverTestCase { public void fail_mergePropagatedTypesAtJoinPoint_1() throws Exception { // https://code.google.com/p/dart/issues/detail?id=19929 assertTypeOfMarkedExpression( createSource( "f1(x) {", " var y = [];", " if (x) {", " y = 0;", " } else {", " y = '';", " }", " // Propagated type is [List] here: incorrect.", " // Best we can do is [Object]?", " return y; // marker", "}"), // Don't care about the static type. null, // TODO(collinsn): // In general, it might be more useful to compute a (structural) // intersection of interfaces here; // the best nominal type may be much less precise. // // The same concern applies in the other [fail_merge*] tests expecting [dynamic] below. getTypeProvider().getDynamicType()); } public void fail_mergePropagatedTypesAtJoinPoint_2() throws Exception { // https://code.google.com/p/dart/issues/detail?id=19929 assertTypeOfMarkedExpression( createSource( "f2(x) {", " var y = [];", " if (x) {", " y = 0;", " } else {", " }", " // Propagated type is [List] here: incorrect.", " // Best we can do is [Object]?", " return y; // marker", "}"), // Don't care about the static type. null, getTypeProvider().getDynamicType()); } public void fail_mergePropagatedTypesAtJoinPoint_3() throws Exception { // https://code.google.com/p/dart/issues/detail?id=19929 assertTypeOfMarkedExpression( createSource( "f4(x) {", " var y = [];", " if (x) {", " y = 0;", " } else {", " y = 1.5;", " }", " // Propagated type is [List] here: incorrect.", " // A correct answer is the least upper bound of [int] and [double],", " // i.e. [num].", " return y; // marker", "}"), // Don't care about the static type. null, getTypeProvider().getNumType()); } public void fail_mergePropagatedTypesAtJoinPoint_5() throws Exception { // https://code.google.com/p/dart/issues/detail?id=19929 assertTypeOfMarkedExpression( createSource( "f6(x,y) {", " var z = [];", " if (x || (z = y) < 0) {", " } else {", " z = 0;", " }", " // Propagated type is [List] here: incorrect.", " // Best we can do is [Object]?", " return z; // marker", "}"), // Don't care about the static type. null, getTypeProvider().getDynamicType()); } public void fail_mergePropagatedTypesAtJoinPoint_7() throws Exception { // https://code.google.com/p/dart/issues/detail?id=19929 // // In general [continue]s are unsafe for the purposes of [isAbruptTerminationStatement]. // // This is like example 6, but less tricky: the code in the branch that // [continue]s is in effect after the [if]. String code = createSource( "f() {", " var x = 0;", " var c = false;", " var d = true;", " while (d) {", " if (c) {", " d = false;", " } else {", " x = '';", " c = true;", " continue;", " }", " x; // marker", " }", "}"); Type t = findMarkedIdentifier(code, "; // marker").getPropagatedType(); assertTrue(getTypeProvider().getIntType().isSubtypeOf(t)); assertTrue(getTypeProvider().getStringType().isSubtypeOf(t)); } public void fail_mergePropagatedTypesAtJoinPoint_8() throws Exception { // https://code.google.com/p/dart/issues/detail?id=19929 // // In nested loops [breaks]s are unsafe for the purposes of [isAbruptTerminationStatement]. // // This is a combination of 6 and 7: we use an unlabeled [break] // like a continue for the outer loop / like a labeled [break] to // jump just above the [if]. String code = createSource( "f() {", " var x = 0;", " var c = false;", " var d = true;", " while (d) {", " while (d) {", " if (c) {", " d = false;", " } else {", " x = '';", " c = true;", " break;", " }", " x; // marker", " }", " }", "}"); Type t = findMarkedIdentifier(code, "; // marker").getPropagatedType(); assertTrue(getTypeProvider().getIntType().isSubtypeOf(t)); assertTrue(getTypeProvider().getStringType().isSubtypeOf(t)); } public void fail_propagatedReturnType_functionExpression() throws Exception { // TODO(scheglov) disabled because we don't resolve function expression String code = createSource(// "main() {", " var v = (() {return 42;})();", "}"); assertPropagatedReturnType( code, getTypeProvider().getDynamicType(), getTypeProvider().getIntType()); } public void test_as() throws Exception { Source source = addSource(createSource(// "class A {", " bool get g => true;", "}", "A f(var p) {", " if ((p as A).g) {", " return p;", " } else {", " return null;", " }", "}")); LibraryElement library = resolve(source); assertNoErrors(source); verify(source); CompilationUnit unit = resolveCompilationUnit(source, library); ClassDeclaration classA = (ClassDeclaration) unit.getDeclarations().get(0); InterfaceType typeA = classA.getElement().getType(); FunctionDeclaration function = (FunctionDeclaration) unit.getDeclarations().get(1); BlockFunctionBody body = (BlockFunctionBody) function.getFunctionExpression().getBody(); IfStatement ifStatement = (IfStatement) body.getBlock().getStatements().get(0); ReturnStatement statement = (ReturnStatement) ((Block) ifStatement.getThenStatement()).getStatements().get( 0); SimpleIdentifier variableName = (SimpleIdentifier) statement.getExpression(); assertSame(typeA, variableName.getPropagatedType()); } public void test_assert() throws Exception { Source source = addSource(createSource(// "class A {}", "A f(var p) {", " assert (p is A);", " return p;", "}")); LibraryElement library = resolve(source); assertNoErrors(source); verify(source); CompilationUnit unit = resolveCompilationUnit(source, library); ClassDeclaration classA = (ClassDeclaration) unit.getDeclarations().get(0); InterfaceType typeA = classA.getElement().getType(); FunctionDeclaration function = (FunctionDeclaration) unit.getDeclarations().get(1); BlockFunctionBody body = (BlockFunctionBody) function.getFunctionExpression().getBody(); ReturnStatement statement = (ReturnStatement) body.getBlock().getStatements().get(1); SimpleIdentifier variableName = (SimpleIdentifier) statement.getExpression(); assertSame(typeA, variableName.getPropagatedType()); } public void test_assignment() throws Exception { Source source = addSource(createSource(// "f() {", " var v;", " v = 0;", " return v;", "}")); LibraryElement library = resolve(source); assertNoErrors(source); verify(source); CompilationUnit unit = resolveCompilationUnit(source, library); FunctionDeclaration function = (FunctionDeclaration) unit.getDeclarations().get(0); BlockFunctionBody body = (BlockFunctionBody) function.getFunctionExpression().getBody(); ReturnStatement statement = (ReturnStatement) body.getBlock().getStatements().get(2); SimpleIdentifier variableName = (SimpleIdentifier) statement.getExpression(); assertSame(getTypeProvider().getIntType(), variableName.getPropagatedType()); } public void test_assignment_afterInitializer() throws Exception { Source source = addSource(createSource(// "f() {", " var v = 0;", " v = 1.0;", " return v;", "}")); LibraryElement library = resolve(source); assertNoErrors(source); verify(source); CompilationUnit unit = resolveCompilationUnit(source, library); FunctionDeclaration function = (FunctionDeclaration) unit.getDeclarations().get(0); BlockFunctionBody body = (BlockFunctionBody) function.getFunctionExpression().getBody(); ReturnStatement statement = (ReturnStatement) body.getBlock().getStatements().get(2); SimpleIdentifier variableName = (SimpleIdentifier) statement.getExpression(); assertSame(getTypeProvider().getDoubleType(), variableName.getPropagatedType()); } public void test_assignment_null() throws Exception { String code = createSource(// "main() {", " int v; // declare", " v = null;", " return v; // return", "}"); CompilationUnit unit; { Source source = addSource(code); LibraryElement library = resolve(source); assertNoErrors(source); verify(source); unit = resolveCompilationUnit(source, library); } { SimpleIdentifier identifier = findNode(unit, code, "v; // declare", SimpleIdentifier.class); assertSame(getTypeProvider().getIntType(), identifier.getStaticType()); assertSame(null, identifier.getPropagatedType()); } { SimpleIdentifier identifier = findNode(unit, code, "v = null;", SimpleIdentifier.class); assertSame(getTypeProvider().getIntType(), identifier.getStaticType()); assertSame(null, identifier.getPropagatedType()); } { SimpleIdentifier identifier = findNode(unit, code, "v; // return", SimpleIdentifier.class); assertSame(getTypeProvider().getIntType(), identifier.getStaticType()); assertSame(null, identifier.getPropagatedType()); } } public void test_CanvasElement_getContext() throws Exception { String code = createSource(// "import 'dart:html';", "main(CanvasElement canvas) {", " var context = canvas.getContext('2d');", "}"); Source source = addSource(code); LibraryElement library = resolve(source); assertNoErrors(source); verify(source); CompilationUnit unit = resolveCompilationUnit(source, library); SimpleIdentifier identifier = findNode(unit, code, "context", SimpleIdentifier.class); assertEquals("CanvasRenderingContext2D", identifier.getPropagatedType().getName()); } public void test_finalPropertyInducingVariable_classMember_instance() throws Exception { addNamedSource("/lib.dart", createSource(// "class A {", " final v = 0;", "}")); String code = createSource(// "import 'lib.dart';", "f(A a) {", " return a.v; // marker", "}"); assertTypeOfMarkedExpression( code, getTypeProvider().getDynamicType(), getTypeProvider().getIntType()); } public void test_finalPropertyInducingVariable_classMember_instance_inherited() throws Exception { addNamedSource("/lib.dart", createSource(// "class A {", " final v = 0;", "}")); String code = createSource(// "import 'lib.dart';", "class B extends A {", " m() {", " return v; // marker", " }", "}"); assertTypeOfMarkedExpression( code, getTypeProvider().getDynamicType(), getTypeProvider().getIntType()); } public void test_finalPropertyInducingVariable_classMember_instance_propagatedTarget() throws Exception { addNamedSource("/lib.dart", createSource(// "class A {", " final v = 0;", "}")); String code = createSource(// "import 'lib.dart';", "f(p) {", " if (p is A) {", " return p.v; // marker", " }", "}"); assertTypeOfMarkedExpression( code, getTypeProvider().getDynamicType(), getTypeProvider().getIntType()); } public void test_finalPropertyInducingVariable_classMember_static() throws Exception { addNamedSource("/lib.dart", createSource(// "class A {", " static final V = 0;", "}")); String code = createSource(// "import 'lib.dart';", "f() {", " return A.V; // marker", "}"); assertTypeOfMarkedExpression( code, getTypeProvider().getDynamicType(), getTypeProvider().getIntType()); } public void test_finalPropertyInducingVariable_topLevelVaraible_prefixed() throws Exception { addNamedSource("/lib.dart", "final V = 0;"); String code = createSource(// "import 'lib.dart' as p;", "f() {", " var v2 = p.V; // marker prefixed", "}"); assertTypeOfMarkedExpression( code, getTypeProvider().getDynamicType(), getTypeProvider().getIntType()); } public void test_finalPropertyInducingVariable_topLevelVaraible_simple() throws Exception { addNamedSource("/lib.dart", "final V = 0;"); String code = createSource(// "import 'lib.dart';", "f() {", " return V; // marker simple", "}"); assertTypeOfMarkedExpression( code, getTypeProvider().getDynamicType(), getTypeProvider().getIntType()); } public void test_forEach() throws Exception { String code = createSource(// "main() {", " var list = <String> [];", " for (var e in list) {", " e;", " }", "}"); Source source = addSource(code); LibraryElement library = resolve(source); assertNoErrors(source); verify(source); CompilationUnit unit = resolveCompilationUnit(source, library); InterfaceType stringType = getTypeProvider().getStringType(); // in the declaration { SimpleIdentifier identifier = findNode(unit, code, "e in", SimpleIdentifier.class); assertSame(stringType, identifier.getPropagatedType()); } // in the loop body { SimpleIdentifier identifier = findNode(unit, code, "e;", SimpleIdentifier.class); assertSame(stringType, identifier.getPropagatedType()); } } public void test_functionExpression_asInvocationArgument() throws Exception { String code = createSource(// "class MyMap<K, V> {", " forEach(f(K key, V value)) {}", "}", "f(MyMap<int, String> m) {", " m.forEach((k, v) {", " k;", " v;", " });", "}"); Source source = addSource(code); LibraryElement library = resolve(source); assertNoErrors(source); verify(source); CompilationUnit unit = resolveCompilationUnit(source, library); // k Type intType = getTypeProvider().getIntType(); FormalParameter kParameter = findNode(unit, code, "k, ", SimpleFormalParameter.class); assertSame(intType, kParameter.getIdentifier().getPropagatedType()); SimpleIdentifier kIdentifier = findNode(unit, code, "k;", SimpleIdentifier.class); assertSame(intType, kIdentifier.getPropagatedType()); assertSame(getTypeProvider().getDynamicType(), kIdentifier.getStaticType()); // v Type stringType = getTypeProvider().getStringType(); FormalParameter vParameter = findNode(unit, code, "v)", SimpleFormalParameter.class); assertSame(stringType, vParameter.getIdentifier().getPropagatedType()); SimpleIdentifier vIdentifier = findNode(unit, code, "v;", SimpleIdentifier.class); assertSame(stringType, vIdentifier.getPropagatedType()); assertSame(getTypeProvider().getDynamicType(), vIdentifier.getStaticType()); } public void test_functionExpression_asInvocationArgument_fromInferredInvocation() throws Exception { String code = createSource(// "class MyMap<K, V> {", " forEach(f(K key, V value)) {}", "}", "f(MyMap<int, String> m) {", " var m2 = m;", " m2.forEach((k, v) {});", "}"); Source source = addSource(code); LibraryElement library = resolve(source); assertNoErrors(source); verify(source); CompilationUnit unit = resolveCompilationUnit(source, library); // k Type intType = getTypeProvider().getIntType(); FormalParameter kParameter = findNode(unit, code, "k, ", SimpleFormalParameter.class); assertSame(intType, kParameter.getIdentifier().getPropagatedType()); // v Type stringType = getTypeProvider().getStringType(); FormalParameter vParameter = findNode(unit, code, "v)", SimpleFormalParameter.class); assertSame(stringType, vParameter.getIdentifier().getPropagatedType()); } public void test_functionExpression_asInvocationArgument_functionExpressionInvocation() throws Exception { String code = createSource(// "main() {", " (f(String value)) {} ((v) {", " v;", " });", "}"); Source source = addSource(code); LibraryElement library = resolve(source); assertNoErrors(source); verify(source); CompilationUnit unit = resolveCompilationUnit(source, library); // v Type dynamicType = getTypeProvider().getDynamicType(); Type stringType = getTypeProvider().getStringType(); FormalParameter vParameter = findNode(unit, code, "v)", FormalParameter.class); assertSame(stringType, vParameter.getIdentifier().getPropagatedType()); assertSame(dynamicType, vParameter.getIdentifier().getStaticType()); SimpleIdentifier vIdentifier = findNode(unit, code, "v;", SimpleIdentifier.class); assertSame(stringType, vIdentifier.getPropagatedType()); assertSame(dynamicType, vIdentifier.getStaticType()); } public void test_functionExpression_asInvocationArgument_keepIfLessSpecific() throws Exception { String code = createSource(// "class MyList {", " forEach(f(Object value)) {}", "}", "f(MyList list) {", " list.forEach((int v) {", " v;", " });", "}"); Source source = addSource(code); LibraryElement library = resolve(source); assertNoErrors(source); verify(source); CompilationUnit unit = resolveCompilationUnit(source, library); // v Type intType = getTypeProvider().getIntType(); FormalParameter vParameter = findNode(unit, code, "v)", SimpleFormalParameter.class); assertSame(null, vParameter.getIdentifier().getPropagatedType()); assertSame(intType, vParameter.getIdentifier().getStaticType()); SimpleIdentifier vIdentifier = findNode(unit, code, "v;", SimpleIdentifier.class); assertSame(intType, vIdentifier.getStaticType()); assertSame(null, vIdentifier.getPropagatedType()); } public void test_functionExpression_asInvocationArgument_notSubtypeOfStaticType() throws Exception { String code = createSource(// "class A {", " m(void f(int i)) {}", "}", "x() {", " A a = new A();", " a.m(() => 0);", "}"); Source source = addSource(code); LibraryElement library = resolve(source); assertErrors(source, StaticWarningCode.ARGUMENT_TYPE_NOT_ASSIGNABLE); verify(source); CompilationUnit unit = resolveCompilationUnit(source, library); // () => 0 FunctionExpression functionExpression = findNode( unit, code, "() => 0)", FunctionExpression.class); assertSame(0, ((FunctionType) functionExpression.getStaticType()).getParameters().length); assertSame(null, functionExpression.getPropagatedType()); } public void test_functionExpression_asInvocationArgument_replaceIfMoreSpecific() throws Exception { String code = createSource(// "class MyList<E> {", " forEach(f(E value)) {}", "}", "f(MyList<String> list) {", " list.forEach((Object v) {", " v;", " });", "}"); Source source = addSource(code); LibraryElement library = resolve(source); assertNoErrors(source); verify(source); CompilationUnit unit = resolveCompilationUnit(source, library); // v Type stringType = getTypeProvider().getStringType(); FormalParameter vParameter = findNode(unit, code, "v)", SimpleFormalParameter.class); assertSame(stringType, vParameter.getIdentifier().getPropagatedType()); assertSame(getTypeProvider().getObjectType(), vParameter.getIdentifier().getStaticType()); SimpleIdentifier vIdentifier = findNode(unit, code, "v;", SimpleIdentifier.class); assertSame(stringType, vIdentifier.getPropagatedType()); } public void test_Future_then() throws Exception { String code = createSource(// "import 'dart:async';", "main(Future<int> firstFuture) {", " firstFuture.then((p1) {", " return 1.0;", " }).then((p2) {", " return new Future<String>.value('str');", " }).then((p3) {", " });", "}"); Source source = addSource(code); LibraryElement library = resolve(source); assertNoErrors(source); verify(source); CompilationUnit unit = resolveCompilationUnit(source, library); // p1 FormalParameter p1 = findNode(unit, code, "p1) {", SimpleFormalParameter.class); assertSame(getTypeProvider().getIntType(), p1.getIdentifier().getPropagatedType()); // p2 FormalParameter p2 = findNode(unit, code, "p2) {", SimpleFormalParameter.class); assertSame(getTypeProvider().getDoubleType(), p2.getIdentifier().getPropagatedType()); // p3 FormalParameter p3 = findNode(unit, code, "p3) {", SimpleFormalParameter.class); assertSame(getTypeProvider().getStringType(), p3.getIdentifier().getPropagatedType()); } public void test_initializer() throws Exception { Source source = addSource(createSource(// "f() {", " var v = 0;", " return v;", "}")); LibraryElement library = resolve(source); assertNoErrors(source); verify(source); CompilationUnit unit = resolveCompilationUnit(source, library); FunctionDeclaration function = (FunctionDeclaration) unit.getDeclarations().get(0); BlockFunctionBody body = (BlockFunctionBody) function.getFunctionExpression().getBody(); NodeList<Statement> statements = body.getBlock().getStatements(); // Type of 'v' in declaration. { VariableDeclarationStatement statement = (VariableDeclarationStatement) statements.get(0); SimpleIdentifier variableName = statement.getVariables().getVariables().get(0).getName(); assertSame(getTypeProvider().getDynamicType(), variableName.getStaticType()); assertSame(getTypeProvider().getIntType(), variableName.getPropagatedType()); } // Type of 'v' in reference. { ReturnStatement statement = (ReturnStatement) statements.get(1); SimpleIdentifier variableName = (SimpleIdentifier) statement.getExpression(); assertSame(getTypeProvider().getIntType(), variableName.getPropagatedType()); } } public void test_initializer_dereference() throws Exception { Source source = addSource(createSource(// "f() {", " var v = 'String';", " v.", "}")); LibraryElement library = resolve(source); CompilationUnit unit = resolveCompilationUnit(source, library); FunctionDeclaration function = (FunctionDeclaration) unit.getDeclarations().get(0); BlockFunctionBody body = (BlockFunctionBody) function.getFunctionExpression().getBody(); ExpressionStatement statement = (ExpressionStatement) body.getBlock().getStatements().get(1); PrefixedIdentifier invocation = (PrefixedIdentifier) statement.getExpression(); SimpleIdentifier variableName = invocation.getPrefix(); assertSame(getTypeProvider().getStringType(), variableName.getPropagatedType()); } public void test_initializer_null() throws Exception { String code = createSource(// "main() {", " int v = null;", " return v; // marker", "}"); CompilationUnit unit; { Source source = addSource(code); LibraryElement library = resolve(source); assertNoErrors(source); verify(source); unit = resolveCompilationUnit(source, library); } { SimpleIdentifier identifier = findNode(unit, code, "v = null;", SimpleIdentifier.class); assertSame(getTypeProvider().getIntType(), identifier.getStaticType()); assertSame(null, identifier.getPropagatedType()); } { SimpleIdentifier identifier = findNode(unit, code, "v; // marker", SimpleIdentifier.class); assertSame(getTypeProvider().getIntType(), identifier.getStaticType()); assertSame(null, identifier.getPropagatedType()); } } public void test_is_conditional() throws Exception { Source source = addSource(createSource(// "class A {}", "A f(var p) {", " return (p is A) ? p : null;", "}")); LibraryElement library = resolve(source); assertNoErrors(source); verify(source); CompilationUnit unit = resolveCompilationUnit(source, library); ClassDeclaration classA = (ClassDeclaration) unit.getDeclarations().get(0); InterfaceType typeA = classA.getElement().getType(); FunctionDeclaration function = (FunctionDeclaration) unit.getDeclarations().get(1); BlockFunctionBody body = (BlockFunctionBody) function.getFunctionExpression().getBody(); ReturnStatement statement = (ReturnStatement) body.getBlock().getStatements().get(0); ConditionalExpression conditional = (ConditionalExpression) statement.getExpression(); SimpleIdentifier variableName = (SimpleIdentifier) conditional.getThenExpression(); assertSame(typeA, variableName.getPropagatedType()); } public void test_is_if() throws Exception { Source source = addSource(createSource(// "class A {}", "A f(var p) {", " if (p is A) {", " return p;", " } else {", " return null;", " }", "}")); LibraryElement library = resolve(source); assertNoErrors(source); verify(source); CompilationUnit unit = resolveCompilationUnit(source, library); ClassDeclaration classA = (ClassDeclaration) unit.getDeclarations().get(0); InterfaceType typeA = classA.getElement().getType(); FunctionDeclaration function = (FunctionDeclaration) unit.getDeclarations().get(1); BlockFunctionBody body = (BlockFunctionBody) function.getFunctionExpression().getBody(); IfStatement ifStatement = (IfStatement) body.getBlock().getStatements().get(0); ReturnStatement statement = (ReturnStatement) ((Block) ifStatement.getThenStatement()).getStatements().get( 0); SimpleIdentifier variableName = (SimpleIdentifier) statement.getExpression(); assertSame(typeA, variableName.getPropagatedType()); } public void test_is_if_lessSpecific() throws Exception { Source source = addSource(createSource(// "class A {}", "A f(A p) {", " if (p is String) {", " return p;", " } else {", " return null;", " }", "}")); LibraryElement library = resolve(source); assertNoErrors(source); verify(source); CompilationUnit unit = resolveCompilationUnit(source, library); // ClassDeclaration classA = (ClassDeclaration) unit.getDeclarations().get(0); // InterfaceType typeA = classA.getElement().getType(); FunctionDeclaration function = (FunctionDeclaration) unit.getDeclarations().get(1); BlockFunctionBody body = (BlockFunctionBody) function.getFunctionExpression().getBody(); IfStatement ifStatement = (IfStatement) body.getBlock().getStatements().get(0); ReturnStatement statement = (ReturnStatement) ((Block) ifStatement.getThenStatement()).getStatements().get( 0); SimpleIdentifier variableName = (SimpleIdentifier) statement.getExpression(); assertSame(null, variableName.getPropagatedType()); } public void test_is_if_logicalAnd() throws Exception { Source source = addSource(createSource(// "class A {}", "A f(var p) {", " if (p is A && p != null) {", " return p;", " } else {", " return null;", " }", "}")); LibraryElement library = resolve(source); assertNoErrors(source); verify(source); CompilationUnit unit = resolveCompilationUnit(source, library); ClassDeclaration classA = (ClassDeclaration) unit.getDeclarations().get(0); InterfaceType typeA = classA.getElement().getType(); FunctionDeclaration function = (FunctionDeclaration) unit.getDeclarations().get(1); BlockFunctionBody body = (BlockFunctionBody) function.getFunctionExpression().getBody(); IfStatement ifStatement = (IfStatement) body.getBlock().getStatements().get(0); ReturnStatement statement = (ReturnStatement) ((Block) ifStatement.getThenStatement()).getStatements().get( 0); SimpleIdentifier variableName = (SimpleIdentifier) statement.getExpression(); assertSame(typeA, variableName.getPropagatedType()); } public void test_is_postConditional() throws Exception { Source source = addSource(createSource(// "class A {}", "A f(var p) {", " A a = (p is A) ? p : throw null;", " return p;", "}")); LibraryElement library = resolve(source); assertNoErrors(source); verify(source); CompilationUnit unit = resolveCompilationUnit(source, library); ClassDeclaration classA = (ClassDeclaration) unit.getDeclarations().get(0); InterfaceType typeA = classA.getElement().getType(); FunctionDeclaration function = (FunctionDeclaration) unit.getDeclarations().get(1); BlockFunctionBody body = (BlockFunctionBody) function.getFunctionExpression().getBody(); ReturnStatement statement = (ReturnStatement) body.getBlock().getStatements().get(1); SimpleIdentifier variableName = (SimpleIdentifier) statement.getExpression(); assertSame(typeA, variableName.getPropagatedType()); } public void test_is_postIf() throws Exception { Source source = addSource(createSource(// "class A {}", "A f(var p) {", " if (p is A) {", " A a = p;", " } else {", " return null;", " }", " return p;", "}")); LibraryElement library = resolve(source); assertNoErrors(source); verify(source); CompilationUnit unit = resolveCompilationUnit(source, library); ClassDeclaration classA = (ClassDeclaration) unit.getDeclarations().get(0); InterfaceType typeA = classA.getElement().getType(); FunctionDeclaration function = (FunctionDeclaration) unit.getDeclarations().get(1); BlockFunctionBody body = (BlockFunctionBody) function.getFunctionExpression().getBody(); ReturnStatement statement = (ReturnStatement) body.getBlock().getStatements().get(1); SimpleIdentifier variableName = (SimpleIdentifier) statement.getExpression(); assertSame(typeA, variableName.getPropagatedType()); } public void test_is_subclass() throws Exception { Source source = addSource(createSource(// "class A {}", "class B extends A {", " B m() => this;", "}", "A f(A p) {", " if (p is B) {", " return p.m();", " }", " return p;", "}")); LibraryElement library = resolve(source); assertNoErrors(source); CompilationUnit unit = resolveCompilationUnit(source, library); FunctionDeclaration function = (FunctionDeclaration) unit.getDeclarations().get(2); BlockFunctionBody body = (BlockFunctionBody) function.getFunctionExpression().getBody(); IfStatement ifStatement = (IfStatement) body.getBlock().getStatements().get(0); ReturnStatement statement = (ReturnStatement) ((Block) ifStatement.getThenStatement()).getStatements().get( 0); MethodInvocation invocation = (MethodInvocation) statement.getExpression(); assertNotNull(invocation.getMethodName().getPropagatedElement()); } public void test_is_while() throws Exception { Source source = addSource(createSource(// "class A {}", "A f(var p) {", " while (p is A) {", " return p;", " }", " return p;", "}")); LibraryElement library = resolve(source); assertNoErrors(source); verify(source); CompilationUnit unit = resolveCompilationUnit(source, library); ClassDeclaration classA = (ClassDeclaration) unit.getDeclarations().get(0); InterfaceType typeA = classA.getElement().getType(); FunctionDeclaration function = (FunctionDeclaration) unit.getDeclarations().get(1); BlockFunctionBody body = (BlockFunctionBody) function.getFunctionExpression().getBody(); WhileStatement whileStatement = (WhileStatement) body.getBlock().getStatements().get(0); ReturnStatement statement = (ReturnStatement) ((Block) whileStatement.getBody()).getStatements().get( 0); SimpleIdentifier variableName = (SimpleIdentifier) statement.getExpression(); assertSame(typeA, variableName.getPropagatedType()); } public void test_isNot_conditional() throws Exception { Source source = addSource(createSource(// "class A {}", "A f(var p) {", " return (p is! A) ? null : p;", "}")); LibraryElement library = resolve(source); assertNoErrors(source); verify(source); CompilationUnit unit = resolveCompilationUnit(source, library); ClassDeclaration classA = (ClassDeclaration) unit.getDeclarations().get(0); InterfaceType typeA = classA.getElement().getType(); FunctionDeclaration function = (FunctionDeclaration) unit.getDeclarations().get(1); BlockFunctionBody body = (BlockFunctionBody) function.getFunctionExpression().getBody(); ReturnStatement statement = (ReturnStatement) body.getBlock().getStatements().get(0); ConditionalExpression conditional = (ConditionalExpression) statement.getExpression(); SimpleIdentifier variableName = (SimpleIdentifier) conditional.getElseExpression(); assertSame(typeA, variableName.getPropagatedType()); } public void test_isNot_if() throws Exception { Source source = addSource(createSource(// "class A {}", "A f(var p) {", " if (p is! A) {", " return null;", " } else {", " return p;", " }", "}")); LibraryElement library = resolve(source); assertNoErrors(source); verify(source); CompilationUnit unit = resolveCompilationUnit(source, library); ClassDeclaration classA = (ClassDeclaration) unit.getDeclarations().get(0); InterfaceType typeA = classA.getElement().getType(); FunctionDeclaration function = (FunctionDeclaration) unit.getDeclarations().get(1); BlockFunctionBody body = (BlockFunctionBody) function.getFunctionExpression().getBody(); IfStatement ifStatement = (IfStatement) body.getBlock().getStatements().get(0); ReturnStatement statement = (ReturnStatement) ((Block) ifStatement.getElseStatement()).getStatements().get( 0); SimpleIdentifier variableName = (SimpleIdentifier) statement.getExpression(); assertSame(typeA, variableName.getPropagatedType()); } public void test_isNot_if_logicalOr() throws Exception { Source source = addSource(createSource(// "class A {}", "A f(var p) {", " if (p is! A || null == p) {", " return null;", " } else {", " return p;", " }", "}")); LibraryElement library = resolve(source); assertNoErrors(source); CompilationUnit unit = resolveCompilationUnit(source, library); ClassDeclaration classA = (ClassDeclaration) unit.getDeclarations().get(0); InterfaceType typeA = classA.getElement().getType(); FunctionDeclaration function = (FunctionDeclaration) unit.getDeclarations().get(1); BlockFunctionBody body = (BlockFunctionBody) function.getFunctionExpression().getBody(); IfStatement ifStatement = (IfStatement) body.getBlock().getStatements().get(0); ReturnStatement statement = (ReturnStatement) ((Block) ifStatement.getElseStatement()).getStatements().get( 0); SimpleIdentifier variableName = (SimpleIdentifier) statement.getExpression(); assertSame(typeA, variableName.getPropagatedType()); } public void test_isNot_postConditional() throws Exception { Source source = addSource(createSource(// "class A {}", "A f(var p) {", " A a = (p is! A) ? throw null : p;", " return p;", "}")); LibraryElement library = resolve(source); assertNoErrors(source); verify(source); CompilationUnit unit = resolveCompilationUnit(source, library); ClassDeclaration classA = (ClassDeclaration) unit.getDeclarations().get(0); InterfaceType typeA = classA.getElement().getType(); FunctionDeclaration function = (FunctionDeclaration) unit.getDeclarations().get(1); BlockFunctionBody body = (BlockFunctionBody) function.getFunctionExpression().getBody(); ReturnStatement statement = (ReturnStatement) body.getBlock().getStatements().get(1); SimpleIdentifier variableName = (SimpleIdentifier) statement.getExpression(); assertSame(typeA, variableName.getPropagatedType()); } public void test_isNot_postIf() throws Exception { Source source = addSource(createSource(// "class A {}", "A f(var p) {", " if (p is! A) {", " return null;", " }", " return p;", "}")); LibraryElement library = resolve(source); assertNoErrors(source); verify(source); CompilationUnit unit = resolveCompilationUnit(source, library); ClassDeclaration classA = (ClassDeclaration) unit.getDeclarations().get(0); InterfaceType typeA = classA.getElement().getType(); FunctionDeclaration function = (FunctionDeclaration) unit.getDeclarations().get(1); BlockFunctionBody body = (BlockFunctionBody) function.getFunctionExpression().getBody(); ReturnStatement statement = (ReturnStatement) body.getBlock().getStatements().get(1); SimpleIdentifier variableName = (SimpleIdentifier) statement.getExpression(); assertSame(typeA, variableName.getPropagatedType()); } public void test_issue20904BuggyTypePromotionAtIfJoin_2() throws Exception { // https://code.google.com/p/dart/issues/detail?id=20904 enableUnionTypes(false); String code = createSource(// "f(var message) {", " if (message is Function) {", " message = '';", " }", " message; // marker", // Here [message] could have any type. "}"); Type t = findMarkedIdentifier(code, "; // marker").getPropagatedType(); assertFalse(getTypeProvider().getStringType().equals(t)); assertFalse(getTypeProvider().getFunctionType().equals(t)); } public void test_issue20904BuggyTypePromotionAtIfJoin_5() throws Exception { // https://code.google.com/p/dart/issues/detail?id=20904 // // This is not an example of the 20904 bug, but rather, // an example of something that one obvious fix changes inadvertently: we // want to avoid using type information from is-checks when it // loses precision. I can't see how to get a bad hint this way, since // it seems the propagated type is not used to generate hints when a // more precise type would cause no hint. For example, for code like the // following, when the propagated type of [x] is [A] -- as happens for the // fix these tests aim to warn against -- there is no warning for // calling a method defined on [B] but not [A] (there aren't any, but pretend), // but there is for calling a method not defined on either. // By not overriding the propagated type via an is-check that loses // precision, we get more precise completion under an is-check. However, // I can only imagine strange code would make use of this feature. // // Here the is-check improves precision, so we use it. String code = createSource(// "class A {}", "class B extends A {}", "f() {", " var a = new A();", " var b = new B();", " b; // B", " if (a is B) {", " return a; // marker", " }", "}"); Type tB = findMarkedIdentifier(code, "; // B").getPropagatedType(); assertTypeOfMarkedExpression(code, null, tB); } public void test_issue20904BuggyTypePromotionAtIfJoin_6() throws Exception { // https://code.google.com/p/dart/issues/detail?id=20904 // // The other half of the *_5() test. // // Here the is-check loses precision, so we don't use it. String code = createSource(// "class A {}", "class B extends A {}", "f() {", " var b = new B();", " b; // B", " if (b is A) {", " return b; // marker", " }", "}"); Type tB = findMarkedIdentifier(code, "; // B").getPropagatedType(); assertTypeOfMarkedExpression(code, null, tB); } public void test_listLiteral_different() throws Exception { Source source = addSource(createSource(// "f() {", " var v = [0, '1', 2];", " return v[2];", "}")); LibraryElement library = resolve(source); assertNoErrors(source); verify(source); CompilationUnit unit = resolveCompilationUnit(source, library); FunctionDeclaration function = (FunctionDeclaration) unit.getDeclarations().get(0); BlockFunctionBody body = (BlockFunctionBody) function.getFunctionExpression().getBody(); ReturnStatement statement = (ReturnStatement) body.getBlock().getStatements().get(1); IndexExpression indexExpression = (IndexExpression) statement.getExpression(); assertNull(indexExpression.getPropagatedType()); } public void test_listLiteral_same() throws Exception { Source source = addSource(createSource(// "f() {", " var v = [0, 1, 2];", " return v[2];", "}")); LibraryElement library = resolve(source); assertNoErrors(source); verify(source); CompilationUnit unit = resolveCompilationUnit(source, library); FunctionDeclaration function = (FunctionDeclaration) unit.getDeclarations().get(0); BlockFunctionBody body = (BlockFunctionBody) function.getFunctionExpression().getBody(); ReturnStatement statement = (ReturnStatement) body.getBlock().getStatements().get(1); IndexExpression indexExpression = (IndexExpression) statement.getExpression(); assertNull(indexExpression.getPropagatedType()); Expression v = indexExpression.getTarget(); InterfaceType propagatedType = (InterfaceType) v.getPropagatedType(); assertSame(getTypeProvider().getListType().getElement(), propagatedType.getElement()); Type[] typeArguments = propagatedType.getTypeArguments(); assertLength(1, typeArguments); assertSame(getTypeProvider().getDynamicType(), typeArguments[0]); } public void test_mapLiteral_different() throws Exception { Source source = addSource(createSource(// "f() {", " var v = {'0' : 0, 1 : '1', '2' : 2};", " return v;", "}")); LibraryElement library = resolve(source); assertNoErrors(source); verify(source); CompilationUnit unit = resolveCompilationUnit(source, library); FunctionDeclaration function = (FunctionDeclaration) unit.getDeclarations().get(0); BlockFunctionBody body = (BlockFunctionBody) function.getFunctionExpression().getBody(); ReturnStatement statement = (ReturnStatement) body.getBlock().getStatements().get(1); SimpleIdentifier identifier = (SimpleIdentifier) statement.getExpression(); InterfaceType propagatedType = (InterfaceType) identifier.getPropagatedType(); assertSame(getTypeProvider().getMapType().getElement(), propagatedType.getElement()); Type[] typeArguments = propagatedType.getTypeArguments(); assertLength(2, typeArguments); assertSame(getTypeProvider().getDynamicType(), typeArguments[0]); assertSame(getTypeProvider().getDynamicType(), typeArguments[1]); } public void test_mapLiteral_same() throws Exception { Source source = addSource(createSource(// "f() {", " var v = {'a' : 0, 'b' : 1, 'c' : 2};", " return v;", "}")); LibraryElement library = resolve(source); assertNoErrors(source); verify(source); CompilationUnit unit = resolveCompilationUnit(source, library); FunctionDeclaration function = (FunctionDeclaration) unit.getDeclarations().get(0); BlockFunctionBody body = (BlockFunctionBody) function.getFunctionExpression().getBody(); ReturnStatement statement = (ReturnStatement) body.getBlock().getStatements().get(1); SimpleIdentifier identifier = (SimpleIdentifier) statement.getExpression(); InterfaceType propagatedType = (InterfaceType) identifier.getPropagatedType(); assertSame(getTypeProvider().getMapType().getElement(), propagatedType.getElement()); Type[] typeArguments = propagatedType.getTypeArguments(); assertLength(2, typeArguments); assertSame(getTypeProvider().getDynamicType(), typeArguments[0]); assertSame(getTypeProvider().getDynamicType(), typeArguments[1]); } public void test_mergePropagatedTypesAtJoinPoint_4() throws Exception { // https://code.google.com/p/dart/issues/detail?id=19929 assertTypeOfMarkedExpression( createSource( "f5(x) {", " var y = [];", " if (x) {", " y = 0;", " } else {", " return y;", " }", " // Propagated type is [int] here: correct.", " return y; // marker", "}"), // Don't care about the static type. null, getTypeProvider().getIntType()); } public void test_mergePropagatedTypesAtJoinPoint_6() throws Exception { // https://code.google.com/p/dart/issues/detail?id=19929 // // Labeled [break]s are unsafe for the purposes of [isAbruptTerminationStatement]. // // This is tricky: the [break] jumps back above the [if], making // it into a loop of sorts. The [if] type-propagation code assumes // that [break] does not introduce a loop. enableUnionTypes(false); String code = createSource( "f() {", " var x = 0;", " var c = false;", " L: ", " if (c) {", " } else {", " x = '';", " c = true;", " break L;", " }", " x; // marker", "}"); Type t = findMarkedIdentifier(code, "; // marker").getPropagatedType(); assertTrue(getTypeProvider().getIntType().isSubtypeOf(t)); assertTrue(getTypeProvider().getStringType().isSubtypeOf(t)); } public void test_objectMethodOnDynamicExpression_doubleEquals() throws Exception { // https://code.google.com/p/dart/issues/detail?id=20342 // // This was not actually part of Issue 20342, since the spec specifies a // static type of [bool] for [==] comparison and the implementation // was already consistent with the spec there. But, it's another // [Object] method, so it's included here. assertTypeOfMarkedExpression(createSource(// "f1(x) {", " var v = (x == x);", " return v; // marker", "}"), null, getTypeProvider().getBoolType()); } public void test_objectMethodOnDynamicExpression_hashCode() throws Exception { // https://code.google.com/p/dart/issues/detail?id=20342 assertTypeOfMarkedExpression(createSource(// "f1(x) {", " var v = x.hashCode;", " return v; // marker", "}"), null, getTypeProvider().getIntType()); } public void test_objectMethodOnDynamicExpression_runtimeType() throws Exception { // https://code.google.com/p/dart/issues/detail?id=20342 assertTypeOfMarkedExpression(createSource(// "f1(x) {", " var v = x.runtimeType;", " return v; // marker", "}"), null, getTypeProvider().getTypeType()); } public void test_objectMethodOnDynamicExpression_toString() throws Exception { // https://code.google.com/p/dart/issues/detail?id=20342 assertTypeOfMarkedExpression(createSource(// "f1(x) {", " var v = x.toString();", " return v; // marker", "}"), null, getTypeProvider().getStringType()); } public void test_propagatedReturnType_function_hasReturnType_returnsNull() throws Exception { String code = createSource(// "String f() => null;", "main() {", " var v = f();", "}"); assertPropagatedReturnType( code, getTypeProvider().getDynamicType(), getTypeProvider().getStringType()); } public void test_propagatedReturnType_function_lessSpecificStaticReturnType() throws Exception { String code = createSource(// "Object f() => 42;", "main() {", " var v = f();", "}"); assertPropagatedReturnType( code, getTypeProvider().getDynamicType(), getTypeProvider().getIntType()); } public void test_propagatedReturnType_function_moreSpecificStaticReturnType() throws Exception { String code = createSource(// "int f(v) => (v as num);", "main() {", " var v = f(3);", "}"); assertPropagatedReturnType( code, getTypeProvider().getDynamicType(), getTypeProvider().getIntType()); } public void test_propagatedReturnType_function_noReturnTypeName_blockBody_multipleReturns() throws Exception { String code = createSource(// "f() {", " if (true) return 0;", " return 1.0;", "}", "main() {", " var v = f();", "}"); assertPropagatedReturnType( code, getTypeProvider().getDynamicType(), getTypeProvider().getNumType()); } public void test_propagatedReturnType_function_noReturnTypeName_blockBody_oneReturn() throws Exception { String code = createSource(// "f() {", " var z = 42;", " return z;", "}", "main() {", " var v = f();", "}"); assertPropagatedReturnType( code, getTypeProvider().getDynamicType(), getTypeProvider().getIntType()); } public void test_propagatedReturnType_function_noReturnTypeName_expressionBody() throws Exception { String code = createSource(// "f() => 42;", "main() {", " var v = f();", "}"); assertPropagatedReturnType( code, getTypeProvider().getDynamicType(), getTypeProvider().getIntType()); } public void test_propagatedReturnType_localFunction() throws Exception { String code = createSource(// "main() {", " f() => 42;", " var v = f();", "}"); assertPropagatedReturnType( code, getTypeProvider().getDynamicType(), getTypeProvider().getIntType()); } public void test_query() throws Exception { Source source = addSource(createSource(// "import 'dart:html';", "", "main() {", " var v1 = query('a');", " var v2 = query('A');", " var v3 = query('body:active');", " var v4 = query('button[foo=\"bar\"]');", " var v5 = query('div.class');", " var v6 = query('input#id');", " var v7 = query('select#id');", " // invocation of method", " var m1 = document.query('div');", " // unsupported currently", " var b1 = query('noSuchTag');", " var b2 = query('DART_EDITOR_NO_SUCH_TYPE');", " var b3 = query('body div');", " return [v1, v2, v3, v4, v5, v6, v7, m1, b1, b2, b3];", "}")); LibraryElement library = resolve(source); assertNoErrors(source); verify(source); CompilationUnit unit = resolveCompilationUnit(source, library); FunctionDeclaration main = (FunctionDeclaration) unit.getDeclarations().get(0); BlockFunctionBody body = (BlockFunctionBody) main.getFunctionExpression().getBody(); ReturnStatement statement = (ReturnStatement) body.getBlock().getStatements().get(11); NodeList<Expression> elements = ((ListLiteral) statement.getExpression()).getElements(); assertEquals("AnchorElement", elements.get(0).getPropagatedType().getName()); assertEquals("AnchorElement", elements.get(1).getPropagatedType().getName()); assertEquals("BodyElement", elements.get(2).getPropagatedType().getName()); assertEquals("ButtonElement", elements.get(3).getPropagatedType().getName()); assertEquals("DivElement", elements.get(4).getPropagatedType().getName()); assertEquals("InputElement", elements.get(5).getPropagatedType().getName()); assertEquals("SelectElement", elements.get(6).getPropagatedType().getName()); assertEquals("DivElement", elements.get(7).getPropagatedType().getName()); assertEquals("Element", elements.get(8).getPropagatedType().getName()); assertEquals("Element", elements.get(9).getPropagatedType().getName()); assertEquals("Element", elements.get(10).getPropagatedType().getName()); } /** * @param code the code that assigns the value to the variable "v", no matter how. We check that * "v" has expected static and propagated type. */ private void assertPropagatedReturnType(String code, Type expectedStaticType, Type expectedPropagatedType) throws Exception { SimpleIdentifier identifier = findMarkedIdentifier(code, "v = "); assertSame(expectedStaticType, identifier.getStaticType()); assertSame(expectedPropagatedType, identifier.getPropagatedType()); } /** * Check the static and propagated types of the expression marked with "; // marker" comment. * * @param code source code to analyze, with the expression to check marked with "// marker". * @param expectedStaticType if non-null, check actual static type is equal to this. * @param expectedPropagatedType if non-null, check actual static type is equal to this. * @throws Exception */ private void assertTypeOfMarkedExpression(String code, Type expectedStaticType, Type expectedPropagatedType) throws Exception { SimpleIdentifier identifier = findMarkedIdentifier(code, "; // marker"); if (expectedStaticType != null) { assertEquals(expectedStaticType, identifier.getStaticType()); } if (expectedPropagatedType != null) { assertEquals(expectedPropagatedType, identifier.getPropagatedType()); } } /** * Return the {@code SimpleIdentifier} marked by {@code marker}. The source code must have no * errors and be verifiable. * * @param code source code to analyze. * @param marker marker identifying sought after expression in source code. * @return expression marked by the marker. * @throws Exception */ private SimpleIdentifier findMarkedIdentifier(String code, String marker) throws Exception { try { Source source = addSource(code); LibraryElement library = resolve(source); assertNoErrors(source); verify(source); CompilationUnit unit = resolveCompilationUnit(source, library); // Could generalize this further by making [SimpleIdentifier.class] a parameter. return findNode(unit, code, marker, SimpleIdentifier.class); } catch (AssertionFailedError exception) { // Is there a better exception to throw here? The point is that an assertion failure // here should be a failure, in both "test_*" and "fail_*" tests. // However, an assertion failure is success for the purpose of "fail_*" tests, so // without catching them here "fail_*" tests can succeed by failing for the wrong reason. throw new Exception("Unexexpected assertion failure: " + exception); } } }