blob: c2cc676f73143657e410f3684d42c85b2a1d2ed8 [file] [log] [blame]
// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
package com.google.dart.compiler.type;
import com.google.dart.compiler.parser.Token;
import com.google.dart.compiler.resolver.ClassElement;
import com.google.dart.compiler.resolver.ClassNodeElement;
import com.google.dart.compiler.resolver.CyclicDeclarationException;
import com.google.dart.compiler.resolver.TypeErrorCode;
import java.util.EnumSet;
import java.util.Map;
/**
* Test of static type analysis. This is mostly a test of {@link TypeAnalyzer}, but this test also
* exercises code in com.google.dart.compiler.resolver.
*/
public class TypeAnalyzerTest extends TypeAnalyzerTestCase {
/**
* There was problem that cyclic class declaration caused infinite loop.
* <p>
* http://code.google.com/p/dart/issues/detail?id=348
*/
public void test_cyclicDeclaration() {
Map<String, ClassNodeElement> source = loadSource(
"class Foo extends Bar {",
"}",
"class Bar extends Foo {",
"}");
analyzeClasses(source);
// Foo and Bar have cyclic declaration
ClassElement classFoo = source.get("Foo");
ClassElement classBar = source.get("Bar");
assertEquals(classFoo, classBar.getSupertype().getElement());
assertEquals(classBar, classFoo.getSupertype().getElement());
}
public void test_operator_indexAssign() {
Map<String, ClassNodeElement> source = loadSource(
"class A {",
"int operator []=(int index, var value) {}",
"}");
analyzeClasses(source, TypeErrorCode.OPERATOR_INDEX_ASSIGN_VOID_RETURN_TYPE);
}
public void testArrayLiteral() {
analyze("['x'];");
analyze("<String>['x'];");
analyzeFail("<int>['x'];", TypeErrorCode.TYPE_NOT_ASSIGNMENT_COMPATIBLE);
analyzeFail("<String>['x', 1];", TypeErrorCode.TYPE_NOT_ASSIGNMENT_COMPATIBLE);
analyze("List<String> strings = ['x'];");
analyze("List<String> strings = <String>['x'];");
analyze("List array = ['x'];");
analyze("List array = <String>['x'];");
analyze("List<int> ints = ['x'];");
analyzeFail("List<int> ints = <String>['x'];", TypeErrorCode.TYPE_NOT_ASSIGNMENT_COMPATIBLE);
}
public void testBadInitializers() {
analyzeFail("int i = .0;", TypeErrorCode.TYPE_NOT_ASSIGNMENT_COMPATIBLE);
analyzeFail("int j = 1.0;", TypeErrorCode.TYPE_NOT_ASSIGNMENT_COMPATIBLE);
}
public void testBinaryOperators() {
ClassElement cls = loadClass("class_with_operators.dart", "ClassWithOperators");
analyzeIn(cls, "i = o[0]", 0);
analyzeIn(cls, "s = o[0]", 1);
analyzeIn(cls, "o['fisk']", 1);
analyzeIn(cls, "i && o", 2);
analyzeIn(cls, "b && o", 1);
analyzeIn(cls, "i && b", 1);
analyzeIn(cls, "b && b", 0);
analyzeIn(cls, "i || o", 2);
analyzeIn(cls, "b || o", 1);
analyzeIn(cls, "i || b", 1);
analyzeIn(cls, "b || b", 0);
EnumSet<Token> userOperators = EnumSet.of(Token.ADD,
Token.SUB,
Token.MUL,
Token.DIV,
Token.TRUNC,
Token.MOD,
Token.LT,
Token.GT,
Token.LTE,
Token.GTE);
for (Token op : userOperators) {
String expression;
expression = String.format("untyped %s untyped", op.getSyntax());
analyzeIn(cls, expression, 0);
expression = String.format("o = untyped %s untyped", op.getSyntax());
analyzeIn(cls, expression, 0);
expression = String.format("s = untyped %s untyped", op.getSyntax());
analyzeIn(cls, expression, 0);
expression = String.format("o %s untyped", op.getSyntax());
analyzeIn(cls, expression, 0);
expression = String.format("o = o %s untyped", op.getSyntax());
analyzeIn(cls, expression, 0);
expression = String.format("s = o %s untyped", op.getSyntax());
analyzeIn(cls, expression, 1);
expression = String.format("o %s null", op.getSyntax());
analyzeIn(cls, expression, 0);
expression = String.format("o = o %s null", op.getSyntax());
analyzeIn(cls, expression, 0);
expression = String.format("s = o %s null", op.getSyntax());
analyzeIn(cls, expression, 1);
expression = String.format("o %s o", op.getSyntax());
analyzeIn(cls, expression, 0);
expression = String.format("o = o %s o", op.getSyntax());
analyzeIn(cls, expression, 0);
expression = String.format("s = o %s o", op.getSyntax());
analyzeIn(cls, expression, 1);
if (!op.equals(Token.ADD)) {
expression = String.format("o %s s", op.getSyntax());
analyzeIn(cls, expression, 1);
}
expression = String.format("o %s i", op.getSyntax());
analyzeIn(cls, expression, 1);
}
EnumSet<Token> equalityOperators = EnumSet.of(Token.EQ,
Token.NE);
for (Token op : equalityOperators) {
String expression;
expression = String.format("untyped %s untyped", op.getSyntax());
analyzeIn(cls, expression, 0);
expression = String.format("b = untyped %s untyped", op.getSyntax());
analyzeIn(cls, expression, 0);
expression = String.format("s = untyped %s untyped", op.getSyntax());
analyzeIn(cls, expression, 1);
expression = String.format("i = untyped %s untyped", op.getSyntax());
analyzeIn(cls, expression, 1);
expression = String.format("o %s o", op.getSyntax());
analyzeIn(cls, expression, 0);
expression = String.format("b = o %s o", op.getSyntax());
analyzeIn(cls, expression, 0);
expression = String.format("s = o %s o", op.getSyntax());
analyzeIn(cls, expression, 1);
expression = String.format("i = o %s o", op.getSyntax());
analyzeIn(cls, expression, 1);
expression = String.format("o %s s", op.getSyntax());
analyzeIn(cls, expression, 0);
expression = String.format("b = o %s s", op.getSyntax());
analyzeIn(cls, expression, 0);
expression = String.format("s = o %s s", op.getSyntax());
analyzeIn(cls, expression, 1);
expression = String.format("i = o %s s", op.getSyntax());
analyzeIn(cls, expression, 1);
}
EnumSet<Token> compoundAssignmentOperators =
EnumSet.of(Token.ASSIGN_ADD,
Token.ASSIGN_SUB,
Token.ASSIGN_MUL,
Token.ASSIGN_DIV,
Token.ASSIGN_MOD,
Token.ASSIGN_TRUNC);
for (Token op : compoundAssignmentOperators) {
String expression;
expression = String.format("o %s untyped", op.getSyntax());
analyzeIn(cls, expression, 0);
expression = String.format("o %s null", op.getSyntax());
analyzeIn(cls, expression, 0);
expression = String.format("o %s o", op.getSyntax());
analyzeIn(cls, expression, 0);
expression = String.format("o %s i", op.getSyntax());
analyzeIn(cls, expression, 1);
if (!op.equals(Token.ASSIGN_ADD)) {
expression = String.format("s %s untyped", op.getSyntax());
analyzeIn(cls, expression, 1);
expression = String.format("s %s null", op.getSyntax());
analyzeIn(cls, expression, 1);
expression = String.format("s %s o", op.getSyntax());
analyzeIn(cls, expression, 1);
}
}
analyzeIn(cls, "untyped is String", 0);
analyzeIn(cls, "b = untyped is String", 0);
analyzeIn(cls, "s = untyped is String", 1);
analyzeIn(cls, "s is String", 0);
analyzeIn(cls, "b = s is String", 0);
analyzeIn(cls, "s = s is String", 1);
analyzeIn(cls, "untyped is !String", 0);
analyzeIn(cls, "b = untyped is !String", 0);
analyzeIn(cls, "s = untyped is !String", 1);
analyzeIn(cls, "s is !String", 0);
analyzeIn(cls, "b = s is !String", 0);
analyzeIn(cls, "s = s is !String", 1);
analyzeFail("1 == !'s';", TypeErrorCode.TYPE_NOT_ASSIGNMENT_COMPATIBLE);
}
public void testBitOperators() {
Map<String, ClassNodeElement> source = loadSource(
"class ClassWithBitops {",
" num n;",
" int i;",
" double d;",
" ClassWithBitops o;",
" num foo() { return 42; }",
" int operator |(int arg) { return arg; }",
" int operator &(int arg) { return arg; }",
" int operator ^(int arg) { return arg; }",
" int operator >>(int arg) { return arg; }",
" int operator <<(int arg) { return arg; }",
" int operator ~() { return 1; }",
"}");
ClassElement cls = source.get("ClassWithBitops");
analyzeClasses(source);
EnumSet<Token> operators = EnumSet.of(Token.BIT_AND,
Token.BIT_OR,
Token.BIT_XOR,
Token.SHL,
Token.SAR);
for (Token operator : operators) {
analyzeIn(cls, String.format("n %s n" , operator), 0);
analyzeIn(cls, String.format("foo() %s i" , operator), 0);
analyzeIn(cls, String.format("o %s i" , operator), 0);
analyzeIn(cls, String.format("n = d %s i", operator), 1);
analyzeIn(cls, String.format("d = o %s i", operator), 1);
analyzeIn(cls, String.format("d = n %s i", operator), 1);
analyzeIn(cls, String.format("n %s o" , operator), 1);
}
EnumSet<Token> assignOperators = EnumSet.of(Token.ASSIGN_BIT_AND,
Token.ASSIGN_BIT_OR,
Token.ASSIGN_BIT_XOR,
Token.ASSIGN_SHL,
Token.ASSIGN_SAR);
for (Token operator : assignOperators) {
analyzeIn(cls, String.format("n %s n" , operator), 0);
analyzeIn(cls, String.format("n %s i" , operator), 0);
analyzeIn(cls, String.format("d %s i", operator), 1);
analyzeIn(cls, String.format("o %s i", operator), 1);
analyzeIn(cls, String.format("n %s o" , operator), 1);
}
analyzeIn(cls, "i = ~o", 0);
analyzeIn(cls, "i = ~n", 0);
analyzeIn(cls, "d = ~n", 1);
}
public void testConditionalExpression() {
analyze("true ? 1 : 2;");
analyze("null ? 1 : 2;");
analyzeFail("0 ? 1 : 2;",
TypeErrorCode.TYPE_NOT_ASSIGNMENT_COMPATIBLE);
analyzeFail("'' ? 1 : 2;",
TypeErrorCode.TYPE_NOT_ASSIGNMENT_COMPATIBLE);
analyzeFail("{ int i; true ? i = 2.7 : 2; }",
TypeErrorCode.TYPE_NOT_ASSIGNMENT_COMPATIBLE);
analyzeFail("{ int i; true ? 2 : i = 2.7; }",
TypeErrorCode.TYPE_NOT_ASSIGNMENT_COMPATIBLE);
analyze("{ int i; i = true ? 2.7 : 2; }");
}
public void testConstructorForwarding() {
Map<String, ClassNodeElement> classes = loadSource(
"class MissingArgument {",
" MissingArgument() : this.bar();",
" MissingArgument.bar(int i) {}",
"}",
"class IntArgument {",
" IntArgument() : this.bar(1);",
" IntArgument.bar(int i) {}",
"}",
"class ExtraIntArgument {",
" ExtraIntArgument() : this.bar(1, 1);",
" ExtraIntArgument.bar(int i) {}",
"}",
"class StringArgument {",
" StringArgument() : this.bar('');",
" StringArgument.bar(int i) {}",
"}",
"class NullArgument {",
" NullArgument() : this.bar(null);",
" NullArgument.bar(int i) {}",
"}",
"class OptionalParameter {",
" OptionalParameter() : this.bar();",
" OptionalParameter.bar([int i = null]) {}",
" OptionalParameter.foo() : this.bar('');",
"}");
analyzeClass(classes.get("MissingArgument"), 1);
analyzeClass(classes.get("IntArgument"), 0);
analyzeClass(classes.get("ExtraIntArgument"), 1);
analyzeClass(classes.get("StringArgument"), 1);
analyzeClass(classes.get("NullArgument"), 0);
analyzeClass(classes.get("OptionalParameter"), 1);
}
public void testCyclicTypeVariable() {
Map<String, ClassNodeElement> classes = loadSource(
"abstract class A<T> { }",
"typedef funcType<T>(T arg);",
"class B<T extends T> {}",
"class C<T extends A<T>> {}",
"class D<T extends funcType<T>> {}");
analyzeClasses(classes,
TypeErrorCode.CYCLIC_REFERENCE_TO_TYPE_VARIABLE);
ClassNodeElement B = classes.get("B");
analyzeClass(B, 1);
assertEquals(1, B.getType().getArguments().size());
ClassNodeElement C = classes.get("C");
analyzeClass(C, 0);
assertEquals(1, C.getType().getArguments().size());
ClassNodeElement D = classes.get("D");
analyzeClass(D, 0);
assertEquals(1, D.getType().getArguments().size());
}
public void testDoWhileStatement() {
analyze("do {} while (true);");
analyze("do {} while (null);");
analyzeFail("do {} while (0);",
TypeErrorCode.TYPE_NOT_ASSIGNMENT_COMPATIBLE);
analyzeFail("do {} while ('');",
TypeErrorCode.TYPE_NOT_ASSIGNMENT_COMPATIBLE);
analyzeFail("do { int i = 0.5; } while (true);",
TypeErrorCode.TYPE_NOT_ASSIGNMENT_COMPATIBLE);
analyzeFail("do { int i = 0.5; } while (null);",
TypeErrorCode.TYPE_NOT_ASSIGNMENT_COMPATIBLE);
}
public void testFieldAccess() {
ClassElement element = loadFile("class_with_supertypes.dart").get("ClassWithSupertypes");
assertNotNull("unable to locate ClassWithSupertypes", element);
analyzeIn(element, "field = 1", 0);
analyzeIn(element, "staticField = 1", 0);
analyzeIn(element, "fieldInSuperclass = 1", 0);
analyzeIn(element, "staticFieldInSuperclass = 1", 0);
analyzeIn(element, "field = field", 0);
analyzeIn(element, "field = staticField", 0);
analyzeIn(element, "field = fieldInSuperclass", 0);
analyzeIn(element, "field = staticFieldInSuperclass", 0);
analyzeIn(element, "field = fieldInInterface", 0);
analyzeIn(element, "field = staticFieldInInterface", 0);
analyzeIn(element, "field = 1", 0);
analyzeIn(element, "staticField = 1", 0);
analyzeIn(element, "fieldInSuperclass = 1", 0);
analyzeIn(element, "staticFieldInSuperclass = 1", 0);
analyzeIn(element, "field = ''", 1);
analyzeIn(element, "staticField = ''", 1);
analyzeIn(element, "fieldInSuperclass = ''", 1);
analyzeIn(element, "staticFieldInSuperclass = ''", 1);
analyzeIn(element, "field.noSuchField", 1);
analyzeIn(element, "staticField.noSuchField", 1);
analyzeIn(element, "fieldInSuperclass.noSuchField", 1);
analyzeIn(element, "staticFieldInSuperclass.noSuchField", 1);
analyzeIn(element, "fieldInInterface.noSuchField", 1);
analyzeIn(element, "staticFieldInInterface.noSuchField", 1);
analyzeIn(element, "new ClassWithSupertypes()", 0); // concrete class with abstract members
analyzeIn(element, "field = new ClassWithSupertypes().field", 0);
analyzeIn(element, "field = new ClassWithSupertypes().staticField", 1);
analyzeIn(element, "field = new ClassWithSupertypes().fieldInSuperclass", 0);
analyzeIn(element, "field = new ClassWithSupertypes().staticFieldInSuperclass", 1);
analyzeIn(element, "field = new ClassWithSupertypes().fieldInInterface", 0);
analyzeIn(element, "field = new ClassWithSupertypes().staticFieldInInterface", 1);
analyzeIn(element, "new ClassWithSupertypes().field = 1", 0);
analyzeIn(element, "new ClassWithSupertypes().staticField = 1", 1);
analyzeIn(element, "new ClassWithSupertypes().fieldInSuperclass = 1", 0);
analyzeIn(element, "new ClassWithSupertypes().staticFieldInSuperclass = 1", 1);
// Enable this test when constness is propagated:
// analyzeIn(element, "new ClassWithSupertypes().fieldInInterface = 1", 1);
analyzeIn(element, "new ClassWithSupertypes().staticFieldInInterface = 1", 1);
}
public void testFieldInitializers() {
Map<String, ClassNodeElement> classes = loadSource(
"class Good {",
" String string;",
" int i;",
" Good() : string = '', i = 1;",
" Good.name() : string = null, i = null;",
" Good.untyped(x) : string = x, i = x;",
" Good.string(String s) : string = s, i = 0;",
"}",
"class Bad {",
" String string;",
" int i;",
" Bad() : string = 1, i = '';",
" Bad.string(String s) : string = s, i = s;",
"}");
analyzeClass(classes.get("Good"), 0);
analyzeClass(classes.get("Bad"), 3);
}
public void testForStatement() {
analyze("for (;true;) {}");
analyze("for (;null;) {}");
analyzeFail("for (;0;) {}",
TypeErrorCode.TYPE_NOT_ASSIGNMENT_COMPATIBLE);
analyzeFail("for (;'';) {}",
TypeErrorCode.TYPE_NOT_ASSIGNMENT_COMPATIBLE);
// Foreach tests
analyze("{ List<String> strings = ['1','2','3']; for (String s in strings) {} }");
analyzeFail("{ List<int> ints = [1,2,3]; for (String s in ints) {} }",
TypeErrorCode.TYPE_NOT_ASSIGNMENT_COMPATIBLE);
analyzeFail("for (String s in true) {}", TypeErrorCode.INTERFACE_HAS_NO_METHOD_NAMED);
}
public void testFunctionObjectLiterals() {
analyze("{ bool b = () {}(); }");
analyze("{ int i = () {}(); }");
analyze("Function f = () {};");
}
public void testFunctionTypeAlias() {
Map<String, ClassNodeElement> classes = loadSource(
"typedef void VoidFunction();",
"typedef String StringFunction();",
"typedef String IntToStringFunction(int i);",
"class Foo {",
" VoidFunction voidFunction;",
" StringFunction stringFunction;",
" IntToStringFunction intToStringFunction;",
" Foo foo;",
" String string;",
" int i;",
"}");
analyzeClasses(classes);
ClassElement foo = classes.get("Foo");
analyzeIn(foo, "voidFunction()", 0);
analyzeIn(foo, "voidFunction(1)", 1);
analyzeIn(foo, "this.voidFunction()", 0);
analyzeIn(foo, "this.voidFunction(1)", 1);
analyzeIn(foo, "foo.voidFunction()", 0);
analyzeIn(foo, "foo.voidFunction(1)", 1);
analyzeIn(foo, "(voidFunction)()", 0);
analyzeIn(foo, "(voidFunction)(1)", 1);
analyzeIn(foo, "(this.voidFunction)()", 0);
analyzeIn(foo, "(this.voidFunction)(1)", 1);
analyzeIn(foo, "(foo.voidFunction)()", 0);
analyzeIn(foo, "(foo.voidFunction)(1)", 1);
analyzeIn(foo, "string = stringFunction()", 0);
analyzeIn(foo, "i = stringFunction()", 1);
analyzeIn(foo, "string = this.stringFunction()", 0);
analyzeIn(foo, "i = this.stringFunction()", 1);
analyzeIn(foo, "string = foo.stringFunction()", 0);
analyzeIn(foo, "i = foo.stringFunction()", 1);
analyzeIn(foo, "string = (stringFunction)()", 0);
analyzeIn(foo, "i = (stringFunction)()", 1);
analyzeIn(foo, "string = (this.stringFunction)()", 0);
analyzeIn(foo, "i = (this.stringFunction)()", 1);
analyzeIn(foo, "string = (foo.stringFunction)()", 0);
analyzeIn(foo, "i = (foo.stringFunction)()", 1);
analyzeIn(foo, "voidFunction = stringFunction", 0);
analyzeIn(foo, "stringFunction = intToStringFunction", 1);
analyzeIn(foo, "stringFunction = () { return ''; }", 0);
analyzeIn(foo, "intToStringFunction = () { return ''; }", 1);
}
public void testFunctionTypes() {
checkFunctionStatement("String foo() {};", "() -> String");
checkFunctionStatement("Object foo() {};", "() -> Object");
checkFunctionStatement("String foo(int i, bool b) {};", "(int, bool) -> String");
}
public void testGetAllSupertypes()
throws CyclicDeclarationException {
Map<String, ClassNodeElement> classes = loadSource(
"class A extends B<String> {",
"}",
"class B<T> extends C<G<T>> implements I<int>, I1<T> {",
"}",
"class C<U> {",
"}",
"abstract class I<S> extends I2<bool> {",
"}",
"class G<V> {",
"}",
"abstract class I1<W> {",
"}",
"abstract class I2<X> {",
"}",
"class D implements I2<int> {",
"}",
"class E extends D implements I2<int> {",
"}");
analyzeClasses(classes);
assertEquals("[]", object.getAllSupertypes().toString());
assertEquals("[B<String>, I<int>, I1<String>, I2<bool>, C<G<String>>, Object]",
classes.get("A").getAllSupertypes().toString());
assertEquals("[I<int>, I1<B.T>, I2<bool>, C<G<B.T>>, Object]",
classes.get("B").getAllSupertypes().toString());
assertEquals("[Object]", classes.get("C").getAllSupertypes().toString());
assertEquals("[I2<bool>, Object]", classes.get("I").getAllSupertypes().toString());
assertEquals("[Object]", classes.get("G").getAllSupertypes().toString());
assertEquals("[Object]", classes.get("I1").getAllSupertypes().toString());
assertEquals("[Object]", classes.get("I2").getAllSupertypes().toString());
assertEquals("[I2<int>, Object]", classes.get("D").getAllSupertypes().toString());
assertEquals("[I2<int>, D, I2<int>, Object]", classes.get("E").getAllSupertypes().toString());
}
public void testIdentifiers() {
analyze("{ int i; i = 2; }");
analyze("{ int j, k; j = 1; k = 3; }");
analyzeFail("{ int i; i = 'string'; }", TypeErrorCode.TYPE_NOT_ASSIGNMENT_COMPATIBLE);
analyzeFail("{ int j, k; k = 'string'; }",
TypeErrorCode.TYPE_NOT_ASSIGNMENT_COMPATIBLE);
analyzeFail("{ int j, k; j = 'string'; }",
TypeErrorCode.TYPE_NOT_ASSIGNMENT_COMPATIBLE);
}
public void testIfStatement() {
analyze("if (true) {}");
analyze("if (null) {}");
analyzeFail("if (0) {}",
TypeErrorCode.TYPE_NOT_ASSIGNMENT_COMPATIBLE);
analyzeFail("if ('') {}",
TypeErrorCode.TYPE_NOT_ASSIGNMENT_COMPATIBLE);
analyzeFail("{ int i = 27; if (true) { i = 2.7; } else {} }",
TypeErrorCode.TYPE_NOT_ASSIGNMENT_COMPATIBLE);
analyzeFail("{ int i = 27; if (true) {} else { i = 2.7; } }",
TypeErrorCode.TYPE_NOT_ASSIGNMENT_COMPATIBLE);
}
public void testImplementsAndOverrides() {
analyzeClasses(loadSource(
"abstract class Interface {",
" void foo(int x);",
" void bar();",
"}",
// Abstract class not reported until first instantiation.
"abstract class Class implements Interface {",
" Class() {}",
" String bar() { return null; }",
"}",
// Abstract class not reported until first instantiation.
"abstract class SubClass extends Class {",
" SubClass() : super() {}",
" Object bar() { return null; }",
"}",
"class SubSubClass extends Class {",
" num bar() { return null; }", // CANNOT_OVERRIDE_METHOD_NOT_SUBTYPE
" void foo(String x) {}", // CANNOT_OVERRIDE_METHOD_NOT_SUBTYPE
"}",
"class Usage {",
" m() {",
" }",
"}"),
TypeErrorCode.CANNOT_OVERRIDE_METHOD_NOT_SUBTYPE,
TypeErrorCode.CANNOT_OVERRIDE_METHOD_NOT_SUBTYPE);
}
public void testImplementsAndOverrides2() {
analyzeClasses(loadSource(
"abstract class Interface {",
" void foo(int x);",
"}",
// Abstract class not reported until first instantiation.
"class Class implements Interface {",
" Class() {}",
" void foo(String x) {}", // CANNOT_OVERRIDE_METHOD_NOT_SUBTYPE
"}"),
TypeErrorCode.CANNOT_OVERRIDE_METHOD_NOT_SUBTYPE);
}
public void testInitializedFields() {
Map<String, ClassNodeElement> classes = loadSource(
"class GoodField {",
" static final int i = 1;",
"}");
analyzeClass(classes.get("GoodField"), 0);
// Note, the TypeAnalyzer doesn't get a chance
// to get its hands on bad initializers anymore
// due to type checking in CompileTimeConstVisitor.
}
public void testInitializedLocals() {
analyze("void f([int x = 1]) {}");
analyzeFail("void f([int x = '']) {}", TypeErrorCode.TYPE_NOT_ASSIGNMENT_COMPATIBLE);
analyze("{ int x = 1; }");
analyzeFail("{ int x = ''; }", TypeErrorCode.TYPE_NOT_ASSIGNMENT_COMPATIBLE);
}
public void testInitializers() {
analyze("int i = 1;");
analyze("double d1 = .0;");
analyze("double d2 = 1.0;");
analyze("int x = null;");
}
public void testLabels() {
// Labels should be inside a function or method to be used
// break
analyze("foo() { L: for (;true;) { break L; } }");
analyze("foo() { int x; List<int> c; L: for (x in c) { break L; } }");
analyze("foo() { List<int> c; L: for (var x in c) { break L; } }");
analyze("foo() { L: while (true) { break L; } }");
analyze("foo() { L: do { break L; } while (true); }");
analyze("foo() { L: for (;true;) { for (;true;) { break L; } } }");
analyze("foo() { int x; List<int> c; L: for (x in c) { for (;true;) { break L; } } }");
analyze("foo() { List<int> c; L: for (var x in c) { for (;true;) { break L; } } }");
analyze("foo() { L: while (true) { for (;true;) { break L; } } }");
analyze("foo() { L: do { for (;true;) { break L; } } while (true); }");
// continue
analyze("foo() { L: for (;true;) { continue L; } }");
analyze("foo() { int x; List<int> c; L: for (x in c) { continue L; } }");
analyze("foo() { List<int> c; L: for (var x in c) { continue L; } }");
analyze("foo() { L: do { continue L; } while (true); }");
analyze("foo() { L: for (;true;) { for (;true;) { continue L; } } }");
analyze(
"foo() { int x; List<int> c; L: for (x in c) { for (;true;) { continue L; } } }");
analyze("foo() { List<int> c; L: for (var x in c) { for (;true;) { continue L; } } }");
analyze("foo() { L: while (true) { for (;true;) { continue L; } } }");
analyze("foo() { L: do { for (;true;) { continue L; } } while (true); }");
// corner cases
analyze("foo() { L: break L; }");
// TODO(zundel): Not type errors, but warnings.
analyze("foo() { L: for (;true;) { } }");
analyze("foo() { while (true) { L: var a; } }");
}
public void testLiterals() {
checkSimpleType(intElement.getType(), "1");
checkSimpleType(doubleElement.getType(), ".0");
checkSimpleType(doubleElement.getType(), "1.0");
checkSimpleType(bool.getType(), "true");
checkSimpleType(bool.getType(), "false");
checkSimpleType(string.getType(), "'fisk'");
checkSimpleType(string.getType(), "'f${null}sk'");
}
public void testMapLiteral() {
analyze("{ var x = {\"key\": 42}; }");
analyze("{ var x = {'key': 42}; }");
analyze("{ var x = <num>{'key': 42}; }");
analyze("{ var x = <int>{'key': 42}; }");
analyze("{ var x = <num>{'key': 0.42}; }");
analyze("{ var x = <num>{'key': 42}; }");
analyzeFail("{ var x = <int>{'key': 0.42}; }",
TypeErrorCode.TYPE_NOT_ASSIGNMENT_COMPATIBLE);
analyzeFail("{ int i; var x = {'key': i = 0.42}; }",
TypeErrorCode.TYPE_NOT_ASSIGNMENT_COMPATIBLE);
analyze("{ var x = const {\"key\": 42}; }");
analyze("{ var x = const {'key': 42}; }");
analyze("{ var x = const <num>{'key': 42}; }");
analyze("{ var x = const <int>{'key': 42}; }");
analyze("{ var x = const <num>{'key': 0.42}; }");
analyze("{ var x = const <num>{'key': 42}; }");
analyzeFail("{ var x = const <int>{'key': 0.42}; }",
TypeErrorCode.TYPE_NOT_ASSIGNMENT_COMPATIBLE);
analyzeFail("{ int i; var x = const {'key': i = 0.42}; }",
TypeErrorCode.TYPE_NOT_ASSIGNMENT_COMPATIBLE);
analyzeFail("{Map<num, num> x = const <num>{}; }",
TypeErrorCode.TYPE_NOT_ASSIGNMENT_COMPATIBLE);
}
public void testMethodInvocationArgumentCount() {
loadFile("class_with_methods.dart");
final String header = "{ ClassWithMethods c; ";
analyzeFail(header + "c.untypedNoArgumentMethod(1); }",
TypeErrorCode.EXTRA_ARGUMENT);
analyzeFail(header + "c.untypedOneArgumentMethod(); }",
TypeErrorCode.MISSING_ARGUMENT);
analyzeFail(header + "c.untypedOneArgumentMethod(1, 1); }",
TypeErrorCode.EXTRA_ARGUMENT);
analyzeFail(header + "c.untypedTwoArgumentMethod(); }",
TypeErrorCode.MISSING_ARGUMENT);
analyzeFail(header + "c.untypedTwoArgumentMethod(1, 2, 3); }",
TypeErrorCode.EXTRA_ARGUMENT);
analyzeFail(header + "c.intNoArgumentMethod(1); }",
TypeErrorCode.EXTRA_ARGUMENT);
analyzeFail(header + "c.intOneArgumentMethod(); }",
TypeErrorCode.MISSING_ARGUMENT);
analyzeFail(header + "c.intOneArgumentMethod(1, 1); }",
TypeErrorCode.EXTRA_ARGUMENT);
analyzeFail(header + "c.intTwoArgumentMethod(); }",
TypeErrorCode.MISSING_ARGUMENT);
analyzeFail(header + "c.intTwoArgumentMethod(1, 2, 3); }",
TypeErrorCode.EXTRA_ARGUMENT);
analyze(header + "c.untypedField(); }");
}
public void testMethodInvocations() {
loadFile("class_with_methods.dart");
final String header = "{ ClassWithMethods c; int i, j; ";
analyze(header + "int k = c.untypedNoArgumentMethod(); }");
analyze(header + "ClassWithMethods x = c.untypedNoArgumentMethod(); }");
analyze(header + "int k = c.untypedOneArgumentMethod(c); }");
analyze(header + "ClassWithMethods x = c.untypedOneArgumentMethod(1); }");
analyze(header + "int k = c.untypedOneArgumentMethod('string'); }");
analyze(header + "int k = c.untypedOneArgumentMethod(i); }");
analyze(header + "int k = c.untypedTwoArgumentMethod(1, 'string'); }");
analyze(header + "int k = c.untypedTwoArgumentMethod(i, j); }");
analyze(header + "ClassWithMethods x = c.untypedTwoArgumentMethod(i, c); }");
analyze(header + "int k = c.intNoArgumentMethod(); }");
analyzeFail(header + "ClassWithMethods x = c.intNoArgumentMethod(); }",
TypeErrorCode.TYPE_NOT_ASSIGNMENT_COMPATIBLE);
analyzeFail(header + "int k = c.intOneArgumentMethod(c); }",
TypeErrorCode.TYPE_NOT_ASSIGNMENT_COMPATIBLE);
analyzeFail(header + "ClassWithMethods x = c.intOneArgumentMethod(1); }",
TypeErrorCode.TYPE_NOT_ASSIGNMENT_COMPATIBLE);
analyzeFail(header + "int k = c.intOneArgumentMethod('string'); }",
TypeErrorCode.TYPE_NOT_ASSIGNMENT_COMPATIBLE);
analyze(header + "int k = c.intOneArgumentMethod(i); }");
analyzeFail(header + "int k = c.intTwoArgumentMethod(1, 'string'); }",
TypeErrorCode.TYPE_NOT_ASSIGNMENT_COMPATIBLE);
analyze(header + "int k = c.intTwoArgumentMethod(i, j); }");
analyzeFail(header + "ClassWithMethods x = c.intTwoArgumentMethod(i, j); }",
TypeErrorCode.TYPE_NOT_ASSIGNMENT_COMPATIBLE);
}
public void testNamedFunctionTypeAlias() {
loadFile("named_function_type_alias.dart");
analyze("VoidFunction f = () {};");
}
public void testOddStuff() {
Map<String, ClassNodeElement> classes = analyzeClasses(loadSource(
"class Class {",
" Class() {}",
" var field;",
" void m() {}",
" static void f() {}",
" static g(int i) {}",
"}"));
ClassElement cls = classes.get("Class");
analyzeIn(cls, "m().foo()", 1);
analyzeIn(cls, "m().x", 1);
analyzeIn(cls, "m()", 0);
analyzeIn(cls, "(m)().foo()", 1);
analyzeIn(cls, "(m)().x", 1);
analyzeIn(cls, "(m)()", 0);
analyzeIn(cls, "field = m()", 1);
analyzeIn(cls, "field = Class.f()", 1);
analyzeIn(cls, "field = (Class.f)()", 1);
analyzeIn(cls, "Class.f()", 0);
analyzeIn(cls, "(Class.f)()", 0);
analyzeIn(cls, "field = Class.g('x')", 1);
analyzeIn(cls, "field = (Class.g)('x')", 1);
analyzeIn(cls, "field = Class.g(0)", 0);
analyzeIn(cls, "field = (Class.g)(0)", 0);
analyzeFail("fisk: while (true) fisk++;", TypeErrorCode.CANNOT_BE_RESOLVED);
analyzeFail("new Class().m().x;", TypeErrorCode.VOID);
analyzeFail("(new Class().m)().x;", TypeErrorCode.VOID);
}
public void testParameterAccess() {
analyze("{ f(int x) { x = 1; } }");
analyzeFail("{ f(String x) { x = 1; } }",
TypeErrorCode.TYPE_NOT_ASSIGNMENT_COMPATIBLE);
analyze("{ f(int x, int y) { x = y; } }");
analyzeFail("{ f(String x, int y) { x = y; } }",
TypeErrorCode.TYPE_NOT_ASSIGNMENT_COMPATIBLE);
analyze("{ f(x, int y) { x = y; } }");
analyze("{ f(x, int y) { x = y; } }");
analyzeFail("{ f(String x) { x = 1;} }",
TypeErrorCode.TYPE_NOT_ASSIGNMENT_COMPATIBLE);
}
public void testParameterInitializers() {
Map<String, ClassNodeElement> classes = loadSource(
"class C1 { int i; C1(this.i) {} }",
"class C2 { String s; C2(int this.s) {} }",
"class C3 { int i; C3(double this.i) {} }",
"class C4 { int i; C4(num this.i) {} }");
analyzeClass(classes.get("C1"), 0);
analyzeClass(classes.get("C2"), 1);
analyzeClass(classes.get("C3"), 1);
analyzeClass(classes.get("C4"), 0);
}
public void testPropertyAccess() {
ClassElement cls = loadClass("classes_with_properties.dart", "ClassWithProperties");
analyzeIn(cls, "null", 0);
analyzeIn(cls, "noSuchField", 1);
analyzeIn(cls, "noSuchMethod()", 1);
analyzeIn(cls, "x.noSuchField", 0);
analyzeIn(cls, "x.noSuchMethod()", 0);
analyzeIn(cls, "x.x.noSuchField", 0);
analyzeIn(cls, "x.x.noSuchMethod()", 0);
analyzeIn(cls, "x.a.noSuchField", 0);
analyzeIn(cls, "x.a.noSuchMethod()", 0);
String[] typedFields = { "a", "b", "c"};
for (String field : typedFields) {
analyzeIn(cls, field + ".noSuchField", 1);
analyzeIn(cls, field + ".noSuchMethod()", 1);
analyzeIn(cls, field + ".a", 0);
analyzeIn(cls, field + ".a()", 1);
}
}
public void testReturn() {
analyzeFail(returnWithType("int", "'string'"),
TypeErrorCode.TYPE_NOT_ASSIGNMENT_COMPATIBLE);
analyze(returnWithType("", "'string'"));
analyze(returnWithType("Object", "'string'"));
analyze(returnWithType("String", "'string'"));
analyze(returnWithType("String", null));
analyze(returnWithType("int", null));
analyze(returnWithType("void", ""));
analyzeFail(returnWithType("void", 1), TypeErrorCode.TYPE_NOT_ASSIGNMENT_COMPATIBLE);
analyze(returnWithType("void", null));
analyzeFail(returnWithType("String", ""), TypeErrorCode.MISSING_RETURN_VALUE);
analyze("String foo() {};"); // Should probably fail, http://b/4484060.
}
public void testSuper() {
ClassElement sub = loadClass("covariant_class.dart", "Sub");
checkAssignIn(sub, "B", "field", 0);
checkAssignIn(sub, "C", "field", 1);
checkAssignIn(sub, "D", "field", 1);
checkAssignIn(sub, "B", "super.field", 0);
checkAssignIn(sub, "C", "super.field", 0);
checkAssignIn(sub, "D", "super.field", 1);
checkAssignIn(sub, "B", "accessor", 0);
checkAssignIn(sub, "C", "accessor", 1);
checkAssignIn(sub, "D", "accessor", 1);
checkAssignIn(sub, "B", "super.accessor", 0);
checkAssignIn(sub, "C", "super.accessor", 0);
checkAssignIn(sub, "D", "super.accessor", 1);
analyzeIn(sub, "accessor = b", 0);
analyzeIn(sub, "accessor = c", 1);
analyzeIn(sub, "accessor = d", 1);
analyzeIn(sub, "super.accessor = b", 0);
analyzeIn(sub, "super.accessor = c", 0);
analyzeIn(sub, "super.accessor = d", 1);
checkAssignIn(sub, "B", "method()", 0);
checkAssignIn(sub, "C", "method()", 1);
checkAssignIn(sub, "D", "method()", 1);
checkAssignIn(sub, "B", "super.untypedMethod()", 0);
checkAssignIn(sub, "C", "super.untypedMethod()", 0);
checkAssignIn(sub, "D", "super.untypedMethod()", 0);
checkAssignIn(sub, "B", "super.untypedField", 0);
checkAssignIn(sub, "C", "super.untypedField", 0);
checkAssignIn(sub, "D", "super.untypedField", 0);
checkAssignIn(sub, "B", "super.untypedAccessor", 0);
checkAssignIn(sub, "C", "super.untypedAccessor", 0);
checkAssignIn(sub, "D", "super.untypedAccessor", 0);
analyzeIn(sub, "super.untypedAccessor = b", 0);
analyzeIn(sub, "super.untypedAccessor = c", 0);
analyzeIn(sub, "super.untypedAccessor = d", 0);
checkAssignIn(sub, "B", "super.untypedMethod()", 0);
checkAssignIn(sub, "C", "super.untypedMethod()", 0);
checkAssignIn(sub, "D", "super.untypedMethod()", 0);
}
public void testSuperConstructorInvocation() {
Map<String, ClassNodeElement> classes = loadSource(
"class Super {",
" Super(int x) {}",
" Super.foo() {}",
" Super.bar([int i = null]) {}",
"}",
"class BadSub extends Super {",
" BadSub() : super('x') {}",
" BadSub.foo() : super.foo('x') {}",
" BadSub.bar() : super() {}",
" BadSub.baz() : super.foo(null) {}",
" BadSub.fisk() : super.bar('') {}",
" BadSub.hest() : super.bar(1, 2) {}",
"}",
"class NullSub extends Super {",
" NullSub() : super(null) {}",
" NullSub.foo() : super.bar(null) {}",
" NullSub.bar() : super.bar() {}",
"}",
"class IntSub extends Super {",
" IntSub() : super(1) {}",
" IntSub.foo() : super.bar(1) {}",
"}",
// The following works fine, but was claimed to be a bug:
"class A {",
" int value;",
" A([this.value = 3]) {}",
"}",
"class B extends A {",
" B() : super() {}",
"}");
analyzeClass(classes.get("Super"), 0);
analyzeClass(classes.get("BadSub"), 6);
analyzeClass(classes.get("NullSub"), 0);
analyzeClass(classes.get("IntSub"), 0);
analyzeClass(classes.get("A"), 0);
analyzeClass(classes.get("B"), 0);
}
public void testSwitch() {
analyze("{ int i = 27; switch(i) { case i: break; } }");
analyzeFail(
"{ switch(true) { case 1: break; case 'foo': break; }}",
TypeErrorCode.TYPE_NOT_ASSIGNMENT_COMPATIBLE);
analyzeFail(
"{ int i = 27; switch(true) { case false: i = 2.7; }}",
TypeErrorCode.TYPE_NOT_ASSIGNMENT_COMPATIBLE);
}
public void testThis() {
Map<String, ClassNodeElement> classes = loadFile("class_with_supertypes.dart");
ClassElement superclass = classes.get("Superclass");
assertNotNull("unable to locate Superclass", superclass);
ClassElement subclass = classes.get("ClassWithSupertypes");
assertNotNull("unable to locate ClassWithSupertypes", subclass);
analyzeIn(superclass, "() { String x = this; }", 1);
analyzeIn(superclass, "() { var x = this; }", 0);
analyzeIn(superclass, "() { ClassWithSupertypes x = this; }", 0);
analyzeIn(superclass, "() { Superclass x = this; }", 0);
analyzeIn(superclass, "() { Interface x = this; }", 1);
analyzeIn(subclass, "() { Interface x = this; }", 0);
}
public void testTryCatchFinally() {
analyze("try { } catch (_) { } finally { }");
analyzeFail("try { int i = 4.2; } catch (_) { } finally { }",
TypeErrorCode.TYPE_NOT_ASSIGNMENT_COMPATIBLE);
analyzeFail("try { } catch (_) { int i = 4.2; } finally { }",
TypeErrorCode.TYPE_NOT_ASSIGNMENT_COMPATIBLE);
analyzeFail("try { } catch (_) { } finally { int i = 4.2; }",
TypeErrorCode.TYPE_NOT_ASSIGNMENT_COMPATIBLE);
}
public void testTypeVariables() {
ClassElement cls = loadFile("class_with_type_parameter.dart").get("ClassWithTypeParameter");
assertNotNull("unable to locate ClassWithTypeParameter", cls);
analyzeIn(cls, "aField = tField", 0);
analyzeIn(cls, "bField = tField", 0);
analyzeIn(cls, "tField = aField", 0);
analyzeIn(cls, "tField = bField", 0);
analyzeIn(cls, "tField = null", 0);
analyzeIn(cls, "tField = 1", 1);
analyzeIn(cls, "tField = ''", 1);
analyzeIn(cls, "tField = true", 1);
analyzeIn(cls, "() { A a = null; T t = a; }()", 0);
analyzeIn(cls, "() { B b = null; T t = b; }()", 0);
analyzeIn(cls, "() { T t = null; A a = t; }()", 0);
analyzeIn(cls, "() { T t = null; B b = t; }()", 0);
analyzeIn(cls, "() { T t = 1; }()", 1);
analyzeIn(cls, "() { T t = ''; }()", 1);
analyzeIn(cls, "() { T t = true; }()", 1);
}
public void testUnaryOperators() {
Map<String, ClassNodeElement> source = loadSource(
"class Foo {",
" Foo foo;",
" bool b;",
" int i;",
" Foo operator +(int operand) { return this; }",
" Foo operator -(int operand) { return this; }",
"}",
"class Bar {",
" Bar bar;",
" Bar operator +(Bar operand) { return this; }",
" Bar operator -(Bar operand) { return this; }",
"}",
"class Baz<T extends Foo> {",
" T baz;",
"}",
"class Qux<T> { ",
" T qux; ",
" void x() { }",
" y() { }",
"}",
"class X {",
" X x;",
" Z operator +(int operand) { return null; }",
" Z operator -(int operand) { return null; }",
"}",
"class Y extends X { Y y; }",
"class Z extends X { Z z; }"
);
analyzeClasses(source);
ClassElement foo = source.get("Foo");
ClassElement bar = source.get("Bar");
ClassElement baz = source.get("Baz");
ClassElement qux = source.get("Qux");
ClassElement y = source.get("Y");
ClassElement z = source.get("Z");
for (Token op : EnumSet.of(Token.DEC, Token.INC)) {
analyzeIn(foo, String.format("%sfoo", op), 0);
analyzeIn(foo, String.format("i = %sfoo", op), 1);
analyzeIn(bar, String.format("%sbar", op), 1);
analyzeIn(baz, String.format("%sbaz", op), 0);
analyzeIn(qux, String.format("%squx", op), 1);
}
analyzeIn(z, "z = x++", 0);
analyzeIn(z, "z = ++x", 0);
analyzeIn(z, "z = x--", 0);
analyzeIn(z, "z = --x", 0);
analyzeIn(y, "y = x++", 0);
analyzeIn(y, "y = ++x", 1);
analyzeIn(y, "y = x--", 0);
analyzeIn(y, "y = --x", 1);
analyzeIn(foo, "b = !b", 0);
analyzeIn(foo, "foo = !foo", 2);
analyzeIn(foo, "b = !i", 1);
analyzeIn(foo, "foo = !b", 1);
analyzeIn(qux, "-x()", 1);
analyzeIn(qux, "-y()", 0);
}
public void testUnqualified() {
ClassElement element = loadClass("class_with_methods.dart", "ClassWithMethods");
checkAssignIn(element, "var", "intNoArgumentMethod()", 0);
checkAssignIn(element, "var", "intOneArgumentMethod(1)", 0);
checkAssignIn(element, "var", "intOneArgumentMethod('')", 1);
checkAssignIn(element, "int", "intNoArgumentMethod()", 0);
checkAssignIn(element, "int", "intOneArgumentMethod(1)", 0);
checkAssignIn(element, "int", "intOneArgumentMethod('')", 1);
checkAssignIn(element, "String", "intNoArgumentMethod()", 1);
checkAssignIn(element, "String", "intOneArgumentMethod(1)", 1);
checkAssignIn(element, "String", "intOneArgumentMethod('')", 2);
checkAssignIn(element, "var", "functionField()", 0);
checkAssignIn(element, "int", "functionField()", 0);
checkAssignIn(element, "String", "functionField()", 0);
checkAssignIn(element, "var", "functionField(1)", 0);
checkAssignIn(element, "int", "functionField('x')", 0);
checkAssignIn(element, "String", "functionField(2.2)", 0);
checkAssignIn(element, "var", "untypedField()", 0);
checkAssignIn(element, "int", "untypedField()", 0);
checkAssignIn(element, "String", "untypedField()", 0);
checkAssignIn(element, "var", "untypedField(1)", 0);
checkAssignIn(element, "int", "untypedField('x')", 0);
checkAssignIn(element, "String", "untypedField(2.2)", 0);
checkAssignIn(element, "var", "intField()", 1);
checkAssignIn(element, "int", "intField()", 1);
checkAssignIn(element, "String", "intField()", 1);
checkAssignIn(element, "var", "intField(1)", 1);
checkAssignIn(element, "int", "intField('x')", 1);
checkAssignIn(element, "String", "intField(2.2)", 1);
analyzeIn(element, "(x) { x(); }", 0);
analyzeIn(element, "(int x) { x(); }", 1);
analyzeIn(element, "(int x()) { int i = x(); }", 0);
analyzeIn(element, "(int x(String s)) { int i = x(1); }", 1);
analyzeIn(element, "(int x(String s)) { int i = x(''); }", 0);
}
public void testUnqualifiedGeneric() {
ClassElement element = loadClass("generic_class_with_supertypes.dart",
"GenericClassWithSupertypes");
checkAssignIn(element, "var", "localField", 0);
checkAssignIn(element, "T1", "localField", 0);
checkAssignIn(element, "T2", "localField", 1);
checkAssignIn(element, "var", "superField", 0);
checkAssignIn(element, "T1", "superField", 1);
checkAssignIn(element, "T2", "superField", 0);
checkAssignIn(element, "var", "interfaceField", 0);
checkAssignIn(element, "T1", "interfaceField", 0);
checkAssignIn(element, "T2", "interfaceField", 1);
checkAssignIn(element, "var", "localMethod(t2)", 0);
checkAssignIn(element, "T1", "localMethod(t2)", 0);
checkAssignIn(element, "T2", "localMethod(t2)", 1);
checkAssignIn(element, "var", "superMethod(t1)", 0);
checkAssignIn(element, "T1", "superMethod(t1)", 1);
checkAssignIn(element, "T2", "superMethod(t1)", 0);
checkAssignIn(element, "var", "interfaceMethod(t1)", 0);
checkAssignIn(element, "T1", "interfaceMethod(t1)", 0);
checkAssignIn(element, "T2", "interfaceMethod(t1)", 1);
}
public void testUnresolved() {
ClassElement element = loadClass("class_with_supertypes.dart", "ClassWithSupertypes");
analyzeIn(element, "this.field", 0);
analyzeIn(element, "null", 0);
analyzeIn(element, "noSuchField", 1);
analyzeIn(element, "noSuchMethod()", 1);
analyzeIn(element, "method()", 0);
analyzeIn(element, "field", 0);
analyzeIn(element, "this.noSuchField", 1);
analyzeIn(element, "this.noSuchMethod()", 1);
analyzeIn(element, "this.method()", 0);
analyzeIn(element, "staticMethod()", 0);
analyzeIn(element, "staticField", 0);
analyzeIn(element, "this.staticMethod()", 1);
analyzeIn(element, "this.staticField", 1);
analyzeIn(element, "ClassWithSupertypes.staticMethod()", 0);
analyzeIn(element, "ClassWithSupertypes.staticField", 0);
analyzeIn(element, "methodInSuperclass()", 0);
analyzeIn(element, "fieldInSuperclass", 0);
analyzeIn(element, "staticMethodInSuperclass()", 0);
analyzeIn(element, "staticFieldInSuperclass", 0);
analyzeIn(element, "this.methodInSuperclass()", 0);
analyzeIn(element, "this.fieldInSuperclass", 0);
analyzeIn(element, "this.staticMethodInSuperclass()", 1);
analyzeIn(element, "this.staticFieldInSuperclass", 1);
analyzeIn(element, "Superclass.staticMethodInSuperclass()", 0);
analyzeIn(element, "Superclass.staticFieldInSuperclass", 0);
analyzeIn(element, "methodInInterface()", 0);
analyzeIn(element, "fieldInInterface", 0);
analyzeIn(element, "this.methodInInterface()", 0);
analyzeIn(element, "this.fieldInInterface", 0);
analyzeIn(element, "staticFieldInInterface", 0);
analyzeIn(element, "Interface.staticFieldInInterface", 0);
analyzeIn(element, "this.staticFieldInInterface", 1);
}
public void testUnresolvedIdentifier() {
setExpectedTypeErrorCount(3);
checkType(typeProvider.getDynamicType(), "y");
checkExpectedTypeErrorCount();
}
public void testVoid() {
// Return a value from a void function.
analyze("void f() { return; }");
analyze("void f() { return null; }");
analyze("void f() { return f(); }");
analyzeFail("void f() { return 1; }", TypeErrorCode.TYPE_NOT_ASSIGNMENT_COMPATIBLE);
analyze("void f() { var x; return x; }");
// No-arg return from non-void function.
analyzeFail("int f() { return; }", TypeErrorCode.MISSING_RETURN_VALUE);
analyze("f() { return; }");
// Calling a method on a void expression, property access.
analyzeFail("void f() { f().m(); }", TypeErrorCode.VOID);
analyzeFail("void f() { f().x; }", TypeErrorCode.VOID);
// Passing a void argument to a method.
analyzeFail("{ void f() {} m(x) {} m(f()); }", TypeErrorCode.VOID);
// Assigning a void expression to a variable.
analyzeFail("{ void f() {} int x = f(); }", TypeErrorCode.VOID);
analyzeFail("{ void f() {} int x; x = f(); }", TypeErrorCode.VOID);
analyzeFail("{ void f() {} int x; x += f(); }", TypeErrorCode.VOID);
// Misc.
analyzeFail("{ void f() {} 1 + f(); }", TypeErrorCode.VOID);
analyzeFail("{ void f() {} f() + 1; }", TypeErrorCode.VOID);
analyzeFail("{ void f() {} var x; x && f(); }", TypeErrorCode.VOID);
analyzeFail("{ void f() {} !f(); }", TypeErrorCode.VOID);
analyzeFail("{ void f() {} -f(); }", TypeErrorCode.VOID);
// We seem to throw away prefix-plus in the parser:
// analyzeFail("{ void f() {} +f(); }", TypeErrorCode.VOID);
analyzeFail("{ void f() {} var x; x == f(); }", TypeErrorCode.VOID);
analyzeFail("{ void f() {} while (f()); }", TypeErrorCode.VOID);
analyzeFail("{ void f() {}; ({ 'x': f() }); }", TypeErrorCode.VOID);
}
public void testWhileStatement() {
analyze("while (true) {}");
analyze("while (null) {}");
analyzeFail("while (0) {}",
TypeErrorCode.TYPE_NOT_ASSIGNMENT_COMPATIBLE);
analyzeFail("while ('') {}",
TypeErrorCode.TYPE_NOT_ASSIGNMENT_COMPATIBLE);
}
}