| /* |
| * 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); |
| } |
| } |
| } |