| // 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.common.collect.Iterables; |
| import com.google.common.collect.Sets; |
| import com.google.dart.compiler.CommandLineOptions.CompilerOptions; |
| import com.google.dart.compiler.CompilerTestCase; |
| import com.google.dart.compiler.DartCompilationError; |
| import com.google.dart.compiler.DartCompilerErrorCode; |
| import com.google.dart.compiler.DefaultCompilerConfiguration; |
| import com.google.dart.compiler.ast.ASTVisitor; |
| import com.google.dart.compiler.ast.DartArrayAccess; |
| import com.google.dart.compiler.ast.DartBinaryExpression; |
| import com.google.dart.compiler.ast.DartClass; |
| import com.google.dart.compiler.ast.DartCommentNewName; |
| import com.google.dart.compiler.ast.DartCommentRefName; |
| import com.google.dart.compiler.ast.DartDeclaration; |
| import com.google.dart.compiler.ast.DartExprStmt; |
| import com.google.dart.compiler.ast.DartExpression; |
| import com.google.dart.compiler.ast.DartField; |
| import com.google.dart.compiler.ast.DartFieldDefinition; |
| import com.google.dart.compiler.ast.DartForInStatement; |
| import com.google.dart.compiler.ast.DartFunctionExpression; |
| import com.google.dart.compiler.ast.DartIdentifier; |
| import com.google.dart.compiler.ast.DartInvocation; |
| import com.google.dart.compiler.ast.DartMapLiteralEntry; |
| import com.google.dart.compiler.ast.DartMethodDefinition; |
| import com.google.dart.compiler.ast.DartMethodInvocation; |
| import com.google.dart.compiler.ast.DartNewExpression; |
| import com.google.dart.compiler.ast.DartNode; |
| import com.google.dart.compiler.ast.DartPropertyAccess; |
| import com.google.dart.compiler.ast.DartTypeNode; |
| import com.google.dart.compiler.ast.DartUnaryExpression; |
| import com.google.dart.compiler.ast.DartUnit; |
| import com.google.dart.compiler.ast.DartUnqualifiedInvocation; |
| import com.google.dart.compiler.parser.ParserErrorCode; |
| import com.google.dart.compiler.resolver.ClassElement; |
| import com.google.dart.compiler.resolver.Element; |
| import com.google.dart.compiler.resolver.ElementKind; |
| import com.google.dart.compiler.resolver.EnclosingElement; |
| import com.google.dart.compiler.resolver.FieldElement; |
| import com.google.dart.compiler.resolver.LibraryElement; |
| import com.google.dart.compiler.resolver.MethodElement; |
| import com.google.dart.compiler.resolver.NodeElement; |
| import com.google.dart.compiler.resolver.ResolverErrorCode; |
| import com.google.dart.compiler.resolver.TypeErrorCode; |
| import com.google.dart.compiler.resolver.VariableElement; |
| |
| import static com.google.dart.compiler.common.ErrorExpectation.assertErrors; |
| import static com.google.dart.compiler.common.ErrorExpectation.errEx; |
| import static com.google.dart.compiler.type.TypeQuality.EXACT; |
| import static com.google.dart.compiler.type.TypeQuality.INFERRED; |
| import static com.google.dart.compiler.type.TypeQuality.INFERRED_EXACT; |
| |
| import java.net.URI; |
| import java.util.List; |
| import java.util.Set; |
| import java.util.concurrent.atomic.AtomicInteger; |
| |
| /** |
| * Variant of {@link TypeAnalyzerTest}, which is based on {@link CompilerTestCase}. It is probably |
| * slower, not actually unit test, but easier to use if you need access to DartNode's. |
| */ |
| public class TypeAnalyzerCompilerTest extends CompilerTestCase { |
| |
| /** |
| * Top-level "main" function should not have parameters. |
| * <p> |
| * http://code.google.com/p/dart/issues/detail?id=3271 |
| */ |
| public void test_topLevelMainFunction() throws Exception { |
| AnalyzeLibraryResult libraryResult = analyzeLibrary( |
| "// filler filler filler filler filler filler filler filler filler filler", |
| "main(var p) {}", |
| "class A {", |
| " main(var p) {}", |
| "}", |
| ""); |
| assertErrors( |
| libraryResult.getErrors(), |
| errEx(ResolverErrorCode.MAIN_FUNCTION_PARAMETERS, 2, 1, 4)); |
| } |
| |
| /** |
| * A setter definition that is prefixed with the static modifier defines a static setter. |
| * Otherwise, it defines an instance setter. The name of a setter is obtained by appending the |
| * string `=' to the identifier given in its signature. |
| * <p> |
| * Hence, a setter name can never conflict with, override or be overridden by a getter or method. |
| * <p> |
| * http://code.google.com/p/dart/issues/detail?id=5153 |
| */ |
| public void test_setterNameImplicitEquals() throws Exception { |
| AnalyzeLibraryResult libraryResult = analyzeLibrary( |
| "// filler filler filler filler filler filler filler filler filler filler", |
| "class A {", |
| " set foo(x) {}", |
| " foo(x) {}", |
| "}", |
| "main() {", |
| " A a = new A();", |
| " a.foo = 0;", |
| " a.foo(0);", |
| "}", |
| ""); |
| assertErrors(libraryResult.getErrors()); |
| } |
| |
| /** |
| * <p> |
| * http://code.google.com/p/dart/issues/detail?id=4785 |
| */ |
| public void test_labelForBlockInSwitchCase() throws Exception { |
| AnalyzeLibraryResult libraryResult = analyzeLibrary( |
| "// filler filler filler filler filler filler filler filler filler filler", |
| "main() {", |
| " switch (0) {", |
| " case 0: qwerty: {", |
| " break qwerty;", |
| " }", |
| " break;", |
| " }", |
| "}", |
| ""); |
| assertErrors(libraryResult.getErrors()); |
| } |
| |
| /** |
| * We should support resolving to the method "call". |
| * <p> |
| * http://code.google.com/p/dart/issues/detail?id=1355 |
| */ |
| public void test_resolveCallMethod() throws Exception { |
| AnalyzeLibraryResult libraryResult = analyzeLibrary( |
| "// filler filler filler filler filler filler filler filler filler filler", |
| "class A {", |
| " call() => 42;", |
| "}", |
| "main() {", |
| " A a = new A();", |
| " a();", |
| "}", |
| ""); |
| assertErrors(libraryResult.getErrors()); |
| // find a() |
| DartIdentifier aVar = findNode(DartIdentifier.class, "a()"); |
| assertNotNull(aVar); |
| DartUnqualifiedInvocation invocation = (DartUnqualifiedInvocation) aVar.getParent(); |
| // analyze a() element |
| MethodElement element = (MethodElement) invocation.getElement(); |
| assertNotNull(element); |
| assertEquals("call", element.getName()); |
| assertEquals( |
| libraryResult.source.indexOf("call() => 42"), |
| element.getNameLocation().getOffset()); |
| } |
| |
| /** |
| * It is a compile-time error if a typedef refers to itself via a chain of references that does |
| * not include a class or interface type. |
| * <p> |
| * http://code.google.com/p/dart/issues/detail?id=3534 |
| */ |
| public void test_functionTypeAlias_selfRerences_direct() throws Exception { |
| AnalyzeLibraryResult libraryResult = analyzeLibrary( |
| "// filler filler filler filler filler filler filler filler filler filler", |
| "typedef A A();", |
| "typedef B(B b);", |
| "typedef C([C c]);", |
| "typedef D({D d});", |
| "typedef E<T extends E>();", |
| ""); |
| assertErrors( |
| libraryResult.getErrors(), |
| errEx(TypeErrorCode.TYPE_ALIAS_CANNOT_REFERENCE_ITSELF, 2, 1, 14), |
| errEx(TypeErrorCode.TYPE_ALIAS_CANNOT_REFERENCE_ITSELF, 3, 1, 15), |
| errEx(TypeErrorCode.TYPE_ALIAS_CANNOT_REFERENCE_ITSELF, 4, 1, 17), |
| errEx(TypeErrorCode.TYPE_ALIAS_CANNOT_REFERENCE_ITSELF, 5, 1, 17), |
| errEx(TypeErrorCode.TYPE_ALIAS_CANNOT_REFERENCE_ITSELF, 6, 1, 25)); |
| } |
| |
| /** |
| * It is a compile-time error if a typedef refers to itself via a chain of references that does |
| * not include a class or interface type. |
| * <p> |
| * http://code.google.com/p/dart/issues/detail?id=3534 |
| */ |
| public void test_functionTypeAlias_selfRerences_indirect() throws Exception { |
| AnalyzeLibraryResult libraryResult = analyzeLibrary( |
| "// filler filler filler filler filler filler filler filler filler filler", |
| "typedef B1 A1();", |
| "typedef A1 B1();", |
| "typedef B2 A2();", |
| "typedef B2(A2 a);", |
| "typedef B3 A3();", |
| "typedef B3([A3 a]);", |
| "typedef B4 A4();", |
| "typedef B4({A4 a});", |
| "typedef A5<T extends B5>();", |
| "typedef B5(A5 a);", |
| ""); |
| assertErrors( |
| libraryResult.getErrors(), |
| errEx(TypeErrorCode.TYPE_ALIAS_CANNOT_REFERENCE_ITSELF, 2, 1, 16), |
| errEx(TypeErrorCode.TYPE_ALIAS_CANNOT_REFERENCE_ITSELF, 3, 1, 16), |
| errEx(TypeErrorCode.TYPE_ALIAS_CANNOT_REFERENCE_ITSELF, 4, 1, 16), |
| errEx(TypeErrorCode.TYPE_ALIAS_CANNOT_REFERENCE_ITSELF, 5, 1, 17), |
| errEx(TypeErrorCode.TYPE_ALIAS_CANNOT_REFERENCE_ITSELF, 6, 1, 16), |
| errEx(TypeErrorCode.TYPE_ALIAS_CANNOT_REFERENCE_ITSELF, 7, 1, 19), |
| errEx(TypeErrorCode.TYPE_ALIAS_CANNOT_REFERENCE_ITSELF, 8, 1, 16), |
| errEx(TypeErrorCode.TYPE_ALIAS_CANNOT_REFERENCE_ITSELF, 9, 1, 19), |
| errEx(TypeErrorCode.TYPE_ALIAS_CANNOT_REFERENCE_ITSELF, 10, 1, 27), |
| errEx(TypeErrorCode.TYPE_ALIAS_CANNOT_REFERENCE_ITSELF, 11, 1, 17)); |
| } |
| |
| /** |
| * Type parameters should not conflict with formal parameters. |
| * <p> |
| * http://code.google.com/p/dart/issues/detail?id=5302 |
| */ |
| public void test_functionTypeAlias_typePaarameter_scope() throws Exception { |
| AnalyzeLibraryResult libraryResult = analyzeLibrary( |
| "// filler filler filler filler filler filler filler filler filler filler", |
| "typedef f<f>(f);", |
| ""); |
| assertErrors(libraryResult.getErrors()); |
| } |
| |
| /** |
| * It is a compile-time error if initializer list contains an initializer for a variable that |
| * is not an instance variable declared in the immediately surrounding class. |
| * <p> |
| * http://code.google.com/p/dart/issues/detail?id=3181 |
| */ |
| public void test_initializerForNotField() throws Exception { |
| AnalyzeLibraryResult libraryResult = analyzeLibrary( |
| "// filler filler filler filler filler filler filler filler filler filler", |
| "var x;", |
| "class A {", |
| " A() : x = 5 {}", |
| "}", |
| ""); |
| assertErrors( |
| libraryResult.getErrors(), |
| errEx(ResolverErrorCode.INIT_FIELD_ONLY_IMMEDIATELY_SURROUNDING_CLASS, 4, 9, 1)); |
| } |
| |
| /** |
| * Tests that we correctly provide {@link Element#getEnclosingElement()} for method of class. |
| */ |
| public void test_resolveClassMethod() throws Exception { |
| AnalyzeLibraryResult libraryResult = |
| analyzeLibrary( |
| "class Object {}", |
| "class Test {", |
| " foo() {", |
| " f();", |
| " }", |
| " f() {", |
| " }", |
| "}"); |
| DartUnit unit = libraryResult.getLibraryUnitResult().getUnits().iterator().next(); |
| // find f() invocation |
| DartInvocation invocation = findInvocationSimple(unit, "f()"); |
| assertNotNull(invocation); |
| // referenced Element should be resolved to MethodElement |
| Element methodElement = invocation.getElement(); |
| assertNotNull(methodElement); |
| assertSame(ElementKind.METHOD, methodElement.getKind()); |
| assertEquals("f", ((MethodElement) methodElement).getOriginalName()); |
| // enclosing Element of MethodElement is ClassElement |
| Element classElement = methodElement.getEnclosingElement(); |
| assertNotNull(classElement); |
| assertSame(ElementKind.CLASS, classElement.getKind()); |
| assertEquals("Test", ((ClassElement) classElement).getOriginalName()); |
| } |
| |
| /** |
| * Test that local {@link DartFunctionExpression} has {@link Element} with enclosing |
| * {@link Element}. |
| * <p> |
| * http://code.google.com/p/dart/issues/detail?id=145 |
| */ |
| public void test_resolveLocalFunction() throws Exception { |
| AnalyzeLibraryResult libraryResult = |
| analyzeLibrary( |
| "class Object {}", |
| "class Test {", |
| " foo() {", |
| " f() {", |
| " }", |
| " f();", |
| " }", |
| "}"); |
| DartUnit unit = libraryResult.getLibraryUnitResult().getUnits().iterator().next(); |
| // find f() invocation |
| DartInvocation invocation = findInvocationSimple(unit, "f()"); |
| assertNotNull(invocation); |
| // referenced Element should be resolved to MethodElement |
| Element functionElement = invocation.getElement(); |
| assertNotNull(functionElement); |
| assertSame(ElementKind.FUNCTION_OBJECT, functionElement.getKind()); |
| assertEquals("f", ((MethodElement) functionElement).getOriginalName()); |
| // enclosing Element of this FUNCTION_OBJECT is enclosing method |
| MethodElement methodElement = (MethodElement) functionElement.getEnclosingElement(); |
| assertNotNull(methodElement); |
| assertSame(ElementKind.METHOD, methodElement.getKind()); |
| assertEquals("foo", methodElement.getName()); |
| // use EnclosingElement methods implementations in MethodElement |
| assertEquals(false, methodElement.isInterface()); |
| assertEquals(true, Iterables.isEmpty(methodElement.getMembers())); |
| assertEquals(null, methodElement.lookupLocalElement("f")); |
| } |
| |
| /** |
| * It is a static warning if the type of "switch expression" may not be assigned to the type of |
| * "case expression". |
| * <p> |
| * http://code.google.com/p/dart/issues/detail?id=3269 |
| */ |
| public void test_switchExpression_case_switchTypeMismatch() throws Exception { |
| AnalyzeLibraryResult libraryResult = analyzeLibrary( |
| "// filler filler filler filler filler filler filler filler filler filler", |
| "main() {", |
| " int v = 1;", |
| " switch (v) {", |
| " case 0: break;", |
| " }", |
| " switch (v) {", |
| " case 'a': break;", |
| " }", |
| "}", |
| ""); |
| assertErrors( |
| libraryResult.getErrors(), |
| errEx(TypeErrorCode.TYPE_NOT_ASSIGNMENT_COMPATIBLE, 8, 10, 3)); |
| } |
| |
| /** |
| * It is a compile-time error if the values of the case expressions are not compile-time |
| * constants. |
| * <p> |
| * http://code.google.com/p/dart/issues/detail?id=4553 |
| */ |
| public void test_switchExpression_case_anyConst() throws Exception { |
| AnalyzeLibraryResult libraryResult = analyzeLibrary( |
| "// filler filler filler filler filler filler filler filler filler filler", |
| "class A {", |
| " const A();", |
| "}", |
| "const A CONST_1 = const A();", |
| "const A CONST_2 = const A();", |
| "foo(var v) {", |
| " switch (v) {", |
| " case 0: break;", |
| " }", |
| " switch (v) {", |
| " case '0': break;", |
| " }", |
| " switch (v) {", |
| " case 0.0: break;", |
| " }", |
| " switch (v) {", |
| " case CONST_1: break;", |
| " case CONST_2: break;", |
| " }", |
| "}", |
| ""); |
| assertErrors(libraryResult.getErrors()); |
| } |
| |
| /** |
| * It is a compile-time error if the values of the case expressions are not compile-time |
| * constants. |
| * <p> |
| * http://code.google.com/p/dart/issues/detail?id=4553 |
| */ |
| public void test_switchExpression_case_notConst() throws Exception { |
| AnalyzeLibraryResult libraryResult = analyzeLibrary( |
| "// filler filler filler filler filler filler filler filler filler filler", |
| "class A {}", |
| "foo(var v) {", |
| " A notConst = new A();", |
| " switch (v) {", |
| " case notConst: break;", |
| " }", |
| "}", |
| ""); |
| assertErrors( |
| libraryResult.getErrors(), |
| errEx(ResolverErrorCode.EXPECTED_CONSTANT_EXPRESSION, 6, 10, 8)); |
| } |
| |
| /** |
| * It is a compile-time error if the class C implements the operator ==. |
| * <p> |
| * http://code.google.com/p/dart/issues/detail?id=4553 |
| */ |
| public void test_switchExpression_case_hasOperatorEquals() throws Exception { |
| AnalyzeLibraryResult libraryResult = analyzeLibrary( |
| "// filler filler filler filler filler filler filler filler filler filler", |
| "class C {", |
| " const C();", |
| " operator ==(other) => false;", |
| "}", |
| "const C CONST = const C();", |
| "foo(var v) {", |
| " switch (v) {", |
| " case CONST: break;", |
| " }", |
| "}", |
| ""); |
| assertErrors( |
| libraryResult.getErrors(), |
| errEx(TypeErrorCode.CASE_EXPRESSION_TYPE_SHOULD_NOT_HAVE_EQUALS, 9, 10, 5)); |
| } |
| |
| /** |
| * It is a compile-time error if the values of the case expressions do not all have the same type. |
| * <p> |
| * http://code.google.com/p/dart/issues/detail?id=3528 |
| */ |
| public void test_switchExpression_case_differentTypes() throws Exception { |
| AnalyzeLibraryResult libraryResult = analyzeLibrary( |
| "// filler filler filler filler filler filler filler filler filler filler", |
| "foo(var v) {", |
| " switch (v) {", |
| " case 0: break;", |
| " case 'a': break;", |
| " }", |
| "}", |
| ""); |
| assertErrors( |
| libraryResult.getErrors(), |
| errEx(TypeErrorCode.CASE_EXPRESSIONS_SHOULD_BE_SAME_TYPE, 5, 10, 3)); |
| } |
| |
| public void test_switchExpression_case_constLocalVariable() throws Exception { |
| AnalyzeLibraryResult libraryResult = analyzeLibrary( |
| "// filler filler filler filler filler filler filler filler filler filler", |
| "foo(var v) {", |
| " const int VALUE = 0;", |
| " switch (v) {", |
| " case VALUE: break;", |
| " }", |
| "}", |
| ""); |
| assertErrors(libraryResult.getErrors()); |
| } |
| |
| /** |
| * <p> |
| * http://code.google.com/p/dart/issues/detail?id=2862 |
| */ |
| public void test_switchCase_fallThrough() throws Exception { |
| AnalyzeLibraryResult libraryResult = analyzeLibrary( |
| "// filler filler filler filler filler filler filler filler filler filler", |
| "foo(int x) {", |
| " while (true) {", |
| " switch (x) {", |
| " case 0:", |
| " break;", |
| " case 1:", |
| " continue;", |
| " case 2:", |
| " return;", |
| " case 3:", |
| " throw new Exception();", |
| " case 4:", |
| " bar();", |
| " }", |
| " }", |
| "}", |
| "bar() {}", |
| ""); |
| assertErrors( |
| libraryResult.getErrors(), |
| errEx(ResolverErrorCode.SWITCH_CASE_FALL_THROUGH, 14, 9, 6)); |
| } |
| |
| /** |
| * Language specification requires that factory should be declared in class. However declaring |
| * factory on top level should not cause exceptions in compiler. |
| * <p> |
| * http://code.google.com/p/dart/issues/detail?id=345 |
| */ |
| public void test_badTopLevelFactory() throws Exception { |
| AnalyzeLibraryResult libraryResult = analyzeLibrary("factory foo() {}"); |
| DartUnit unit = libraryResult.getLibraryUnitResult().getUnits().iterator().next(); |
| DartMethodDefinition factory = (DartMethodDefinition) unit.getTopLevelNodes().get(0); |
| assertNotNull(factory); |
| // this factory has name, which is allowed for normal method |
| assertEquals(true, factory.getName() instanceof DartIdentifier); |
| assertEquals("foo", ((DartIdentifier) factory.getName()).getName()); |
| // compilation error expected |
| assertBadTopLevelFactoryError(libraryResult); |
| } |
| |
| /** |
| * Asserts that given {@link AnalyzeLibraryResult} contains {@link DartCompilationError} for |
| * invalid factory on top level. |
| */ |
| private void assertBadTopLevelFactoryError(AnalyzeLibraryResult libraryResult) { |
| List<DartCompilationError> compilationErrors = libraryResult.getCompilationErrors(); |
| assertEquals(1, compilationErrors.size()); |
| DartCompilationError compilationError = compilationErrors.get(0); |
| assertEquals(ParserErrorCode.FACTORY_CANNOT_BE_TOP_LEVEL, compilationError.getErrorCode()); |
| assertEquals(1, compilationError.getLineNumber()); |
| assertEquals(1, compilationError.getColumnNumber()); |
| assertEquals("factory".length(), compilationError.getLength()); |
| } |
| |
| /** |
| * @return the {@link DartInvocation} with given source. This is inaccurate approach, but good |
| * enough for specific tests. |
| */ |
| private static DartInvocation findInvocationSimple(DartNode rootNode, |
| final String invocationString) { |
| final DartInvocation invocationRef[] = new DartInvocation[1]; |
| rootNode.accept(new ASTVisitor<Void>() { |
| @Override |
| public Void visitInvocation(DartInvocation node) { |
| if (node.toSource().equals(invocationString)) { |
| invocationRef[0] = node; |
| } |
| return super.visitInvocation(node); |
| } |
| }); |
| return invocationRef[0]; |
| } |
| |
| /** |
| * In contrast, if A is intended to be concrete, the checker should warn about all unimplemented |
| * methods, but allow clients to instantiate it freely. |
| */ |
| public void test_warnAbstract_onConcreteClassDeclaration_hasUnimplemented_method_fromInterface() |
| throws Exception { |
| AnalyzeLibraryResult libraryResult = |
| analyzeLibrary( |
| "abstract class Foo {", |
| " int fooA;", |
| " void fooB();", |
| "}", |
| "abstract class Bar {", |
| " void barA();", |
| "}", |
| "class A implements Foo, Bar {", |
| "}", |
| "main() {", |
| " new A();", |
| "}"); |
| assertErrors( |
| libraryResult.getErrors(), |
| errEx(TypeErrorCode.CONTRETE_CLASS_WITH_UNIMPLEMENTED_MEMBERS, 8, 7, 1)); |
| { |
| DartCompilationError typeError = libraryResult.getErrors().get(0); |
| String message = typeError.getMessage(); |
| assertTrue(message.contains("# From Foo:")); |
| assertTrue(message.contains("int fooA")); |
| assertTrue(message.contains("void fooB()")); |
| assertTrue(message.contains("# From Bar:")); |
| assertTrue(message.contains("void barA()")); |
| } |
| } |
| |
| /** |
| * In contrast, if A is intended to be concrete, the checker should warn about all unimplemented |
| * methods, but allow clients to instantiate it freely. |
| */ |
| public void test_warnAbstract_onConcreteClassDeclaration_hasUnimplemented_method_inherited() |
| throws Exception { |
| AnalyzeLibraryResult libraryResult = |
| analyzeLibrary( |
| "abstract class A {", |
| " void foo();", |
| "}", |
| "class B extends A {", |
| "}", |
| "main() {", |
| " new B();", |
| "}"); |
| assertErrors( |
| libraryResult.getErrors(), |
| errEx(TypeErrorCode.CONTRETE_CLASS_WITH_UNIMPLEMENTED_MEMBERS, 4, 7, 1)); |
| { |
| DartCompilationError typeError = libraryResult.getErrors().get(0); |
| String message = typeError.getMessage(); |
| assertTrue(message.contains("# From A:")); |
| assertTrue(message.contains("void foo()")); |
| } |
| } |
| |
| /** |
| * In contrast, if A is intended to be concrete, the checker should warn about all unimplemented |
| * methods, but allow clients to instantiate it freely. |
| */ |
| public void test_warnAbstract_onConcreteClassDeclaration_hasUnimplemented_method_self() |
| throws Exception { |
| AnalyzeLibraryResult libraryResult = |
| analyzeLibrary( |
| "class A {", |
| " void foo();", |
| "}", |
| "main() {", |
| " new A();", |
| "}"); |
| assertErrors( |
| libraryResult.getErrors(), |
| errEx(TypeErrorCode.CONTRETE_CLASS_WITH_UNIMPLEMENTED_MEMBERS, 1, 7, 1)); |
| { |
| DartCompilationError typeError = libraryResult.getErrors().get(0); |
| String message = typeError.getMessage(); |
| assertTrue(message.contains("# From A:")); |
| assertTrue(message.contains("void foo()")); |
| } |
| } |
| |
| public void test_warnAbstract_onConcreteClassDeclaration_hasUnimplemented_getter() |
| throws Exception { |
| AnalyzeLibraryResult libraryResult = |
| analyzeLibrary( |
| "class A {", |
| " get x;", |
| "}", |
| "main() {", |
| " new A();", |
| "}"); |
| assertErrors( |
| libraryResult.getErrors(), |
| errEx(TypeErrorCode.CONTRETE_CLASS_WITH_UNIMPLEMENTED_MEMBERS, 1, 7, 1)); |
| } |
| |
| /** |
| * There was bug that implementing setter still caused warning. |
| * <p> |
| * http://code.google.com/p/dart/issues/detail?id=5327 |
| */ |
| public void test_warnAbstract_whenInstantiate_implementSetter() throws Exception { |
| AnalyzeLibraryResult libraryResult = analyzeLibrary( |
| "abstract class I {", |
| " set foo(x);", |
| "}", |
| "class A implements I {", |
| " set foo(x) {}", |
| "}", |
| "main() {", |
| " new A();", |
| "}"); |
| assertErrors(libraryResult.getErrors()); |
| } |
| |
| /** |
| * When both getter and setter were abstract and only getter implemented, we should report error. |
| * <p> |
| * http://code.google.com/p/dart/issues/detail?id=5327 |
| */ |
| public void test_warnAbstract_whenInstantiate_implementsOnlyGetter() throws Exception { |
| AnalyzeLibraryResult libraryResult = analyzeLibrary( |
| "abstract class I {", |
| " get foo;", |
| " set foo(x);", |
| "}", |
| "class A implements I {", |
| " get foo => 0;", |
| "}", |
| "main() {", |
| " new A();", |
| "}"); |
| assertErrors( |
| libraryResult.getErrors(), |
| errEx(TypeErrorCode.CONTRETE_CLASS_WITH_UNIMPLEMENTED_MEMBERS, 5, 7, 1)); |
| } |
| |
| /** |
| * <p> |
| * http://code.google.com/p/dart/issues/detail?id=5327 |
| */ |
| public void test_warnAbstract_whenInstantiate_implementsSetter_inSuperClass() throws Exception { |
| AnalyzeLibraryResult libraryResult = analyzeLibrary( |
| "abstract class I {", |
| " get foo;", |
| " set foo(x);", |
| "}", |
| "abstract class A implements I {", |
| " get foo;", |
| " set foo(x) {}", |
| "}", |
| "class B extends A {", |
| " get foo => 0;", |
| "}", |
| "main() {", |
| " new B();", |
| "}"); |
| assertErrors(libraryResult.getErrors()); |
| } |
| |
| public void test_warnAbstract_onAbstractClass_whenInstantiate_normalConstructor() |
| throws Exception { |
| AnalyzeLibraryResult libraryResult = |
| analyzeLibrary( |
| "abstract class A {", |
| " void bar();", |
| "}", |
| "main() {", |
| " new A();", |
| "}"); |
| assertErrors( |
| libraryResult.getErrors(), |
| errEx(TypeErrorCode.INSTANTIATION_OF_ABSTRACT_CLASS, 5, 7, 1)); |
| } |
| |
| /** |
| * Factory constructor can instantiate any class and return it non-abstract class instance, Even |
| * thought this is an abstract class, there should be no warnings for the invocation of the |
| * factory constructor. |
| */ |
| public void test_warnAbstract_onAbstractClass_whenInstantiate_factoryConstructor() |
| throws Exception { |
| AnalyzeLibraryResult libraryResult = analyzeLibrary( |
| "abstract class A {", // explicitly abstract |
| " factory A() {", |
| " return null;", |
| " }", |
| "}", |
| "class C {", |
| " foo() {", |
| " return new A();", // no error - factory constructor |
| " }", |
| "}"); |
| assertErrors(libraryResult.getErrors()); |
| } |
| |
| /** |
| * Factory constructor can instantiate any class and return it non-abstract class instance, Even |
| * thought this is an abstract class, there should be no warnings for the invocation of the |
| * factory constructor. |
| */ |
| public void test_wanrAbstract_onAbstractClass_whenInstantiate_factoryConstructor2() |
| throws Exception { |
| AnalyzeLibraryResult libraryResult = |
| analyzeLibrary( |
| "abstract class A {", // class is abstract |
| " factory A() {", |
| " return null;", |
| " }", |
| " method();", |
| "}", |
| "class C {", |
| " foo() {", |
| " return new A();", // no error, factory constructor |
| " }", |
| "}"); |
| assertErrors(libraryResult.getErrors()); |
| } |
| |
| /** |
| * Spec 7.3 It is a static warning if a setter declares a return type other than void. |
| */ |
| public void testWarnOnNonVoidSetter() throws Exception { |
| AnalyzeLibraryResult libraryResult = |
| analyzeLibrary( |
| "class A {", |
| " void set foo(bool a) {}", |
| " set bar(bool a) {}", |
| " dynamic set baz(bool a) {}", |
| " bool set bob(bool a) {}", |
| "}"); |
| assertErrors( |
| libraryResult.getErrors(), |
| errEx(TypeErrorCode.SETTER_RETURN_TYPE, 4, 3, 7), |
| errEx(TypeErrorCode.SETTER_RETURN_TYPE, 5, 3, 4)); |
| } |
| |
| public void test_callUnknownFunction() throws Exception { |
| AnalyzeLibraryResult libraryResult = analyzeLibrary( |
| "// filler filler filler filler filler filler filler filler filler filler", |
| "main() {", |
| " foo();", |
| "}", |
| ""); |
| assertErrors(libraryResult.getErrors(), errEx(ResolverErrorCode.CANNOT_RESOLVE_METHOD, 3, 3, 3)); |
| } |
| |
| /** |
| * We should be able to call <code>Function</code> even if it is in the field. |
| * <p> |
| * http://code.google.com/p/dart/issues/detail?id=933 |
| */ |
| public void test_callFunctionFromField() throws Exception { |
| AnalyzeLibraryResult libraryResult = |
| analyzeLibrary( |
| "class WorkElement {", |
| " Function run;", |
| "}", |
| "foo(WorkElement e) {", |
| " e.run();", |
| "}"); |
| assertErrors(libraryResult.getErrors()); |
| } |
| |
| /** |
| * When we attempt to use function as type, we should report only one error. |
| * <p> |
| * http://code.google.com/p/dart/issues/detail?id=3309 |
| */ |
| public void test_useFunctionAsType() throws Exception { |
| AnalyzeLibraryResult libraryResult = analyzeLibrary( |
| "// filler filler filler filler filler filler filler filler filler filler", |
| "func() {}", |
| "main() {", |
| " new func();", |
| "}", |
| ""); |
| assertErrors(libraryResult.getErrors(), errEx(TypeErrorCode.NOT_A_TYPE, 4, 7, 4)); |
| } |
| |
| /** |
| * There was problem that {@link DartForInStatement} visits "iterable" two times. At first time we |
| * set {@link MethodElement}, because we resolve it to getter. However because of this at second |
| * time we can not resolve. Solution - don't try to resolve second time, we already done at first |
| * time. Note: double getter is important. |
| */ |
| public void test_doubleGetterAccess_inForEach() throws Exception { |
| AnalyzeLibraryResult libraryResult = analyzeLibrary( |
| "class Test {", |
| " Iterable get iter {}", |
| "}", |
| "Test get test {}", |
| "f() {", |
| " for (var v in test.iter) {}", |
| "}", |
| ""); |
| assertErrors(libraryResult.getErrors()); |
| } |
| |
| /** |
| * Test for errors and warnings related to positional and named arguments for required and |
| * optional parameters. |
| */ |
| public void test_invocationArguments() throws Exception { |
| AnalyzeLibraryResult libraryResult = |
| analyzeLibrary( |
| "/* 01 */ foo() {", |
| "/* 02 */ f_0_0();", |
| "/* 03 */ f_0_0(-1);", |
| "/* 04 */", |
| "/* 05 */ f_1_0();", |
| "/* 06 */ f_1_0(-1);", |
| "/* 07 */ f_1_0(-1, -2, -3);", |
| "/* 08 */", |
| "/* 09 */ f_2_0();", |
| "/* 10 */", |
| "/* 11 */ f_0_1();", |
| "/* 12 */ f_0_1(1);", |
| "/* 13 */ f_0_1(0, 0);", |
| "/* 14 */ f_0_1(n1: 1);", |
| "/* 15 */ f_0_1(x: 1);", |
| "/* 16 */ f_0_1(n1: 1, n1: 2);", |
| "}", |
| "", |
| "f_0_0() {}", |
| "f_1_0(r1) {}", |
| "f_2_0(r1, r2) {}", |
| "f_0_1({n1}) {}", |
| "f_0_2({n1, n2}) {}", |
| ""); |
| assertErrors( |
| libraryResult.getErrors(), |
| errEx(TypeErrorCode.EXTRA_ARGUMENT, 3, 18, 2), |
| errEx(TypeErrorCode.MISSING_ARGUMENT, 5, 12, 5), |
| errEx(TypeErrorCode.EXTRA_ARGUMENT, 7, 22, 2), |
| errEx(TypeErrorCode.EXTRA_ARGUMENT, 7, 26, 2), |
| errEx(TypeErrorCode.MISSING_ARGUMENT, 9, 12, 5), |
| errEx(TypeErrorCode.EXTRA_ARGUMENT, 12, 18, 1), |
| errEx(TypeErrorCode.EXTRA_ARGUMENT, 13, 18, 1), |
| errEx(TypeErrorCode.EXTRA_ARGUMENT, 13, 21, 1), |
| errEx(TypeErrorCode.NO_SUCH_NAMED_PARAMETER, 15, 18, 4), |
| errEx(ResolverErrorCode.DUPLICATE_NAMED_ARGUMENT, 16, 25, 5)); |
| } |
| |
| /** |
| * Test that optional positional and named parameters are handled separately. |
| */ |
| public void test_invocationArguments2() throws Exception { |
| AnalyzeLibraryResult libraryResult = analyzeLibrary( |
| "// filler filler filler filler filler filler filler filler filler filler", |
| "func([int np1, int np2, int np3]) {}", |
| "main() {", |
| " func(np1: 1, np2: 2, np3: 2);", |
| "}", |
| ""); |
| assertErrors( |
| libraryResult.getErrors(), |
| errEx(TypeErrorCode.NO_SUCH_NAMED_PARAMETER, 4, 8, 6), |
| errEx(TypeErrorCode.NO_SUCH_NAMED_PARAMETER, 4, 16, 6), |
| errEx(TypeErrorCode.NO_SUCH_NAMED_PARAMETER, 4, 24, 6)); |
| } |
| |
| /** |
| * We should return correct {@link Type} for {@link DartNewExpression}. |
| */ |
| public void test_DartNewExpression_getType() throws Exception { |
| AnalyzeLibraryResult libraryResult = |
| analyzeLibrary( |
| "// filler filler filler filler filler filler filler filler filler filler", |
| "class A {", |
| " A() {}", |
| " A.foo() {}", |
| "}", |
| "var a1 = new A();", |
| "var a2 = new A.foo();", |
| ""); |
| assertErrors(libraryResult.getErrors()); |
| // new A() |
| { |
| DartNewExpression newExpression = (DartNewExpression) getTopLevelFieldInitializer(testUnit, 1); |
| Type newType = newExpression.getType(); |
| assertEquals("A", newType.getElement().getName()); |
| } |
| // new A.foo() |
| { |
| DartNewExpression newExpression = (DartNewExpression) getTopLevelFieldInitializer(testUnit, 2); |
| Type newType = newExpression.getType(); |
| assertEquals("A", newType.getElement().getName()); |
| } |
| } |
| |
| /** |
| * Expects that given {@link DartUnit} has {@link DartFieldDefinition} as <code>index</code> top |
| * level node and return initializer of first {@link DartField}. |
| */ |
| private static DartExpression getTopLevelFieldInitializer(DartUnit unit, int index) { |
| DartFieldDefinition fieldDefinition = (DartFieldDefinition) unit.getTopLevelNodes().get(index); |
| DartField field = fieldDefinition.getFields().get(0); |
| return field.getValue(); |
| } |
| |
| /** |
| * If property has only setter, no getter, then attempt to use getter should cause static type |
| * warning. |
| * <p> |
| * http://code.google.com/p/dart/issues/detail?id=1251 |
| */ |
| public void test_setterOnlyProperty_noGetter() throws Exception { |
| AnalyzeLibraryResult libraryResult = |
| analyzeLibrary( |
| "class SetOnly {", |
| " set foo(arg) {}", |
| "}", |
| "class SetOnlyWrapper {", |
| " SetOnly setOnly;", |
| "}", |
| "", |
| "main() {", |
| " SetOnly setOnly = new SetOnly();", |
| " setOnly.foo = 1;", // 10: OK, use setter |
| " setOnly.foo += 2;", // 11: ERR, no getter |
| " print(setOnly.foo);", // 12: ERR, no getter |
| " var bar;", |
| " bar = setOnly.foo;", // 14: ERR, assignment, but we are not LHS |
| " bar = new SetOnlyWrapper().setOnly.foo;", // 15: ERR, even in chained expression |
| " new SetOnlyWrapper().setOnly.foo = 3;", // 16: OK |
| "}"); |
| assertErrors( |
| libraryResult.getErrors(), |
| errEx(TypeErrorCode.FIELD_HAS_NO_GETTER, 11, 11, 3), |
| errEx(TypeErrorCode.FIELD_HAS_NO_GETTER, 12, 17, 3), |
| errEx(TypeErrorCode.FIELD_HAS_NO_GETTER, 14, 17, 3), |
| errEx(TypeErrorCode.FIELD_HAS_NO_GETTER, 15, 38, 3)); |
| } |
| |
| public void test_setterOnlyProperty_normalField() throws Exception { |
| AnalyzeLibraryResult libraryResult = |
| analyzeLibrary( |
| "class A {", |
| " var foo;", |
| "}", |
| "", |
| "main() {", |
| " A a = new A();", |
| " a.foo = 1;", |
| " a.foo += 2;", |
| " print(a.foo);", |
| "}"); |
| assertErrors(libraryResult.getErrors()); |
| } |
| |
| public void test_setterOnlyProperty_getterInSuper() throws Exception { |
| AnalyzeLibraryResult libraryResult = |
| analyzeLibrary( |
| "class A {", |
| " get foo {}", |
| "}", |
| "class B extends A {", |
| " set foo(arg) {}", |
| "}", |
| "", |
| "main() {", |
| " B b = new B();", |
| " b.foo = 1;", |
| " b.foo += 2;", |
| " print(b.foo);", |
| "}"); |
| assertErrors(libraryResult.getErrors()); |
| } |
| |
| public void test_setterOnlyProperty_getterInInterface() throws Exception { |
| AnalyzeLibraryResult libraryResult = |
| analyzeLibrary( |
| "abstract class A {", |
| " get foo {}", |
| "}", |
| "abstract class B implements A {", |
| " set foo(arg) {}", |
| "}", |
| "", |
| "main() {", |
| " B b = new B();", |
| " b.foo = 1;", |
| " b.foo += 2;", |
| " print(b.foo);", |
| "}"); |
| assertErrors( |
| libraryResult.getErrors(), |
| errEx(TypeErrorCode.INSTANTIATION_OF_ABSTRACT_CLASS, 9, 13, 1)); |
| } |
| |
| public void test_getterOnlyProperty_noSetter() throws Exception { |
| AnalyzeLibraryResult libraryResult = |
| analyzeLibrary( |
| "class GetOnly {", |
| " get foo {}", |
| "}", |
| "class GetOnlyWrapper {", |
| " GetOnly getOnly;", |
| "}", |
| "", |
| "main() {", |
| " GetOnly getOnly = new GetOnly();", |
| " print(getOnly.foo);", // 10: OK, use getter |
| " getOnly.foo = 1;", // 11: ERR, no setter |
| " getOnly.foo += 2;", // 12: ERR, no setter |
| " var bar;", |
| " bar = getOnly.foo;", // 14: OK, use getter |
| " new GetOnlyWrapper().getOnly.foo = 3;", // 15: ERR, no setter |
| " bar = new GetOnlyWrapper().getOnly.foo;", // 16: OK, use getter |
| "}"); |
| assertErrors( |
| libraryResult.getErrors(), |
| errEx(TypeErrorCode.FIELD_HAS_NO_SETTER, 11, 11, 3), |
| errEx(TypeErrorCode.FIELD_HAS_NO_SETTER, 12, 11, 3), |
| errEx(TypeErrorCode.FIELD_HAS_NO_SETTER, 15, 32, 3)); |
| } |
| |
| public void test_getterOnlyProperty_setterInSuper() throws Exception { |
| AnalyzeLibraryResult libraryResult = |
| analyzeLibrary( |
| "class A {", |
| " set foo(arg) {}", |
| "}", |
| "class B extends A {", |
| " get foo {}", |
| "}", |
| "", |
| "main() {", |
| " B b = new B();", |
| " b.foo = 1;", |
| " b.foo += 2;", |
| " print(b.foo);", |
| "}"); |
| assertErrors(libraryResult.getErrors()); |
| } |
| |
| public void test_getterOnlyProperty_setterInInterface() throws Exception { |
| AnalyzeLibraryResult libraryResult = |
| analyzeLibrary( |
| "abstract class A {", |
| " set foo(arg) {}", |
| "}", |
| "abstract class B implements A {", |
| " get foo {}", |
| "}", |
| "", |
| "main() {", |
| " B b = new B();", |
| " b.foo = 1;", |
| " b.foo += 2;", |
| " print(b.foo);", |
| "}"); |
| assertErrors( |
| libraryResult.getErrors(), |
| errEx(TypeErrorCode.INSTANTIATION_OF_ABSTRACT_CLASS, 9, 13, 1)); |
| } |
| |
| public void test_assert_notUserFunction() throws Exception { |
| AnalyzeLibraryResult libraryResult = analyzeLibrary( |
| "// filler filler filler filler filler filler filler filler filler filler", |
| "main() {", |
| " assert(true);", |
| " assert(false);", |
| " assert('message');", // not 'bool' |
| " assert('null');", // not 'bool' |
| " assert(0);", // not 'bool' |
| " assert(f() {});", // OK, dynamic |
| " assert(bool f() {});", // OK, '() -> bool' |
| " assert(Object f() {});", // OK, 'Object' compatible with 'bool' |
| " assert(String f() {});", // not '() -> bool', return type |
| " assert(bool f(x) {});", // not '() -> bool', parameter |
| " assert(true, false);", // not single argument |
| " assert;", // incomplete |
| "}", |
| ""); |
| assertErrors( |
| libraryResult.getErrors(), |
| errEx(ParserErrorCode.EXPECTED_TOKEN, 13, 10, 4), |
| errEx(ParserErrorCode.EXPECTED_TOKEN, 14, 9, 1), |
| errEx(TypeErrorCode.ASSERT_BOOL, 5, 10, 9), |
| errEx(TypeErrorCode.ASSERT_BOOL, 6, 10, 6), |
| errEx(TypeErrorCode.ASSERT_BOOL, 7, 10, 1), |
| errEx(TypeErrorCode.ASSERT_BOOL, 11, 10, 13), |
| errEx(TypeErrorCode.ASSERT_BOOL, 12, 10, 12)); |
| } |
| |
| /** |
| * <p> |
| * http://code.google.com/p/dart/issues/detail?id=3264 |
| */ |
| public void test_initializingFormalType_useFieldType() throws Exception { |
| AnalyzeLibraryResult libraryResult = analyzeLibrary( |
| "// filler filler filler filler filler filler filler filler filler filler", |
| "class A {", |
| " final double f;", |
| " A(this.f);", |
| "}", |
| "class B {", |
| " B(this.f);", |
| " final double f;", |
| "}", |
| "", |
| "main() {", |
| " new A('0');", |
| " new B('0');", |
| "}", |
| ""); |
| assertErrors( |
| libraryResult.getErrors(), |
| errEx(TypeErrorCode.TYPE_NOT_ASSIGNMENT_COMPATIBLE, 12, 9, 3), |
| errEx(TypeErrorCode.TYPE_NOT_ASSIGNMENT_COMPATIBLE, 13, 9, 3)); |
| } |
| |
| /** |
| * If "this.field" parameter has declared type, it should be assignable to the field. |
| * <p> |
| * http://code.google.com/p/dart/issues/detail?id=3264 |
| */ |
| public void test_initializingFormalType_compatilityWithFieldType() throws Exception { |
| AnalyzeLibraryResult libraryResult = analyzeLibrary( |
| "// filler filler filler filler filler filler filler filler filler filler", |
| "class A {", |
| " final double f;", |
| " A.useDynamic(dynamic this.f);", |
| " A.useNum(num this.f);", |
| " A.useString(String this.f);", |
| "}", |
| ""); |
| assertErrors( |
| libraryResult.getErrors(), |
| errEx(TypeErrorCode.TYPE_NOT_ASSIGNMENT_COMPATIBLE, 6, 15, 13)); |
| } |
| |
| public void test_finalField_inClass() throws Exception { |
| AnalyzeLibraryResult libraryResult = analyzeLibrary( |
| "// filler filler filler filler filler filler filler filler filler filler", |
| "class A {", |
| " final f = 0;", |
| "}", |
| "main() {", |
| " A a = new A();", |
| " a.f = 0;", // 6: ERR, is final |
| " a.f += 1;", // 7: ERR, is final |
| " print(a.f);", // 8: OK, can read |
| "}"); |
| assertErrors( |
| libraryResult.getErrors(), |
| errEx(TypeErrorCode.FIELD_IS_FINAL, 7, 5, 1), |
| errEx(TypeErrorCode.FIELD_IS_FINAL, 8, 5, 1)); |
| } |
| |
| public void test_notFinalField() throws Exception { |
| AnalyzeLibraryResult libraryResult = analyzeLibrary( |
| "// filler filler filler filler filler filler filler filler filler filler", |
| "class A {", |
| " var f;", |
| "}", |
| "main() {", |
| " A a = new A();", |
| " a.f = 0;", // 6: OK, field "f" is not final |
| " a.f += 1;", // 7: OK, field "f" is not final |
| " print(a.f);", // 8: OK, can read |
| "}"); |
| assertErrors(libraryResult.getErrors()); |
| } |
| |
| public void test_constField() throws Exception { |
| AnalyzeLibraryResult libraryResult = analyzeLibrary( |
| "// filler filler filler filler filler filler filler filler filler filler", |
| "const f = 1;", |
| "class A {", |
| " const f = 1;", |
| " method() {", |
| " f = 2;", |
| " this.f = 2;", |
| " }", |
| "}", |
| "main() {", |
| " f = 2;", |
| " A a = new A();", |
| " a.f = 2;", |
| "}", |
| ""); |
| assertErrors( |
| libraryResult.getErrors(), |
| errEx(ResolverErrorCode.CANNOT_ASSIGN_TO_FINAL, 6, 5, 1), |
| errEx(ResolverErrorCode.CANNOT_ASSIGN_TO_FINAL, 7, 5, 6), |
| errEx(ResolverErrorCode.CANNOT_ASSIGN_TO_FINAL, 11, 3, 1), |
| errEx(TypeErrorCode.FIELD_IS_FINAL, 13, 5, 1)); |
| } |
| |
| public void test_identicalFunction() throws Exception { |
| AnalyzeLibraryResult libraryResult = analyzeLibrary( |
| "// filler filler filler filler filler filler filler filler filler filler", |
| "const A = 1;", |
| "const B = 2;", |
| "const C = identical(A, B);", |
| ""); |
| assertErrors(libraryResult.getErrors()); |
| } |
| |
| /** |
| * It is a compile-time error to use type variables in "const" instance creation. |
| * <p> |
| * http://code.google.com/p/dart/issues/detail?id=2379 |
| */ |
| public void test_constInstantiation_withTypeVariable() throws Exception { |
| AnalyzeLibraryResult libraryResult = analyzeLibrary( |
| "// filler filler filler filler filler filler filler filler filler filler", |
| "class A<T> {", |
| " const A();", |
| " const A.name();", |
| "}", |
| "class B<U> {", |
| " test() {", |
| " const A<U>();", |
| " const A<U>.name();", |
| " }", |
| "}", |
| ""); |
| assertErrors( |
| libraryResult.getErrors(), |
| errEx(ResolverErrorCode.CONST_WITH_TYPE_VARIABLE, 8, 13, 1), |
| errEx(ResolverErrorCode.CONST_WITH_TYPE_VARIABLE, 9, 13, 1)); |
| } |
| |
| /** |
| * <p> |
| * http://code.google.com/p/dart/issues/detail?id=3182 |
| */ |
| public void test_extendNotType() throws Exception { |
| AnalyzeLibraryResult libraryResult = analyzeLibrary( |
| "// filler filler filler filler filler filler filler filler filler filler", |
| "int A;", |
| "class B extends A {", |
| "}", |
| "", |
| ""); |
| assertErrors( |
| libraryResult.getErrors(), |
| errEx(ResolverErrorCode.NOT_A_TYPE, 3, 17, 1)); |
| } |
| |
| public void test_constInstanceCreation_noSuchType() throws Exception { |
| AnalyzeLibraryResult libraryResult = analyzeLibrary( |
| "// filler filler filler filler filler filler filler filler filler filler", |
| "main() {", |
| " const NoSuchType();", |
| "}", |
| ""); |
| assertErrors( |
| libraryResult.getErrors(), |
| errEx(ResolverErrorCode.NO_SUCH_TYPE_CONST, 3, 9, 10)); |
| } |
| |
| public void test_constInstanceCreation_noSuchConstructor() throws Exception { |
| AnalyzeLibraryResult libraryResult = analyzeLibrary( |
| "// filler filler filler filler filler filler filler filler filler filler", |
| "class A {}", |
| "main() {", |
| " const A.noSuchName();", |
| "}", |
| ""); |
| assertErrors( |
| libraryResult.getErrors(), |
| errEx(ResolverErrorCode.NEW_EXPRESSION_NOT_CONST_CONSTRUCTOR, 4, 11, 10)); |
| } |
| |
| public void test_constInstanceCreation_notType() throws Exception { |
| AnalyzeLibraryResult libraryResult = analyzeLibrary( |
| "// filler filler filler filler filler filler filler filler filler filler", |
| "var notType;", |
| "main() {", |
| " const notType();", |
| "}", |
| ""); |
| assertErrors( |
| libraryResult.getErrors(), |
| errEx(ResolverErrorCode.NOT_A_TYPE, 4, 9, 7)); |
| } |
| |
| /** |
| * Test for variants of {@link DartMethodDefinition} return types. |
| */ |
| public void test_methodReturnTypes() throws Exception { |
| AnalyzeLibraryResult libraryResult = |
| analyzeLibrary( |
| "// filler filler filler filler filler filler filler filler filler filler", |
| "int fA() {}", |
| "dynamic fB() {}", |
| "void fC() {}", |
| "fD() {}", |
| ""); |
| assertErrors(libraryResult.getErrors()); |
| { |
| DartMethodDefinition fA = (DartMethodDefinition) testUnit.getTopLevelNodes().get(0); |
| assertEquals("int", fA.getElement().getReturnType().getElement().getName()); |
| } |
| { |
| DartMethodDefinition fB = (DartMethodDefinition) testUnit.getTopLevelNodes().get(1); |
| assertEquals("dynamic", fB.getElement().getReturnType().getElement().getName()); |
| } |
| { |
| DartMethodDefinition fC = (DartMethodDefinition) testUnit.getTopLevelNodes().get(2); |
| assertEquals("void", fC.getElement().getReturnType().getElement().getName()); |
| } |
| { |
| DartMethodDefinition fD = (DartMethodDefinition) testUnit.getTopLevelNodes().get(3); |
| assertEquals("dynamic", fD.getElement().getReturnType().getElement().getName()); |
| } |
| } |
| |
| public void test_bindToLibraryFunctionFirst() throws Exception { |
| analyzeLibrary( |
| "// filler filler filler filler filler filler filler filler filler filler", |
| "foo() {}", |
| "class A {", |
| " foo() {}", |
| "}", |
| "class B extends A {", |
| " bar() {", |
| " foo();", |
| " }", |
| "}", |
| ""); |
| // Find foo() invocation. |
| DartUnqualifiedInvocation invocation; |
| { |
| DartClass classB = (DartClass) testUnit.getTopLevelNodes().get(2); |
| DartMethodDefinition methodBar = (DartMethodDefinition) classB.getMembers().get(0); |
| DartExprStmt stmt = (DartExprStmt) methodBar.getFunction().getBody().getStatements().get(0); |
| invocation = (DartUnqualifiedInvocation) stmt.getExpression(); |
| } |
| // Check that unqualified foo() invocation is resolved to the top-level (library) function. |
| NodeElement element = invocation.getTarget().getElement(); |
| assertNotNull(element); |
| assertSame(testUnit, element.getNode().getParent()); |
| } |
| |
| /** |
| * If there was <code>import</code> with invalid {@link URI}, it should be reported as error, not |
| * as an exception. |
| */ |
| public void test_invalidImportUri() throws Exception { |
| AnalyzeLibraryResult libraryResult = analyzeLibrary( |
| "// filler filler filler filler filler filler filler filler filler filler", |
| "library test;", |
| "import 'badURI';", |
| ""); |
| assertErrors(libraryResult.getErrors(), errEx(DartCompilerErrorCode.MISSING_SOURCE, 3, 1, 16)); |
| } |
| |
| /** |
| * If there was <code>part</code> with invalid {@link URI}, it should be reported as error, not |
| * as an exception. |
| */ |
| public void test_invalidSourceUri() throws Exception { |
| AnalyzeLibraryResult libraryResult = analyzeLibrary( |
| "// filler filler filler filler filler filler filler filler filler filler", |
| "library test;", |
| "part 'badURI';", |
| ""); |
| assertErrors(libraryResult.getErrors(), errEx(DartCompilerErrorCode.MISSING_SOURCE, 3, 1, 14)); |
| } |
| |
| public void test_mapLiteralKeysUnique() throws Exception { |
| AnalyzeLibraryResult libraryResult = analyzeLibrary( |
| "// filler filler filler filler filler filler filler filler filler filler", |
| "var m = {'a' : 0, 'b': 1, 'a': 2};", |
| ""); |
| assertErrors(libraryResult.getErrors(), errEx(TypeErrorCode.MAP_LITERAL_KEY_UNIQUE, 2, 27, 3)); |
| } |
| |
| /** |
| * No required parameter "x". |
| */ |
| public void test_implementsAndOverrides_noRequiredParameter() throws Exception { |
| AnalyzeLibraryResult result = |
| analyzeLibrary( |
| "abstract class I {", |
| " foo(x);", |
| "}", |
| "class C implements I {", |
| " foo() {}", |
| "}"); |
| assertErrors( |
| result.getErrors(), |
| errEx(ResolverErrorCode.CANNOT_OVERRIDE_METHOD_NUM_REQUIRED_PARAMS, 5, 3, 3)); |
| } |
| |
| /** |
| * It is OK to add more named parameters, if list prefix is same as in "super". |
| */ |
| public void test_implementsAndOverrides_additionalNamedParameter() throws Exception { |
| AnalyzeLibraryResult result = |
| analyzeLibrary( |
| "abstract class I {", |
| " foo({x});", |
| "}", |
| "class C implements I {", |
| " foo({x,y}) {}", |
| "}"); |
| assertErrors(result.getErrors()); |
| } |
| |
| public void test_implementsAndOverrides_lessNamedParameter() throws Exception { |
| AnalyzeLibraryResult result = analyzeLibrary( |
| "abstract class A {", |
| " foo({x, y});", |
| "}", |
| "abstract class B extends A {", |
| " foo({x});", |
| "}"); |
| assertErrors( |
| result.getErrors(), |
| errEx(ResolverErrorCode.CANNOT_OVERRIDE_METHOD_NAMED_PARAMS, 5, 3, 3)); |
| } |
| |
| /** |
| * We override "foo" with method that has named parameter. So, this method is not abstract and |
| * class is not abstract too, so no warning. |
| */ |
| public void test_implementsAndOverrides_additionalNamedParameter_notAbstract() throws Exception { |
| AnalyzeLibraryResult result = |
| analyzeLibrary( |
| "abstract class A {", |
| " foo();", |
| "}", |
| "class B extends A {", |
| " foo({x}) {}", |
| "}", |
| "bar() {", |
| " new B();", |
| "}", |
| ""); |
| assertErrors(result.getErrors()); |
| } |
| |
| public void test_implementsAndOverrides_lessOptionalPositionalParameter() throws Exception { |
| AnalyzeLibraryResult result = analyzeLibrary( |
| "abstract class A {", |
| " foo([x, y]);", |
| "}", |
| "abstract class B extends A {", |
| " foo([x]);", |
| "}"); |
| assertErrors( |
| result.getErrors(), |
| errEx(ResolverErrorCode.CANNOT_OVERRIDE_METHOD_OPTIONAL_PARAMS, 5, 3, 3)); |
| } |
| |
| public void test_implementsAndOverrides_moreOptionalPositionalParameter() throws Exception { |
| AnalyzeLibraryResult result = analyzeLibrary( |
| "abstract class A {", |
| " foo([x]);", |
| "}", |
| "abstract class B extends A {", |
| " foo([a, b]);", |
| "}"); |
| assertErrors(result.getErrors()); |
| } |
| |
| /** |
| * No required parameter "x". Named parameter "x" is not enough. |
| */ |
| public void test_implementsAndOverrides_extraRequiredParameter() throws Exception { |
| AnalyzeLibraryResult result = |
| analyzeLibrary( |
| "abstract class I {", |
| " foo();", |
| "}", |
| "class C implements I {", |
| " foo(x) {}", |
| "}"); |
| assertErrors( |
| result.getErrors(), |
| errEx(ResolverErrorCode.CANNOT_OVERRIDE_METHOD_NUM_REQUIRED_PARAMS, 5, 3, 3)); |
| } |
| |
| /** |
| * <p> |
| * http://code.google.com/p/dart/issues/detail?id=3183 |
| */ |
| public void test_implementsAndOverrides_differentDefaultValue_optional() throws Exception { |
| AnalyzeLibraryResult result = |
| analyzeLibrary( |
| "// filler filler filler filler filler filler filler filler filler filler", |
| "class A {", |
| " f1([x]) {}", |
| " f2([x = 1]) {}", |
| " f3([x = 1]) {}", |
| " f4([x = 1]) {}", |
| "}", |
| "class B extends A {", |
| " f1([x = 2]) {}", |
| " f2([x]) {}", |
| " f3([x = 2]) {}", |
| " f4([x = '2']) {}", |
| "}", |
| ""); |
| assertErrors( |
| result.getErrors(), |
| errEx(TypeErrorCode.CANNOT_OVERRIDE_METHOD_DEFAULT_VALUE, 10, 7, 1), |
| errEx(TypeErrorCode.CANNOT_OVERRIDE_METHOD_DEFAULT_VALUE, 11, 7, 5), |
| errEx(TypeErrorCode.CANNOT_OVERRIDE_METHOD_DEFAULT_VALUE, 12, 7, 7)); |
| } |
| |
| /** |
| * <p> |
| * http://code.google.com/p/dart/issues/detail?id=3183 |
| */ |
| public void test_implementsAndOverrides_differentDefaultValue_named() throws Exception { |
| AnalyzeLibraryResult result = |
| analyzeLibrary( |
| "// filler filler filler filler filler filler filler filler filler filler", |
| "class A {", |
| " f1({x}) {}", |
| " f2({x: 1}) {}", |
| " f3({x: 1}) {}", |
| " f4({x: 1}) {}", |
| "}", |
| "class B extends A {", |
| " f1({x: 2}) {}", |
| " f2({x]) {}", |
| " f3({x: 2}) {}", |
| " f4({x: '2'}) {}", |
| "}", |
| ""); |
| assertErrors( |
| result.getErrors(), |
| errEx(TypeErrorCode.CANNOT_OVERRIDE_METHOD_DEFAULT_VALUE, 10, 7, 1), |
| errEx(TypeErrorCode.CANNOT_OVERRIDE_METHOD_DEFAULT_VALUE, 11, 7, 4), |
| errEx(TypeErrorCode.CANNOT_OVERRIDE_METHOD_DEFAULT_VALUE, 12, 7, 6)); |
| } |
| |
| /** |
| * It is a compile-time error if an instance method m1 overrides an instance member m2 and m1 does |
| * not declare all the named parameters declared by m2 in the same order. |
| * <p> |
| * Here: no "y" parameter. |
| */ |
| public void test_implementsAndOverrides_noNamedParameter() throws Exception { |
| AnalyzeLibraryResult result = |
| analyzeLibrary( |
| "abstract class I {", |
| " foo({x,y});", |
| "}", |
| "class C implements I {", |
| " foo({x}) {}", |
| "}"); |
| assertErrors( |
| result.getErrors(), |
| errEx(ResolverErrorCode.CANNOT_OVERRIDE_METHOD_NAMED_PARAMS, 5, 3, 3)); |
| } |
| |
| public void test_metadataCommentOverride_OK_method() throws Exception { |
| AnalyzeLibraryResult result = analyzeLibrary( |
| "// filler filler filler filler filler filler filler filler filler filler", |
| "class A {", |
| " foo() {}", |
| "}", |
| "class B extends A {", |
| " // @override", |
| " foo() {}", |
| "}", |
| ""); |
| assertErrors(result.getErrors()); |
| } |
| |
| public void test_metadataCommentOverride_Bad_method() throws Exception { |
| AnalyzeLibraryResult result = analyzeLibrary( |
| "// filler filler filler filler filler filler filler filler filler filler", |
| "const override = 0;", |
| "class A {", |
| "}", |
| "class B extends A {", |
| " @override", |
| " foo() {}", |
| "}", |
| ""); |
| assertErrors( |
| result.getErrors(), |
| errEx(ResolverErrorCode.INVALID_OVERRIDE_METADATA, 7, 3, 3)); |
| } |
| |
| public void testImplementsAndOverrides5() throws Exception { |
| AnalyzeLibraryResult result = |
| analyzeLibrary( |
| "abstract class I {", |
| " foo({y,x});", |
| "}", |
| "class C implements I {", |
| " foo({x,y}) {}", |
| "}"); |
| assertErrors(result.getErrors()); |
| } |
| |
| /** |
| * <p> |
| * http://code.google.com/p/dart/issues/detail?id=1936 |
| */ |
| public void test_propertyAccess_whenExtendsUnknown() throws Exception { |
| AnalyzeLibraryResult result = |
| analyzeLibrary( |
| "// filler filler filler filler filler filler filler filler filler filler", |
| "class C extends Unknown {", |
| " foo() {", |
| " this.elements;", |
| " }", |
| "}"); |
| assertErrors(result.getErrors(), errEx(ResolverErrorCode.NO_SUCH_TYPE, 2, 17, 7)); |
| } |
| |
| /** |
| * <p> |
| * http://code.google.com/p/dart/issues/detail?id=3860 |
| */ |
| public void test_setterGetterDifferentStatic() throws Exception { |
| AnalyzeLibraryResult result = |
| analyzeLibrary( |
| "// filler filler filler filler filler filler filler filler filler filler", |
| "class A {", |
| " static get field => 0;", |
| " set field(var v) {}", |
| "}", |
| "class B {", |
| " get field => 0;", |
| " static set field(var v) {}", |
| "}", |
| ""); |
| assertErrors(result.getErrors(), |
| errEx(ResolverErrorCode.FIELD_GETTER_SETTER_SAME_STATIC, 4, 14, 5), |
| errEx(ResolverErrorCode.FIELD_GETTER_SETTER_SAME_STATIC, 8, 14, 5)); |
| } |
| |
| /** |
| * <p> |
| * http://code.google.com/p/dart/issues/detail?id=380 |
| */ |
| public void test_setterGetterDifferentType() throws Exception { |
| AnalyzeLibraryResult result = |
| analyzeLibrary( |
| "// filler filler filler filler filler filler filler filler filler filler", |
| "class A {} ", |
| "class B extends A {}", |
| "class C {", |
| " A getterField; ", |
| " B setterField; ", |
| " A get field { return getterField; }", |
| " void set field(B arg) { setterField = arg; }", |
| "}", |
| "main() {", |
| " C instance = new C();", |
| " instance.field = new B();", |
| " A resultA = instance.field;", |
| " instance.field = new A();", |
| " B resultB = instance.field;", |
| "}"); |
| assertErrors(result.getErrors()); |
| } |
| |
| |
| public void test_setterGetterAssignable1() throws Exception { |
| AnalyzeLibraryResult result = |
| analyzeLibrary( |
| "// filler filler filler filler filler filler filler filler filler filler", |
| "class A {} ", |
| "A topGetterField; ", |
| "var topSetterField; ", |
| "A get topField { return topGetterField; }", |
| "void set topField(arg) { topSetterField = arg; }", |
| "class C {", |
| " A getterField; ", |
| " var setterField; ", |
| " A get field { return getterField; }", |
| " void set field(arg) { setterField = arg; }", |
| "}"); |
| assertErrors(result.getErrors()); |
| } |
| |
| public void test_setterGetterAssignable2() throws Exception { |
| AnalyzeLibraryResult result = |
| analyzeLibrary( |
| "// filler filler filler filler filler filler filler filler filler filler", |
| "class A {} ", |
| "var topGetterField; ", |
| "A topSetterField; ", |
| "get topField { return topGetterField; }", |
| "void set topField(A arg) { topSetterField = arg; }", |
| "class C {", |
| " var getterField; ", |
| " A setterField; ", |
| " get field { return getterField; }", |
| " void set field(A arg) { setterField = arg; }", |
| "}"); |
| assertErrors(result.getErrors()); |
| } |
| |
| public void test_setterGetterNotAssignable() throws Exception { |
| AnalyzeLibraryResult result = |
| analyzeLibrary( |
| "// filler filler filler filler filler filler filler filler filler filler", |
| "class A {} ", |
| "class B {}", |
| "A topGetterField; ", |
| "B topSetterField; ", |
| "A get topField { return topGetterField; }", |
| "void set topField(B arg) { topSetterField = arg; }", |
| "class C {", |
| " A getterField; ", |
| " B setterField; ", |
| " A get field { return getterField; }", |
| " void set field(B arg) { setterField = arg; }", |
| "}"); |
| assertErrors(result.getErrors(), |
| errEx(TypeErrorCode.SETTER_TYPE_MUST_BE_ASSIGNABLE, 7, 19, 5), |
| errEx(TypeErrorCode.SETTER_TYPE_MUST_BE_ASSIGNABLE, 12, 18, 5)); |
| } |
| |
| public void test_setterInvokedAsMethod() throws Exception { |
| AnalyzeLibraryResult result = |
| analyzeLibrary( |
| "// filler filler filler filler filler filler filler filler filler filler", |
| "class C {", |
| " void set foo(String arg) {}", |
| "} ", |
| "method() {", |
| " C c = new C();", |
| " c.foo(1);", |
| "}"); |
| /* This could probably use a better error message. The user likely intends |
| * to set the property foo, but it is invoking foo as a getter and |
| * invoking the result. |
| */ |
| assertErrors(result.getErrors(), |
| errEx(TypeErrorCode.USE_ASSIGNMENT_ON_SETTER, 7, 4, 3)); |
| } |
| |
| /** |
| * <p> |
| * http://code.google.com/p/dart/issues/detail?id=3221 |
| */ |
| public void test_conditionalExpressionType() throws Exception { |
| AnalyzeLibraryResult result = |
| analyzeLibrary( |
| "// filler filler filler filler filler filler filler filler filler filler", |
| "main() {", |
| " bool x = (true ? 1 : 2.0);", |
| "}", ""); |
| List<DartCompilationError> errors = result.getErrors(); |
| assertErrors(errors, errEx(TypeErrorCode.TYPE_NOT_ASSIGNMENT_COMPATIBLE, 3, 12, 16)); |
| { |
| String message = errors.get(0).getMessage(); |
| assertTrue(message.contains("'num'")); |
| assertTrue(message.contains("'bool'")); |
| } |
| } |
| |
| /** |
| * <p> |
| * http://code.google.com/p/dart/issues/detail?id=4394 |
| */ |
| public void test_conditionalExpressionType_genericInterface() throws Exception { |
| AnalyzeLibraryResult result = analyzeLibrary( |
| "// filler filler filler filler filler filler filler filler filler filler", |
| "main() {", |
| " Collection<int> test = true ? new Set<int>() : const [null];", |
| "}", |
| ""); |
| assertErrors(result.getErrors()); |
| } |
| |
| public void test_typeVariableBoundsMismatch() throws Exception { |
| AnalyzeLibraryResult result = |
| analyzeLibrary( |
| "// filler filler filler filler filler filler filler filler filler filler", |
| "abstract class I<T extends num> { }", |
| "class A<T extends num> implements I<T> { }", |
| "class B<T> implements I<T> { }"); // static type error B.T not assignable to num |
| assertErrors(result.getErrors(), errEx(TypeErrorCode.TYPE_NOT_ASSIGNMENT_COMPATIBLE, 4, 25, 1)); |
| } |
| |
| public void test_typeVariableBoundsMismatch2() throws Exception { |
| AnalyzeLibraryResult result = |
| analyzeLibrary( |
| "// filler filler filler filler filler filler filler filler filler filler", |
| "class C<T extends num> { }", |
| "class A<T extends num> extends C<T> { }", |
| "class B<T> extends C<T> { }"); // static type error B.T not assignable to num |
| assertErrors(result.getErrors(), errEx(TypeErrorCode.TYPE_NOT_ASSIGNMENT_COMPATIBLE, 4, 22, 1)); |
| } |
| |
| public void test_typeVariableBoundsCheckNew() throws Exception { |
| AnalyzeLibraryResult libraryResult = analyzeLibrary( |
| "class Object {}", |
| "class A { }", |
| "class B { }", |
| "class C<T extends A> { }", |
| "method() {", |
| " new C<B>();", // B not assignable to A |
| "}"); |
| assertErrors( |
| libraryResult.getErrors(), |
| errEx(TypeErrorCode.TYPE_NOT_ASSIGNMENT_COMPATIBLE, 6, 9, 1)); |
| } |
| |
| /** |
| * When we check getter/setter compatibility, we should compare propagated type variables. |
| * <p> |
| * http://code.google.com/p/dart/issues/detail?id=3067 |
| */ |
| public void test_typeVariables_getterSetter() throws Exception { |
| AnalyzeLibraryResult result = |
| analyzeLibrary( |
| "// filler filler filler filler filler filler filler filler filler filler", |
| "class Base1<T1> {", |
| " T1 get val {}", |
| "}", |
| "class Base2<T2> extends Base1<T2> {", |
| "}", |
| "class Sub<T3> extends Base2<T3> {", |
| " void set val(T3 value) {}", |
| "}", |
| ""); |
| assertErrors(result.getErrors()); |
| } |
| |
| public void test_inferredTypes_noMemberWarnings() throws Exception { |
| // disabled by default |
| { |
| AnalyzeLibraryResult result = analyzeLibrary( |
| "// filler filler filler filler filler filler filler filler filler filler", |
| "class A {}", |
| "foo(A a) {", |
| " var v = a;", |
| " v.f = 0;", |
| " v.m();", |
| "}", |
| ""); |
| assertErrors(result.getErrors()); |
| } |
| // use CompilerConfiguration to enable |
| { |
| compilerConfiguration = new DefaultCompilerConfiguration(new CompilerOptions() { |
| @Override |
| public boolean typeChecksForInferredTypes() { |
| return true; |
| } |
| }); |
| AnalyzeLibraryResult result = analyzeLibrary( |
| "// filler filler filler filler filler filler filler filler filler filler", |
| "class A {}", |
| "foo(A a) {", |
| " var v = a;", |
| " v.f = 0;", |
| " v.m();", |
| "}", |
| ""); |
| assertErrors( |
| result.getErrors(), |
| errEx(TypeErrorCode.NOT_A_MEMBER_OF_INFERRED, 5, 5, 1), |
| errEx(TypeErrorCode.INTERFACE_HAS_NO_METHOD_NAMED_INFERRED, 6, 5, 1)); |
| } |
| } |
| |
| /** |
| * There was bug that for-in loop did not mark type of variable as inferred, so we produced |
| * warnings even when this is disabled. |
| * <p> |
| * http://code.google.com/p/dart/issues/detail?id=4460 |
| */ |
| public void test_inferredTypes_noMemberWarnings_forInLoop() throws Exception { |
| compilerConfiguration = new DefaultCompilerConfiguration(new CompilerOptions() { |
| @Override |
| public boolean typeChecksForInferredTypes() { |
| return false; |
| } |
| }); |
| AnalyzeLibraryResult result = analyzeLibrary( |
| "// filler filler filler filler filler filler filler filler filler filler", |
| "class A {}", |
| "foo() {", |
| " List<A> values;", |
| " for (var v in values) {", |
| " v.bar();", |
| " }", |
| "}", |
| ""); |
| assertErrors(result.getErrors()); |
| } |
| |
| public void test_inferredTypes_whenInvocationArgument_checkAssignable() throws Exception { |
| // disabled by default |
| { |
| AnalyzeLibraryResult result = analyzeLibrary( |
| "// filler filler filler filler filler filler filler filler filler filler", |
| "class A {}", |
| "class B {}", |
| "foo(A a) {}", |
| "main() {", |
| " var v = new B();", |
| " foo(v);", |
| "}", |
| ""); |
| assertErrors(result.getErrors()); |
| } |
| // use CompilerConfiguration to enable |
| { |
| compilerConfiguration = new DefaultCompilerConfiguration(new CompilerOptions() { |
| @Override |
| public boolean typeChecksForInferredTypes() { |
| return true; |
| } |
| }); |
| AnalyzeLibraryResult result = analyzeLibrary( |
| "// filler filler filler filler filler filler filler filler filler filler", |
| "class A {}", |
| "class B {}", |
| "foo(A a) {}", |
| "main() {", |
| " var v = new B();", |
| " foo(v);", |
| "}", |
| ""); |
| assertErrors( |
| result.getErrors(), |
| errEx(TypeErrorCode.TYPE_NOT_ASSIGNMENT_COMPATIBLE_INFERRED, 7, 7, 1)); |
| } |
| } |
| |
| /** |
| * When we resolved method from inferred type, it is possible that arguments of invocation |
| * don't is not assignable to the parameters. So, we report warning. But if we would not infer |
| * types, there would be no warnings. |
| * <p> |
| * http://code.google.com/p/dart/issues/detail?id=4849 |
| */ |
| public void test_inferredTypes_invocationOfMethodFromInferredType_arguments() throws Exception { |
| AnalyzeLibraryResult result = analyzeLibrary( |
| "// filler filler filler filler filler filler filler filler filler filler", |
| "class A {", |
| " foo(int p) {}", |
| "}", |
| "main() {", |
| " var a = new A();", |
| " a.foo('');", |
| "}", |
| ""); |
| assertErrors(result.getErrors()); |
| } |
| |
| public void test_typesPropagation_assignAtDeclaration() throws Exception { |
| analyzeLibrary( |
| "f() {", |
| " var v0 = true;", |
| " var v1 = true && false;", |
| " var v2 = 1;", |
| " var v3 = 1 + 2;", |
| " var v4 = 1.0;", |
| " var v5 = 1.0 + 2.0;", |
| " var v6 = new Map<String, int>();", |
| " var v7 = new Map().length;", |
| "}", |
| ""); |
| assertInferredElementTypeString(testUnit, "v0", "bool", INFERRED_EXACT); |
| assertInferredElementTypeString(testUnit, "v1", "bool", INFERRED_EXACT); |
| assertInferredElementTypeString(testUnit, "v2", "int", INFERRED_EXACT); |
| assertInferredElementTypeString(testUnit, "v3", "int", INFERRED_EXACT); |
| assertInferredElementTypeString(testUnit, "v4", "double", INFERRED_EXACT); |
| assertInferredElementTypeString(testUnit, "v5", "double", INFERRED_EXACT); |
| assertInferredElementTypeString(testUnit, "v6", "Map<String, int>", INFERRED); |
| assertInferredElementTypeString(testUnit, "v7", "int", INFERRED); |
| } |
| |
| public void test_typesPropagation_arrayAccess() throws Exception { |
| analyzeLibrary( |
| "class A {}", |
| "class B extends A {}", |
| "List<A> list() => [new B()];", |
| "main() {", |
| " var v0 = list();", |
| " var v1 = list();", |
| " var v2 = v1[0];", |
| "}", |
| ""); |
| { |
| DartExpression expr = findNode(DartUnqualifiedInvocation.class, "list();"); |
| assertInferredElementTypeString(expr.getType(), "v0", "List<A>", EXACT); |
| } |
| assertInferredElementTypeString(testUnit, "v1", "List<A>", INFERRED); |
| { |
| DartExpression expr = findNode(DartArrayAccess.class, "v1[0]"); |
| assertInferredElementTypeString(expr.getType(), "v2", "A", INFERRED); |
| } |
| } |
| |
| /** |
| * We should infer types only if variable declared without type. |
| */ |
| public void test_typesPropagation_dontChangeDeclaredType() throws Exception { |
| AnalyzeLibraryResult libraryResult = analyzeLibrary( |
| "// filler filler filler filler filler filler filler filler filler filler", |
| "class A {}", |
| "class B extends A {}", |
| "main() {", |
| " B v = new B();", |
| " var v1 = v;", |
| " v = new A();", |
| " var v2 = v;", |
| "}", |
| ""); |
| assertErrors(libraryResult.getErrors()); |
| assertInferredElementTypeString(testUnit, "v1", "B", INFERRED_EXACT); |
| assertInferredElementTypeString(testUnit, "v2", "B", INFERRED_EXACT); |
| } |
| |
| public void test_typesPropagation_multiAssign() throws Exception { |
| analyzeLibrary( |
| "// filler filler filler filler filler filler filler filler filler filler", |
| "f() {", |
| " var v = true;", |
| " var v1 = v;", |
| " v = 0;", |
| " var v2 = v;", |
| "}", |
| ""); |
| assertInferredElementTypeString(testUnit, "v1", "bool", INFERRED_EXACT); |
| assertInferredElementTypeString(testUnit, "v2", "int", INFERRED_EXACT); |
| } |
| |
| public void test_typesPropagation_multiAssign_noInitialValue() throws Exception { |
| analyzeLibrary( |
| "// filler filler filler filler filler filler filler filler filler filler", |
| "f() {", |
| " var v;", |
| " v = 0;", |
| " var v1 = v;", |
| "}", |
| ""); |
| assertInferredElementTypeString(testUnit, "v1", "int", INFERRED_EXACT); |
| } |
| |
| public void test_typesPropagation_multiAssign_IfThen() throws Exception { |
| analyzeLibrary( |
| "// filler filler filler filler filler filler filler filler filler filler", |
| "f() {", |
| " var v = true;", |
| " var v1 = v;", |
| " if (true) {", |
| " v = 0;", |
| " var v2 = v;", |
| " }", |
| " var v3 = v;", |
| "}", |
| ""); |
| assertInferredElementTypeString(testUnit, "v1", "bool", INFERRED_EXACT); |
| assertInferredElementTypeString(testUnit, "v2", "int", INFERRED_EXACT); |
| assertInferredElementTypeString(testUnit, "v3", "Object", INFERRED); |
| } |
| |
| public void test_typesPropagation_multiAssign_IfThenElse() throws Exception { |
| analyzeLibrary( |
| "// filler filler filler filler filler filler filler filler filler filler", |
| "f() {", |
| " var a = true;", |
| " var b = true;", |
| " var c = true;", |
| " var d = true;", |
| " if (true) {", |
| " a = 0;", |
| " b = 0;", |
| " } else {", |
| " a = 0;", |
| " c = 0;", |
| " }", |
| " var a1 = a;", |
| " var b1 = b;", |
| " var c1 = c;", |
| " var d1 = d;", |
| "}", |
| ""); |
| assertInferredElementTypeString(testUnit, "a1", "int", INFERRED_EXACT); |
| assertInferredElementTypeString(testUnit, "b1", "Object", INFERRED); |
| assertInferredElementTypeString(testUnit, "c1", "Object", INFERRED); |
| assertInferredElementTypeString(testUnit, "d1", "bool", INFERRED_EXACT); |
| } |
| |
| public void test_typesPropagation_multiAssign_IfThenElse_whenAsTypeCondition() throws Exception { |
| analyzeLibrary( |
| "// filler filler filler filler filler filler filler filler filler filler", |
| "f(var v) {", |
| " if (v is String) {", |
| " var v1 = v;", |
| " v = null;", |
| " } else {", |
| " var v2 = v;", |
| " }", |
| " var v3 = v;", |
| "}", |
| ""); |
| assertInferredElementTypeString(testUnit, "v1", "String", INFERRED); |
| assertInferredElementTypeString(testUnit, "v2", "dynamic", EXACT); |
| assertInferredElementTypeString(testUnit, "v3", "dynamic", EXACT); |
| } |
| |
| public void test_typesPropagation_multiAssign_While() throws Exception { |
| analyzeLibrary( |
| "// filler filler filler filler filler filler filler filler filler filler", |
| "f() {", |
| " var v = true;", |
| " var v1 = v;", |
| " while (true) {", |
| " var v2 = v;", |
| " v = 0;", |
| " var v3 = v;", |
| " }", |
| " var v4 = v;", |
| "}", |
| ""); |
| assertInferredElementTypeString(testUnit, "v1", "bool", INFERRED_EXACT); |
| assertInferredElementTypeString(testUnit, "v2", "bool", INFERRED_EXACT); |
| assertInferredElementTypeString(testUnit, "v3", "int", INFERRED_EXACT); |
| assertInferredElementTypeString(testUnit, "v4", "Object", INFERRED); |
| } |
| |
| public void test_typesPropagation_multiAssign_DoWhile() throws Exception { |
| analyzeLibrary( |
| "// filler filler filler filler filler filler filler filler filler filler", |
| "f() {", |
| " var v = true;", |
| " var v1 = v;", |
| " do {", |
| " var v2 = v;", |
| " v = 0;", |
| " var v3 = v;", |
| " } while (true);", |
| " var v4 = v;", |
| "}", |
| ""); |
| assertInferredElementTypeString(testUnit, "v1", "bool", INFERRED_EXACT); |
| assertInferredElementTypeString(testUnit, "v2", "bool", INFERRED_EXACT); |
| assertInferredElementTypeString(testUnit, "v3", "int", INFERRED_EXACT); |
| assertInferredElementTypeString(testUnit, "v4", "int", INFERRED_EXACT); |
| } |
| |
| public void test_typesPropagation_multiAssign_For() throws Exception { |
| analyzeLibrary( |
| "// filler filler filler filler filler filler filler filler filler filler", |
| "f() {", |
| " var v = true;", |
| " var v1 = v;", |
| " for (int i = 0; i < 10; i++) {", |
| " var v2 = v;", |
| " v = 0;", |
| " var v3 = v;", |
| " }", |
| " var v4 = v;", |
| "}", |
| ""); |
| assertInferredElementTypeString(testUnit, "v1", "bool", INFERRED_EXACT); |
| assertInferredElementTypeString(testUnit, "v2", "bool", INFERRED_EXACT); |
| assertInferredElementTypeString(testUnit, "v3", "int", INFERRED_EXACT); |
| assertInferredElementTypeString(testUnit, "v4", "Object", INFERRED); |
| } |
| |
| public void test_typesPropagation_multiAssign_ForIn() throws Exception { |
| analyzeLibrary( |
| "// filler filler filler filler filler filler filler filler filler filler", |
| "f() {", |
| " var v = true;", |
| " var v1 = v;", |
| " List<String> names = [];", |
| " for (var name in names) {", |
| " var v2 = v;", |
| " v = 0;", |
| " var v3 = v;", |
| " }", |
| " var v4 = v;", |
| "}", |
| ""); |
| assertInferredElementTypeString(testUnit, "v1", "bool", INFERRED_EXACT); |
| assertInferredElementTypeString(testUnit, "v2", "bool", INFERRED_EXACT); |
| assertInferredElementTypeString(testUnit, "v3", "int", INFERRED_EXACT); |
| assertInferredElementTypeString(testUnit, "v4", "Object", INFERRED); |
| } |
| |
| /** |
| * We should understand type with type arguments and choose the most generic version. |
| * <p> |
| * http://code.google.com/p/dart/issues/detail?id=4792 |
| */ |
| public void test_typesPropagation_multiAssign_withGenerics_type_type() throws Exception { |
| analyzeLibrary( |
| "// filler filler filler filler filler filler filler filler filler filler", |
| "f() {", |
| " var a = new List<String>();", |
| " var b = <Object>[];", |
| " if (true) {", |
| " a = <Object>[];", |
| " b = new List<String>();", |
| " }", |
| " var a1 = a;", |
| " var b1 = b;", |
| "}", |
| ""); |
| assertInferredElementTypeString(testUnit, "a1", "List<Object>", INFERRED); |
| assertInferredElementTypeString(testUnit, "b1", "List<Object>", INFERRED); |
| } |
| |
| /** |
| * Prefer specific type, not "dynamic" type argument. |
| * <p> |
| * http://code.google.com/p/dart/issues/detail?id=4792 |
| */ |
| public void test_typesPropagation_multiAssign_withGenerics_type_dynamic() throws Exception { |
| analyzeLibrary( |
| "// filler filler filler filler filler filler filler filler filler filler", |
| "f() {", |
| " var a = new List<String>();", |
| " var b = [];", |
| " if (true) {", |
| " a = [];", |
| " b = new List<String>();", |
| " }", |
| " var a1 = a;", |
| " var b1 = b;", |
| "}", |
| ""); |
| assertInferredElementTypeString(testUnit, "a1", "List<String>", INFERRED); |
| assertInferredElementTypeString(testUnit, "b1", "List<String>", INFERRED); |
| } |
| |
| /** |
| * <p> |
| * http://code.google.com/p/dart/issues/detail?id=4791 |
| */ |
| public void test_typesPropagation_multiAssign_type_null() throws Exception { |
| analyzeLibrary( |
| "// filler filler filler filler filler filler filler filler filler filler", |
| "f() {", |
| " var v = null;", |
| " var v1 = v;", |
| " if (true) {", |
| " v = '';", |
| " var v2 = v;", |
| " }", |
| " var v3 = v;", |
| "}", |
| ""); |
| assertInferredElementTypeString(testUnit, "v1", "dynamic", EXACT); |
| assertInferredElementTypeString(testUnit, "v2", "String", INFERRED_EXACT); |
| assertInferredElementTypeString(testUnit, "v3", "String", INFERRED); |
| } |
| |
| /** |
| * There was bug that when we analyze assignment in initializer, we don't have context. |
| */ |
| public void test_typesPropagation_multiAssign_assignmentOutsideFunction() throws Exception { |
| analyzeLibrary( |
| "// filler filler filler filler filler filler filler filler filler filler", |
| "class A {", |
| " A(p) {}", |
| "}", |
| "class B extends A {", |
| " B(p) : super(p = 0) {}", |
| "}", |
| ""); |
| // no exceptions |
| } |
| |
| /** |
| * When we can not identify type of assigned value we should keep "dynamic" as type of variable. |
| */ |
| public void test_typesPropagation_assign_newUnknownType() throws Exception { |
| analyzeLibrary( |
| "// filler filler filler filler filler filler filler filler filler filler", |
| "f() {", |
| " var v1 = new Unknown();", |
| " var v2 = new Unknown.name();", |
| "}", |
| ""); |
| assertInferredElementTypeString(testUnit, "v1", "dynamic", EXACT); |
| assertInferredElementTypeString(testUnit, "v2", "dynamic", EXACT); |
| } |
| |
| public void test_typesPropagation_ifAsType() throws Exception { |
| analyzeLibrary( |
| "// filler filler filler filler filler filler filler filler filler filler", |
| "f(var v) {", |
| " if ((v as String).length != 0) {", |
| " var v1 = v;", |
| " }", |
| " var v2 = v;", |
| "}", |
| ""); |
| assertInferredElementTypeString(testUnit, "v1", "String", INFERRED); |
| assertInferredElementTypeString(testUnit, "v2", "dynamic", EXACT); |
| } |
| |
| /** |
| * Even if there is negation, we still apply "as" cast, so visit "then" statement only if cast was |
| * successful. |
| */ |
| public void test_typesPropagation_ifAsType_negation() throws Exception { |
| analyzeLibrary( |
| "// filler filler filler filler filler filler filler filler filler filler", |
| "f(var v) {", |
| " if (!(v as String).isEmpty()) {", |
| " var v1 = v;", |
| " }", |
| " var v2 = v;", |
| "}", |
| ""); |
| assertInferredElementTypeString(testUnit, "v1", "String", INFERRED); |
| assertInferredElementTypeString(testUnit, "v2", "dynamic", EXACT); |
| } |
| |
| public void test_typesPropagation_ifIsType() throws Exception { |
| analyzeLibrary( |
| "f(var v) {", |
| " if (v is List<String>) {", |
| " var v1 = v;", |
| " }", |
| " if (v is Map<int, String>) {", |
| " var v2 = v;", |
| " }", |
| " var v3 = v;", |
| "}", |
| ""); |
| assertInferredElementTypeString(testUnit, "v1", "List<String>", INFERRED); |
| assertInferredElementTypeString(testUnit, "v2", "Map<int, String>", INFERRED); |
| assertInferredElementTypeString(testUnit, "v3", "dynamic", EXACT); |
| } |
| |
| /** |
| * We should not make variable type less specific, even if there is such (useless) user code. |
| */ |
| public void test_typesPropagation_ifIsType_mostSpecific() throws Exception { |
| analyzeLibrary( |
| "f() {", |
| " int a;", |
| " num b;", |
| " if (a is num) {", |
| " var a1 = a;", |
| " }", |
| " if (a is dynamic) {", |
| " var a2 = a;", |
| " }", |
| " if (b is int) {", |
| " var b1 = b;", |
| " }", |
| "}", |
| ""); |
| assertInferredElementTypeString(testUnit, "a1", "int", INFERRED_EXACT); |
| assertInferredElementTypeString(testUnit, "a2", "int", INFERRED_EXACT); |
| assertInferredElementTypeString(testUnit, "b1", "int", INFERRED); |
| } |
| |
| /** |
| * When single variable has conflicting type constraints, we use union of types. |
| */ |
| public void test_typesPropagation_ifIsType_conflictingTypes() throws Exception { |
| compilerConfiguration = new DefaultCompilerConfiguration(new CompilerOptions() { |
| @Override |
| public boolean typeChecksForInferredTypes() { |
| return true; |
| } |
| }); |
| AnalyzeLibraryResult result = analyzeLibrary( |
| "// filler filler filler filler filler filler filler filler filler filler", |
| "f(int v) {", |
| " if (v is String) {", |
| " var v1 = v;", |
| " // should be OK because 'v' is String", |
| " v.abs; // from num", |
| " v.length; // from String", |
| " processInt(v);", |
| " processString(v);", |
| " }", |
| "}", |
| "processInt(int p) {}", |
| "processString(String p) {}", |
| ""); |
| // should be no errors, we because "v" is String |
| assertErrors(result.getErrors()); |
| assertInferredElementTypeString(testUnit, "v1", "[int, String]", INFERRED); |
| } |
| |
| public void test_typesPropagation_ifIsType_negation() throws Exception { |
| analyzeLibrary( |
| "f(var v) {", |
| " if (v is! String) {", |
| " var v1 = v;", |
| " }", |
| " if (!(v is String)) {", |
| " var v2 = v;", |
| " }", |
| " if (!!(v is String)) {", |
| " var v3 = v;", |
| " }", |
| "}", |
| ""); |
| assertInferredElementTypeString(testUnit, "v1", "dynamic", EXACT); |
| assertInferredElementTypeString(testUnit, "v2", "dynamic", EXACT); |
| assertInferredElementTypeString(testUnit, "v3", "String", INFERRED); |
| } |
| |
| public void test_typesPropagation_ifIsType_and() throws Exception { |
| analyzeLibrary( |
| "// filler filler filler filler filler filler filler filler filler filler", |
| "f(var a, var b) {", |
| " if (a is String && b is List<String>) {", |
| " var a1 = a;", |
| " var b1 = b;", |
| " }", |
| "}", |
| ""); |
| assertInferredElementTypeString(testUnit, "a1", "String", INFERRED); |
| assertInferredElementTypeString(testUnit, "b1", "List<String>", INFERRED); |
| } |
| |
| public void test_typesPropagation_ifIsType_or() throws Exception { |
| analyzeLibrary( |
| "// filler filler filler filler filler filler filler filler filler filler", |
| "f(var v) {", |
| " if (true || v is String) {", |
| " var v1 = v;", |
| " }", |
| " if (v is String || true) {", |
| " var v2 = v;", |
| " }", |
| "}", |
| ""); |
| assertInferredElementTypeString(testUnit, "v1", "dynamic", EXACT); |
| assertInferredElementTypeString(testUnit, "v2", "dynamic", EXACT); |
| } |
| |
| public void test_typesPropagation_whileIsType() throws Exception { |
| analyzeLibrary( |
| "// filler filler filler filler filler filler filler filler filler filler", |
| "f(var v) {", |
| " var v = null;", |
| " while (v is String) {", |
| " var v1 = v;", |
| " }", |
| " var v2 = v;", |
| "}", |
| ""); |
| assertInferredElementTypeString(testUnit, "v1", "String", INFERRED); |
| assertInferredElementTypeString(testUnit, "v2", "dynamic", EXACT); |
| } |
| |
| public void test_typesPropagation_forIsType() throws Exception { |
| analyzeLibrary( |
| "// filler filler filler filler filler filler filler filler filler filler", |
| "f(var v) {", |
| " var v = null;", |
| " for (; v is String; () {var v2 = v;} ()) {", |
| " var v1 = v;", |
| " }", |
| " var v3 = v;", |
| "}", |
| ""); |
| assertInferredElementTypeString(testUnit, "v1", "String", INFERRED); |
| assertInferredElementTypeString(testUnit, "v2", "String", INFERRED); |
| assertInferredElementTypeString(testUnit, "v3", "dynamic", EXACT); |
| } |
| |
| public void test_typesPropagation_forEach() throws Exception { |
| analyzeLibrary( |
| "// filler filler filler filler filler filler filler filler filler filler", |
| "f(var v) {", |
| " List<String> values = [];", |
| " for (var v in values) {", |
| " var v1 = v;", |
| " }", |
| "}", |
| ""); |
| assertInferredElementTypeString(testUnit, "v1", "String", INFERRED); |
| } |
| |
| public void test_typesPropagation_ifIsNotType_withElse() throws Exception { |
| analyzeLibrary( |
| "// filler filler filler filler filler filler filler filler filler filler", |
| "f(var v) {", |
| " if (v is! String) {", |
| " var v1 = v;", |
| " } else {", |
| " var v2 = v;", |
| " }", |
| " var v3 = v;", |
| "}", |
| ""); |
| // we don't know type, but not String |
| assertInferredElementTypeString(testUnit, "v1", "dynamic", EXACT); |
| // we know that String |
| assertInferredElementTypeString(testUnit, "v2", "String", INFERRED); |
| // again, we don't know after "if" |
| assertInferredElementTypeString(testUnit, "v3", "dynamic", EXACT); |
| } |
| |
| public void test_typesPropagation_ifIsNotType_hasThenReturn() throws Exception { |
| analyzeLibrary( |
| "// filler filler filler filler filler filler filler filler filler filler", |
| "f(var v) {", |
| " var v1 = v;", |
| " if (v is! String) {", |
| " return;", |
| " }", |
| " var v2 = v;", |
| "}", |
| ""); |
| assertInferredElementTypeString(testUnit, "v1", "dynamic", EXACT); |
| assertInferredElementTypeString(testUnit, "v2", "String", INFERRED); |
| } |
| |
| public void test_typesPropagation_ifIsNotType_hasThenThrow() throws Exception { |
| analyzeLibrary( |
| "// filler filler filler filler filler filler filler filler filler filler", |
| "f(var v) {", |
| " if (v is! String) {", |
| " throw new Exception();", |
| " }", |
| " var v1 = v;", |
| "}", |
| ""); |
| assertInferredElementTypeString(testUnit, "v1", "String", INFERRED); |
| } |
| |
| public void test_typesPropagation_ifIsNotType_emptyThen() throws Exception { |
| analyzeLibrary( |
| "// filler filler filler filler filler filler filler filler filler filler", |
| "f(var v) {", |
| " if (v is! String) {", |
| " }", |
| " var v1 = v;", |
| "}", |
| ""); |
| assertInferredElementTypeString(testUnit, "v1", "dynamic", EXACT); |
| } |
| |
| public void test_typesPropagation_ifIsNotType_otherThen() throws Exception { |
| analyzeLibrary( |
| "// filler filler filler filler filler filler filler filler filler filler", |
| "f(var v) {", |
| " if (v is! String) {", |
| " ;", |
| " }", |
| " var v1 = v;", |
| "}", |
| ""); |
| assertInferredElementTypeString(testUnit, "v1", "dynamic", EXACT); |
| } |
| |
| public void test_typesPropagation_ifIsNotType_hasThenThrow_withCatch() throws Exception { |
| analyzeLibrary( |
| "// filler filler filler filler filler filler filler filler filler filler", |
| "f(var v) {", |
| " try {", |
| " if (v is! String) {", |
| " throw new Exception();", |
| " }", |
| " } catch (var e) {", |
| " }", |
| " var v1 = v;", |
| "}", |
| ""); |
| assertInferredElementTypeString(testUnit, "v1", "dynamic", EXACT); |
| } |
| |
| public void test_typesPropagation_ifIsNotType_hasThenContinue() throws Exception { |
| AnalyzeLibraryResult libraryResult = analyzeLibrary( |
| "// filler filler filler filler filler filler filler filler filler filler", |
| "main() {", |
| " for (var v in <Object>[1, 'two', 3]) {", |
| " var v1 = v;", |
| " if (v is! String) {", |
| " continue;", |
| " }", |
| " var v2 = v;", |
| " }", |
| "}", |
| ""); |
| assertErrors(libraryResult.getErrors()); |
| assertInferredElementTypeString(testUnit, "v1", "Object", INFERRED); |
| assertInferredElementTypeString(testUnit, "v2", "String", INFERRED); |
| } |
| |
| public void test_typesPropagation_ifIsNotType_hasThenBreak() throws Exception { |
| AnalyzeLibraryResult libraryResult = analyzeLibrary( |
| "// filler filler filler filler filler filler filler filler filler filler", |
| "main() {", |
| " for (var v in <Object>[1, 'two', 3]) {", |
| " var v1 = v;", |
| " if (v is! String) {", |
| " break;", |
| " }", |
| " var v2 = v;", |
| " }", |
| "}", |
| ""); |
| assertErrors(libraryResult.getErrors()); |
| assertInferredElementTypeString(testUnit, "v1", "Object", INFERRED); |
| assertInferredElementTypeString(testUnit, "v2", "String", INFERRED); |
| } |
| |
| public void test_typesPropagation_ifIsNotType_or() throws Exception { |
| analyzeLibrary( |
| "// filler filler filler filler filler filler filler filler filler filler", |
| "f(var p1, var p2) {", |
| " if (p1 is! int || p2 is! String) {", |
| " return;", |
| " }", |
| " var v1 = p1;", |
| " var v2 = p2;", |
| "}", |
| ""); |
| assertInferredElementTypeString(testUnit, "v1", "int", INFERRED); |
| assertInferredElementTypeString(testUnit, "v2", "String", INFERRED); |
| } |
| |
| public void test_typesPropagation_ifIsNotType_and() throws Exception { |
| analyzeLibrary( |
| "// filler filler filler filler filler filler filler filler filler filler", |
| "f(var v) {", |
| " if (v is! String && true) {", |
| " return;", |
| " }", |
| " var v1 = v;", |
| "}", |
| ""); |
| assertInferredElementTypeString(testUnit, "v1", "dynamic", EXACT); |
| } |
| |
| public void test_typesPropagation_ifIsNotType_not() throws Exception { |
| analyzeLibrary( |
| "// filler filler filler filler filler filler filler filler filler filler", |
| "f(var v) {", |
| " if (!(v is! String)) {", |
| " return;", |
| " }", |
| " var v1 = v;", |
| "}", |
| ""); |
| assertInferredElementTypeString(testUnit, "v1", "dynamic", EXACT); |
| } |
| |
| public void test_typesPropagation_ifIsNotType_not2() throws Exception { |
| analyzeLibrary( |
| "// filler filler filler filler filler filler filler filler filler filler", |
| "f(var v) {", |
| " if (!!(v is! String)) {", |
| " return;", |
| " }", |
| " var v1 = v;", |
| "}", |
| ""); |
| assertInferredElementTypeString(testUnit, "v1", "String", INFERRED); |
| } |
| |
| public void test_typesPropagation_ifNotIsType() throws Exception { |
| analyzeLibrary( |
| "// filler filler filler filler filler filler filler filler filler filler", |
| "f(var v) {", |
| " if (!(v is String)) {", |
| " return;", |
| " }", |
| " var v1 = v;", |
| "}", |
| ""); |
| assertInferredElementTypeString(testUnit, "v1", "String", INFERRED); |
| } |
| |
| /** |
| * <p> |
| * http://code.google.com/p/dart/issues/detail?id=4410 |
| */ |
| public void test_typesPropagation_assertIsType() throws Exception { |
| analyzeLibrary( |
| "f(var v) {", |
| " if (true) {", |
| " var v1 = v;", |
| " assert(v is String);", |
| " var v2 = v;", |
| " {", |
| " var v3 = v;", |
| " }", |
| " var v4 = v;", |
| " }", |
| " var v5 = v;", |
| "}", |
| ""); |
| // we don't know type initially |
| assertInferredElementTypeString(testUnit, "v1", "dynamic", EXACT); |
| // after "assert" all next statements know type |
| assertInferredElementTypeString(testUnit, "v2", "String", INFERRED); |
| assertInferredElementTypeString(testUnit, "v3", "String", INFERRED); |
| // type is set to unknown only when we exit control Block, not just any Block |
| assertInferredElementTypeString(testUnit, "v4", "String", INFERRED); |
| // we exited "if" Block, so "assert" may be was not executed, so we don't know type |
| assertInferredElementTypeString(testUnit, "v5", "dynamic", EXACT); |
| } |
| |
| /** |
| * <p> |
| * http://code.google.com/p/dart/issues/detail?id=4410 |
| */ |
| public void test_typesPropagation_assertIsType_twoVariables() throws Exception { |
| analyzeLibrary( |
| "f(a, b) {", |
| " while (true) {", |
| " var a1 = a;", |
| " var b1 = b;", |
| " assert(a is String);", |
| " assert(b is String);", |
| " var a2 = a;", |
| " var b2 = b;", |
| " }", |
| " var a3 = a;", |
| " var b3 = b;", |
| "}", |
| ""); |
| // we don't know type initially |
| assertInferredElementTypeString(testUnit, "a1", "dynamic", EXACT); |
| assertInferredElementTypeString(testUnit, "b1", "dynamic", EXACT); |
| // after "assert" all next statements know type |
| assertInferredElementTypeString(testUnit, "a2", "String", INFERRED); |
| assertInferredElementTypeString(testUnit, "b2", "String", INFERRED); |
| // we exited "if" Block, so "assert" may be was not executed, so we don't know type |
| assertInferredElementTypeString(testUnit, "a3", "dynamic", EXACT); |
| assertInferredElementTypeString(testUnit, "b3", "dynamic", EXACT); |
| } |
| |
| /** |
| * When variable has explicit type, we should not fall to 'dynamic', we need to keep this type. |
| * <p> |
| * http://code.google.com/p/dart/issues/detail?id=6399 |
| */ |
| public void test_typesPropagation_assertIsType_hasExplicitType() throws Exception { |
| analyzeLibrary( |
| "class A {}", |
| "class B extends A {}", |
| "class C extends B {}", |
| "main() {", |
| " B v;", |
| " if (v is A) {", |
| " var v1 = v;", |
| " }", |
| " if (v is B) {", |
| " var v2 = v;", |
| " }", |
| " if (v is C) {", |
| " var v3 = v;", |
| " }", |
| " if (v is String) {", |
| " var v4 = v;", |
| " }", |
| "}", |
| ""); |
| assertInferredElementTypeString(testUnit, "v1", "B", INFERRED_EXACT); |
| assertInferredElementTypeString(testUnit, "v2", "B", INFERRED_EXACT); |
| assertInferredElementTypeString(testUnit, "v3", "C", INFERRED); |
| assertInferredElementTypeString(testUnit, "v4", "[B, String]", INFERRED); |
| } |
| |
| public void test_typesPropagation_field_inClass_final() throws Exception { |
| analyzeLibrary( |
| "// filler filler filler filler filler filler filler filler filler filler", |
| "class A {", |
| " final v1 = 123;", |
| " final v2 = 1 + 2.0;", |
| "}", |
| ""); |
| assertInferredElementTypeString(testUnit, "v1", "int", INFERRED_EXACT); |
| assertInferredElementTypeString(testUnit, "v2", "double", INFERRED_EXACT); |
| } |
| |
| public void test_typesPropagation_field_inClass_const() throws Exception { |
| analyzeLibrary( |
| "// filler filler filler filler filler filler filler filler filler filler", |
| "class A {", |
| " const v1 = 123;", |
| " final v2 = 1 + 2.0;", |
| "}", |
| ""); |
| assertInferredElementTypeString(testUnit, "v1", "int", INFERRED_EXACT); |
| assertInferredElementTypeString(testUnit, "v2", "double", INFERRED_EXACT); |
| } |
| |
| /** |
| * If field is not final, we don't know if is will be assigned somewhere else, may be even not in |
| * there same unit, so we cannot be sure about its type. |
| */ |
| public void test_typesPropagation_field_inClass_notFinal() throws Exception { |
| analyzeLibrary( |
| "// filler filler filler filler filler filler filler filler filler filler", |
| "class A {", |
| " var v1 = 123;", |
| "}", |
| ""); |
| assertInferredElementTypeString(testUnit, "v1", "dynamic", EXACT); |
| } |
| |
| public void test_typesPropagation_field_topLevel_final() throws Exception { |
| analyzeLibrary( |
| "// filler filler filler filler filler filler filler filler filler filler", |
| "final v1 = 123;", |
| "final v2 = 1 + 2.0;", |
| ""); |
| assertInferredElementTypeString(testUnit, "v1", "int", INFERRED_EXACT); |
| assertInferredElementTypeString(testUnit, "v2", "double", INFERRED_EXACT); |
| } |
| |
| public void test_typesPropagation_field_topLevel_const() throws Exception { |
| analyzeLibrary( |
| "// filler filler filler filler filler filler filler filler filler filler", |
| "const v1 = 123;", |
| "const v2 = 1 + 2.0;", |
| ""); |
| assertInferredElementTypeString(testUnit, "v1", "int", INFERRED_EXACT); |
| assertInferredElementTypeString(testUnit, "v2", "double", INFERRED_EXACT); |
| } |
| |
| /** |
| * If field is not final, we don't know if is will be assigned somewhere else, may be even not in |
| * there same unit, so we cannot be sure about its type. |
| */ |
| public void test_typesPropagation_field_topLevel_notFinal() throws Exception { |
| analyzeLibrary( |
| "// filler filler filler filler filler filler filler filler filler filler", |
| "var v1 = 123;", |
| ""); |
| assertInferredElementTypeString(testUnit, "v1", "dynamic", EXACT); |
| } |
| |
| public void test_typesPropagation_FunctionAliasType() throws Exception { |
| analyzeLibrary( |
| "// filler filler filler filler filler filler filler filler filler filler", |
| "typedef F();", |
| "foo(F f) {", |
| " var v = f;", |
| " v();", |
| "}", |
| "", |
| ""); |
| assertInferredElementTypeString(testUnit, "v", "F", INFERRED_EXACT); |
| } |
| |
| /** |
| * When we pass "function literal" into invocation on some method, we may know exact |
| * <code>Function</code> type expected by this method, so we know types of "function literal" |
| * parameters. So, if these types are not specified in "function literal", we can use "expected" |
| * types. |
| * <p> |
| * http://code.google.com/p/dart/issues/detail?id=3712 |
| */ |
| public void test_typesPropagation_parameterOfClosure_invocationNormalParameter() throws Exception { |
| analyzeLibrary( |
| "// filler filler filler filler filler filler filler filler filler filler", |
| "class Event {}", |
| "typedef void EventListener(Event event);", |
| "foo(EventListener listener) {", |
| "}", |
| "main() {", |
| " foo((e) {", |
| " var v = e;", |
| " });", |
| "}", |
| ""); |
| assertInferredElementTypeString(testUnit, "v", "Event", INFERRED); |
| } |
| |
| /** |
| * <p> |
| * http://code.google.com/p/dart/issues/detail?id=3712 |
| */ |
| public void test_typesPropagation_parameterOfClosure_invocationNamedPositionalParameter() throws Exception { |
| analyzeLibrary( |
| "// filler filler filler filler filler filler filler filler filler filler", |
| "class Event {}", |
| "typedef void EventListener(Event event);", |
| "foo([EventListener listener]) {", |
| "}", |
| "main() {", |
| " foo((e) {", |
| " var v = e;", |
| " });", |
| "}", |
| ""); |
| assertInferredElementTypeString(testUnit, "v", "Event", INFERRED); |
| } |
| |
| /** |
| * <p> |
| * http://code.google.com/p/dart/issues/detail?id=3712 |
| */ |
| public void test_typesPropagation_parameterOfClosure_invocationNamedParameter() throws Exception { |
| analyzeLibrary( |
| "// filler filler filler filler filler filler filler filler filler filler", |
| "class Event {}", |
| "typedef void EventListener(Event event);", |
| "foo({EventListener listener}) {", |
| "}", |
| "main() {", |
| " foo(listener: (e) {", |
| " var v = e;", |
| " });", |
| "}", |
| ""); |
| assertInferredElementTypeString(testUnit, "v", "Event", INFERRED); |
| } |
| |
| /** |
| * http://code.google.com/p/dart/issues/detail?id=3712 |
| */ |
| public void test_typesPropagation_parameterOfClosure_invocationOfMethod() throws Exception { |
| analyzeLibrary( |
| "// filler filler filler filler filler filler filler filler filler filler", |
| "class Event {}", |
| "typedef void EventListener(Event event);", |
| "class Button {", |
| " onClick(EventListener listener) {", |
| " }", |
| "}", |
| "main() {", |
| " Button button = new Button();", |
| " button.onClick((e) {", |
| " var v = e;", |
| " });", |
| "}", |
| ""); |
| assertInferredElementTypeString(testUnit, "v", "Event", INFERRED); |
| } |
| |
| /** |
| * We should infer closure parameter types even in {@link FunctionType} is specified directly, |
| * without using {@link FunctionAliasType}. |
| */ |
| public void test_typesPropagation_parameterOfClosure_functionType() throws Exception { |
| analyzeLibrary( |
| "// filler filler filler filler filler filler filler filler filler filler", |
| "class Event {}", |
| "class Button<T> {", |
| " onClick(listener(T e)) {", |
| " }", |
| "}", |
| "main() {", |
| " var button = new Button<Event>();", |
| " button.onClick((e) {", |
| " var v = e;", |
| " });", |
| "}", |
| ""); |
| assertInferredElementTypeString(testUnit, "v", "Event", INFERRED); |
| } |
| |
| public void test_typesPropagation_parameterOfClosure_assignVariable() throws Exception { |
| analyzeLibrary( |
| "// filler filler filler filler filler filler filler filler filler filler", |
| "class Event {}", |
| "typedef void EventListener(Event event);", |
| "main() {", |
| " // local variable assign", |
| " {", |
| " EventListener listener;", |
| " listener = (e) {", |
| " var v1 = e;", |
| " };", |
| " }", |
| " // local variable declare", |
| " {", |
| " EventListener listener = (e) {", |
| " var v2 = e;", |
| " };", |
| " }", |
| "}", |
| ""); |
| assertInferredElementTypeString(testUnit, "v1", "Event", INFERRED); |
| assertInferredElementTypeString(testUnit, "v2", "Event", INFERRED); |
| } |
| |
| public void test_typesPropagation_parameterOfClosure_assignField() throws Exception { |
| analyzeLibrary( |
| "// filler filler filler filler filler filler filler filler filler filler", |
| "class Event {}", |
| "typedef void EventListener(Event event);", |
| "class Button {", |
| " EventListener listener;", |
| "}", |
| "EventListener topLevelListener;", |
| "main() {", |
| " // top-level field", |
| " {", |
| " topLevelListener = (e) {", |
| " var v1 = e;", |
| " };", |
| " }", |
| " // member field", |
| " {", |
| " Button button = new Button();", |
| " button.listener = (e) {", |
| " var v2 = e;", |
| " };", |
| " }", |
| "}", |
| "EventListener topLevelListener2 = (e) {", |
| " var v3 = e;", |
| "};", |
| ""); |
| assertInferredElementTypeString(testUnit, "v1", "Event", INFERRED); |
| assertInferredElementTypeString(testUnit, "v2", "Event", INFERRED); |
| assertInferredElementTypeString(testUnit, "v3", "Event", INFERRED); |
| } |
| |
| /** |
| * Helpful (but not perfectly satisfying Specification) type of "conditional" is intersection of |
| * then/else types, not just their "least upper bounds". And this corresponds runtime behavior. |
| */ |
| public void test_typesPropagation_conditional() throws Exception { |
| AnalyzeLibraryResult libraryResult = analyzeLibrary( |
| "// filler filler filler filler filler filler filler filler filler filler", |
| "abstract class I1 {", |
| " f1();", |
| "}", |
| "abstract class I2 {", |
| " f2();", |
| "}", |
| "class A implements I1, I2 {", |
| " f1() => 11;", |
| " f2() => 12;", |
| "}", |
| "class B implements I1, I2 {", |
| " f1() => 21;", |
| " f2() => 22;", |
| "}", |
| "main() {", |
| " var v = true ? new A() : new B();", |
| " v.f1();", |
| " v.f2();", |
| "}", |
| ""); |
| // no errors, because both f1() and f2() invocations were resolved |
| assertErrors(libraryResult.getErrors()); |
| // v.f1() was resolved |
| { |
| DartExpression expression = findNodeBySource(testUnit, "v.f1()"); |
| assertNotNull(expression); |
| assertNotNull(expression.getElement()); |
| } |
| // v.f2() was resolved |
| { |
| DartExpression expression = findNodeBySource(testUnit, "v.f1()"); |
| assertNotNull(expression); |
| assertNotNull(expression.getElement()); |
| } |
| } |
| |
| public void test_getType_binaryExpression() throws Exception { |
| analyzeLibrary( |
| "f(var arg) {", |
| " var v1 = 1 + 2;", |
| " var v2 = 1 - 2;", |
| " var v3 = 1 * 2;", |
| " var v4 = 1 ~/ 2;", |
| " var v5 = 1 % 2;", |
| " var v6 = 1 / 2;", |
| " var v7 = 1.0 + 2;", |
| " var v8 = 1 + 2.0;", |
| " var v9 = 1 - 2.0;", |
| " var v10 = 1.0 - 2;", |
| " var v11 = 1 * 2.0;", |
| " var v12 = 1.0 * 2;", |
| " var v13 = 1.0 / 2;", |
| " var v14 = 1 / 2.0;", |
| " var v15 = 1.0 ~/ 2.0;", |
| " var v16 = 1.0 ~/ 2;", |
| " var v17 = arg as int", |
| "}", |
| ""); |
| assertInferredElementTypeString(testUnit, "v1", "int", INFERRED_EXACT); |
| assertInferredElementTypeString(testUnit, "v2", "int", INFERRED_EXACT); |
| assertInferredElementTypeString(testUnit, "v3", "int", INFERRED_EXACT); |
| assertInferredElementTypeString(testUnit, "v4", "int", INFERRED_EXACT); |
| assertInferredElementTypeString(testUnit, "v5", "int", INFERRED_EXACT); |
| assertInferredElementTypeString(testUnit, "v6", "double", INFERRED_EXACT); |
| assertInferredElementTypeString(testUnit, "v7", "double", INFERRED_EXACT); |
| assertInferredElementTypeString(testUnit, "v8", "double", INFERRED_EXACT); |
| assertInferredElementTypeString(testUnit, "v9", "double", INFERRED_EXACT); |
| assertInferredElementTypeString(testUnit, "v10", "double", INFERRED_EXACT); |
| assertInferredElementTypeString(testUnit, "v11", "double", INFERRED_EXACT); |
| assertInferredElementTypeString(testUnit, "v12", "double", INFERRED_EXACT); |
| assertInferredElementTypeString(testUnit, "v13", "double", INFERRED_EXACT); |
| assertInferredElementTypeString(testUnit, "v14", "double", INFERRED_EXACT); |
| assertInferredElementTypeString(testUnit, "v15", "double", INFERRED_EXACT); |
| assertInferredElementTypeString(testUnit, "v16", "double", INFERRED_EXACT); |
| assertInferredElementTypeString(testUnit, "v17", "int", INFERRED); |
| } |
| |
| /** |
| * It was requested that even if Editor can be helpful and warn about types incompatibility, it |
| * should not do this to completely satisfy specification. |
| * <p> |
| * http://code.google.com/p/dart/issues/detail?id=3223 |
| * <p> |
| * This feature was requested by users, so we introduce it again, but disabled to command line. |
| * <p> |
| * http://code.google.com/p/dart/issues/detail?id=4518 |
| */ |
| public void test_typesPropagation_noExtraWarnings() throws Exception { |
| AnalyzeLibraryResult libraryResult = analyzeLibrary( |
| "// filler filler filler filler filler filler filler filler filler filler", |
| "f(int v) {}", |
| "f1() {", |
| " var v = true;", |
| " f(v);", |
| "}", |
| "f2(var v) {", |
| " if (v is bool) {", |
| " f(v);", |
| " }", |
| "}", |
| "f3(var v) {", |
| " while (v is bool) {", |
| " f(v);", |
| " }", |
| "}", |
| ""); |
| assertErrors(libraryResult.getErrors()); |
| } |
| |
| /** |
| * There was problem that using <code>() -> bool</code> getter in negation ('!') caused assignment |
| * warnings. Actual reason was that with negation getter access is visited twice and on the second |
| * time type of getter method, instead of return type, was returned. |
| */ |
| public void test_getType_getterInNegation() throws Exception { |
| AnalyzeLibraryResult libraryResult = analyzeLibrary( |
| "class A {", |
| " int get intProperty => 42;", |
| " bool get boolProperty => true;", |
| "}", |
| "f() {", |
| " var a = new A();", |
| " var v1 = a.intProperty;", |
| " var v2 = a.boolProperty;", |
| " if (a.boolProperty) {", |
| " }", |
| " if (!a.boolProperty) {", |
| " }", |
| "}", |
| ""); |
| assertErrors(libraryResult.getErrors()); |
| assertInferredElementTypeString(testUnit, "v1", "int", INFERRED); |
| assertInferredElementTypeString(testUnit, "v2", "bool", INFERRED); |
| } |
| |
| public void test_getType_getterInNegation_generic() throws Exception { |
| AnalyzeLibraryResult libraryResult = analyzeLibrary( |
| "class A<T> {", |
| " T field;", |
| " T get prop => null;", |
| "}", |
| "f() {", |
| " var a = new A<bool>();", |
| " var v1 = a.field;", |
| " var v2 = a.prop;", |
| " if (a.field) {", |
| " }", |
| " if (!a.field) {", |
| " }", |
| " if (a.prop) {", |
| " }", |
| " if (!a.prop) {", |
| " }", |
| "}", |
| ""); |
| assertErrors(libraryResult.getErrors()); |
| assertInferredElementTypeString(testUnit, "v1", "bool", INFERRED); |
| assertInferredElementTypeString(testUnit, "v2", "bool", INFERRED); |
| } |
| |
| public void test_getType_getterInSwitch_default() throws Exception { |
| AnalyzeLibraryResult libraryResult = analyzeLibrary( |
| "// filler filler filler filler filler filler filler filler filler filler", |
| "int get foo {}", |
| "f() {", |
| " switch (true) {", |
| " default:", |
| " int v = foo;", |
| " }", |
| "}", |
| ""); |
| assertErrors(libraryResult.getErrors()); |
| } |
| |
| /** |
| * <p> |
| * http://code.google.com/p/dart/issues/detail?id=3515 |
| */ |
| public void test_getType_getterInSwitchExpression_topLevel() throws Exception { |
| AnalyzeLibraryResult libraryResult = analyzeLibrary( |
| "// filler filler filler filler filler filler filler filler filler filler", |
| "int get foo => 42;", |
| "f() {", |
| " switch (foo) {", |
| " case 2:", |
| " break;", |
| " }", |
| "}", |
| ""); |
| assertErrors(libraryResult.getErrors()); |
| } |
| |
| /** |
| * <p> |
| * http://code.google.com/p/dart/issues/detail?id=3515 |
| */ |
| public void test_getType_getterInSwitchExpression_inClass() throws Exception { |
| AnalyzeLibraryResult libraryResult = analyzeLibrary( |
| "// filler filler filler filler filler filler filler filler filler filler", |
| "class A<T> {", |
| " T get foo => null;", |
| "}", |
| "f() {", |
| " A<int> a = new A<int>();", |
| " switch (a.foo) {", |
| " case 2:", |
| " break;", |
| " }", |
| "}", |
| ""); |
| assertErrors(libraryResult.getErrors()); |
| } |
| |
| /** |
| * <p> |
| * http://code.google.com/p/dart/issues/detail?id=3272 |
| */ |
| public void test_assignVoidToDynamic() throws Exception { |
| AnalyzeLibraryResult libraryResult = analyzeLibrary( |
| "// filler filler filler filler filler filler filler filler filler filler", |
| "void foo() {}", |
| "main() {", |
| " var v = foo();", |
| "}", |
| ""); |
| assertErrors(libraryResult.getErrors()); |
| } |
| |
| /** |
| * <p> |
| * http://code.google.com/p/dart/issues/detail?id=5114 |
| */ |
| public void test_lowerCaseDynamicType() throws Exception { |
| AnalyzeLibraryResult libraryResult = analyzeLibrary( |
| "// filler filler filler filler filler filler filler filler filler filler", |
| "main() {", |
| " dynamic v = null;", |
| "}", |
| ""); |
| assertErrors(libraryResult.getErrors()); |
| } |
| |
| /** |
| * It is a static warning if the return type of the user-declared operator == is explicitly |
| * declared and not bool. |
| */ |
| public void test_equalsOperator_type() throws Exception { |
| AnalyzeLibraryResult libraryResult = analyzeLibrary( |
| "// filler filler filler filler filler filler filler filler filler filler", |
| "class A {", |
| " bool operator ==(other) {}", |
| "}", |
| "class B {", |
| " String operator ==(other) {}", |
| "}", |
| "class C {", |
| " Object operator ==(other) {}", |
| "}", |
| ""); |
| assertErrors( |
| libraryResult.getErrors(), |
| errEx(TypeErrorCode.CANNOT_OVERRIDE_METHOD_NOT_SUBTYPE, 6, 19, 2), |
| errEx(TypeErrorCode.OPERATOR_EQUALS_BOOL_RETURN_TYPE, 6, 3, 6), |
| errEx(TypeErrorCode.OPERATOR_EQUALS_BOOL_RETURN_TYPE, 9, 3, 6)); |
| } |
| |
| /** |
| * We should be able to resolve "a == b" to the "equals" operator. |
| */ |
| public void test_equalsOperator_resolving() throws Exception { |
| AnalyzeLibraryResult libraryResult = analyzeLibrary( |
| "// filler filler filler filler filler filler filler filler filler filler", |
| "class C {", |
| " operator ==(other) => false;", |
| "}", |
| "main() {", |
| " new C() == new C();", |
| "}", |
| ""); |
| assertErrors(libraryResult.getErrors()); |
| // find == expression |
| DartExpression expression = findNodeBySource(testUnit, "new C() == new C()"); |
| assertNotNull(expression); |
| // validate == element |
| MethodElement equalsElement = (MethodElement) expression.getElement(); |
| assertNotNull(equalsElement); |
| } |
| |
| /** |
| * We can not override getter. But setter has name "setter=", so there are no conflict. |
| */ |
| public void test_supertypeHasMethod() throws Exception { |
| AnalyzeLibraryResult libraryResult = analyzeLibrary( |
| "// filler filler filler filler filler filler filler filler filler filler", |
| "class A {}", |
| "abstract class I {", |
| " foo();", |
| " bar();", |
| "}", |
| "abstract class J implements I {", |
| " get foo;", |
| " set bar(x);", |
| "}"); |
| assertErrors(libraryResult.getErrors(), |
| errEx(TypeErrorCode.SUPERTYPE_HAS_METHOD, 8, 7, 3)); |
| } |
| |
| /** |
| * Ensure that "operator call()" is parsed, and "operator" is not considered as return type. This |
| * too weak test, but for now we are interested only in parsing. |
| */ |
| public void test_callOperator_parsing() throws Exception { |
| AnalyzeLibraryResult libraryResult = analyzeLibrary( |
| "// filler filler filler filler filler filler filler filler filler filler", |
| "class A {", |
| " operator call() => 42;", |
| "}", |
| ""); |
| assertErrors(libraryResult.getErrors()); |
| } |
| |
| /** |
| * The spec in the section 10.28 says: |
| * "It is a compile-time error to use a built-in identifier other than dynamic as a type annotation." |
| * <p> |
| * http://code.google.com/p/dart/issues/detail?id=3307 |
| */ |
| public void test_builtInIdentifier_asTypeAnnotation() throws Exception { |
| AnalyzeLibraryResult libraryResult = analyzeLibrary( |
| "// filler filler filler filler filler filler filler filler filler filler", |
| "main() {", |
| " abstract v01;", |
| " as v02;", |
| " dynamic v03;", |
| " export v04;", |
| " external v05;", |
| " factory v06;", |
| " get v07;", |
| " implements v08;", |
| " import v09;", |
| " library v10;", |
| " operator v11;", |
| " part v12;", |
| " set v13;", |
| " static v14;", |
| "// typedef v15;", |
| "}", |
| ""); |
| assertErrors( |
| libraryResult.getErrors(), |
| errEx(ResolverErrorCode.BUILT_IN_IDENTIFIER_AS_TYPE, 3, 3, 8), // abstract |
| errEx(ResolverErrorCode.BUILT_IN_IDENTIFIER_AS_TYPE, 4, 3, 2), // as |
| // dynamic |
| errEx(ResolverErrorCode.BUILT_IN_IDENTIFIER_AS_TYPE, 6, 3, 6), // export |
| errEx(ResolverErrorCode.BUILT_IN_IDENTIFIER_AS_TYPE, 7, 3, 8), // external |
| errEx(ResolverErrorCode.BUILT_IN_IDENTIFIER_AS_TYPE, 8, 3, 7), // factory |
| errEx(ResolverErrorCode.BUILT_IN_IDENTIFIER_AS_TYPE, 9, 3, 3), // get |
| errEx(ResolverErrorCode.BUILT_IN_IDENTIFIER_AS_TYPE, 10, 3, 10), // implements |
| errEx(ResolverErrorCode.BUILT_IN_IDENTIFIER_AS_TYPE, 11, 3, 6), // import |
| errEx(ResolverErrorCode.BUILT_IN_IDENTIFIER_AS_TYPE, 12, 3, 7), // library |
| errEx(ResolverErrorCode.BUILT_IN_IDENTIFIER_AS_TYPE, 13, 3, 8), // operator |
| errEx(ResolverErrorCode.BUILT_IN_IDENTIFIER_AS_TYPE, 14, 3, 4), // part |
| errEx(ResolverErrorCode.BUILT_IN_IDENTIFIER_AS_TYPE, 15, 3, 3), // set |
| errEx(ResolverErrorCode.BUILT_IN_IDENTIFIER_AS_TYPE, 16, 3, 6) // static |
| // ,errEx(ResolverErrorCode.BUILT_IN_IDENTIFIER_AS_TYPE, 17, 3, 7) // typedef |
| ); |
| } |
| |
| public void test_supertypeHasField() throws Exception { |
| AnalyzeLibraryResult libraryResult = analyzeLibrary( |
| "// filler filler filler filler filler filler filler filler filler filler", |
| "class A {", |
| " var foo;", |
| " var bar;", |
| "}", |
| "class B extends A {", |
| " foo() {}", |
| " bar() {}", |
| "}"); |
| assertErrors(libraryResult.getErrors(), |
| errEx(TypeErrorCode.SUPERTYPE_HAS_FIELD, 7, 3, 3), |
| errEx(TypeErrorCode.SUPERTYPE_HAS_FIELD, 8, 3, 3)); |
| } |
| |
| /** |
| * We can not override getter. But setter has name "setter=", so there are no conflict. |
| */ |
| public void test_supertypeHasGetterSetter() throws Exception { |
| AnalyzeLibraryResult libraryResult = analyzeLibrary( |
| "// filler filler filler filler filler filler filler filler filler filler", |
| "class A {", |
| " get foo => 0;", |
| " set bar(x) {}", |
| "}", |
| "class B extends A {", |
| " foo() {}", |
| " bar() {}", |
| "}"); |
| assertErrors(libraryResult.getErrors(), |
| errEx(TypeErrorCode.SUPERTYPE_HAS_FIELD, 7, 3, 3)); |
| } |
| |
| /** |
| * <p> |
| * http://code.google.com/p/dart/issues/detail?id=3280 |
| */ |
| public void test_typeVariableExtendsFunctionAliasType() throws Exception { |
| AnalyzeLibraryResult libraryResult = analyzeLibrary( |
| "// filler filler filler filler filler filler filler filler filler filler", |
| "typedef void F();", |
| "class C<T extends F> {", |
| " test() {", |
| " new C<T>();", |
| " }", |
| "}", |
| ""); |
| assertErrors(libraryResult.getErrors()); |
| } |
| |
| /** |
| * <p> |
| * http://code.google.com/p/dart/issues/detail?id=3344 |
| */ |
| public void test_typeVariableExtendsTypeVariable() throws Exception { |
| AnalyzeLibraryResult libraryResult = analyzeLibrary( |
| "// filler filler filler filler filler filler filler filler filler filler", |
| "class A<T, U extends T> {", |
| " f1(U u) {", |
| " T t = u;", |
| " }", |
| " f2(T t) {", |
| " U u = t;", |
| " }", |
| "}", |
| ""); |
| assertErrors(libraryResult.getErrors()); |
| } |
| |
| public void test_staticMemberAccessThroughInstance() throws Exception { |
| AnalyzeLibraryResult libraryResult = analyzeLibrary( |
| "// filler filler filler filler filler filler filler filler filler filler", |
| "class A {", |
| " static var x;", |
| " static y() {}", |
| " static method() {", |
| " var a = new A();", |
| " a.x = 1;", |
| " var foo = a.x;", |
| " a.y();", |
| " a.y = 1;", |
| " }", |
| "}", |
| ""); |
| assertErrors(libraryResult.getErrors(), |
| errEx(TypeErrorCode.STATIC_MEMBER_ACCESSED_THROUGH_INSTANCE, 7, 7, 1), |
| errEx(TypeErrorCode.STATIC_MEMBER_ACCESSED_THROUGH_INSTANCE, 8, 17, 1), |
| errEx(TypeErrorCode.IS_STATIC_METHOD_IN, 9, 7, 1), |
| errEx(TypeErrorCode.STATIC_MEMBER_ACCESSED_THROUGH_INSTANCE, 10, 7, 1), |
| errEx(TypeErrorCode.CANNOT_ASSIGN_TO, 10, 5, 3)); |
| } |
| |
| public void testExpectedPositionalArgument() throws Exception { |
| AnalyzeLibraryResult libraryResult = analyzeLibrary( |
| "// filler filler filler filler filler filler filler filler filler filler", |
| "method1(a, [b]) {}", |
| "method2() {", |
| " method1(b:1);", |
| "}"); |
| assertErrors(libraryResult.getErrors(), |
| errEx(TypeErrorCode.EXPECTED_POSITIONAL_ARGUMENT, 4, 11, 3)); |
| } |
| |
| public void test_cannotResolveMethod_unqualified() throws Exception { |
| AnalyzeLibraryResult libraryResult = analyzeLibrary( |
| "// filler filler filler filler filler filler filler filler filler filler", |
| "class A {", |
| " f() {", |
| " foo();", |
| " }", |
| "}", |
| ""); |
| assertErrors( |
| libraryResult.getErrors(), |
| errEx(TypeErrorCode.INTERFACE_HAS_NO_METHOD_NAMED, 4, 5, 3)); |
| } |
| |
| public void test_canNotResolveMethod_qualified() throws Exception { |
| AnalyzeLibraryResult libraryResult = analyzeLibrary( |
| "// filler filler filler filler filler filler filler filler filler filler", |
| "class A {", |
| "}", |
| "main() {", |
| " A a = new A();", |
| " a.foo();", |
| "}", |
| ""); |
| assertErrors( |
| libraryResult.getErrors(), |
| errEx(TypeErrorCode.INTERFACE_HAS_NO_METHOD_NAMED, 6, 5, 3)); |
| } |
| |
| public void test_operatorLocation() throws Exception { |
| AnalyzeLibraryResult libraryResult = analyzeLibrary( |
| "// filler filler filler filler filler filler filler filler filler filler", |
| "class A {", |
| "}", |
| "main() {", |
| " A a = new A();", |
| " a + 0;", |
| " -a;", |
| " a--;", |
| "}", |
| ""); |
| assertErrors( |
| libraryResult.getErrors(), |
| errEx(TypeErrorCode.INTERFACE_HAS_NO_METHOD_NAMED, 6, 5, 1), |
| errEx(TypeErrorCode.CANNOT_BE_RESOLVED, 7, 3, 1), |
| errEx(TypeErrorCode.CANNOT_BE_RESOLVED, 8, 4, 2)); |
| } |
| |
| /** |
| * It is a static warning if T does not denote a type available in the current lexical scope. |
| * <p> |
| * http://code.google.com/p/dart/issues/detail?id=2373 |
| */ |
| public void test_asType_unknown() throws Exception { |
| AnalyzeLibraryResult libraryResult = analyzeLibrary( |
| "// filler filler filler filler filler filler filler filler filler filler", |
| "main() {", |
| " null as T;", |
| "}", |
| ""); |
| assertErrors( |
| libraryResult.getErrors(), |
| errEx(TypeErrorCode.NO_SUCH_TYPE, 3, 11, 1)); |
| } |
| |
| /** |
| * It is a compile-time error if T is a parameterized type of the form G < T1; : : : ; Tn > and G |
| * is not a generic type with n type parameters. |
| * <p> |
| * http://code.google.com/p/dart/issues/detail?id=2373 |
| */ |
| public void test_asType_wrongNumberOfTypeArguments() throws Exception { |
| AnalyzeLibraryResult libraryResult = analyzeLibrary( |
| "// filler filler filler filler filler filler filler filler filler filler", |
| "class A {}", |
| "main() {", |
| " null as A<int, bool>;", |
| "}", |
| ""); |
| assertErrors( |
| libraryResult.getErrors(), |
| errEx(ResolverErrorCode.WRONG_NUMBER_OF_TYPE_ARGUMENTS, 4, 11, 12)); |
| } |
| |
| /** |
| * It is a static warning if T does not denote a type available in the current lexical scope. |
| * <p> |
| * http://code.google.com/p/dart/issues/detail?id=2373 |
| */ |
| public void test_isType_unknown() throws Exception { |
| AnalyzeLibraryResult libraryResult = analyzeLibrary( |
| "// filler filler filler filler filler filler filler filler filler filler", |
| "main() {", |
| " null is T;", |
| "}", |
| ""); |
| assertErrors( |
| libraryResult.getErrors(), |
| errEx(TypeErrorCode.NO_SUCH_TYPE, 3, 11, 1)); |
| } |
| |
| public void test_incompatibleTypesInHierarchy1() throws Exception { |
| AnalyzeLibraryResult libraryResult = analyzeLibrary( |
| "// filler filler filler filler filler filler filler filler filler filler", |
| "abstract class Interface<T> {", |
| " T m();", |
| "}", |
| "abstract class A implements Interface {", |
| "}", |
| "class C extends A implements Interface<int> {", |
| " int m() => 0;", |
| "}"); |
| assertErrors( |
| libraryResult.getErrors()); |
| } |
| |
| public void test_incompatibleTypesInHierarchy2() throws Exception { |
| AnalyzeLibraryResult libraryResult = analyzeLibrary( |
| "// filler filler filler filler filler filler filler filler filler filler", |
| "abstract class Interface<T> {", |
| " T m();", |
| "}", |
| "abstract class A implements Interface<String> {", |
| "}", |
| "class C extends A implements Interface<int> {", |
| " int m() => 0;", |
| "}"); |
| assertErrors( |
| libraryResult.getErrors(), |
| errEx(TypeErrorCode.CANNOT_OVERRIDE_METHOD_NOT_SUBTYPE, 8, 7, 1), |
| errEx(TypeErrorCode.INCOMPATIBLE_TYPES_IN_HIERARCHY, 7, 7, 1)); |
| } |
| |
| public void test_variableUsedAsType() throws Exception { |
| AnalyzeLibraryResult libraryResult = analyzeLibrary( |
| "// filler filler filler filler filler filler filler filler filler filler", |
| "var func;", |
| "func i;"); |
| assertErrors( |
| libraryResult.getErrors(), |
| errEx(TypeErrorCode.NOT_A_TYPE, 3, 1, 4)); |
| } |
| |
| public void test_metadataComment_deprecated_1() throws Exception { |
| AnalyzeLibraryResult libraryResult = analyzeLibrary( |
| "// filler filler filler filler filler filler filler filler filler filler", |
| "const deprecated = 0;", |
| "@deprecated", |
| "ttt() {}", |
| "@deprecated", |
| "get topLevelGet => 42;", |
| "class A {", |
| " var @deprecated fff;", |
| " @deprecated", |
| " mmmm() {}", |
| " @deprecated", |
| " operator + (other) {}", |
| "}", |
| "method() {", |
| " ttt();", |
| " print(topLevelGet);", |
| " A a = new A();", |
| " a.fff = 0;", |
| " a.mmmm();", |
| " a + 0;", |
| "}", |
| ""); |
| assertErrors( |
| libraryResult.getErrors(), |
| errEx(TypeErrorCode.DEPRECATED_ELEMENT, 16, 9, 11), |
| errEx(TypeErrorCode.DEPRECATED_ELEMENT, 15, 3, 3), |
| errEx(TypeErrorCode.DEPRECATED_ELEMENT, 18, 5, 3), |
| errEx(TypeErrorCode.DEPRECATED_ELEMENT, 19, 5, 4), |
| errEx(TypeErrorCode.DEPRECATED_ELEMENT, 20, 5, 1)); |
| } |
| |
| public void test_metadataComment_deprecated_2() throws Exception { |
| AnalyzeLibraryResult libraryResult = analyzeLibrary( |
| "// filler filler filler filler filler filler filler filler filler filler", |
| "const deprecated = 0;", |
| "@deprecated", |
| "class A {", |
| " A.named() {}", |
| " @deprecated", |
| " A.depreca() {}", |
| "}", |
| "method() {", |
| " new A.named();", |
| " new A.depreca();", |
| "}", |
| ""); |
| assertErrors( |
| libraryResult.getErrors(), |
| errEx(TypeErrorCode.DEPRECATED_ELEMENT, 10, 7, 1), |
| errEx(TypeErrorCode.DEPRECATED_ELEMENT, 11, 7, 1), |
| errEx(TypeErrorCode.DEPRECATED_ELEMENT, 11, 9, 7)); |
| } |
| |
| public void test_metadata_resolving() throws Exception { |
| AnalyzeLibraryResult libraryResult = analyzeLibrary( |
| "// filler filler filler filler filler filler filler filler filler filler", |
| "const test = 0;", |
| "", |
| "@test", |
| "class A {", |
| " @test", |
| " m(@test p) {", |
| " @test var v = 0;", |
| " }", |
| "}", |
| "", |
| "f(@test p) {}", |
| "", |
| "@test typedef F();", |
| "", |
| ""); |
| // @deprecated should be resolved at every place, so no errors |
| assertErrors(libraryResult.getErrors()); |
| } |
| |
| public void test_assignMethod() throws Exception { |
| AnalyzeLibraryResult libraryResult = analyzeLibrary( |
| "// filler filler filler filler filler filler filler filler filler filler", |
| "class C {" + |
| " method() { }", |
| "}", |
| "main () {", |
| " new C().method = _() {};", |
| "}"); |
| assertErrors( |
| libraryResult.getErrors(), |
| errEx(TypeErrorCode.CANNOT_ASSIGN_TO, 5, 3, 14)); |
| } |
| |
| public void test_assignSetter() throws Exception { |
| AnalyzeLibraryResult libraryResult = analyzeLibrary( |
| "// filler filler filler filler filler filler filler filler filler filler", |
| "class C {" + |
| " set method(arg) { }", |
| "}", |
| "main () {", |
| " new C().method = _() {};", |
| "}"); |
| assertErrors( |
| libraryResult.getErrors()); |
| } |
| |
| public void test_assignGetter() throws Exception { |
| AnalyzeLibraryResult libraryResult = analyzeLibrary( |
| "// filler filler filler filler filler filler filler filler filler filler", |
| "class C {" + |
| " get method { }", |
| "}", |
| "main () {", |
| " new C().method = _() {};", |
| "}"); |
| assertErrors( |
| libraryResult.getErrors(), |
| errEx(TypeErrorCode.FIELD_HAS_NO_SETTER, 5, 11, 6)); |
| } |
| |
| /** |
| * Test for "operator []=". |
| * <p> |
| * http://code.google.com/p/dart/issues/detail?id=4881 |
| */ |
| public void test_assignArrayElement() throws Exception { |
| AnalyzeLibraryResult libraryResult = analyzeLibrary( |
| "// filler filler filler filler filler filler filler filler filler filler", |
| "class C {" + |
| " get method { }", |
| " operator []=(k, v) {}", |
| "}", |
| "main () {", |
| " new C()[0] = 1;", |
| "}"); |
| assertErrors( |
| libraryResult.getErrors()); |
| } |
| |
| /** |
| * Test for resolving variants of array access and unary/binary expressions. |
| * <p> |
| * http://code.google.com/p/dart/issues/detail?id=5042 |
| */ |
| public void test_opAssignArrayElement() throws Exception { |
| AnalyzeLibraryResult libraryResult = analyzeLibrary( |
| "// filler filler filler filler filler filler filler filler filler filler", |
| "class A {", |
| " B operator [](k) => new B();", |
| " operator []=(k, v) { }", |
| "}", |
| "class B {", |
| " B operator +(x) => new B();", |
| "}", |
| "main () {", |
| " var a = new A();", |
| " process( a[2] );", |
| " a[0]++;", |
| " ++a[0];", |
| " a[0] += 1;", |
| " a[0] = 1;", |
| "}", |
| "process(x) {}", |
| ""); |
| assertErrors(libraryResult.getErrors()); |
| // print( a[2] ) |
| { |
| DartArrayAccess access = findNode(DartArrayAccess.class, "a[2]"); |
| // a[2] is invocation of method "[]" |
| assertHasMethodElement(access, "A", "[]"); |
| } |
| // a[0]++ |
| { |
| DartUnaryExpression unary = findNode(DartUnaryExpression.class, "a[0]++"); |
| // a[0]++ is invocation of method "+" |
| assertHasMethodElement(unary, "B", "+"); |
| // a[0] is invocation of method [] |
| assertHasMethodElement(unary.getArg(), "A", "[]"); |
| } |
| // ++a[0] |
| { |
| DartUnaryExpression unary = findNode(DartUnaryExpression.class, "++a[0]"); |
| // ++a[0] is invocation of method "+" |
| assertHasMethodElement(unary, "B", "+"); |
| // a[0] is invocation of method [] |
| assertHasMethodElement(unary.getArg(), "A", "[]"); |
| } |
| // a[0] += 1 |
| { |
| DartBinaryExpression binary = findNode(DartBinaryExpression.class, "a[0] += 1"); |
| // a[0] += 1 is invocation of method "+" |
| assertHasMethodElement(binary, "B", "+"); |
| // a[0] is invocation of method [] |
| assertHasMethodElement(binary.getArg1(), "A", "[]"); |
| } |
| // a[0] = 1 |
| { |
| DartBinaryExpression binary = findNode(DartBinaryExpression.class, "a[0] = 1"); |
| // a[0] = 1 is invocation of method "[]=" |
| assertHasMethodElement(binary, "A", "[]="); |
| // a[0] is invocation of method []= |
| assertHasMethodElement(binary.getArg1(), "A", "[]="); |
| } |
| } |
| |
| /** |
| * Test for resolving variants of property access and unary/binary expressions. |
| * <p> |
| * http://code.google.com/p/dart/issues/detail?id=5049 |
| */ |
| public void test_opAssignPropertyAccess_instance() throws Exception { |
| AnalyzeLibraryResult libraryResult = analyzeLibrary( |
| "// filler filler filler filler filler filler filler filler filler filler", |
| "class A {", |
| " B get b => new B();", |
| " set b(B x) {}", |
| "}", |
| "class B {", |
| " B operator +(x) => new B();", |
| "}", |
| "main () {", |
| " A a = new A();", |
| " process( a.b );", |
| " a.b++;", |
| " ++a.b;", |
| " a.b += 1;", |
| " a.b = null;", |
| "}", |
| "process(x) {}", |
| ""); |
| assertErrors(libraryResult.getErrors()); |
| // print( a.b ) |
| { |
| DartPropertyAccess access = findNode(DartPropertyAccess.class, "a.b"); |
| // a.b is field "A.b" |
| assertHasFieldElement(access, "A", "b"); |
| } |
| // a.b++ |
| { |
| DartUnaryExpression unary = findNode(DartUnaryExpression.class, "a.b++"); |
| // a.b++ is invocation of method "+" |
| assertHasMethodElement(unary, "B", "+"); |
| // a.b is field "A.b" |
| assertHasFieldElement(unary.getArg(), "A", "b"); |
| } |
| // ++a.b |
| { |
| DartUnaryExpression unary = findNode(DartUnaryExpression.class, "++a.b"); |
| // ++a.b is invocation of method "+" |
| assertHasMethodElement(unary, "B", "+"); |
| // a.b is field "A.b" |
| assertHasFieldElement(unary.getArg(), "A", "b"); |
| } |
| // a.b += 1 |
| { |
| DartBinaryExpression binary = findNode(DartBinaryExpression.class, "a.b += 1"); |
| // a.b += 1 is invocation of method "+" |
| assertHasMethodElement(binary, "B", "+"); |
| // a.b is field "A.b" |
| assertHasFieldElement(binary.getArg1(), "A", "b"); |
| } |
| // a.b = null |
| { |
| DartBinaryExpression binary = findNode(DartBinaryExpression.class, "a.b = null"); |
| // a.b = null has no Element |
| assertSame(null, binary.getElement()); |
| // a.b is field "A.b" |
| assertHasFieldElement(binary.getArg1(), "A", "b"); |
| } |
| } |
| |
| /** |
| * Test for resolving variants of static property access and unary/binary expressions. |
| * <p> |
| * http://code.google.com/p/dart/issues/detail?id=5049 |
| */ |
| public void test_opAssignPropertyAccess_static() throws Exception { |
| AnalyzeLibraryResult libraryResult = analyzeLibrary( |
| "// filler filler filler filler filler filler filler filler filler filler", |
| "class A {", |
| " static B get b => new B();", |
| " static set b(B x) {}", |
| "}", |
| "class B {", |
| " B operator +(x) => new B();", |
| "}", |
| "main () {", |
| " process( A.b );", |
| " A.b++;", |
| " ++A.b;", |
| " A.b += 1;", |
| " A.b = null;", |
| "}", |
| "process(x) {}", |
| ""); |
| assertErrors(libraryResult.getErrors()); |
| // print( A.b ) |
| { |
| DartPropertyAccess access = findNode(DartPropertyAccess.class, "A.b"); |
| // A.b is field "A.b" |
| assertHasFieldElement(access, "A", "b"); |
| } |
| // A.b++ |
| { |
| DartUnaryExpression unary = findNode(DartUnaryExpression.class, "A.b++"); |
| // A.b++ is invocation of method "+" |
| assertHasMethodElement(unary, "B", "+"); |
| // A.b is field "A.b" |
| assertHasFieldElement(unary.getArg(), "A", "b"); |
| } |
| // ++A.b |
| { |
| DartUnaryExpression unary = findNode(DartUnaryExpression.class, "++A.b"); |
| // ++A.b is invocation of method "+" |
| assertHasMethodElement(unary, "B", "+"); |
| // A.b is field "A.b" |
| assertHasFieldElement(unary.getArg(), "A", "b"); |
| } |
| // A.b += 1 |
| { |
| DartBinaryExpression binary = findNode(DartBinaryExpression.class, "A.b += 1"); |
| // A.b += 1 is invocation of method "+" |
| assertHasMethodElement(binary, "B", "+"); |
| // A.b is field "A.b" |
| assertHasFieldElement(binary.getArg1(), "A", "b"); |
| } |
| // A.b = null |
| { |
| DartBinaryExpression binary = findNode(DartBinaryExpression.class, "A.b = null"); |
| // A.b = null has no Element |
| assertSame(null, binary.getElement()); |
| // A.b is field "A.b" |
| assertHasFieldElement(binary.getArg1(), "A", "b"); |
| } |
| } |
| |
| /** |
| * Test for resolving variants of top-level property access and unary/binary expressions. |
| * <p> |
| * http://code.google.com/p/dart/issues/detail?id=5049 |
| */ |
| public void test_opAssignPropertyAccess_topLevel() throws Exception { |
| AnalyzeLibraryResult libraryResult = analyzeLibrary( |
| "// filler filler filler filler filler filler filler filler filler filler", |
| "B get field => new B();", |
| " set field(B x) {}", |
| "class B {", |
| " B operator +(x) => new B();", |
| "}", |
| "main () {", |
| " process( field );", |
| " field++;", |
| " ++field;", |
| " field += 1;", |
| " field = null;", |
| "}", |
| "process(x) {}", |
| ""); |
| assertErrors(libraryResult.getErrors()); |
| // print( field ) |
| { |
| DartIdentifier access = findNode(DartIdentifier.class, "field );"); |
| // "field" is top-level field |
| assertHasFieldElement(access, "<library>", "field"); |
| } |
| // field++ |
| { |
| DartUnaryExpression unary = findNode(DartUnaryExpression.class, "field++"); |
| // field++ is invocation of method "+" |
| assertHasMethodElement(unary, "B", "+"); |
| // "field" is top-level field |
| assertHasFieldElement(unary.getArg(), "<library>", "field"); |
| } |
| // ++field |
| { |
| DartUnaryExpression unary = findNode(DartUnaryExpression.class, "++field"); |
| // ++field is invocation of method "+" |
| assertHasMethodElement(unary, "B", "+"); |
| // "field" is top-level field |
| assertHasFieldElement(unary.getArg(), "<library>", "field"); |
| } |
| // field += 1 |
| { |
| DartBinaryExpression binary = findNode(DartBinaryExpression.class, "field += 1"); |
| // field += 1 is invocation of method "+" |
| assertHasMethodElement(binary, "B", "+"); |
| // "field" is top-level field |
| assertHasFieldElement(binary.getArg1(), "<library>", "field"); |
| } |
| // field = null |
| { |
| DartBinaryExpression binary = findNode(DartBinaryExpression.class, "field = null"); |
| // field = null is no Element |
| assertSame(null, binary.getElement()); |
| // "field" is top-level field |
| assertHasFieldElement(binary.getArg1(), "<library>", "field"); |
| } |
| } |
| |
| private static void assertHasFieldElement(DartNode node, String className, String fieldName) { |
| Element element = node.getElement(); |
| assertTrue("" + node + " " + element, element instanceof FieldElement); |
| FieldElement fieldElement = (FieldElement) element; |
| assertHasFieldElement(fieldElement, className, fieldName); |
| } |
| |
| private static void assertHasFieldElement(FieldElement element, String className, String fieldName) { |
| EnclosingElement enclosingElement = element.getEnclosingElement(); |
| String enclosingName; |
| if (enclosingElement instanceof LibraryElement) { |
| enclosingName = "<library>"; |
| } else { |
| enclosingName = enclosingElement.getName(); |
| } |
| assertEquals(className, enclosingName); |
| // |
| String elementName = element.getName(); |
| assertEquals(fieldName, elementName); |
| } |
| |
| private static void assertHasMethodElement(DartNode node, String className, String methodName) { |
| Element element = node.getElement(); |
| assertTrue("" + node + " " + element, element instanceof MethodElement); |
| MethodElement methodElement = (MethodElement) element; |
| assertMethodElement(methodElement, className, methodName); |
| } |
| |
| private static void assertMethodElement(MethodElement element, String className, String methodName) { |
| EnclosingElement enclosingElement = element.getEnclosingElement(); |
| String enclosingName; |
| if (enclosingElement instanceof LibraryElement) { |
| enclosingName = "<library>"; |
| } else { |
| enclosingName = enclosingElement.getName(); |
| } |
| assertEquals(className, enclosingName); |
| // |
| String elementName = element.getName(); |
| if (element.getModifiers().isGetter()) { |
| elementName = "get " + elementName; |
| } |
| if (element.getModifiers().isSetter()) { |
| elementName = "set " + elementName; |
| } |
| assertEquals(methodName, elementName); |
| } |
| |
| public void test_invokeStaticFieldAsMethod() throws Exception { |
| AnalyzeLibraryResult libraryResult = analyzeLibrary( |
| "// filler filler filler filler filler filler filler filler filler filler", |
| "class C {", |
| " static foo() { }", |
| "}", |
| "main () {", |
| " var a = new C().foo();", |
| "}"); |
| assertErrors( |
| libraryResult.getErrors(), |
| errEx(TypeErrorCode.IS_STATIC_METHOD_IN, 6, 19, 3)); |
| } |
| |
| public void test_invokeNonFunction() throws Exception { |
| AnalyzeLibraryResult libraryResult = analyzeLibrary( |
| "// filler filler filler filler filler filler filler filler filler filler", |
| "class C {", |
| " String foo;", |
| " method() {", |
| " foo();", |
| " }", |
| "}", |
| "method() {", |
| " String foo;", |
| " foo();", |
| " (1 + 5)();", |
| "}"); |
| assertErrors( |
| libraryResult.getErrors(), |
| errEx(TypeErrorCode.NOT_A_METHOD_IN, 5, 5, 3), |
| errEx(TypeErrorCode.NOT_A_FUNCTION_TYPE, 10, 3, 3), |
| errEx(TypeErrorCode.NOT_A_FUNCTION_TYPE, 11, 3, 9)); |
| } |
| |
| public void test_invokeNonFunction_getter() throws Exception { |
| AnalyzeLibraryResult libraryResult = analyzeLibrary( |
| "// filler filler filler filler filler filler filler filler filler filler", |
| "class A {", |
| " int get foo => 0;", |
| "}", |
| "main() {", |
| " A a = new A();", |
| " a.foo();", |
| "}"); |
| assertErrors( |
| libraryResult.getErrors(), |
| errEx(TypeErrorCode.NOT_A_FUNCTION_TYPE_FIELD, 7, 5, 3)); |
| } |
| |
| public void test_wrongOperandTypeForUnaryExpression() throws Exception { |
| AnalyzeLibraryResult libraryResult = analyzeLibrary( |
| "// filler filler filler filler filler filler filler filler filler filler", |
| "class C {", |
| " operator -(String arg) {}", |
| " operator +(String arg) {}", |
| "}", |
| "method1(arg) {}", |
| "method2() {", |
| " C foo = new C();", |
| " method1(++foo);", |
| " method1(--foo);", |
| "}"); |
| assertErrors( |
| libraryResult.getErrors(), |
| errEx(TypeErrorCode.OPERATOR_WRONG_OPERAND_TYPE, 9, 11, 5), |
| errEx(TypeErrorCode.OPERATOR_WRONG_OPERAND_TYPE, 10, 11, 5)); |
| } |
| |
| /** |
| * Missing value in {@link DartMapLiteralEntry} is parsing error, but should not cause exception. |
| * <p> |
| * http://code.google.com/p/dart/issues/detail?id=3931 |
| */ |
| public void test_mapLiteralEntry_noValue() throws Exception { |
| AnalyzeLibraryResult libraryResult = analyzeLibrary( |
| "// filler filler filler filler filler filler filler filler filler filler", |
| "main() {", |
| " var v = {'key' : /*no value*/};", |
| "}", |
| ""); |
| // has some errors |
| assertTrue(libraryResult.getErrors().size() != 0); |
| } |
| |
| public void test_fieldOverrideWrongType() throws Exception { |
| AnalyzeLibraryResult libraryResult = analyzeLibrary( |
| "// filler filler filler filler filler filler filler filler filler filler", |
| "class A {", |
| " int foo;", |
| "}", |
| "class B extends A {", |
| " String foo;", |
| "}"); |
| assertErrors( |
| libraryResult.getErrors(), |
| errEx(TypeErrorCode.CANNOT_OVERRIDE_TYPED_MEMBER, 6, 10, 3)); |
| } |
| |
| public void test_overrideInstanceMember() throws Exception { |
| AnalyzeLibraryResult libraryResult = analyzeLibrary( |
| "class A {", |
| " var field;", |
| " method() {}", |
| "}", |
| "class B extends A {", |
| " static var field;", |
| " static method() {}", |
| "}"); |
| assertErrors( |
| libraryResult.getErrors(), |
| errEx(ResolverErrorCode.CANNOT_OVERRIDE_INSTANCE_MEMBER, 6, 14, 5), |
| errEx(ResolverErrorCode.CANNOT_OVERRIDE_INSTANCE_MEMBER, 7, 10, 6)); |
| } |
| |
| public void test_overrideStaticMember() throws Exception { |
| AnalyzeLibraryResult libraryResult = analyzeLibrary( |
| "// filler filler filler filler filler filler filler filler filler filler", |
| "class A {", |
| " static var foo;", |
| " static bar() {}", |
| "}", |
| "class B extends A {", |
| " var foo;", |
| " bar() {}", |
| "}"); |
| assertErrors( |
| libraryResult.getErrors(), |
| errEx(TypeErrorCode.OVERRIDING_STATIC_MEMBER, 7, 7, 3), |
| errEx(TypeErrorCode.OVERRIDING_STATIC_MEMBER, 8, 3, 3)); |
| } |
| |
| public void test_rethrowNotInCatch() throws Exception { |
| AnalyzeLibraryResult libraryResult = analyzeLibrary( |
| "class Object {}", |
| "method() {", |
| " throw;", |
| "}"); |
| assertErrors( |
| libraryResult.getErrors(), |
| errEx(ResolverErrorCode.RETHROW_NOT_IN_CATCH, 3, 3, 5)); |
| } |
| |
| public void test_externalKeyword_OK() throws Exception { |
| AnalyzeLibraryResult libraryResult = analyzeLibrary( |
| "// filler filler filler filler filler filler filler filler filler filler", |
| "external topFunction();", |
| "external get topGetter;", |
| "external set topSetter(var v);", |
| "class A {", |
| " external const A.con();", |
| " external A();", |
| " external factory A.named();", |
| " external classMethod();", |
| " external static classMethodStatic();", |
| " external get classGetter;", |
| " external set classSetter(var v);", |
| "}", |
| ""); |
| assertErrors(libraryResult.getErrors()); |
| // all method-like nodes here are "external" |
| final AtomicInteger methodCounter = new AtomicInteger(); |
| testUnit.accept(new ASTVisitor<Void>() { |
| @Override |
| public Void visitMethodDefinition(DartMethodDefinition node) { |
| methodCounter.incrementAndGet(); |
| assertTrue(node.getModifiers().isExternal()); |
| return null; |
| } |
| }); |
| assertEquals(10, methodCounter.get()); |
| } |
| |
| /** |
| * Modifier "external" can be applied only to method-like elements. |
| */ |
| public void test_externalKeyword_bad_field() throws Exception { |
| AnalyzeLibraryResult libraryResult = analyzeLibrary( |
| "// filler filler filler filler filler filler filler filler filler filler", |
| "external var topVar1;", |
| "external int topVar2;", |
| "class A {", |
| " external var field1;", |
| " external int field2;", |
| "}", |
| ""); |
| assertErrors( |
| libraryResult.getErrors(), |
| errEx(ParserErrorCode.EXTERNAL_ONLY_METHOD, 2, 14, 7), |
| errEx(ParserErrorCode.EXTERNAL_ONLY_METHOD, 3, 14, 7), |
| errEx(ParserErrorCode.EXTERNAL_ONLY_METHOD, 5, 16, 6), |
| errEx(ParserErrorCode.EXTERNAL_ONLY_METHOD, 6, 16, 6)); |
| } |
| |
| /** |
| * Methods with "external" cannot have body. |
| */ |
| public void test_externalKeyword_bad_body() throws Exception { |
| AnalyzeLibraryResult libraryResult = analyzeLibrary( |
| "// filler filler filler filler filler filler filler filler filler filler", |
| "external topFunction() {}", |
| "abstract class A {", |
| " external A() {}", |
| " external factory A.named() {}", |
| " external classMethod() {}", |
| "}", |
| ""); |
| assertErrors( |
| libraryResult.getErrors(), |
| errEx(ParserErrorCode.EXTERNAL_METHOD_BODY, 2, 24, 2), |
| errEx(ParserErrorCode.EXTERNAL_METHOD_BODY, 4, 16, 2), |
| errEx(ParserErrorCode.EXTERNAL_METHOD_BODY, 5, 30, 2), |
| errEx(ParserErrorCode.EXTERNAL_METHOD_BODY, 6, 26, 2)); |
| } |
| |
| public void test_cascade_type() throws Exception { |
| analyzeLibrary( |
| "// filler filler filler filler filler filler filler filler filler filler", |
| "main() {", |
| " String s = '';", |
| " var v = s..length;", |
| "}", |
| ""); |
| assertInferredElementTypeString(testUnit, "v", "String", INFERRED); |
| } |
| |
| /** |
| * There should be no problem reported, because assignment of "a" cascade to "b" with type "B" |
| * implicitly set type of "a" to "B". |
| * <p> |
| * http://code.google.com/p/dart/issues/detail?id=6107 |
| */ |
| public void test_cascade_inferType_varDeclaration() throws Exception { |
| AnalyzeLibraryResult result = analyzeLibrary( |
| "// filler filler filler filler filler filler filler filler filler filler", |
| "class A {}", |
| "class B extends A {", |
| " bMethod() {}", |
| "}", |
| "main() {", |
| " A a = new B();", |
| " B b = a..bMethod();", |
| "}", |
| ""); |
| assertErrors(result.getErrors()); |
| } |
| |
| /** |
| * There should be no problem reported, because assignment of "a" cascade to "b" with type "B" |
| * implicitly set type of "a" to "B". |
| * <p> |
| * http://code.google.com/p/dart/issues/detail?id=6107 |
| */ |
| public void test_cascade_inferType_varAssignment() throws Exception { |
| AnalyzeLibraryResult result = analyzeLibrary( |
| "// filler filler filler filler filler filler filler filler filler filler", |
| "class A {}", |
| "class B extends A {", |
| " bMethod() {}", |
| "}", |
| "main() {", |
| " A a = new B();", |
| " B b = null;", |
| " b = a..bMethod();", |
| "}", |
| ""); |
| assertErrors(result.getErrors()); |
| } |
| |
| /** |
| * We assign "a" to field "Holder.b" of type "B", so implicitly set type of "a" to "B". |
| * <p> |
| * http://code.google.com/p/dart/issues/detail?id=6107 |
| */ |
| public void test_cascade_inferType_fieldDeclaration() throws Exception { |
| AnalyzeLibraryResult result = analyzeLibrary( |
| "// filler filler filler filler filler filler filler filler filler filler", |
| "class Holder {", |
| " B b = getA()..bMethod();", |
| "}", |
| "class A {}", |
| "class B extends A {", |
| " bMethod() {}", |
| "}", |
| "A getA() => new B();", |
| ""); |
| assertErrors(result.getErrors()); |
| } |
| |
| /** |
| * We assign "a" to field "Holder.b" of type "B", so implicitly set type of "a" to "B". |
| * <p> |
| * http://code.google.com/p/dart/issues/detail?id=6107 |
| */ |
| public void test_cascade_inferType_fieldAssignment() throws Exception { |
| AnalyzeLibraryResult result = analyzeLibrary( |
| "// filler filler filler filler filler filler filler filler filler filler", |
| "class Holder {", |
| " B b;", |
| "}", |
| "class A {}", |
| "class B extends A {", |
| " bMethod() {}", |
| "}", |
| "main() {", |
| " A a = new B();", |
| " Holder holder = new Holder();", |
| " holder.b = a..bMethod();", |
| "}", |
| ""); |
| assertErrors(result.getErrors()); |
| } |
| |
| /** |
| * <p> |
| * http://code.google.com/p/dart/issues/detail?id=4315 |
| */ |
| public void test_cascade_propertyAccess() throws Exception { |
| AnalyzeLibraryResult libraryResult = analyzeLibrary( |
| "// filler filler filler filler filler filler filler filler filler filler", |
| "class A {", |
| " int f;", |
| "}", |
| "main() {", |
| " A a = new A();", |
| " a", |
| " ..f = 1", |
| " ..f = 2;", |
| "}", |
| ""); |
| assertErrors(libraryResult.getErrors()); |
| { |
| DartPropertyAccess access = findNode(DartPropertyAccess.class, "..f = 1"); |
| assertNotNull(access.getElement()); |
| } |
| { |
| DartPropertyAccess access = findNode(DartPropertyAccess.class, "..f = 2"); |
| assertNotNull(access.getElement()); |
| } |
| } |
| |
| /** |
| * <p> |
| * http://code.google.com/p/dart/issues/detail?id=4315 |
| */ |
| public void test_cascade_methodInvocation() throws Exception { |
| AnalyzeLibraryResult libraryResult = analyzeLibrary( |
| "// filler filler filler filler filler filler filler filler filler filler", |
| "class A {", |
| " int m(p) {}", |
| "}", |
| "main() {", |
| " A a = new A();", |
| " a", |
| " ..m(1)", |
| " ..m(2);", |
| "}", |
| ""); |
| assertErrors(libraryResult.getErrors()); |
| { |
| DartMethodInvocation invocation = findNode(DartMethodInvocation.class, "..m(1)"); |
| assertNotNull(invocation.getElement()); |
| } |
| { |
| DartMethodInvocation invocation = findNode(DartMethodInvocation.class, "..m(2)"); |
| assertNotNull(invocation.getElement()); |
| } |
| } |
| |
| /** |
| * Source is invalid, but should not cause {@link NullPointerException}. |
| * <p> |
| * http://code.google.com/p/dart/issues/detail?id=4354 |
| */ |
| public void test_switchCase_withoutExpression() throws Exception { |
| AnalyzeLibraryResult libraryResult = analyzeLibrary( |
| "// filler filler filler filler filler filler filler filler filler filler", |
| "main() {", |
| " switch (0) {", |
| " case }", |
| " }", |
| "}", |
| ""); |
| // has some errors, no exception |
| assertTrue(libraryResult.getErrors().size() != 0); |
| } |
| |
| /** |
| * <p> |
| * http://code.google.com/p/dart/issues/detail?id=6836 |
| */ |
| public void test_unresolvedIdentifier_inStatic_notPropertyAccess() throws Exception { |
| AnalyzeLibraryResult libraryResult = analyzeLibrary( |
| "// filler filler filler filler filler filler filler filler filler filler", |
| "process(x) {}", |
| "", |
| "class A {", |
| " static foo() {", |
| " unknown = 0;", |
| " }", |
| "}", |
| "", |
| "main() {", |
| " unknown = 0;", |
| " process(unknown);", |
| "}"); |
| assertErrors( |
| libraryResult.getErrors(), |
| errEx(TypeErrorCode.CANNOT_BE_RESOLVED, 6, 5, 7), |
| errEx(TypeErrorCode.CANNOT_BE_RESOLVED, 11, 3, 7), |
| errEx(TypeErrorCode.CANNOT_BE_RESOLVED, 12, 11, 7)); |
| } |
| |
| /** |
| * If "unknown" is separate identifier, it is handled as "this.unknown", but "this" is not |
| * accessible in static context. |
| * <p> |
| * http://code.google.com/p/dart/issues/detail?id=3084 |
| */ |
| public void test_unresolvedIdentifier_inInstance_notPropertyAccess() throws Exception { |
| AnalyzeLibraryResult libraryResult = analyzeLibrary( |
| "// filler filler filler filler filler filler filler filler filler filler", |
| "process(x) {}", |
| "class A {", |
| " foo() {", |
| " unknown = 0;", |
| " process(unknown);", |
| " }", |
| "}"); |
| assertErrors( |
| libraryResult.getErrors(), |
| errEx(TypeErrorCode.CANNOT_BE_RESOLVED, 5, 5, 7), |
| errEx(TypeErrorCode.CANNOT_BE_RESOLVED, 6, 13, 7)); |
| } |
| |
| /** |
| * <p> |
| * http://code.google.com/p/dart/issues/detail?id=3084 |
| */ |
| public void test_unresolvedIdentifier_inStatic_inPropertyAccess() throws Exception { |
| AnalyzeLibraryResult libraryResult = analyzeLibrary( |
| "// filler filler filler filler filler filler filler filler filler filler", |
| "process(x) {}", |
| "main() {", |
| " Unknown.foo = 0;", |
| " process(Unknown.foo);", |
| "}"); |
| assertErrors( |
| libraryResult.getErrors(), |
| errEx(TypeErrorCode.CANNOT_BE_RESOLVED, 4, 3, 7), |
| errEx(TypeErrorCode.CANNOT_BE_RESOLVED, 5, 11, 7)); |
| } |
| |
| /** |
| * Unresolved constructor is warning. |
| * <p> |
| * http://code.google.com/p/dart/issues/detail?id=3800 |
| */ |
| public void test_unresolvedConstructor() throws Exception { |
| AnalyzeLibraryResult libraryResult = analyzeLibrary( |
| "// filler filler filler filler filler filler filler filler filler filler", |
| "class A {}", |
| "main() {", |
| " new A(); // OK", |
| " new A.noSuchConstructor(); // warning", |
| " new B(); // warning", |
| " new B.noSuchConstructor(); // warning", |
| "}"); |
| assertErrors( |
| libraryResult.getErrors(), |
| errEx(ResolverErrorCode.NEW_EXPRESSION_NOT_CONSTRUCTOR, 5, 9, 17), |
| errEx(TypeErrorCode.NO_SUCH_TYPE, 6, 7, 1), |
| errEx(TypeErrorCode.NO_SUCH_TYPE, 7, 7, 1), |
| errEx(ResolverErrorCode.NEW_EXPRESSION_NOT_CONSTRUCTOR, 7, 9, 17)); |
| } |
| |
| /** |
| * <p> |
| * http://code.google.com/p/dart/issues/detail?id=4383 |
| */ |
| public void test_callFieldWithoutGetter_topLevel() throws Exception { |
| AnalyzeLibraryResult libraryResult = analyzeLibrary( |
| "// filler filler filler filler filler filler filler filler filler filler", |
| "set setOnlyField(v) {}", |
| "main() {", |
| " setOnlyField(0);", |
| "}"); |
| assertErrors( |
| libraryResult.getErrors(), |
| errEx(ResolverErrorCode.USE_ASSIGNMENT_ON_SETTER, 4, 3, 12)); |
| } |
| |
| /** |
| * Every {@link DartExpression} should have {@link Type} set. Just to don't guess this type at |
| * many other points in the Editor. |
| */ |
| public void test_typeForEveryExpression_variable() throws Exception { |
| analyzeLibrary( |
| "// filler filler filler filler filler filler filler filler filler filler", |
| "class A {}", |
| "process(x) {}", |
| "main() {", |
| " A aaa = new A();", |
| " process(aaa);", |
| "}"); |
| testUnit.accept(new ASTVisitor<Void>() { |
| public Void visitIdentifier(DartIdentifier node) { |
| // ignore declaration |
| if (node.getParent() instanceof DartDeclaration) { |
| return null; |
| } |
| // check "aaa" |
| if (node.toString().equals("aaa")) { |
| Type type = node.getType(); |
| assertNotNull(type); |
| assertEquals("A", type.toString()); |
| } |
| return null; |
| } |
| }); |
| } |
| |
| /** |
| * Every {@link DartExpression} should have {@link Type} set. Just to don't guess this type at |
| * many other points in the Editor. |
| */ |
| public void test_typeForEveryExpression_typeNode() throws Exception { |
| analyzeLibrary( |
| "// filler filler filler filler filler filler filler filler filler filler", |
| "class AAA {", |
| " static foo() {}", |
| "}", |
| "main() {", |
| " AAA.foo();", |
| "}"); |
| testUnit.accept(new ASTVisitor<Void>() { |
| public Void visitIdentifier(DartIdentifier node) { |
| // ignore declaration |
| if (node.getParent() instanceof DartDeclaration) { |
| return null; |
| } |
| // check "AAA" |
| if (node.toString().equals("AAA")) { |
| Type type = node.getType(); |
| assertNotNull(type); |
| assertEquals("AAA", type.toString()); |
| } |
| return null; |
| } |
| }); |
| } |
| |
| /** |
| * <p> |
| * http://code.google.com/p/dart/issues/detail?id=4383 |
| */ |
| public void test_callFieldWithoutGetter_member() throws Exception { |
| AnalyzeLibraryResult libraryResult = analyzeLibrary( |
| "// filler filler filler filler filler filler filler filler filler filler", |
| "class A {", |
| " set setOnlyField(v) {}", |
| " foo() {", |
| " setOnlyField(0);", |
| " }", |
| "}", |
| "main() {", |
| " A a = new A();", |
| " a.setOnlyField(0);", |
| "}"); |
| assertErrors( |
| libraryResult.getErrors(), |
| errEx(TypeErrorCode.USE_ASSIGNMENT_ON_SETTER, 5, 5, 12), |
| errEx(TypeErrorCode.USE_ASSIGNMENT_ON_SETTER, 10, 5, 12)); |
| } |
| |
| private abstract static class ArgumentsBindingTester { |
| static List<DartExpression> arguments; |
| void doTest(DartUnit unit) { |
| unit.accept(new ASTVisitor<Void>() { |
| int invocationIndex = 0; |
| @Override |
| public Void visitUnqualifiedInvocation(DartUnqualifiedInvocation node) { |
| arguments = node.getArguments(); |
| checkArgs(invocationIndex++); |
| return super.visitUnqualifiedInvocation(node); |
| } |
| }); |
| } |
| abstract void checkArgs(int invocationIndex); |
| void assertId(int index, String expectedParameterName) { |
| DartExpression argument = arguments.get(index); |
| String idString = argument.getInvocationParameterId().toString(); |
| assertEquals("PARAMETER " + expectedParameterName, idString); |
| } |
| } |
| |
| public void test_formalParameters_positional_optional() throws Exception { |
| AnalyzeLibraryResult libraryResult = analyzeLibrary( |
| "// filler filler filler filler filler filler filler filler filler filler", |
| "method(var a, var b, [var c = 3, var d = 4]) {}", |
| "main() {", |
| " method(10, 20);", |
| " method(10, 20, 30);", |
| " method(10, 20, 30, 40);", |
| "}"); |
| assertErrors(libraryResult.getErrors()); |
| new ArgumentsBindingTester() { |
| @Override |
| void checkArgs(int invocationIndex) { |
| switch (invocationIndex) { |
| case 0: { |
| assertId(0, "a"); |
| assertId(1, "b"); |
| break; |
| } |
| case 1: { |
| assertId(0, "a"); |
| assertId(1, "b"); |
| assertId(2, "c"); |
| break; |
| } |
| case 3: { |
| assertId(0, "a"); |
| assertId(1, "b"); |
| assertId(2, "c"); |
| assertId(3, "d"); |
| break; |
| } |
| } |
| } |
| }.doTest(testUnit); |
| } |
| |
| public void test_formalParameters_positional_named() throws Exception { |
| AnalyzeLibraryResult libraryResult = analyzeLibrary( |
| "// filler filler filler filler filler filler filler filler filler filler", |
| "method(var a, var b, {var c : 3, var d : 4}) {}", |
| "main() {", |
| " method(10, 20);", |
| " method(10, 20, c: 30);", |
| " method(10, 20, d: 40);", |
| " method(10, 20, d: 40, c: 30);", |
| "}"); |
| assertErrors(libraryResult.getErrors()); |
| new ArgumentsBindingTester() { |
| @Override |
| void checkArgs(int invocationIndex) { |
| switch (invocationIndex) { |
| case 0: { |
| assertId(0, "a"); |
| assertId(1, "b"); |
| break; |
| } |
| case 1: { |
| assertId(0, "a"); |
| assertId(1, "b"); |
| assertId(2, "c"); |
| break; |
| } |
| case 2: { |
| assertId(0, "a"); |
| assertId(1, "b"); |
| assertId(2, "d"); |
| break; |
| } |
| case 3: { |
| assertId(0, "a"); |
| assertId(1, "b"); |
| assertId(2, "d"); |
| assertId(3, "c"); |
| break; |
| } |
| } |
| } |
| }.doTest(testUnit); |
| } |
| |
| /** |
| * A constructor name always begins with the name of its immediately enclosing class, and may |
| * optionally be followed by a dot and an identifier id. It is a compile-time error if id is the |
| * name of a member declared in the immediately enclosing class. |
| * <p> |
| * http://code.google.com/p/dart/issues/detail?id=3989 |
| */ |
| public void test_constructorName_sameAsMemberName() throws Exception { |
| AnalyzeLibraryResult libraryResult = analyzeLibrary( |
| "// filler filler filler filler filler filler filler filler filler filler", |
| "class A {", |
| " A.foo() {}", |
| " foo() {}", |
| "}"); |
| assertErrors( |
| libraryResult.getErrors(), |
| errEx(ResolverErrorCode.CONSTRUCTOR_WITH_NAME_OF_MEMBER, 3, 3, 5)); |
| } |
| |
| /** |
| * <p> |
| * http://code.google.com/p/dart/issues/detail?id=3904 |
| */ |
| public void test_reifiedClasses() throws Exception { |
| AnalyzeLibraryResult libraryResult = analyzeLibrary( |
| "// filler filler filler filler filler filler filler filler filler filler", |
| "class A {}", |
| "process(x) {}", |
| "main() {", |
| " process(A);", |
| "}"); |
| assertErrors(libraryResult.getErrors()); |
| } |
| |
| /** |
| * <p> |
| * http://code.google.com/p/dart/issues/detail?id=3968 |
| */ |
| public void test_redirectingFactoryConstructor() throws Exception { |
| AnalyzeLibraryResult libraryResult = analyzeLibrary( |
| "// filler filler filler filler filler filler filler filler filler filler", |
| "class A {", |
| " A() {}", |
| " A.named() {}", |
| "}", |
| "", |
| "class B {", |
| " factory B.foo() = A;", |
| " factory B.bar() = A.named;", |
| "}", |
| ""); |
| assertErrors(libraryResult.getErrors()); |
| // prepare "class A" |
| ClassElement elementA = findNode(DartClass.class, "class A").getElement(); |
| Type typeA = elementA.getType(); |
| // = A; |
| { |
| DartTypeNode typeNode = findNode(DartTypeNode.class, "A;"); |
| Type type = typeNode.getType(); |
| assertSame(typeA, type); |
| } |
| // = A.named; |
| { |
| DartTypeNode typeNode = findNode(DartTypeNode.class, "A.named;"); |
| Type type = typeNode.getType(); |
| assertSame(typeA, type); |
| // .named |
| DartIdentifier nameNode = findNode(DartIdentifier.class, "named;"); |
| NodeElement nameElement = nameNode.getElement(); |
| assertNotNull(nameElement); |
| assertSame(elementA.lookupConstructor("named"), nameElement); |
| } |
| } |
| |
| public void test_redirectingFactoryConstructor_cycle() throws Exception { |
| AnalyzeLibraryResult libraryResult = analyzeLibrary( |
| "// filler filler filler filler filler filler filler filler filler filler", |
| "class A {", |
| " factory A.nameA() = C.nameC;", |
| "}", |
| "class B {", |
| " factory B.nameB() = A.nameA;", |
| "}", |
| "class C {", |
| " factory C.nameC() = B.nameB;", |
| "}", |
| ""); |
| assertErrors( |
| libraryResult.getErrors(), |
| errEx(ResolverErrorCode.REDIRECTION_CONSTRUCTOR_CYCLE, 3, 11, 7), |
| errEx(ResolverErrorCode.REDIRECTION_CONSTRUCTOR_CYCLE, 6, 11, 7), |
| errEx(ResolverErrorCode.REDIRECTION_CONSTRUCTOR_CYCLE, 9, 11, 7)); |
| } |
| |
| public void test_redirectingFactoryConstructor_notConst_fromConst() throws Exception { |
| AnalyzeLibraryResult libraryResult = analyzeLibrary( |
| "// filler filler filler filler filler filler filler filler filler filler", |
| "class A {", |
| " A.named() {}", |
| "}", |
| "", |
| "class B {", |
| " const factory B.bar() = A.named;", |
| "}", |
| ""); |
| assertErrors( |
| libraryResult.getErrors(), |
| errEx(ResolverErrorCode.REDIRECTION_CONSTRUCTOR_TARGET_MUST_BE_CONST, 7, 29, 5)); |
| } |
| |
| /** |
| * <p> |
| * http://code.google.com/p/dart/issues/detail?id=4778 |
| */ |
| public void test_unqualifiedAccessToGenericTypeField() throws Exception { |
| AnalyzeLibraryResult libraryResult = analyzeLibrary( |
| "// filler filler filler filler filler filler filler filler filler filler", |
| "class Game {}", |
| "class GameRenderer<G extends Game> {", |
| " G get game => null;", |
| "}", |
| "class SpaceShooterGame extends Game {", |
| " int score;", |
| "}", |
| "class SpaceShooterRenderer extends GameRenderer<SpaceShooterGame> {", |
| " someMethod() {", |
| " var a = game.score;", |
| " }", |
| "}", |
| ""); |
| assertErrors(libraryResult.getErrors()); |
| } |
| |
| /** |
| * <p> |
| * http://code.google.com/p/dart/issues/detail?id=4900 |
| */ |
| public void test_forInLoop_fieldAsVariable() throws Exception { |
| AnalyzeLibraryResult result = analyzeLibrary( |
| "// filler filler filler filler filler filler filler filler filler filler", |
| "var v;", |
| "get l => v;", |
| "set l(x) {v = x;}", |
| "main() {", |
| " for (l in [1, 2, 3]) {", |
| " process(l);", |
| " }", |
| "}", |
| "process(x) {}", |
| ""); |
| assertErrors(result.getErrors()); |
| } |
| |
| /** |
| * Don't report "no such member" if class implements "noSuchMethod" method. |
| */ |
| public void test_dontReport_ifHas_noSuchMember_method() throws Exception { |
| String[] lines = { |
| "// filler filler filler filler filler filler filler filler filler filler", |
| "class A {", |
| " noSuchMethod(InvocationMirror invocation) {}", |
| "}", |
| "class B extends A {}", |
| "class C {}", |
| "main() {", |
| " new A().notExistingMethod();", |
| " new B().notExistingMethod();", |
| " new C().notExistingMethod();", |
| "}", |
| "process(x) {}", |
| ""}; |
| // report by default |
| { |
| AnalyzeLibraryResult result = analyzeLibrary(lines); |
| assertErrors( |
| result.getErrors(), |
| errEx(TypeErrorCode.INTERFACE_HAS_NO_METHOD_NAMED, 8, 11, 17), |
| errEx(TypeErrorCode.INTERFACE_HAS_NO_METHOD_NAMED, 9, 11, 17), |
| errEx(TypeErrorCode.INTERFACE_HAS_NO_METHOD_NAMED, 10, 11, 17)); |
| } |
| // don't report |
| { |
| compilerConfiguration = new DefaultCompilerConfiguration(new CompilerOptions() { |
| @Override |
| public boolean reportNoMemberWhenHasInterceptor() { |
| return false; |
| } |
| }); |
| AnalyzeLibraryResult result = analyzeLibrary(lines); |
| assertErrors( |
| result.getErrors(), |
| errEx(TypeErrorCode.INTERFACE_HAS_NO_METHOD_NAMED, 10, 11, 17)); |
| } |
| } |
| |
| /** |
| * Don't report "no such member" if class implements "noSuchMethod" method. |
| */ |
| public void test_dontReport_ifHas_noSuchMember_getter() throws Exception { |
| String[] lines = { |
| "// filler filler filler filler filler filler filler filler filler filler", |
| "class A {", |
| " noSuchMethod(InvocationMirror invocation) {}", |
| "}", |
| "class B extends A {}", |
| "class C {}", |
| "main() {", |
| " process( new A().notExistingGetter );", |
| " process( new B().notExistingGetter );", |
| " process( new C().notExistingGetter );", |
| "}", |
| "process(x) {}", |
| ""}; |
| // report by default |
| { |
| AnalyzeLibraryResult result = analyzeLibrary(lines); |
| assertErrors( |
| result.getErrors(), |
| errEx(TypeErrorCode.NOT_A_MEMBER_OF, 8, 20, 17), |
| errEx(TypeErrorCode.NOT_A_MEMBER_OF, 9, 20, 17), |
| errEx(TypeErrorCode.NOT_A_MEMBER_OF, 10, 20, 17)); |
| } |
| // don't report |
| { |
| compilerConfiguration = new DefaultCompilerConfiguration(new CompilerOptions() { |
| @Override |
| public boolean reportNoMemberWhenHasInterceptor() { |
| return false; |
| } |
| }); |
| AnalyzeLibraryResult result = analyzeLibrary(lines); |
| assertErrors(result.getErrors(), errEx(TypeErrorCode.NOT_A_MEMBER_OF, 10, 20, 17)); |
| } |
| } |
| |
| /** |
| * <p> |
| * http://code.google.com/p/dart/issues/detail?id=5084 |
| */ |
| public void test_duplicateSuperInterface_errorInClassImplements() throws Exception { |
| AnalyzeLibraryResult result = analyzeLibrary( |
| "// filler filler filler filler filler filler filler filler filler filler", |
| "class A {}", |
| "class B implements A, A {}", |
| ""); |
| assertErrors(result.getErrors(), errEx(ResolverErrorCode.DUPLICATE_IMPLEMENTS_TYPE, 3, 23, 1)); |
| } |
| |
| /** |
| * We should report only "no such type", but not duplicate. |
| * <p> |
| * http://code.google.com/p/dart/issues/detail?id=5084 |
| */ |
| public void test_duplicateSuperInterface_whenNoSuchType() throws Exception { |
| AnalyzeLibraryResult result = analyzeLibrary( |
| "// filler filler filler filler filler filler filler filler filler filler", |
| "class B implements X, Y {}", |
| ""); |
| assertErrors( |
| result.getErrors(), |
| errEx(ResolverErrorCode.NO_SUCH_TYPE, 2, 20, 1), |
| errEx(ResolverErrorCode.NO_SUCH_TYPE, 2, 23, 1)); |
| } |
| |
| /** |
| * <p> |
| * http://code.google.com/p/dart/issues/detail?id=5082 |
| */ |
| public void test_argumentDefinitionTest_type() throws Exception { |
| AnalyzeLibraryResult result = analyzeLibrary( |
| "// filler filler filler filler filler filler filler filler filler filler", |
| "foo(p) {", |
| " ?p;", |
| "}", |
| ""); |
| assertErrors(result.getErrors()); |
| DartUnaryExpression unary = findNode(DartUnaryExpression.class, "?p"); |
| Type type = unary.getType(); |
| assertNotNull(type); |
| assertEquals("bool", type.toString()); |
| } |
| |
| /** |
| * <p> |
| * http://code.google.com/p/dart/issues/detail?id=5082 |
| */ |
| public void test_argumentDefinitionTest_shouldBeFormalParameter() throws Exception { |
| AnalyzeLibraryResult result = analyzeLibrary( |
| "// filler filler filler filler filler filler filler filler filler filler", |
| "foo(p) {", |
| " var v;", |
| " ?p;", |
| " ?v;", |
| "}", |
| ""); |
| assertErrors( |
| result.getErrors(), |
| errEx(ResolverErrorCode.FORMAL_PARAMETER_NAME_EXPECTED, 5, 4, 1)); |
| } |
| |
| /** |
| * <p> |
| * http://code.google.com/p/dart/issues/detail?id=5148 |
| */ |
| public void test_errorIfNoBodyForStaticMethod() throws Exception { |
| AnalyzeLibraryResult result = analyzeLibrary( |
| "// filler filler filler filler filler filler filler filler filler filler", |
| "class A {", |
| " static foo();", |
| "}", |
| ""); |
| assertErrors( |
| result.getErrors(), |
| errEx(ResolverErrorCode.STATIC_METHOD_MUST_HAVE_BODY, 3, 3, 13)); |
| } |
| |
| /** |
| * <p> |
| * http://code.google.com/p/dart/issues/detail?id=5162 |
| */ |
| public void test_initializeFinalInstanceVariable_atDeclaration_inInitializer() throws Exception { |
| AnalyzeLibraryResult result = analyzeLibrary( |
| "// filler filler filler filler filler filler filler filler filler filler", |
| "class A {", |
| " final f = 0;", |
| " A() : f = 1 {}", |
| "}", |
| ""); |
| assertErrors( |
| result.getErrors(), |
| errEx(ResolverErrorCode.DUPLICATE_INITIALIZATION, 4, 9, 5)); |
| } |
| |
| public void test_getOverridden_method() throws Exception { |
| analyzeLibrary( |
| "// filler filler filler filler filler filler filler filler filler filler", |
| "class A {", |
| " foo() => 1;", |
| "}", |
| "class B extends A {", |
| " foo() => 2;", |
| "}", |
| ""); |
| DartMethodDefinition node = findNode(DartMethodDefinition.class, "foo() => 2"); |
| Set<Element> superElements = node.getElement().getOverridden(); |
| assertClassMembers(superElements, "method A.foo"); |
| } |
| |
| public void test_getOverridden_field_withGetterSetter() throws Exception { |
| analyzeLibrary( |
| "// filler filler filler filler filler filler filler filler filler filler", |
| "class A {", |
| " var foo;", |
| "}", |
| "class B extends A {", |
| " get foo => 0;", |
| " set foo(x) {}", |
| "}", |
| ""); |
| // getter |
| { |
| DartMethodDefinition node = findNode(DartMethodDefinition.class, "get foo"); |
| Set<Element> superElements = node.getElement().getOverridden(); |
| assertClassMembers(superElements, "field A.foo"); |
| } |
| // setter |
| { |
| DartMethodDefinition node = findNode(DartMethodDefinition.class, "set foo"); |
| Set<Element> superElements = node.getElement().getOverridden(); |
| assertClassMembers(superElements, "field A.foo"); |
| } |
| } |
| |
| public void test_getOverridden_field_withGetter() throws Exception { |
| analyzeLibrary( |
| "// filler filler filler filler filler filler filler filler filler filler", |
| "class A {", |
| " var foo;", |
| "}", |
| "class B extends A {", |
| " get foo => 0;", |
| " set foo(x) {}", |
| "}", |
| ""); |
| // getter |
| { |
| DartMethodDefinition node = findNode(DartMethodDefinition.class, "get foo"); |
| Set<Element> superElements = node.getElement().getOverridden(); |
| assertClassMembers(superElements, "field A.foo"); |
| } |
| } |
| |
| public void test_getOverridden_field_withSetter() throws Exception { |
| analyzeLibrary( |
| "// filler filler filler filler filler filler filler filler filler filler", |
| "class A {", |
| " var foo;", |
| "}", |
| "class B extends A {", |
| " set foo(x) {}", |
| "}", |
| ""); |
| // setter |
| { |
| DartMethodDefinition node = findNode(DartMethodDefinition.class, "set foo"); |
| Set<Element> superElements = node.getElement().getOverridden(); |
| assertClassMembers(superElements, "field A.foo"); |
| } |
| } |
| |
| public void test_getOverridden_getterSetter_withField() throws Exception { |
| analyzeLibrary( |
| "// filler filler filler filler filler filler filler filler filler filler", |
| "class A {", |
| " get foo => 0;", |
| " set foo(x) {}", |
| "}", |
| "class B extends A {", |
| " var foo = 42;", |
| "}", |
| ""); |
| DartField node = findNode(DartField.class, "foo = 42"); |
| Set<Element> superElements = node.getElement().getOverridden(); |
| assertClassMembers(superElements, "field A.foo"); |
| } |
| |
| public void test_getOverridden_getter_withField() throws Exception { |
| analyzeLibrary( |
| "// filler filler filler filler filler filler filler filler filler filler", |
| "class A {", |
| " get foo => 0;", |
| "}", |
| "class B extends A {", |
| " var foo = 42;", |
| "}", |
| ""); |
| DartField node = findNode(DartField.class, "foo = 42"); |
| Set<Element> superElements = node.getElement().getOverridden(); |
| assertClassMembers(superElements, "field A.foo"); |
| } |
| |
| public void test_getOverridden_setter_withField() throws Exception { |
| analyzeLibrary( |
| "// filler filler filler filler filler filler filler filler filler filler", |
| "class A {", |
| " set foo(x) {}", |
| "}", |
| "class B extends A {", |
| " var foo = 42;", |
| "}", |
| ""); |
| DartField node = findNode(DartField.class, "foo = 42"); |
| Set<Element> superElements = node.getElement().getOverridden(); |
| assertClassMembers(superElements, "field A.setter foo"); |
| } |
| |
| public void test_getOverridden_setter_withSetter() throws Exception { |
| analyzeLibrary( |
| "// filler filler filler filler filler filler filler filler filler filler", |
| "class A {", |
| " set foo(x) {} // A", |
| "}", |
| "class B extends A {", |
| " set foo(x) {} // B", |
| "}", |
| ""); |
| DartField node = findNode(DartField.class, "set foo(x) {} // B"); |
| Set<Element> superElements = node.getElement().getOverridden(); |
| assertClassMembers(superElements, "field A.setter foo"); |
| } |
| |
| private static void assertClassMembers(Set<Element> superElements, String... expectedNames) { |
| Set<String> superNames = Sets.newHashSet(); |
| for (Element element : superElements) { |
| String name = element.getEnclosingElement().getName() + "." + element.getName(); |
| if (element instanceof FieldElement) { |
| superNames.add("field " + name); |
| } |
| if (element instanceof MethodElement) { |
| superNames.add("method " + name); |
| } |
| } |
| for (String name : expectedNames) { |
| assertTrue(name, superNames.remove(name)); |
| } |
| assertTrue(superNames.toString(), superNames.isEmpty()); |
| } |
| |
| public void test_fieldAccess_declared_noGetter() throws Exception { |
| AnalyzeLibraryResult result = analyzeLibrary( |
| "// filler filler filler filler filler filler filler filler filler filler", |
| "class A {", |
| " static set f(x) {}", |
| "}", |
| "main() {", |
| " print(A.f);", |
| "}", |
| ""); |
| assertErrors(result.getErrors(), errEx(ResolverErrorCode.FIELD_DOES_NOT_HAVE_A_GETTER, 6, 11, 1)); |
| } |
| |
| public void test_fieldAccess_notDeclared() throws Exception { |
| AnalyzeLibraryResult result = analyzeLibrary( |
| "// filler filler filler filler filler filler filler filler filler filler", |
| "class A {", |
| "}", |
| "main() {", |
| " print(A.f);", |
| "}", |
| ""); |
| assertErrors(result.getErrors(), errEx(TypeErrorCode.CANNOT_BE_RESOLVED, 5, 11, 1)); |
| } |
| |
| public void test_fieldAssign_declared_noSetter() throws Exception { |
| AnalyzeLibraryResult result = analyzeLibrary( |
| "// filler filler filler filler filler filler filler filler filler filler", |
| "class A {", |
| " static get f => 0;", |
| "}", |
| "main() {", |
| " A.f = 0;", |
| "}", |
| ""); |
| assertErrors(result.getErrors(), errEx(ResolverErrorCode.FIELD_DOES_NOT_HAVE_A_SETTER, 6, 5, 1)); |
| } |
| |
| public void test_fieldAssign_notDeclared() throws Exception { |
| AnalyzeLibraryResult result = analyzeLibrary( |
| "// filler filler filler filler filler filler filler filler filler filler", |
| "class A {", |
| "}", |
| "main() {", |
| " A.f = 0;", |
| "}", |
| ""); |
| assertErrors(result.getErrors(), errEx(TypeErrorCode.CANNOT_BE_RESOLVED, 5, 5, 1)); |
| } |
| |
| public void test_field_unqualifiedAccess_read() throws Exception { |
| AnalyzeLibraryResult result = analyzeLibrary( |
| "// filler filler filler filler filler filler filler filler filler filler", |
| "class A {", |
| " set f(x) {}", |
| " run() {", |
| " var v = f;", |
| " }", |
| "}", |
| ""); |
| assertErrors(result.getErrors(), errEx(ResolverErrorCode.FIELD_DOES_NOT_HAVE_A_GETTER, 5, 13, 1)); |
| } |
| |
| public void test_field_unqualifiedAccess_write() throws Exception { |
| AnalyzeLibraryResult result = analyzeLibrary( |
| "// filler filler filler filler filler filler filler filler filler filler", |
| "class A {", |
| " get f => 0;", |
| " run() {", |
| " f = 1;", |
| " }", |
| "}", |
| ""); |
| assertErrors(result.getErrors(), errEx(ResolverErrorCode.FIELD_DOES_NOT_HAVE_A_SETTER, 5, 5, 1)); |
| } |
| |
| public void test_typeVariableScope_staticField() throws Exception { |
| AnalyzeLibraryResult result = analyzeLibrary( |
| "// filler filler filler filler filler filler filler filler filler filler", |
| "class A<T> {", |
| " static T v;", |
| "}", |
| ""); |
| assertErrors( |
| result.getErrors(), |
| errEx(ResolverErrorCode.TYPE_VARIABLE_IN_STATIC_CONTEXT, 3, 10, 1)); |
| } |
| |
| public void test_typeVariableScope_staticMethod() throws Exception { |
| AnalyzeLibraryResult result = analyzeLibrary( |
| "// filler filler filler filler filler filler filler filler filler filler", |
| "class A<T> {", |
| " static foo() {", |
| " T v = null;", |
| " }", |
| "}", |
| ""); |
| assertErrors( |
| result.getErrors(), |
| errEx(ResolverErrorCode.TYPE_VARIABLE_IN_STATIC_CONTEXT, 4, 5, 1)); |
| } |
| |
| public void test_typeVariableScope_instanceField() throws Exception { |
| AnalyzeLibraryResult result = analyzeLibrary( |
| "// filler filler filler filler filler filler filler filler filler filler", |
| "class A<T> {", |
| " final List<T> values = new List<T>();", |
| "}", |
| ""); |
| assertErrors(result.getErrors()); |
| } |
| |
| public void test_unresolvedMethod_inFactoryConstructor() throws Exception { |
| AnalyzeLibraryResult result = analyzeLibrary( |
| "// filler filler filler filler filler filler filler filler filler filler", |
| "class A {", |
| " factory A() {", |
| " foo();", |
| " }", |
| "}", |
| ""); |
| assertErrors( |
| result.getErrors(), |
| errEx(ResolverErrorCode.CANNOT_RESOLVE_METHOD, 4, 5, 3)); |
| } |
| |
| /** |
| * Developers unfamiliar with Dart frequently write (x/y).toInt() instead of x ~/ y. The editor |
| * should recognize that pattern. |
| * <p> |
| * http://code.google.com/p/dart/issues/detail?id=5652 |
| */ |
| public void test_useEffectiveIntegerDivision_int() throws Exception { |
| AnalyzeLibraryResult result = analyzeLibrary( |
| "// filler filler filler filler filler filler filler filler filler filler", |
| "main() {", |
| " int x = 7;", |
| " int y = 2;", |
| " print( (x / y).toInt() );", |
| "}", |
| ""); |
| assertErrors(result.getErrors(), errEx(TypeErrorCode.USE_INTEGER_DIVISION, 5, 10, 15)); |
| } |
| |
| /** |
| * We need to report warning only when arguments are integers. |
| * <p> |
| * http://code.google.com/p/dart/issues/detail?id=5652 |
| */ |
| public void test_useEffectiveIntegerDivision_num() throws Exception { |
| AnalyzeLibraryResult result = analyzeLibrary( |
| "// filler filler filler filler filler filler filler filler filler filler", |
| "main() {", |
| " num x = 7;", |
| " num y = 2;", |
| " print( (x / y).toInt() );", |
| "}", |
| ""); |
| assertErrors(result.getErrors()); |
| } |
| |
| /** |
| * <p> |
| * http://code.google.com/p/dart/issues/detail?id=5157 |
| */ |
| public void test_trySubTypeMember_forInferredType() throws Exception { |
| compilerConfiguration = new DefaultCompilerConfiguration(new CompilerOptions() { |
| @Override |
| public boolean typeChecksForInferredTypes() { |
| return true; |
| } |
| }); |
| AnalyzeLibraryResult result = analyzeLibrary( |
| "// filler filler filler filler filler filler filler filler filler filler", |
| "class Event {}", |
| "class MouseEvent extends Event {", |
| " int clientX;", |
| " void stop() {}", |
| "}", |
| "typedef Listener(Event event);", |
| "class Button {", |
| " addListener(Listener listener) {}", |
| "}", |
| "main() {", |
| " Button button = new Button();", |
| " button.addListener((event) {", |
| " event.clientX;", |
| " event.stop();", |
| " });", |
| "}", |
| ""); |
| assertErrors(result.getErrors()); |
| } |
| |
| /** |
| * We should resolve sub-type member only if there is only sub-type with such member. |
| * <p> |
| * http://code.google.com/p/dart/issues/detail?id=6776 |
| */ |
| public void test_trySubTypeMember_moreThanOneMember() throws Exception { |
| compilerConfiguration = new DefaultCompilerConfiguration(new CompilerOptions() { |
| @Override |
| public boolean typeChecksForInferredTypes() { |
| return true; |
| } |
| }); |
| AnalyzeLibraryResult result = analyzeLibrary( |
| "// filler filler filler filler filler filler filler filler filler filler", |
| "class Event {}", |
| "class MouseEvent extends Event {", |
| " int clientX;", |
| "}", |
| "class ScreenEvent extends Event {", |
| " int clientX;", |
| "}", |
| "typedef Listener(Event event);", |
| "class Button {", |
| " addListener(Listener listener) {}", |
| "}", |
| "main() {", |
| " Button button = new Button();", |
| " button.addListener((event) {", |
| " event.clientX;", |
| " });", |
| "}", |
| ""); |
| assertErrors(result.getErrors(), errEx(TypeErrorCode.NOT_A_MEMBER_OF_INFERRED, 16, 11, 7)); |
| } |
| |
| /** |
| * <p> |
| * http://code.google.com/p/dart/issues/detail?id=6491 |
| */ |
| public void test_annotationOnGetter() throws Exception { |
| AnalyzeLibraryResult result = analyzeLibrary( |
| "// filler filler filler filler filler filler filler filler filler filler", |
| "const myAnnotation = 0;", |
| "class A {", |
| " @myAnnotation bool get isEmpty => true;", |
| "}", |
| ""); |
| assertErrors(result.getErrors()); |
| } |
| |
| public void test_resolveRefInComment_ofClass() throws Exception { |
| AnalyzeLibraryResult result = analyzeLibrary( |
| "// filler filler filler filler filler filler filler filler filler filler", |
| "/** This class [A] has method [foo]. */", |
| "class A {", |
| " foo() {}", |
| "}", |
| ""); |
| assertErrors(result.getErrors()); |
| // [A] |
| { |
| DartCommentRefName node = findNode(DartCommentRefName.class, "A]"); |
| ClassElement element = (ClassElement) node.getElement(); |
| assertEquals("A", element.getName()); |
| } |
| // [foo] |
| { |
| DartCommentRefName node = findNode(DartCommentRefName.class, "foo]"); |
| MethodElement element = (MethodElement) node.getElement(); |
| assertEquals("foo", element.getName()); |
| } |
| } |
| |
| public void test_resolveRefInComment_ofFunction() throws Exception { |
| AnalyzeLibraryResult result = analyzeLibrary( |
| "// filler filler filler filler filler filler filler filler filler filler", |
| "class A {}", |
| "/** This function has parameter [aaa] of type [A] ans also [bbb]. */", |
| "foo(A aaa, bbb) {}", |
| ""); |
| assertErrors(result.getErrors()); |
| // [aaa] |
| { |
| DartCommentRefName node = findNode(DartCommentRefName.class, "aaa]"); |
| VariableElement element = (VariableElement) node.getElement(); |
| assertSame(ElementKind.PARAMETER, ElementKind.of(element)); |
| assertEquals("aaa", element.getName()); |
| } |
| // [A] |
| { |
| DartCommentRefName node = findNode(DartCommentRefName.class, "A]"); |
| ClassElement element = (ClassElement) node.getElement(); |
| assertEquals("A", element.getName()); |
| } |
| // [bbb] |
| { |
| DartCommentRefName node = findNode(DartCommentRefName.class, "bbb]"); |
| VariableElement element = (VariableElement) node.getElement(); |
| assertSame(ElementKind.PARAMETER, ElementKind.of(element)); |
| assertEquals("bbb", element.getName()); |
| } |
| } |
| |
| public void test_resolveRefInComment_ofMethod() throws Exception { |
| AnalyzeLibraryResult result = analyzeLibrary( |
| "// filler filler filler filler filler filler filler filler filler filler", |
| "class A {", |
| " var fff;", |
| " /** Initializes [fff] and then calls [bar]. */", |
| " foo() {}", |
| " bar() {}", |
| "}", |
| ""); |
| assertErrors(result.getErrors()); |
| // [fff] |
| { |
| DartCommentRefName node = findNode(DartCommentRefName.class, "fff]"); |
| FieldElement element = (FieldElement) node.getElement(); |
| assertEquals("fff", element.getName()); |
| } |
| // [bar] |
| { |
| DartCommentRefName node = findNode(DartCommentRefName.class, "bar]"); |
| MethodElement element = (MethodElement) node.getElement(); |
| assertEquals("bar", element.getName()); |
| } |
| } |
| |
| public void test_resolveNewInComment() throws Exception { |
| AnalyzeLibraryResult result = analyzeLibrary( |
| "// filler filler filler filler filler filler filler filler filler filler", |
| "class A {", |
| " A() {}", |
| " A.named() {}", |
| "}", |
| "/** Creates [A] using [new A] or [new A.named] constructors. */", |
| "foo() {}", |
| ""); |
| assertErrors(result.getErrors()); |
| // [new A] |
| { |
| DartCommentNewName node = findNode(DartCommentNewName.class, "new A]"); |
| assertEquals("", node.getConstructorElement().getName()); |
| assertEquals("A", node.getClassElement().getName()); |
| } |
| // [new A.named] |
| { |
| DartCommentNewName node = findNode(DartCommentNewName.class, "new A.named]"); |
| assertEquals("named", node.getConstructorElement().getName()); |
| assertEquals("A", node.getClassElement().getName()); |
| } |
| } |
| |
| public void test_notGenerativeConstructor_default() throws Exception { |
| AnalyzeLibraryResult result = analyzeLibrary( |
| "// filler filler filler filler filler filler filler filler filler filler", |
| "class A {", |
| " factory A() {", |
| " return null;", |
| " }", |
| "}", |
| "class B extends A {", |
| " B() : super() {}", // explicit call of super constructor |
| "}", |
| "class C extends A {", |
| " C() {}", // implicit call of super constructor |
| "}", |
| "class D extends A {", // implicit call of super constructor |
| "}", |
| ""); |
| assertErrors(result.getErrors(), |
| errEx(ResolverErrorCode.NOT_GENERATIVE_SUPER_CONSTRUCTOR, 8, 9, 7), |
| errEx(ResolverErrorCode.NOT_GENERATIVE_SUPER_CONSTRUCTOR, 11, 3, 1), |
| errEx(ResolverErrorCode.NOT_GENERATIVE_SUPER_CONSTRUCTOR, 13, 7, 1)); |
| } |
| |
| public void test_notGenerativeConstructor_explicitNamed() throws Exception { |
| AnalyzeLibraryResult result = analyzeLibrary( |
| "// filler filler filler filler filler filler filler filler filler filler", |
| "class A {", |
| " factory A.named() {", |
| " return null;", |
| " }", |
| "}", |
| "class B extends A {", |
| " B() : super.named() {}", // explicit call of super constructor |
| "}", |
| ""); |
| assertErrors(result.getErrors(), |
| errEx(ResolverErrorCode.NOT_GENERATIVE_SUPER_CONSTRUCTOR, 8, 9, 13)); |
| } |
| |
| /** |
| * <p> |
| * http://code.google.com/p/dart/issues/detail?id=6718 |
| */ |
| public void test_initializerInMethod() throws Exception { |
| AnalyzeLibraryResult result = analyzeLibrary( |
| "// filler filler filler filler filler filler filler filler filler filler", |
| "class A {", |
| " var x;", |
| " B() : x = (foo() { }) {}", |
| "}", |
| ""); |
| assertErrors(result.getErrors(), |
| errEx(ResolverErrorCode.INITIALIZER_ONLY_IN_GENERATIVE_CONSTRUCTOR, 4, 9, 15)); |
| } |
| |
| public void test_variableReferencesItselfInInitializer() throws Exception { |
| AnalyzeLibraryResult result = analyzeLibrary( |
| "// filler filler filler filler filler filler filler filler filler filler", |
| "main() {", |
| " var x = x;", |
| "}", |
| ""); |
| assertErrors(result.getErrors(), |
| errEx(ResolverErrorCode.VARIABLE_REFERENCES_SAME_NAME_IN_INITIALIZER, 3, 11, 1)); |
| } |
| } |