// Copyright (c) 2015, 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.

// TODO(jmesserly): this file needs to be refactored, it's a port from
// package:dev_compiler's tests
/// Tests for type inference.
library analyzer.test.src.task.strong.inferred_type_test;

import 'package:analyzer/dart/element/element.dart';
import 'package:test/test.dart';
import 'package:test_reflective_loader/test_reflective_loader.dart';

import 'strong_test_helper.dart' as helper;

void main() {
  defineReflectiveSuite(() {
    defineReflectiveTests(InferredTypeTest);
  });
}

abstract class InferredTypeMixin {
  /**
   * If `true` then types of local elements may be checked.
   */
  bool get mayCheckTypesOfLocals;

  /**
   * Add a new file with the given [name] and [content].
   */
  void addFile(String content, {String name: '/main.dart'});

  /**
   * Add the file, process it (resolve, validate, etc) and return the resolved
   * unit element.
   */
  CompilationUnitElement checkFile(String content);

  void test_blockBodiedLambdas_async_allReturnsAreFutures() {
    if (!mayCheckTypesOfLocals) {
      return;
    }
    var mainUnit = checkFile(r'''
import 'dart:async';
import 'dart:math' show Random;
main() {
  var f = /*info:INFERRED_TYPE_CLOSURE*/() async {
    if (new Random().nextBool()) {
      return new Future<int>.value(1);
    } else {
      return new Future<double>.value(2.0);
    }
  };
  Future<num> g = f();
  Future<int> h = /*info:ASSIGNMENT_CAST*/f();
}
''');
    var f = mainUnit.functions[0].localVariables[0];
    expect(f.type.toString(), '() → Future<num>');
  }

  void test_blockBodiedLambdas_async_allReturnsAreFutures_topLevel() {
    var mainUnit = checkFile(r'''
import 'dart:async';
import 'dart:math' show Random;
var f = /*info:INFERRED_TYPE_CLOSURE,warning:UNSAFE_BLOCK_CLOSURE_INFERENCE*/() async {
  if (new Random().nextBool()) {
    return new Future<int>.value(1);
  } else {
    return new Future<double>.value(2.0);
  }
};
''');
    var f = mainUnit.topLevelVariables[0];
    expect(f.type.toString(), '() → Future<num>');
  }

  void test_blockBodiedLambdas_async_allReturnsAreValues() {
    if (!mayCheckTypesOfLocals) {
      return;
    }
    var mainUnit = checkFile(r'''
import 'dart:async';
import 'dart:math' show Random;
main() {
  var f = /*info:INFERRED_TYPE_CLOSURE*/() async {
    if (new Random().nextBool()) {
      return 1;
    } else {
      return 2.0;
    }
  };
  Future<num> g = f();
  Future<int> h = /*info:ASSIGNMENT_CAST*/f();
}
''');
    var f = mainUnit.functions[0].localVariables[0];
    expect(f.type.toString(), '() → Future<num>');
  }

  void test_blockBodiedLambdas_async_allReturnsAreValues_topLevel() {
    var mainUnit = checkFile(r'''
import 'dart:async';
import 'dart:math' show Random;
var f = /*info:INFERRED_TYPE_CLOSURE,warning:UNSAFE_BLOCK_CLOSURE_INFERENCE*/() async {
  if (new Random().nextBool()) {
    return 1;
  } else {
    return 2.0;
  }
};
''');
    var f = mainUnit.topLevelVariables[0];
    expect(f.type.toString(), '() → Future<num>');
  }

  void test_blockBodiedLambdas_async_mixOfValuesAndFutures() {
    if (!mayCheckTypesOfLocals) {
      return;
    }
    var mainUnit = checkFile(r'''
import 'dart:async';
import 'dart:math' show Random;
main() {
  var f = /*info:INFERRED_TYPE_CLOSURE*/() async {
    if (new Random().nextBool()) {
      return new Future<int>.value(1);
    } else {
      return 2.0;
    }
  };
  Future<num> g = f();
  Future<int> h = /*info:ASSIGNMENT_CAST*/f();
}
''');
    var f = mainUnit.functions[0].localVariables[0];
    expect(f.type.toString(), '() → Future<num>');
  }

  void test_blockBodiedLambdas_async_mixOfValuesAndFutures_topLevel() {
    var mainUnit = checkFile(r'''
import 'dart:async';
import 'dart:math' show Random;
var f = /*info:INFERRED_TYPE_CLOSURE,warning:UNSAFE_BLOCK_CLOSURE_INFERENCE*/() async {
  if (new Random().nextBool()) {
    return new Future<int>.value(1);
  } else {
    return 2.0;
  }
};
''');
    var f = mainUnit.topLevelVariables[0];
    expect(f.type.toString(), '() → Future<num>');
  }

  void test_blockBodiedLambdas_asyncStar() {
    if (!mayCheckTypesOfLocals) {
      return;
    }
    var mainUnit = checkFile(r'''
import 'dart:async';
main() {
  var f = /*info:INFERRED_TYPE_CLOSURE*/() async* {
    yield 1;
    Stream<double> s;
    yield* s;
  };
  Stream<num> g = f();
  Stream<int> h = /*info:ASSIGNMENT_CAST*/f();
}
''');
    var f = mainUnit.functions[0].localVariables[0];
    expect(f.type.toString(), '() → Stream<num>');
  }

  void test_blockBodiedLambdas_asyncStar_topLevel() {
    var mainUnit = checkFile(r'''
  import 'dart:async';
var f = /*info:INFERRED_TYPE_CLOSURE,warning:UNSAFE_BLOCK_CLOSURE_INFERENCE*/() async* {
  yield 1;
  Stream<double> s;
  yield* s;
};
''');
    var f = mainUnit.topLevelVariables[0];
    expect(f.type.toString(), '() → Stream<num>');
  }

  void test_blockBodiedLambdas_basic() {
    checkFile(r'''
test1() {
  List<int> o;
  var y = o.map(/*info:INFERRED_TYPE_CLOSURE,info:INFERRED_TYPE_CLOSURE*/(x) { return x + 1; });
  Iterable<int> z = y;
}
''');
  }

  void test_blockBodiedLambdas_basic_topLevel() {
    checkFile(r'''
List<int> o;
var y = o.map(/*info:INFERRED_TYPE_CLOSURE,warning:UNSAFE_BLOCK_CLOSURE_INFERENCE*/(x) { return x + 1; });
Iterable<int> z = y;
''');
  }

  void test_blockBodiedLambdas_doesNotInferBottom_async() {
    if (!mayCheckTypesOfLocals) {
      return;
    }
    var mainUnit = checkFile(r'''
import 'dart:async';
main() async {
  var f = () async { return null; };
  Future y = f();
  Future<String> z = /*warning:DOWN_CAST_COMPOSITE*/f();
  String s = /*info:DYNAMIC_CAST*/await f();
}
''');
    var f = mainUnit.functions[0].localVariables[0];
    expect(f.type.toString(), '() → Future<dynamic>');
  }

  void test_blockBodiedLambdas_doesNotInferBottom_async_topLevel() {
    var mainUnit = checkFile(r'''
import 'dart:async';
var f = /*warning:UNSAFE_BLOCK_CLOSURE_INFERENCE*/() async { return null; };
''');
    var f = mainUnit.topLevelVariables[0];
    expect(f.type.toString(), '() → Future<dynamic>');
  }

  void test_blockBodiedLambdas_doesNotInferBottom_asyncStar() {
    if (!mayCheckTypesOfLocals) {
      return;
    }
    var mainUnit = checkFile(r'''
import 'dart:async';
main() async {
  var f = () async* { yield null; };
  Stream y = f();
  Stream<String> z = /*warning:DOWN_CAST_COMPOSITE*/f();
  String s = /*info:DYNAMIC_CAST*/await f().first;
}
''');
    var f = mainUnit.functions[0].localVariables[0];
    expect(f.type.toString(), '() → Stream<dynamic>');
  }

  void test_blockBodiedLambdas_doesNotInferBottom_asyncStar_topLevel() {
    var mainUnit = checkFile(r'''
import 'dart:async';
var f = /*warning:UNSAFE_BLOCK_CLOSURE_INFERENCE*/() async* { yield null; };
''');
    var f = mainUnit.topLevelVariables[0];
    expect(f.type.toString(), '() → Stream<dynamic>');
  }

  void test_blockBodiedLambdas_doesNotInferBottom_sync() {
    if (!mayCheckTypesOfLocals) {
      return;
    }
    var mainUnit = checkFile(r'''
var h = null;
void foo(int f(Object _)) {}

main() {
  var f = (Object x) { return null; };
  String y = /*info:DYNAMIC_CAST*/f(42);

  f = /*info:INFERRED_TYPE_CLOSURE*/(x) => 'hello';

  foo(/*info:INFERRED_TYPE_CLOSURE*/(x) { return null; });
  foo(/*info:INFERRED_TYPE_CLOSURE*/(x) { throw "not implemented"; });
}
''');

    var f = mainUnit.functions[1].localVariables[0];
    expect(f.type.toString(), '(Object) → dynamic');
  }

  void test_blockBodiedLambdas_doesNotInferBottom_sync_topLevel() {
    var mainUnit = checkFile(r'''
var f = (Object x) { return null; };
''');
    var f = mainUnit.topLevelVariables[0];
    expect(f.type.toString(), '(Object) → dynamic');
  }

  void test_blockBodiedLambdas_doesNotInferBottom_syncStar() {
    if (!mayCheckTypesOfLocals) {
      return;
    }
    var mainUnit = checkFile(r'''
main() {
  var f = () sync* { yield null; };
  Iterable y = f();
  Iterable<String> z = /*warning:DOWN_CAST_COMPOSITE*/f();
  String s = /*info:DYNAMIC_CAST*/f().first;
}
''');
    var f = mainUnit.functions[0].localVariables[0];
    expect(f.type.toString(), '() → Iterable<dynamic>');
  }

  void test_blockBodiedLambdas_doesNotInferBottom_syncStar_topLevel() {
    var mainUnit = checkFile(r'''
var f = /*warning:UNSAFE_BLOCK_CLOSURE_INFERENCE*/() sync* { yield null; };
''');
    var f = mainUnit.topLevelVariables[0];
    expect(f.type.toString(), '() → Iterable<dynamic>');
  }

  void test_blockBodiedLambdas_downwardsIncompatibleWithUpwardsInference() {
    if (!mayCheckTypesOfLocals) {
      return;
    }
    var mainUnit = checkFile(r'''
main() {
  String f() => null;
  var g = f;
  g = /*info:INFERRED_TYPE_CLOSURE*/() { return /*error:RETURN_OF_INVALID_TYPE*/1; };
}
''');
    var f = mainUnit.functions[0].localVariables[0];
    expect(f.type.toString(), '() → String');
  }

  void
      test_blockBodiedLambdas_downwardsIncompatibleWithUpwardsInference_topLevel() {
    var mainUnit = checkFile(r'''
String f() => null;
var g = f;
''');
    var f = mainUnit.topLevelVariables[0];
    expect(f.type.toString(), '() → String');
  }

  void test_blockBodiedLambdas_LUB() {
    checkFile(r'''
import 'dart:math' show Random;
test2() {
  List<num> o;
  var y = o.map(/*info:INFERRED_TYPE_CLOSURE, info:INFERRED_TYPE_CLOSURE*/(x) {
    if (new Random().nextBool()) {
      return x.toInt() + 1;
    } else {
      return x.toDouble();
    }
  });
  Iterable<num> w = y;
  Iterable<int> z = /*info:ASSIGNMENT_CAST*/y;
}
''');
  }

  void test_blockBodiedLambdas_LUB_topLevel() {
    checkFile(r'''
import 'dart:math' show Random;
List<num> o;
var y = o.map(/*info:INFERRED_TYPE_CLOSURE,warning:UNSAFE_BLOCK_CLOSURE_INFERENCE*/(x) {
  if (new Random().nextBool()) {
    return x.toInt() + 1;
  } else {
    return x.toDouble();
  }
});
Iterable<num> w = y;
Iterable<int> z = /*info:ASSIGNMENT_CAST*/y;
''');
  }

  void test_blockBodiedLambdas_nestedLambdas() {
    if (!mayCheckTypesOfLocals) {
      return;
    }
    // Original feature request: https://github.com/dart-lang/sdk/issues/25487
    var mainUnit = checkFile(r'''
main() {
  var f = /*info:INFERRED_TYPE_CLOSURE*/() {
    return /*info:INFERRED_TYPE_CLOSURE*/(int x) { return 2.0 * x; };
  };
}
''');
    var f = mainUnit.functions[0].localVariables[0];
    expect(f.type.toString(), '() → (int) → double');
  }

  void test_blockBodiedLambdas_nestedLambdas_topLevel() {
    // Original feature request: https://github.com/dart-lang/sdk/issues/25487
    var mainUnit = checkFile(r'''
var f = /*info:INFERRED_TYPE_CLOSURE,warning:UNSAFE_BLOCK_CLOSURE_INFERENCE*/() {
  return /*info:INFERRED_TYPE_CLOSURE*/(int x) { return 2.0 * x; };
};
''');
    var f = mainUnit.topLevelVariables[0];
    expect(f.type.toString(), '() → (int) → double');
  }

  void test_blockBodiedLambdas_noReturn() {
    if (!mayCheckTypesOfLocals) {
      return;
    }
    var mainUnit = checkFile(r'''
test1() {
  List<int> o;
  var y = o.map(/*info:INFERRED_TYPE_CLOSURE*/(x) { });
  Iterable<int> z = /*warning:DOWN_CAST_COMPOSITE*/y;
}
''');
    var f = mainUnit.functions[0].localVariables[1];
    expect(f.type.toString(), 'Iterable<dynamic>');
  }

  void test_blockBodiedLambdas_noReturn_topLevel() {
    var mainUnit = checkFile(r'''
final List<int> o = <int>[];
var y = o.map((x) { });
''');
    var f = mainUnit.topLevelVariables[1];
    expect(f.type.toString(), 'Iterable<dynamic>');
  }

  void test_blockBodiedLambdas_syncStar() {
    if (!mayCheckTypesOfLocals) {
      return;
    }
    var mainUnit = checkFile(r'''
main() {
  var f = /*info:INFERRED_TYPE_CLOSURE*/() sync* {
    yield 1;
    yield* /*info:INFERRED_TYPE_LITERAL*/[3, 4.0];
  };
  Iterable<num> g = f();
  Iterable<int> h = /*info:ASSIGNMENT_CAST*/f();
}
''');
    var f = mainUnit.functions[0].localVariables[0];
    expect(f.type.toString(), '() → Iterable<num>');
  }

  void test_blockBodiedLambdas_syncStar_topLevel() {
    var mainUnit = checkFile(r'''
var f = /*info:INFERRED_TYPE_CLOSURE,warning:UNSAFE_BLOCK_CLOSURE_INFERENCE*/() sync* {
  yield 1;
  yield* /*info:INFERRED_TYPE_LITERAL*/[3, 4.0];
};
''');
    var f = mainUnit.topLevelVariables[0];
    expect(f.type.toString(), '() → Iterable<num>');
  }

  void test_bottom() {
    // When a type is inferred from the expression `null`, the inferred type is
    // `dynamic`, but the inferred type of the initializer is `bottom`.
    // TODO(paulberry): Is this intentional/desirable?
    var mainUnit = checkFile('''
var v = null;
''');
    var v = mainUnit.topLevelVariables[0];
    expect(v.type.toString(), 'dynamic');
    expect(v.initializer.type.toString(), '() → <bottom>');
  }

  void test_bottom_inClosure() {
    // When a closure's return type is inferred from the expression `null`, the
    // inferred type is `dynamic`.
    var mainUnit = checkFile('''
var v = () => null;
''');
    var v = mainUnit.topLevelVariables[0];
    expect(v.type.toString(), '() → dynamic');
    expect(v.initializer.type.toString(), '() → () → dynamic');
  }

  void test_canInferAlsoFromStaticAndInstanceFieldsFlagOn() {
    addFile(
        '''
import 'b.dart';
class A {
  static final a1 = B.b1;
  final a2 = new B().b2;
}
''',
        name: '/a.dart');
    addFile(
        '''
class B {
  static final b1 = 1;
  final b2 = 1;
}
''',
        name: '/b.dart');
    checkFile('''
import "a.dart";

test1() {
  int x = 0;
  // inference in A now works.
  x = A.a1;
  x = new A().a2;
}
''');
  }

  void test_circularReference_viaClosures() {
    var mainUnit = checkFile('''
var x = () => y;
var y = () => x;
''');
    var x = mainUnit.topLevelVariables[0];
    var y = mainUnit.topLevelVariables[1];
    expect(x.name, 'x');
    expect(y.name, 'y');
    expect(x.type.toString(), 'dynamic');
    expect(y.type.toString(), 'dynamic');
  }

  void test_circularReference_viaClosures_initializerTypes() {
    var mainUnit = checkFile('''
var x = () => y;
var y = () => x;
''');
    var x = mainUnit.topLevelVariables[0];
    var y = mainUnit.topLevelVariables[1];
    expect(x.name, 'x');
    expect(y.name, 'y');
    expect(x.initializer.returnType.toString(), '() → dynamic');
    expect(y.initializer.returnType.toString(), '() → dynamic');
  }

  void test_conflictsCanHappen() {
    checkFile('''
class I1 {
  int x;
}
class I2 extends I1 {
  int y;
}

class A {
  final I1 a = null;
}

class B {
  final I2 a = null;
}

class C1 implements A, B {
  /*error:INVALID_METHOD_OVERRIDE*/get a => null;
}

// Still ambiguous
class C2 implements B, A {
  /*error:INVALID_METHOD_OVERRIDE*/get a => null;
}
''');
  }

  void test_conflictsCanHappen2() {
    checkFile('''
class I1 {
  int x;
}
class I2 {
  int y;
}

class I3 implements I1, I2 {
  int x;
  int y;
}

class A {
  final I1 a = null;
}

class B {
  final I2 a = null;
}

class C1 implements A, B {
  I3 get a => null;
}

class C2 implements A, B {
  /*error:INVALID_METHOD_OVERRIDE*/get a => null;
}
''');
  }

  void test_constructors_downwardsWithConstraint() {
    // Regression test for https://github.com/dart-lang/sdk/issues/26431
    checkFile(r'''
class Foo<T extends Iterable> {}
void main() {
  Foo<List> foo = /*info:INFERRED_TYPE_ALLOCATION*/new Foo();
}
    ''');
  }

  void test_constructors_inferenceFBounded() {
    // Regression for https://github.com/dart-lang/sdk/issues/26990
    var unit = checkFile('''
class Clonable<T> {}

class Pair<T extends Clonable<T>, U extends Clonable<U>> {
  T t;
  U u;
  Pair(this.t, this.u);
  Pair._();
  Pair<U, T> get reversed => /*info:INFERRED_TYPE_ALLOCATION*/new Pair(u, t);
}
final x = /*info:INFERRED_TYPE_ALLOCATION*/new Pair._();
    ''');
    var x = unit.topLevelVariables[0];
    expect(x.type.toString(), 'Pair<Clonable<dynamic>, Clonable<dynamic>>');
  }

  void test_constructors_inferFromArguments() {
    var unit = checkFile('''
class C<T> {
  T t;
  C(this.t);
}

var x = /*info:INFERRED_TYPE_ALLOCATION*/new C(42);

num y;
C<int> c_int = /*info:INFERRED_TYPE_ALLOCATION*/new /*error:COULD_NOT_INFER*/C(/*info:DOWN_CAST_IMPLICIT*/y);

// These hints are not reported because we resolve with a null error listener.
C<num> c_num = /*pass should be info:INFERRED_TYPE_ALLOCATION*/new C(123);
C<num> c_num2 = (/*pass should be info:INFERRED_TYPE_ALLOCATION*/new C(456))
    ..t = /*error:INVALID_ASSIGNMENT*/1.0;

// Down't infer from explicit dynamic.
var c_dynamic = new C<dynamic>(42);

main() {
  x.t = /*error:INVALID_ASSIGNMENT*/'hello';
}
''');
    var vars = unit.topLevelVariables;
    expect(vars[0].type.toString(), 'C<int>');
    expect(vars.firstWhere((e) => e.name == 'c_int').type.toString(), 'C<int>');
    expect(vars.firstWhere((e) => e.name == 'c_num').type.toString(), 'C<num>');
    expect(vars.firstWhere((e) => e.name == 'c_dynamic').type.toString(),
        'C<dynamic>');
  }

  void test_constructors_inferFromArguments_const() {
    var unit = checkFile('''
class C<T> {
  final T t;
  const C(this.t);
}

var x = /*info:INFERRED_TYPE_ALLOCATION*/const C(42);
''');
    expect(unit.topLevelVariables[0].type.toString(), 'C<int>');
  }

  void test_constructors_inferFromArguments_constWithUpperBound() {
    // Regression for https://github.com/dart-lang/sdk/issues/26993
    checkFile('''
class C<T extends num> {
  final T x;
  const C(this.x);
}
class D<T extends num> {
  const D();
}
void f() {
  const c = /*info:INFERRED_TYPE_ALLOCATION*/const C(0);
  const D<int> d = /*info:INFERRED_TYPE_ALLOCATION*/const D();
}
    ''');
  }

  void test_constructors_inferFromArguments_factory() {
    var unit = checkFile('''
class C<T> {
  T t;

  C._();

  factory C(T t) {
    var x = new C<T>._();
    x.t = t;
    return x;
  }
}

var x = /*info:INFERRED_TYPE_ALLOCATION*/new C(42);

main() {
  x.t = /*error:INVALID_ASSIGNMENT*/'hello';
}
''');
    expect(unit.topLevelVariables[0].type.toString(), 'C<int>');
  }

  void test_constructors_inferFromArguments_factory_callsConstructor() {
    checkFile(r'''
class A<T> {
  A<T> f = /*info:INFERRED_TYPE_ALLOCATION*/new A();
  A();
  factory A.factory() => /*info:INFERRED_TYPE_ALLOCATION*/new A();
  A<T> m() => /*info:INFERRED_TYPE_ALLOCATION*/new A();
}
    ''');
  }

  void test_constructors_inferFromArguments_named() {
    var unit = checkFile('''
class C<T> {
  T t;
  C.named(List<T> t);
}

var x = /*info:INFERRED_TYPE_ALLOCATION*/new C.named(<int>[]);

main() {
  x.t = /*error:INVALID_ASSIGNMENT*/'hello';
}
''');
    expect(unit.topLevelVariables[0].type.toString(), 'C<int>');
  }

  void test_constructors_inferFromArguments_namedFactory() {
    var unit = checkFile('''
class C<T> {
  T t;
  C();

  factory C.named(T t) {
    var x = new C<T>();
    x.t = t;
    return x;
  }
}

var x = /*info:INFERRED_TYPE_ALLOCATION*/new C.named(42);

main() {
  x.t = /*error:INVALID_ASSIGNMENT*/'hello';
}
''');
    expect(unit.topLevelVariables[0].type.toString(), 'C<int>');
  }

  void test_constructors_inferFromArguments_redirecting() {
    var unit = checkFile('''
class C<T> {
  T t;
  C(this.t);
  C.named(List<T> t) : this(t[0]);
}

var x = /*info:INFERRED_TYPE_ALLOCATION*/new C.named(<int>[42]);

main() {
  x.t = /*error:INVALID_ASSIGNMENT*/'hello';
}
''');
    expect(unit.topLevelVariables[0].type.toString(), 'C<int>');
  }

  void test_constructors_inferFromArguments_redirectingFactory() {
    var unit = checkFile('''
abstract class C<T> {
  T get t;
  void set t(T x);

  factory C(T t) = CImpl<T>;
}

class CImpl<T> implements C<T> {
  T t;
  CImpl(this.t);
}

var x = /*info:INFERRED_TYPE_ALLOCATION*/new C(42);

main() {
  x.t = /*error:INVALID_ASSIGNMENT*/'hello';
}
''');
    expect(unit.topLevelVariables[0].type.toString(), 'C<int>');
  }

  void test_constructors_reverseTypeParameters() {
    // Regression for https://github.com/dart-lang/sdk/issues/26990
    checkFile('''
class Pair<T, U> {
  T t;
  U u;
  Pair(this.t, this.u);
  Pair<U, T> get reversed => /*info:INFERRED_TYPE_ALLOCATION*/new Pair(u, t);
}
    ''');
  }

  void test_doNotInferOverriddenFieldsThatExplicitlySayDynamic_infer() {
    checkFile('''
class A {
  final int x = 2;
}

class B implements A {
  /*error:INVALID_METHOD_OVERRIDE*/dynamic get x => 3;
}

foo() {
  String y = /*info:DYNAMIC_CAST*/new B().x;
  int z = /*info:DYNAMIC_CAST*/new B().x;
}
''');
  }

  void test_dontInferFieldTypeWhenInitializerIsNull() {
    checkFile('''
var x = null;
var y = 3;
class A {
  static var x = null;
  static var y = 3;

  var x2 = null;
  var y2 = 3;
}

test() {
  x = "hi";
  y = /*error:INVALID_ASSIGNMENT*/"hi";
  A.x = "hi";
  A.y = /*error:INVALID_ASSIGNMENT*/"hi";
  new A().x2 = "hi";
  new A().y2 = /*error:INVALID_ASSIGNMENT*/"hi";
}
''');
  }

  void test_dontInferTypeOnDynamic() {
    checkFile('''
test() {
  dynamic x = 3;
  x = "hi";
}
''');
  }

  void test_dontInferTypeWhenInitializerIsNull() {
    checkFile('''
test() {
  var x = null;
  x = "hi";
  x = 3;
}
''');
  }

  void test_downwardInference_miscellaneous() {
    checkFile('''
typedef T Function2<S, T>(S x);
class A<T> {
  Function2<T, T> x;
  A(this.x);
}
void main() {
  {  // Variables, nested literals
    var x = "hello";
    var y = 3;
    void f(List<Map<int, String>> l) {};
    f(/*info:INFERRED_TYPE_LITERAL*/[/*info:INFERRED_TYPE_LITERAL*/{y: x}]);
  }
  {
    int f(int x) => 0;
    A<int> a = /*info:INFERRED_TYPE_ALLOCATION*/new A(f);
  }
}
''');
  }

  void test_downwardsInferenceAnnotations() {
    checkFile('''
class Foo {
  const Foo(List<String> l);
  const Foo.named(List<String> l);
}
@Foo(/*info:INFERRED_TYPE_LITERAL*/const [])
class Bar {}
@Foo.named(/*info:INFERRED_TYPE_LITERAL*/const [])
class Baz {}
''');
  }

  void test_downwardsInferenceAssignmentStatements() {
    checkFile('''
void main() {
  List<int> l;
  l = /*info:INFERRED_TYPE_LITERAL,error:COULD_NOT_INFER*/[/*error:LIST_ELEMENT_TYPE_NOT_ASSIGNABLE*/"hello"];
  l = (l = /*info:INFERRED_TYPE_LITERAL*/[1]);
}
''');
  }

  void test_downwardsInferenceAsyncAwait() {
    checkFile('''
import 'dart:async';
Future test() async {
  dynamic d;
  List<int> l0 = await /*info:INFERRED_TYPE_LITERAL,error:COULD_NOT_INFER*/[/*info:DYNAMIC_CAST*/d];
  List<int> l1 = await /*info:INFERRED_TYPE_ALLOCATION*/new Future.value(/*info:INFERRED_TYPE_LITERAL,error:COULD_NOT_INFER*/[/*info:DYNAMIC_CAST*/d]);
}
''');
  }

  void test_downwardsInferenceForEach() {
    checkFile('''
import 'dart:async';
Future main() async {
  for(int x in /*info:INFERRED_TYPE_LITERAL*/[1, 2, 3]) {}
  await for(int x in /*info:INFERRED_TYPE_ALLOCATION*/new Stream()) {}
}
''');
  }

  void test_downwardsInferenceInitializingFormalDefaultFormal() {
    checkFile('''
typedef T Function2<S, T>([S x]);
class Foo {
  List<int> x;
  Foo([this.x = /*info:INFERRED_TYPE_LITERAL*/const [1]]);
  Foo.named([List<int> x = /*info:INFERRED_TYPE_LITERAL*/const [1]]);
}
void f([List<int> l = /*info:INFERRED_TYPE_LITERAL*/const [1]]) {}
// We do this inference in an early task but don't preserve the infos.
Function2<List<int>, String> g = /*pass should be info:INFERRED_TYPE_CLOSURE*/([llll = /*info:INFERRED_TYPE_LITERAL*/const [1]]) => "hello";
''');
  }

  void test_downwardsInferenceOnConstructorArguments_inferDownwards() {
    checkFile('''
class F0 {
  F0(List<int> a) {}
}
class F1 {
  F1({List<int> a}) {}
}
class F2 {
  F2(Iterable<int> a) {}
}
class F3 {
  F3(Iterable<Iterable<int>> a) {}
}
class F4 {
  F4({Iterable<Iterable<int>> a}) {}
}
void main() {
  new F0(/*info:INFERRED_TYPE_LITERAL*/[]);
  new F0(/*info:INFERRED_TYPE_LITERAL*/[3]);
  new F0(/*info:INFERRED_TYPE_LITERAL,error:COULD_NOT_INFER*/[/*error:LIST_ELEMENT_TYPE_NOT_ASSIGNABLE*/"hello"]);
  new F0(/*info:INFERRED_TYPE_LITERAL,error:COULD_NOT_INFER*/[/*error:LIST_ELEMENT_TYPE_NOT_ASSIGNABLE*/"hello",
                                      3]);

  new F1(a: /*info:INFERRED_TYPE_LITERAL*/[]);
  new F1(a: /*info:INFERRED_TYPE_LITERAL*/[3]);
  new F1(a: /*info:INFERRED_TYPE_LITERAL,error:COULD_NOT_INFER*/[/*error:LIST_ELEMENT_TYPE_NOT_ASSIGNABLE*/"hello"]);
  new F1(a: /*info:INFERRED_TYPE_LITERAL,error:COULD_NOT_INFER*/[/*error:LIST_ELEMENT_TYPE_NOT_ASSIGNABLE*/"hello", 3]);

  new F2(/*info:INFERRED_TYPE_LITERAL*/[]);
  new F2(/*info:INFERRED_TYPE_LITERAL*/[3]);
  new F2(/*info:INFERRED_TYPE_LITERAL,error:COULD_NOT_INFER*/[/*error:LIST_ELEMENT_TYPE_NOT_ASSIGNABLE*/"hello"]);
  new F2(/*info:INFERRED_TYPE_LITERAL,error:COULD_NOT_INFER*/[/*error:LIST_ELEMENT_TYPE_NOT_ASSIGNABLE*/"hello", 3]);

  new F3(/*info:INFERRED_TYPE_LITERAL*/[]);
  new F3(/*info:INFERRED_TYPE_LITERAL*/[/*info:INFERRED_TYPE_LITERAL*/[3]]);
  new F3(/*info:INFERRED_TYPE_LITERAL*/[/*info:INFERRED_TYPE_LITERAL,error:COULD_NOT_INFER*/[/*error:LIST_ELEMENT_TYPE_NOT_ASSIGNABLE*/"hello"]]);
  new F3(/*info:INFERRED_TYPE_LITERAL*/[/*info:INFERRED_TYPE_LITERAL,error:COULD_NOT_INFER*/[/*error:LIST_ELEMENT_TYPE_NOT_ASSIGNABLE*/"hello"],
                   /*info:INFERRED_TYPE_LITERAL*/[3]]);

  new F4(a: /*info:INFERRED_TYPE_LITERAL*/[]);
  new F4(a: /*info:INFERRED_TYPE_LITERAL*/[/*info:INFERRED_TYPE_LITERAL*/[3]]);
  new F4(a: /*info:INFERRED_TYPE_LITERAL*/[/*info:INFERRED_TYPE_LITERAL,error:COULD_NOT_INFER*/[/*error:LIST_ELEMENT_TYPE_NOT_ASSIGNABLE*/"hello"]]);
  new F4(a: /*info:INFERRED_TYPE_LITERAL*/[/*info:INFERRED_TYPE_LITERAL,error:COULD_NOT_INFER*/[/*error:LIST_ELEMENT_TYPE_NOT_ASSIGNABLE*/"hello"],
                      /*info:INFERRED_TYPE_LITERAL*/[3]]);
}
''');
  }

  void test_downwardsInferenceOnFunctionArguments_inferDownwards() {
    checkFile('''
void f0(List<int> a) {}
void f1({List<int> a}) {}
void f2(Iterable<int> a) {}
void f3(Iterable<Iterable<int>> a) {}
void f4({Iterable<Iterable<int>> a}) {}
void main() {
  f0(/*info:INFERRED_TYPE_LITERAL*/[]);
  f0(/*info:INFERRED_TYPE_LITERAL*/[3]);
  f0(/*info:INFERRED_TYPE_LITERAL,error:COULD_NOT_INFER*/[/*error:LIST_ELEMENT_TYPE_NOT_ASSIGNABLE*/"hello"]);
  f0(/*info:INFERRED_TYPE_LITERAL,error:COULD_NOT_INFER*/[/*error:LIST_ELEMENT_TYPE_NOT_ASSIGNABLE*/"hello", 3]);

  f1(a: /*info:INFERRED_TYPE_LITERAL*/[]);
  f1(a: /*info:INFERRED_TYPE_LITERAL*/[3]);
  f1(a: /*info:INFERRED_TYPE_LITERAL,error:COULD_NOT_INFER*/[/*error:LIST_ELEMENT_TYPE_NOT_ASSIGNABLE*/"hello"]);
  f1(a: /*info:INFERRED_TYPE_LITERAL,error:COULD_NOT_INFER*/[/*error:LIST_ELEMENT_TYPE_NOT_ASSIGNABLE*/"hello", 3]);

  f2(/*info:INFERRED_TYPE_LITERAL*/[]);
  f2(/*info:INFERRED_TYPE_LITERAL*/[3]);
  f2(/*info:INFERRED_TYPE_LITERAL,error:COULD_NOT_INFER*/[/*error:LIST_ELEMENT_TYPE_NOT_ASSIGNABLE*/"hello"]);
  f2(/*info:INFERRED_TYPE_LITERAL,error:COULD_NOT_INFER*/[/*error:LIST_ELEMENT_TYPE_NOT_ASSIGNABLE*/"hello", 3]);

  f3(/*info:INFERRED_TYPE_LITERAL*/[]);
  f3(/*info:INFERRED_TYPE_LITERAL*/[/*info:INFERRED_TYPE_LITERAL*/[3]]);
  f3(/*info:INFERRED_TYPE_LITERAL*/[/*info:INFERRED_TYPE_LITERAL,error:COULD_NOT_INFER*/[/*error:LIST_ELEMENT_TYPE_NOT_ASSIGNABLE*/"hello"]]);
  f3(/*info:INFERRED_TYPE_LITERAL*/[/*info:INFERRED_TYPE_LITERAL,error:COULD_NOT_INFER*/[/*error:LIST_ELEMENT_TYPE_NOT_ASSIGNABLE*/"hello"], /*info:INFERRED_TYPE_LITERAL*/[3]]);

  f4(a: /*info:INFERRED_TYPE_LITERAL*/[]);
  f4(a: /*info:INFERRED_TYPE_LITERAL*/[/*info:INFERRED_TYPE_LITERAL*/[3]]);
  f4(a: /*info:INFERRED_TYPE_LITERAL*/[/*info:INFERRED_TYPE_LITERAL,error:COULD_NOT_INFER*/[/*error:LIST_ELEMENT_TYPE_NOT_ASSIGNABLE*/"hello"]]);
  f4(a: /*info:INFERRED_TYPE_LITERAL*/[/*info:INFERRED_TYPE_LITERAL,error:COULD_NOT_INFER*/[/*error:LIST_ELEMENT_TYPE_NOT_ASSIGNABLE*/"hello"], /*info:INFERRED_TYPE_LITERAL*/[3]]);
}
''');
  }

  void test_downwardsInferenceOnFunctionExpressions() {
    checkFile('''
typedef T Function2<S, T>(S x);

void main () {
  {
    Function2<int, String> l0 = /*info:INFERRED_TYPE_CLOSURE*/(int x) => null;
    Function2<int, String> l1 = (int x) => "hello";
    Function2<int, String> l2 = /*error:INVALID_ASSIGNMENT*/(String x) => "hello";
    Function2<int, String> l3 = /*error:INVALID_ASSIGNMENT*/(int x) => 3;
    Function2<int, String> l4 = /*info:INFERRED_TYPE_CLOSURE*/(int x) {return /*error:RETURN_OF_INVALID_TYPE*/3;};
  }
  {
    Function2<int, String> l0 = /*info:INFERRED_TYPE_CLOSURE*/(x) => null;
    Function2<int, String> l1 = /*info:INFERRED_TYPE_CLOSURE*/(x) => "hello";
    Function2<int, String> l2 = /*info:INFERRED_TYPE_CLOSURE, error:INVALID_ASSIGNMENT*/(x) => 3;
    Function2<int, String> l3 = /*info:INFERRED_TYPE_CLOSURE*/(x) {return /*error:RETURN_OF_INVALID_TYPE*/3;};
    Function2<int, String> l4 = /*info:INFERRED_TYPE_CLOSURE*/(x) {return /*error:RETURN_OF_INVALID_TYPE*/x;};
  }
  {
    Function2<int, List<String>> l0 = /*info:INFERRED_TYPE_CLOSURE*/(int x) => null;
    Function2<int, List<String>> l1 = (int x) => /*info:INFERRED_TYPE_LITERAL*/["hello"];
    Function2<int, List<String>> l2 = /*error:INVALID_ASSIGNMENT*/(String x) => /*info:INFERRED_TYPE_LITERAL*/["hello"];
    Function2<int, List<String>> l3 = (int x) => /*info:INFERRED_TYPE_LITERAL,error:COULD_NOT_INFER*/[/*error:LIST_ELEMENT_TYPE_NOT_ASSIGNABLE*/3];
    Function2<int, List<String>> l4 = /*info:INFERRED_TYPE_CLOSURE*/(int x) {return /*info:INFERRED_TYPE_LITERAL,error:COULD_NOT_INFER*/[/*error:LIST_ELEMENT_TYPE_NOT_ASSIGNABLE*/3];};
  }
  {
    Function2<int, int> l0 = /*info:INFERRED_TYPE_CLOSURE*/(x) => x;
    Function2<int, int> l1 = /*info:INFERRED_TYPE_CLOSURE*/(x) => x+1;
    Function2<int, String> l2 = /*info:INFERRED_TYPE_CLOSURE, error:INVALID_ASSIGNMENT*/(x) => x;
    Function2<int, String> l3 = /*info:INFERRED_TYPE_CLOSURE*/(x) => /*info:DYNAMIC_CAST, info:DYNAMIC_INVOKE*/x.substring(3);
    Function2<String, String> l4 = /*info:INFERRED_TYPE_CLOSURE*/(x) => x.substring(3);
  }
}
''');
  }

  void test_downwardsInferenceOnFunctionOfTUsingTheT() {
    checkFile('''
void main () {
  {
    T f<T>(T x) => null;
    var v1 = f;
    v1 = /*info:INFERRED_TYPE_CLOSURE*/<S>(x) => x;
  }
  {
    List<T> f<T>(T x) => null;
    var v2 = f;
    v2 = /*info:INFERRED_TYPE_CLOSURE*/<S>(x) => /*info:INFERRED_TYPE_LITERAL*/[x];
    Iterable<int> r = v2(42);
    Iterable<String> s = v2('hello');
    Iterable<List<int>> t = v2(<int>[]);
    Iterable<num> u = v2(42);
    Iterable<num> v = v2<num>(42);
  }
}
''');
  }

  void test_downwardsInferenceOnFunctionOfTUsingTheT_comment() {
    checkFile('''
void main () {
  {
    /*=T*/ f/*<T>*/(/*=T*/ x) => null;
    var v1 = f;
    v1 = /*info:INFERRED_TYPE_CLOSURE*//*<S>*/(x) => x;
  }
  {
    /*=List<T>*/ f/*<T>*/(/*=T*/ x) => null;
    var v2 = f;
    v2 = /*info:INFERRED_TYPE_CLOSURE*//*<S>*/(x) => /*info:INFERRED_TYPE_LITERAL*/[x];
    Iterable<int> r = v2(42);
    Iterable<String> s = v2('hello');
    Iterable<List<int>> t = v2(<int>[]);
    Iterable<num> u = v2(42);
    Iterable<num> v = v2/*<num>*/(42);
  }
}
''');
  }

  void test_downwardsInferenceOnGenericConstructorArguments_inferDownwards() {
    checkFile('''
class F0<T> {
  F0(List<T> a) {}
}
class F1<T> {
  F1({List<T> a}) {}
}
class F2<T> {
  F2(Iterable<T> a) {}
}
class F3<T> {
  F3(Iterable<Iterable<T>> a) {}
}
class F4<T> {
  F4({Iterable<Iterable<T>> a}) {}
}
void main() {
  new F0<int>(/*info:INFERRED_TYPE_LITERAL*/[]);
  new F0<int>(/*info:INFERRED_TYPE_LITERAL*/[3]);
  new F0<int>(/*info:INFERRED_TYPE_LITERAL,error:COULD_NOT_INFER*/[/*error:LIST_ELEMENT_TYPE_NOT_ASSIGNABLE*/"hello"]);
  new F0<int>(/*info:INFERRED_TYPE_LITERAL,error:COULD_NOT_INFER*/[/*error:LIST_ELEMENT_TYPE_NOT_ASSIGNABLE*/"hello",
                                      3]);

  new F1<int>(a: /*info:INFERRED_TYPE_LITERAL*/[]);
  new F1<int>(a: /*info:INFERRED_TYPE_LITERAL*/[3]);
  new F1<int>(a: /*info:INFERRED_TYPE_LITERAL,error:COULD_NOT_INFER*/[/*error:LIST_ELEMENT_TYPE_NOT_ASSIGNABLE*/"hello"]);
  new F1<int>(a: /*info:INFERRED_TYPE_LITERAL,error:COULD_NOT_INFER*/[/*error:LIST_ELEMENT_TYPE_NOT_ASSIGNABLE*/"hello", 3]);

  new F2<int>(/*info:INFERRED_TYPE_LITERAL*/[]);
  new F2<int>(/*info:INFERRED_TYPE_LITERAL*/[3]);
  new F2<int>(/*info:INFERRED_TYPE_LITERAL,error:COULD_NOT_INFER*/[/*error:LIST_ELEMENT_TYPE_NOT_ASSIGNABLE*/"hello"]);
  new F2<int>(/*info:INFERRED_TYPE_LITERAL,error:COULD_NOT_INFER*/[/*error:LIST_ELEMENT_TYPE_NOT_ASSIGNABLE*/"hello", 3]);

  new F3<int>(/*info:INFERRED_TYPE_LITERAL*/[]);
  new F3<int>(/*info:INFERRED_TYPE_LITERAL*/[/*info:INFERRED_TYPE_LITERAL*/[3]]);
  new F3<int>(/*info:INFERRED_TYPE_LITERAL*/[/*info:INFERRED_TYPE_LITERAL,error:COULD_NOT_INFER*/[/*error:LIST_ELEMENT_TYPE_NOT_ASSIGNABLE*/"hello"]]);
  new F3<int>(/*info:INFERRED_TYPE_LITERAL*/[/*info:INFERRED_TYPE_LITERAL,error:COULD_NOT_INFER*/[/*error:LIST_ELEMENT_TYPE_NOT_ASSIGNABLE*/"hello"],
                   /*info:INFERRED_TYPE_LITERAL*/[3]]);

  new F4<int>(a: /*info:INFERRED_TYPE_LITERAL*/[]);
  new F4<int>(a: /*info:INFERRED_TYPE_LITERAL*/[/*info:INFERRED_TYPE_LITERAL*/[3]]);
  new F4<int>(a: /*info:INFERRED_TYPE_LITERAL*/[/*info:INFERRED_TYPE_LITERAL,error:COULD_NOT_INFER*/[/*error:LIST_ELEMENT_TYPE_NOT_ASSIGNABLE*/"hello"]]);
  new F4<int>(a: /*info:INFERRED_TYPE_LITERAL*/[/*info:INFERRED_TYPE_LITERAL,error:COULD_NOT_INFER*/[/*error:LIST_ELEMENT_TYPE_NOT_ASSIGNABLE*/"hello"],
                      /*info:INFERRED_TYPE_LITERAL*/[3]]);

  new F3(/*info:INFERRED_TYPE_LITERAL*/[]);
  /*info:INFERRED_TYPE_ALLOCATION*/new F3(/*info:INFERRED_TYPE_LITERAL*/[/*info:INFERRED_TYPE_LITERAL*/[3]]);
  /*info:INFERRED_TYPE_ALLOCATION*/new F3(/*info:INFERRED_TYPE_LITERAL*/[/*info:INFERRED_TYPE_LITERAL*/["hello"]]);
  /*info:INFERRED_TYPE_ALLOCATION*/new F3(/*info:INFERRED_TYPE_LITERAL*/[/*info:INFERRED_TYPE_LITERAL*/["hello"],
                                        /*info:INFERRED_TYPE_LITERAL*/[3]]);

  new F4(a: /*info:INFERRED_TYPE_LITERAL*/[]);
  /*info:INFERRED_TYPE_ALLOCATION*/new F4(a: /*info:INFERRED_TYPE_LITERAL*/[/*info:INFERRED_TYPE_LITERAL*/[3]]);
  /*info:INFERRED_TYPE_ALLOCATION*/new F4(a: /*info:INFERRED_TYPE_LITERAL*/[/*info:INFERRED_TYPE_LITERAL*/["hello"]]);
  /*info:INFERRED_TYPE_ALLOCATION*/new F4(a: /*info:INFERRED_TYPE_LITERAL*/[/*info:INFERRED_TYPE_LITERAL*/["hello"],
                                           /*info:INFERRED_TYPE_LITERAL*/[3]]);
}
''');
  }

  void test_downwardsInferenceOnGenericFunctionExpressions() {
    checkFile('''
void main () {
  {
    String f<S>(int x) => null;
    var v = f;
    v = /*info:INFERRED_TYPE_CLOSURE*/<T>(int x) => null;
    v = <T>(int x) => "hello";
    v = /*error:INVALID_ASSIGNMENT*/<T>(String x) => "hello";
    v = /*error:INVALID_ASSIGNMENT*/<T>(int x) => 3;
    v = /*info:INFERRED_TYPE_CLOSURE*/<T>(int x) {return /*error:RETURN_OF_INVALID_TYPE*/3;};
  }
  {
    String f<S>(int x) => null;
    var v = f;
    v = /*info:INFERRED_TYPE_CLOSURE, info:INFERRED_TYPE_CLOSURE*/<T>(x) => null;
    v = /*info:INFERRED_TYPE_CLOSURE*/<T>(x) => "hello";
    v = /*info:INFERRED_TYPE_CLOSURE, error:INVALID_ASSIGNMENT*/<T>(x) => 3;
    v = /*info:INFERRED_TYPE_CLOSURE, info:INFERRED_TYPE_CLOSURE*/<T>(x) {return /*error:RETURN_OF_INVALID_TYPE*/3;};
    v = /*info:INFERRED_TYPE_CLOSURE, info:INFERRED_TYPE_CLOSURE*/<T>(x) {return /*error:RETURN_OF_INVALID_TYPE*/x;};
  }
  {
    List<String> f<S>(int x) => null;
    var v = f;
    v = /*info:INFERRED_TYPE_CLOSURE*/<T>(int x) => null;
    v = <T>(int x) => /*info:INFERRED_TYPE_LITERAL*/["hello"];
    v = /*error:INVALID_ASSIGNMENT*/<T>(String x) => /*info:INFERRED_TYPE_LITERAL*/["hello"];
    v = <T>(int x) => /*info:INFERRED_TYPE_LITERAL,error:COULD_NOT_INFER*/[/*error:LIST_ELEMENT_TYPE_NOT_ASSIGNABLE*/3];
    v = /*info:INFERRED_TYPE_CLOSURE*/<T>(int x) {return /*info:INFERRED_TYPE_LITERAL,error:COULD_NOT_INFER*/[/*error:LIST_ELEMENT_TYPE_NOT_ASSIGNABLE*/3];};
  }
  {
    int int2int<S>(int x) => null;
    String int2String<T>(int x) => null;
    String string2String<T>(String x) => null;
    var x = int2int;
    x = /*info:INFERRED_TYPE_CLOSURE*/<T>(x) => x;
    x = /*info:INFERRED_TYPE_CLOSURE*/<T>(x) => x+1;
    var y = int2String;
    y = /*info:INFERRED_TYPE_CLOSURE, error:INVALID_ASSIGNMENT*/<T>(x) => x;
    y = /*info:INFERRED_TYPE_CLOSURE, info:INFERRED_TYPE_CLOSURE*/<T>(x) => /*info:DYNAMIC_INVOKE, info:DYNAMIC_CAST*/x.substring(3);
    var z = string2String;
    z = /*info:INFERRED_TYPE_CLOSURE*/<T>(x) => x.substring(3);
  }
}
''');
  }

  void test_downwardsInferenceOnGenericFunctionExpressions_comment() {
    checkFile('''
void main () {
  {
    String f/*<S>*/(int x) => null;
    var v = f;
    v = /*info:INFERRED_TYPE_CLOSURE*//*<T>*/(int x) => null;
    v = /*<T>*/(int x) => "hello";
    v = /*error:INVALID_ASSIGNMENT*//*<T>*/(String x) => "hello";
    v = /*error:INVALID_ASSIGNMENT*//*<T>*/(int x) => 3;
    v = /*info:INFERRED_TYPE_CLOSURE*//*<T>*/(int x) {return /*error:RETURN_OF_INVALID_TYPE*/3;};
  }
  {
    String f/*<S>*/(int x) => null;
    var v = f;
    v = /*info:INFERRED_TYPE_CLOSURE, info:INFERRED_TYPE_CLOSURE*//*<T>*/(x) => null;
    v = /*info:INFERRED_TYPE_CLOSURE*//*<T>*/(x) => "hello";
    v = /*info:INFERRED_TYPE_CLOSURE, error:INVALID_ASSIGNMENT*//*<T>*/(x) => 3;
    v = /*info:INFERRED_TYPE_CLOSURE, info:INFERRED_TYPE_CLOSURE*//*<T>*/(x) {return /*error:RETURN_OF_INVALID_TYPE*/3;};
    v = /*info:INFERRED_TYPE_CLOSURE, info:INFERRED_TYPE_CLOSURE*//*<T>*/(x) {return /*error:RETURN_OF_INVALID_TYPE*/x;};
  }
  {
    List<String> f/*<S>*/(int x) => null;
    var v = f;
    v = /*info:INFERRED_TYPE_CLOSURE*//*<T>*/(int x) => null;
    v = /*<T>*/(int x) => /*info:INFERRED_TYPE_LITERAL*/["hello"];
    v = /*error:INVALID_ASSIGNMENT*//*<T>*/(String x) => /*info:INFERRED_TYPE_LITERAL*/["hello"];
    v = /*<T>*/(int x) => /*info:INFERRED_TYPE_LITERAL,error:COULD_NOT_INFER*/[/*error:LIST_ELEMENT_TYPE_NOT_ASSIGNABLE*/3];
    v = /*info:INFERRED_TYPE_CLOSURE*//*<T>*/(int x) {return /*info:INFERRED_TYPE_LITERAL,error:COULD_NOT_INFER*/[/*error:LIST_ELEMENT_TYPE_NOT_ASSIGNABLE*/3];};
  }
  {
    int int2int/*<S>*/(int x) => null;
    String int2String/*<T>*/(int x) => null;
    String string2String/*<T>*/(String x) => null;
    var x = int2int;
    x = /*info:INFERRED_TYPE_CLOSURE*//*<T>*/(x) => x;
    x = /*info:INFERRED_TYPE_CLOSURE*//*<T>*/(x) => x+1;
    var y = int2String;
    y = /*info:INFERRED_TYPE_CLOSURE, error:INVALID_ASSIGNMENT*//*<T>*/(x) => x;
    y = /*info:INFERRED_TYPE_CLOSURE, info:INFERRED_TYPE_CLOSURE*//*<T>*/(x) => /*info:DYNAMIC_INVOKE, info:DYNAMIC_CAST*/x.substring(3);
    var z = string2String;
    z = /*info:INFERRED_TYPE_CLOSURE*//*<T>*/(x) => x.substring(3);
  }
}
''');
  }

  void test_downwardsInferenceOnInstanceCreations_inferDownwards() {
    checkFile('''
class A<S, T> {
  S x;
  T y;
  A(this.x, this.y);
  A.named(this.x, this.y);
}

class B<S, T> extends A<T, S> {
  B(S y, T x) : super(x, y);
  B.named(S y, T x) : super.named(x, y);
}

class C<S> extends B<S, S> {
  C(S a) : super(a, a);
  C.named(S a) : super.named(a, a);
}

class D<S, T> extends B<T, int> {
  D(T a) : super(a, 3);
  D.named(T a) : super.named(a, 3);
}

class E<S, T> extends A<C<S>, T> {
  E(T a) : super(null, a);
}

class F<S, T> extends A<S, T> {
  F(S x, T y, {List<S> a, List<T> b}) : super(x, y);
  F.named(S x, T y, [S a, T b]) : super(a, b);
}

void main() {
  {
    A<int, String> a0 = /*info:INFERRED_TYPE_ALLOCATION*/new A(3, "hello");
    A<int, String> a1 = /*info:INFERRED_TYPE_ALLOCATION*/new A.named(3, "hello");
    A<int, String> a2 = new A<int, String>(3, "hello");
    A<int, String> a3 = new A<int, String>.named(3, "hello");
    A<int, String> a4 = /*error:INVALID_CAST_NEW_EXPR*/new A<int, dynamic>(3, "hello");
    A<int, String> a5 = /*error:INVALID_CAST_NEW_EXPR*/new A<dynamic, dynamic>.named(3, "hello");
  }
  {
    A<int, String> a0 = /*info:INFERRED_TYPE_ALLOCATION*/new /*error:COULD_NOT_INFER,error:COULD_NOT_INFER*/A(
        /*error:ARGUMENT_TYPE_NOT_ASSIGNABLE*/"hello",
        /*error:ARGUMENT_TYPE_NOT_ASSIGNABLE*/3);
    A<int, String> a1 = /*info:INFERRED_TYPE_ALLOCATION*/new /*error:COULD_NOT_INFER,error:COULD_NOT_INFER*/A.named(
        /*error:ARGUMENT_TYPE_NOT_ASSIGNABLE*/"hello",
        /*error:ARGUMENT_TYPE_NOT_ASSIGNABLE*/3);
  }
  {
    A<int, String> a0 = /*info:INFERRED_TYPE_ALLOCATION*/new B("hello", 3);
    A<int, String> a1 = /*info:INFERRED_TYPE_ALLOCATION*/new B.named("hello", 3);
    A<int, String> a2 = new B<String, int>("hello", 3);
    A<int, String> a3 = new B<String, int>.named("hello", 3);
    A<int, String> a4 = /*error:INVALID_ASSIGNMENT*/new B<String, dynamic>("hello", 3);
    A<int, String> a5 = /*error:INVALID_ASSIGNMENT*/new B<dynamic, dynamic>.named("hello", 3);
  }
  {
    A<int, String> a0 = /*info:INFERRED_TYPE_ALLOCATION*/new /*error:COULD_NOT_INFER,error:COULD_NOT_INFER*/B(
        /*error:ARGUMENT_TYPE_NOT_ASSIGNABLE*/3,
        /*error:ARGUMENT_TYPE_NOT_ASSIGNABLE*/"hello");
    A<int, String> a1 = /*info:INFERRED_TYPE_ALLOCATION*/new /*error:COULD_NOT_INFER,error:COULD_NOT_INFER*/B.named(
        /*error:ARGUMENT_TYPE_NOT_ASSIGNABLE*/3,
        /*error:ARGUMENT_TYPE_NOT_ASSIGNABLE*/"hello");
  }
  {
    A<int, int> a0 = /*info:INFERRED_TYPE_ALLOCATION*/new C(3);
    A<int, int> a1 = /*info:INFERRED_TYPE_ALLOCATION*/new C.named(3);
    A<int, int> a2 = new C<int>(3);
    A<int, int> a3 = new C<int>.named(3);
    A<int, int> a4 = /*error:INVALID_ASSIGNMENT*/new C<dynamic>(3);
    A<int, int> a5 = /*error:INVALID_ASSIGNMENT*/new C<dynamic>.named(3);
  }
  {
    A<int, int> a0 = /*info:INFERRED_TYPE_ALLOCATION*/new /*error:COULD_NOT_INFER*/C(
        /*error:ARGUMENT_TYPE_NOT_ASSIGNABLE*/"hello");
    A<int, int> a1 = /*info:INFERRED_TYPE_ALLOCATION*/new /*error:COULD_NOT_INFER*/C.named(
        /*error:ARGUMENT_TYPE_NOT_ASSIGNABLE*/"hello");
  }
  {
    A<int, String> a0 = /*info:INFERRED_TYPE_ALLOCATION*/new D("hello");
    A<int, String> a1 = /*info:INFERRED_TYPE_ALLOCATION*/new D.named("hello");
    A<int, String> a2 = new D<int, String>("hello");
    A<int, String> a3 = new D<String, String>.named("hello");
    A<int, String> a4 = /*error:INVALID_ASSIGNMENT*/new D<num, dynamic>("hello");
    A<int, String> a5 = /*error:INVALID_ASSIGNMENT*/new D<dynamic, dynamic>.named("hello");
  }
  {
    A<int, String> a0 = /*info:INFERRED_TYPE_ALLOCATION*/new /*error:COULD_NOT_INFER*/D(
        /*error:ARGUMENT_TYPE_NOT_ASSIGNABLE*/3);
    A<int, String> a1 = /*info:INFERRED_TYPE_ALLOCATION*/new /*error:COULD_NOT_INFER*/D.named(
        /*error:ARGUMENT_TYPE_NOT_ASSIGNABLE*/3);
  }
  {
    A<C<int>, String> a0 = /*info:INFERRED_TYPE_ALLOCATION*/new E("hello");
  }
  { // Check named and optional arguments
    A<int, String> a0 = /*info:INFERRED_TYPE_ALLOCATION*/new F(3, "hello",
        a: /*info:INFERRED_TYPE_LITERAL*/[3],
        b: /*info:INFERRED_TYPE_LITERAL*/["hello"]);
    A<int, String> a1 = /*info:INFERRED_TYPE_ALLOCATION*/new F(3, "hello",
        a: /*info:INFERRED_TYPE_LITERAL,error:COULD_NOT_INFER*/[/*error:LIST_ELEMENT_TYPE_NOT_ASSIGNABLE*/"hello"],
        b: /*info:INFERRED_TYPE_LITERAL,error:COULD_NOT_INFER*/[/*error:LIST_ELEMENT_TYPE_NOT_ASSIGNABLE*/3]);
    A<int, String> a2 = /*info:INFERRED_TYPE_ALLOCATION*/new F.named(3, "hello", 3, "hello");
    A<int, String> a3 = /*info:INFERRED_TYPE_ALLOCATION*/new F.named(3, "hello");
    A<int, String> a4 = /*info:INFERRED_TYPE_ALLOCATION*/new /*error:COULD_NOT_INFER,error:COULD_NOT_INFER*/F.named(3, "hello",
        /*error:ARGUMENT_TYPE_NOT_ASSIGNABLE*/"hello", /*error:ARGUMENT_TYPE_NOT_ASSIGNABLE*/3);
    A<int, String> a5 = /*info:INFERRED_TYPE_ALLOCATION*/new /*error:COULD_NOT_INFER*/F.named(3, "hello",
        /*error:ARGUMENT_TYPE_NOT_ASSIGNABLE*/"hello");
  }
}
  ''');
  }

  void test_downwardsInferenceOnListLiterals_inferDownwards() {
    checkFile('''
void foo([List<String> list1 = /*info:INFERRED_TYPE_LITERAL*/const [],
          List<String> list2 = /*info:INFERRED_TYPE_LITERAL,error:COULD_NOT_INFER*/const [/*error:LIST_ELEMENT_TYPE_NOT_ASSIGNABLE,error:LIST_ELEMENT_TYPE_NOT_ASSIGNABLE*/42]]) {
}

void main() {
  {
    List<int> l0 = /*info:INFERRED_TYPE_LITERAL*/[];
    List<int> l1 = /*info:INFERRED_TYPE_LITERAL*/[3];
    List<int> l2 = /*info:INFERRED_TYPE_LITERAL,error:COULD_NOT_INFER*/[/*error:LIST_ELEMENT_TYPE_NOT_ASSIGNABLE*/"hello"];
    List<int> l3 = /*info:INFERRED_TYPE_LITERAL,error:COULD_NOT_INFER*/[/*error:LIST_ELEMENT_TYPE_NOT_ASSIGNABLE*/"hello", 3];
  }
  {
    List<dynamic> l0 = [];
    List<dynamic> l1 = /*info:INFERRED_TYPE_LITERAL*/[3];
    List<dynamic> l2 = /*info:INFERRED_TYPE_LITERAL*/["hello"];
    List<dynamic> l3 = /*info:INFERRED_TYPE_LITERAL*/["hello", 3];
  }
  {
    List<int> l0 = /*error:INVALID_CAST_LITERAL_LIST*/<num>[];
    List<int> l1 = /*error:INVALID_CAST_LITERAL_LIST*/<num>[3];
    List<int> l2 = /*error:INVALID_CAST_LITERAL_LIST*/<num>[/*error:LIST_ELEMENT_TYPE_NOT_ASSIGNABLE*/"hello"];
    List<int> l3 = /*error:INVALID_CAST_LITERAL_LIST*/<num>[/*error:LIST_ELEMENT_TYPE_NOT_ASSIGNABLE*/"hello", 3];
  }
  {
    Iterable<int> i0 = /*info:INFERRED_TYPE_LITERAL*/[];
    Iterable<int> i1 = /*info:INFERRED_TYPE_LITERAL*/[3];
    Iterable<int> i2 = /*info:INFERRED_TYPE_LITERAL,error:COULD_NOT_INFER*/[/*error:LIST_ELEMENT_TYPE_NOT_ASSIGNABLE*/"hello"];
    Iterable<int> i3 = /*info:INFERRED_TYPE_LITERAL,error:COULD_NOT_INFER*/[/*error:LIST_ELEMENT_TYPE_NOT_ASSIGNABLE*/"hello", 3];
  }
  {
    const List<int> c0 = /*info:INFERRED_TYPE_LITERAL*/const [];
    const List<int> c1 = /*info:INFERRED_TYPE_LITERAL*/const [3];
    const List<int> c2 = /*info:INFERRED_TYPE_LITERAL,error:COULD_NOT_INFER*/const [/*error:LIST_ELEMENT_TYPE_NOT_ASSIGNABLE,error:LIST_ELEMENT_TYPE_NOT_ASSIGNABLE*/"hello"];
    const List<int> c3 = /*info:INFERRED_TYPE_LITERAL,error:COULD_NOT_INFER*/const [/*error:LIST_ELEMENT_TYPE_NOT_ASSIGNABLE,error:LIST_ELEMENT_TYPE_NOT_ASSIGNABLE*/"hello", 3];
  }
}
''');
  }

  void test_downwardsInferenceOnListLiterals_inferIfValueTypesMatchContext() {
    checkFile(r'''
class DartType {}
typedef void Asserter<T>(T type);
typedef Asserter<T> AsserterBuilder<S, T>(S arg);

Asserter<DartType> _isInt;
Asserter<DartType> _isString;

abstract class C {
  static AsserterBuilder<List<Asserter<DartType>>, DartType> assertBOf;
  static AsserterBuilder<List<Asserter<DartType>>, DartType> get assertCOf => null;

  AsserterBuilder<List<Asserter<DartType>>, DartType> assertAOf;
  AsserterBuilder<List<Asserter<DartType>>, DartType> get assertDOf;

  method(AsserterBuilder<List<Asserter<DartType>>, DartType> assertEOf) {
    assertAOf(/*info:INFERRED_TYPE_LITERAL*/[_isInt, _isString]);
    assertBOf(/*info:INFERRED_TYPE_LITERAL*/[_isInt, _isString]);
    assertCOf(/*info:INFERRED_TYPE_LITERAL*/[_isInt, _isString]);
    assertDOf(/*info:INFERRED_TYPE_LITERAL*/[_isInt, _isString]);
    assertEOf(/*info:INFERRED_TYPE_LITERAL*/[_isInt, _isString]);
  }
  }

  abstract class G<T> {
  AsserterBuilder<List<Asserter<DartType>>, DartType> assertAOf;
  AsserterBuilder<List<Asserter<DartType>>, DartType> get assertDOf;

  method(AsserterBuilder<List<Asserter<DartType>>, DartType> assertEOf) {
    assertAOf(/*info:INFERRED_TYPE_LITERAL*/[_isInt, _isString]);
    this.assertAOf(/*info:INFERRED_TYPE_LITERAL*/[_isInt, _isString]);
    this.assertDOf(/*info:INFERRED_TYPE_LITERAL*/[_isInt, _isString]);
    assertEOf(/*info:INFERRED_TYPE_LITERAL*/[_isInt, _isString]);
  }
}

AsserterBuilder<List<Asserter<DartType>>, DartType> assertBOf;
AsserterBuilder<List<Asserter<DartType>>, DartType> get assertCOf => null;

main() {
  AsserterBuilder<List<Asserter<DartType>>, DartType> assertAOf;
  assertAOf(/*info:INFERRED_TYPE_LITERAL*/[_isInt, _isString]);
  assertBOf(/*info:INFERRED_TYPE_LITERAL*/[_isInt, _isString]);
  assertCOf(/*info:INFERRED_TYPE_LITERAL*/[_isInt, _isString]);
  C.assertBOf(/*info:INFERRED_TYPE_LITERAL*/[_isInt, _isString]);
  C.assertCOf(/*info:INFERRED_TYPE_LITERAL*/[_isInt, _isString]);

  C c;
  c.assertAOf(/*info:INFERRED_TYPE_LITERAL*/[_isInt, _isString]);
  c.assertDOf(/*info:INFERRED_TYPE_LITERAL*/[_isInt, _isString]);

  G<int> g;
  g.assertAOf(/*info:INFERRED_TYPE_LITERAL*/[_isInt, _isString]);
  g.assertDOf(/*info:INFERRED_TYPE_LITERAL*/[_isInt, _isString]);
}
  ''');
  }

  void test_downwardsInferenceOnMapLiterals() {
    checkFile('''
void foo([Map<int, String> m1 = /*info:INFERRED_TYPE_LITERAL*/const {1: "hello"},
    Map<int, String> m2 = /*info:INFERRED_TYPE_LITERAL,error:COULD_NOT_INFER*/const {
      // One error is from type checking and the other is from const evaluation.
      /*error:MAP_KEY_TYPE_NOT_ASSIGNABLE,error:MAP_KEY_TYPE_NOT_ASSIGNABLE*/"hello":
          "world"
    }]) {
}
void main() {
  {
    Map<int, String> l0 = /*info:INFERRED_TYPE_LITERAL*/{};
    Map<int, String> l1 = /*info:INFERRED_TYPE_LITERAL*/{3: "hello"};
    Map<int, String> l2 = /*info:INFERRED_TYPE_LITERAL,error:COULD_NOT_INFER*/{
      /*error:MAP_KEY_TYPE_NOT_ASSIGNABLE*/"hello": "hello"
    };
    Map<int, String> l3 = /*info:INFERRED_TYPE_LITERAL,error:COULD_NOT_INFER*/{
      3: /*error:MAP_VALUE_TYPE_NOT_ASSIGNABLE*/3
    };
    Map<int, String> l4 = /*info:INFERRED_TYPE_LITERAL,error:COULD_NOT_INFER,error:COULD_NOT_INFER*/{
      3: "hello",
      /*error:MAP_KEY_TYPE_NOT_ASSIGNABLE*/"hello":
          /*error:MAP_VALUE_TYPE_NOT_ASSIGNABLE*/3
    };
  }
  {
    Map<dynamic, dynamic> l0 = {};
    Map<dynamic, dynamic> l1 = /*info:INFERRED_TYPE_LITERAL*/{3: "hello"};
    Map<dynamic, dynamic> l2 = /*info:INFERRED_TYPE_LITERAL*/{"hello": "hello"};
    Map<dynamic, dynamic> l3 = /*info:INFERRED_TYPE_LITERAL*/{3: 3};
    Map<dynamic, dynamic> l4 = /*info:INFERRED_TYPE_LITERAL*/{3:"hello", "hello": 3};
  }
  {
    Map<dynamic, String> l0 = /*info:INFERRED_TYPE_LITERAL*/{};
    Map<dynamic, String> l1 = /*info:INFERRED_TYPE_LITERAL*/{3: "hello"};
    Map<dynamic, String> l2 = /*info:INFERRED_TYPE_LITERAL*/{"hello": "hello"};
    Map<dynamic, String> l3 = /*info:INFERRED_TYPE_LITERAL,error:COULD_NOT_INFER*/{
      3: /*error:MAP_VALUE_TYPE_NOT_ASSIGNABLE*/3
    };
    Map<dynamic, String> l4 = /*info:INFERRED_TYPE_LITERAL,error:COULD_NOT_INFER*/{
      3: "hello",
      "hello": /*error:MAP_VALUE_TYPE_NOT_ASSIGNABLE*/3
    };
  }
  {
    Map<int, dynamic> l0 = /*info:INFERRED_TYPE_LITERAL*/{};
    Map<int, dynamic> l1 = /*info:INFERRED_TYPE_LITERAL*/{3: "hello"};
    Map<int, dynamic> l2 = /*info:INFERRED_TYPE_LITERAL,error:COULD_NOT_INFER*/{
      /*error:MAP_KEY_TYPE_NOT_ASSIGNABLE*/"hello": "hello"
    };
    Map<int, dynamic> l3 = /*info:INFERRED_TYPE_LITERAL*/{3: 3};
    Map<int, dynamic> l4 = /*info:INFERRED_TYPE_LITERAL,error:COULD_NOT_INFER*/{
      3:"hello",
      /*error:MAP_KEY_TYPE_NOT_ASSIGNABLE*/"hello": 3
    };
  }
  {
    Map<int, String> l0 = /*error:INVALID_CAST_LITERAL_MAP*/<num, dynamic>{};
    Map<int, String> l1 = /*error:INVALID_CAST_LITERAL_MAP*/<num, dynamic>{3: "hello"};
    Map<int, String> l3 = /*error:INVALID_CAST_LITERAL_MAP*/<num, dynamic>{3: 3};
  }
  {
    const Map<int, String> l0 = /*info:INFERRED_TYPE_LITERAL*/const {};
    const Map<int, String> l1 = /*info:INFERRED_TYPE_LITERAL*/const {3: "hello"};
    const Map<int, String> l2 = /*info:INFERRED_TYPE_LITERAL,error:COULD_NOT_INFER*/const {
      /*error:MAP_KEY_TYPE_NOT_ASSIGNABLE,error:MAP_KEY_TYPE_NOT_ASSIGNABLE*/"hello":
          "hello"
    };
    const Map<int, String> l3 = /*info:INFERRED_TYPE_LITERAL,error:COULD_NOT_INFER*/const {
      3: /*error:MAP_VALUE_TYPE_NOT_ASSIGNABLE,error:MAP_VALUE_TYPE_NOT_ASSIGNABLE*/3
    };
    const Map<int, String> l4 = /*info:INFERRED_TYPE_LITERAL,error:COULD_NOT_INFER,error:COULD_NOT_INFER*/const {
      3:"hello",
      /*error:MAP_KEY_TYPE_NOT_ASSIGNABLE,error:MAP_KEY_TYPE_NOT_ASSIGNABLE*/"hello":
          /*error:MAP_VALUE_TYPE_NOT_ASSIGNABLE,error:MAP_VALUE_TYPE_NOT_ASSIGNABLE*/3
    };
  }
}
''');
  }

  void test_downwardsInferenceYieldYieldStar() {
    checkFile('''
import 'dart:async';
Stream<List<int>> foo() async* {
  yield /*info:INFERRED_TYPE_LITERAL*/[];
  yield /*error:YIELD_OF_INVALID_TYPE*/new Stream();
  yield* /*error:YIELD_OF_INVALID_TYPE*/[];
  yield* /*info:INFERRED_TYPE_ALLOCATION*/new Stream();
}

Iterable<Map<int, int>> bar() sync* {
  yield /*info:INFERRED_TYPE_LITERAL*/{};
  yield /*error:YIELD_OF_INVALID_TYPE*/new List();
  yield* /*error:YIELD_OF_INVALID_TYPE*/{};
  yield* /*info:INFERRED_TYPE_ALLOCATION*/new List();
}
  ''');
  }

  void test_fieldRefersToStaticGetter() {
    var mainUnit = checkFile('''
class C {
  final x = _x;
  static int get _x => null;
}
''');
    var x = mainUnit.types[0].fields[0];
    expect(x.type.toString(), 'int');
  }

  void test_fieldRefersToTopLevelGetter() {
    var mainUnit = checkFile('''
class C {
  final x = y;
}
int get y => null;
''');
    var x = mainUnit.types[0].fields[0];
    expect(x.type.toString(), 'int');
  }

  void test_futureThen() {
    String build({String declared, String downwards, String upwards}) => '''
import 'dart:async';
class MyFuture<T> implements Future<T> {
  MyFuture() {}
  MyFuture.value(T x) {}
  dynamic noSuchMethod(invocation);
  MyFuture<S> then<S>(dynamic f(T x), {Function onError}) => null;
}

void main() {
  $declared f;
  $downwards<int> t1 = f.then((_) async => await new $upwards<int>.value(3));
  $downwards<int> t2 = f.then(/*info:INFERRED_TYPE_CLOSURE*/(_) async {
     return await new $upwards<int>.value(3);});
  $downwards<int> t3 = f.then((_) async => 3);
  $downwards<int> t4 = f.then(/*info:INFERRED_TYPE_CLOSURE*/(_) async {
    return 3;});
  $downwards<int> t5 = f.then((_) => new $upwards<int>.value(3));
  $downwards<int> t6 = f.then(/*info:INFERRED_TYPE_CLOSURE*/(_) {return new $upwards<int>.value(3);});
  $downwards<int> t7 = f.then((_) async => new $upwards<int>.value(3));
  $downwards<int> t8 = f.then(/*info:INFERRED_TYPE_CLOSURE*/(_) async {
    return new $upwards<int>.value(3);});
}
''';

    checkFile(
        build(declared: "MyFuture", downwards: "Future", upwards: "Future"));
    checkFile(
        build(declared: "MyFuture", downwards: "Future", upwards: "MyFuture"));
    checkFile(
        build(declared: "MyFuture", downwards: "MyFuture", upwards: "Future"));
    checkFile(build(
        declared: "MyFuture", downwards: "MyFuture", upwards: "MyFuture"));
    checkFile(
        build(declared: "Future", downwards: "Future", upwards: "MyFuture"));
    checkFile(
        build(declared: "Future", downwards: "Future", upwards: "Future"));
  }

  void test_futureThen_comment() {
    String build({String declared, String downwards, String upwards}) => '''
import 'dart:async';
class MyFuture<T> implements Future<T> {
  MyFuture() {}
  MyFuture.value(T x) {}
  dynamic noSuchMethod(invocation);
  MyFuture/*<S>*/ then/*<S>*/(dynamic f(T x), {Function onError}) => null;
}

void main() {
  $declared f;
  $downwards<int> t1 = f.then((_) async => await new $upwards<int>.value(3));
  $downwards<int> t2 = f.then(/*info:INFERRED_TYPE_CLOSURE*/(_) async {
     return await new $upwards<int>.value(3);});
  $downwards<int> t3 = f.then((_) async => 3);
  $downwards<int> t4 = f.then(/*info:INFERRED_TYPE_CLOSURE*/(_) async {
    return 3;});
  $downwards<int> t5 = f.then((_) => new $upwards<int>.value(3));
  $downwards<int> t6 = f.then(/*info:INFERRED_TYPE_CLOSURE*/(_) {return new $upwards<int>.value(3);});
  $downwards<int> t7 = f.then((_) async => new $upwards<int>.value(3));
  $downwards<int> t8 = f.then(/*info:INFERRED_TYPE_CLOSURE*/(_) async {
    return new $upwards<int>.value(3);});
}
''';

    checkFile(
        build(declared: "MyFuture", downwards: "Future", upwards: "Future"));
    checkFile(
        build(declared: "MyFuture", downwards: "Future", upwards: "MyFuture"));
    checkFile(
        build(declared: "MyFuture", downwards: "MyFuture", upwards: "Future"));
    checkFile(build(
        declared: "MyFuture", downwards: "MyFuture", upwards: "MyFuture"));
    checkFile(
        build(declared: "Future", downwards: "Future", upwards: "MyFuture"));
    checkFile(
        build(declared: "Future", downwards: "Future", upwards: "Future"));
  }

  void test_futureThen_conditional() {
    String build({String declared, String downwards, String upwards}) => '''
import 'dart:async';
class MyFuture<T> implements Future<T> {
  MyFuture() {}
  MyFuture.value(T x) {}
  dynamic noSuchMethod(invocation);
  MyFuture<S> then<S>(dynamic f(T x), {Function onError}) => null;
}

void main() {
  $declared<bool> f;
  $downwards<int> t1 = f.then(/*info:INFERRED_TYPE_CLOSURE*/
      (x) async => x ? 2 : await new $upwards<int>.value(3));
  $downwards<int> t2 = f.then(/*info:INFERRED_TYPE_CLOSURE,info:INFERRED_TYPE_CLOSURE*/(x) async { // TODO(leafp): Why the duplicate here?
    return await x ? 2 : new $upwards<int>.value(3);});
  $downwards<int> t5 = f.then(/*info:INFERRED_TYPE_CLOSURE*/
      (x) => x ? 2 : new $upwards<int>.value(3));
  $downwards<int> t6 = f.then(/*info:INFERRED_TYPE_CLOSURE*/
      (x) {return x ? 2 : new $upwards<int>.value(3);});
}
''';
    checkFile(
        build(declared: "MyFuture", downwards: "Future", upwards: "Future"));
    checkFile(
        build(declared: "MyFuture", downwards: "Future", upwards: "MyFuture"));
    checkFile(
        build(declared: "MyFuture", downwards: "MyFuture", upwards: "Future"));
    checkFile(build(
        declared: "MyFuture", downwards: "MyFuture", upwards: "MyFuture"));
    checkFile(
        build(declared: "Future", downwards: "Future", upwards: "MyFuture"));
    checkFile(
        build(declared: "Future", downwards: "Future", upwards: "Future"));
  }

  void test_futureThen_conditional_comment() {
    String build({String declared, String downwards, String upwards}) => '''
import 'dart:async';
class MyFuture<T> implements Future<T> {
  MyFuture() {}
  MyFuture.value(T x) {}
  dynamic noSuchMethod(invocation);
  MyFuture/*<S>*/ then/*<S>*/(dynamic f(T x), {Function onError}) => null;
}

void main() {
  $declared<bool> f;
  $downwards<int> t1 = f.then(/*info:INFERRED_TYPE_CLOSURE*/
      (x) async => x ? 2 : await new $upwards<int>.value(3));
  $downwards<int> t2 = f.then(/*info:INFERRED_TYPE_CLOSURE,info:INFERRED_TYPE_CLOSURE*/(x) async { // TODO(leafp): Why the duplicate here?
    return await x ? 2 : new $upwards<int>.value(3);});
  $downwards<int> t5 = f.then(/*info:INFERRED_TYPE_CLOSURE*/
      (x) => x ? 2 : new $upwards<int>.value(3));
  $downwards<int> t6 = f.then(/*info:INFERRED_TYPE_CLOSURE*/
      (x) {return x ? 2 : new $upwards<int>.value(3);});
}
''';
    checkFile(
        build(declared: "MyFuture", downwards: "Future", upwards: "Future"));
    checkFile(
        build(declared: "MyFuture", downwards: "Future", upwards: "MyFuture"));
    checkFile(
        build(declared: "MyFuture", downwards: "MyFuture", upwards: "Future"));
    checkFile(build(
        declared: "MyFuture", downwards: "MyFuture", upwards: "MyFuture"));
    checkFile(
        build(declared: "Future", downwards: "Future", upwards: "MyFuture"));
    checkFile(
        build(declared: "Future", downwards: "Future", upwards: "Future"));
  }

  void test_futureThen_downwardsMethodTarget() {
    // Not working yet, see: https://github.com/dart-lang/sdk/issues/27114
    checkFile(r'''
import 'dart:async';
main() {
  Future<int> f;
  Future<List<int>> b = /*info:ASSIGNMENT_CAST should be pass*/f
      .then(/*info:INFERRED_TYPE_CLOSURE*/(x) => [])
      .whenComplete(/*pass should be info:INFERRED_TYPE_LITERAL*/() {});
  b = f.then(/*info:INFERRED_TYPE_CLOSURE*/(x) => /*info:INFERRED_TYPE_LITERAL*/[]);
}
    ''');
  }

  void test_futureThen_explicitFuture() {
    checkFile(r'''
import "dart:async";
main() {
  Future<int> f;
  var x = f.then<Future<List<int>>>(/*info:INFERRED_TYPE_CLOSURE*/(x) => /*info:INFERRED_TYPE_LITERAL*/[]);
  Future<List<int>> y = x;
}
    ''');
  }

  void test_futureThen_explicitFuture_comment() {
    checkFile(r'''
import "dart:async";
main() {
  Future<int> f;
  var x = f.then/*<Future<List<int>>>*/(/*info:INFERRED_TYPE_CLOSURE*/(x) => /*info:INFERRED_TYPE_LITERAL*/[]);
  Future<List<int>> y = x;
}
    ''');
  }

  void test_futureThen_upwards() {
    // Regression test for https://github.com/dart-lang/sdk/issues/27088.
    String build({String declared, String downwards, String upwards}) => '''
import 'dart:async';
class MyFuture<T> implements Future<T> {
  MyFuture() {}
  MyFuture.value(T x) {}
  dynamic noSuchMethod(invocation);
  MyFuture<S> then<S>(dynamic f(T x), {Function onError}) => null;
}

void main() {
  var f = foo().then((_) => 2.3);
  $downwards<int> f2 = /*error:INVALID_ASSIGNMENT*/f;

  // The unnecessary cast is to illustrate that we inferred <double> for
  // the generic type args, even though we had a return type context.
  $downwards<num> f3 = /*info:UNNECESSARY_CAST*/foo().then(
      (_) => 2.3) as $upwards<double>;
}
$declared foo() => new $declared<int>.value(1);
    ''';
    checkFile(
        build(declared: "MyFuture", downwards: "Future", upwards: "Future"));
    checkFile(build(
        declared: "MyFuture", downwards: "MyFuture", upwards: "MyFuture"));
    checkFile(
        build(declared: "Future", downwards: "Future", upwards: "Future"));
  }

  void test_futureThen_upwards_comment() {
    // Regression test for https://github.com/dart-lang/sdk/issues/27088.
    String build({String declared, String downwards, String upwards}) => '''
import 'dart:async';
class MyFuture<T> implements Future<T> {
  MyFuture() {}
  MyFuture.value(T x) {}
  dynamic noSuchMethod(invocation);
  MyFuture/*<S>*/ then/*<S>*/(dynamic f(T x), {Function onError}) => null;
}

void main() {
  var f = foo().then((_) => 2.3);
  $downwards<int> f2 = /*error:INVALID_ASSIGNMENT*/f;

  // The unnecessary cast is to illustrate that we inferred <double> for
  // the generic type args, even though we had a return type context.
  $downwards<num> f3 = /*info:UNNECESSARY_CAST*/foo().then(
      (_) => 2.3) as $upwards<double>;
}
$declared foo() => new $declared<int>.value(1);
    ''';
    checkFile(
        build(declared: "MyFuture", downwards: "Future", upwards: "Future"));
    checkFile(build(
        declared: "MyFuture", downwards: "MyFuture", upwards: "MyFuture"));
    checkFile(
        build(declared: "Future", downwards: "Future", upwards: "Future"));
  }

  void test_futureThen_upwardsFromBlock() {
    // Regression test for https://github.com/dart-lang/sdk/issues/27113.
    checkFile(r'''
import 'dart:async';
main() {
  Future<int> base;
  var f = base.then(/*info:INFERRED_TYPE_CLOSURE,info:INFERRED_TYPE_CLOSURE*/(x) { return x == 0; });
  var g = base.then(/*info:INFERRED_TYPE_CLOSURE*/(x) => x == 0);
  Future<bool> b = f;
  b = g;
}
    ''');
  }

  void test_futureUnion_asyncConditional() {
    String build({String declared, String downwards, String upwards}) => '''
import 'dart:async';
class MyFuture<T> implements Future<T> {
  MyFuture() {}
  MyFuture.value(T x) {}
  dynamic noSuchMethod(invocation);
  MyFuture<S> then<S>(dynamic f(T x), {Function onError}) => null;
}

$downwards<int> g1(bool x) async {
  return x ? 42 : /*info:INFERRED_TYPE_ALLOCATION*/new $upwards.value(42); }
$downwards<int> g2(bool x) async =>
  x ? 42 : /*info:INFERRED_TYPE_ALLOCATION*/new $upwards.value(42);
$downwards<int> g3(bool x) async {
  var y = x ? 42 : /*info:INFERRED_TYPE_ALLOCATION*/new $upwards.value(42);
  return y;
}
    ''';
    checkFile(build(downwards: "Future", upwards: "Future"));
    checkFile(build(downwards: "Future", upwards: "MyFuture"));
  }

  void test_futureUnion_asyncConditional_comment() {
    String build({String declared, String downwards, String upwards}) => '''
import 'dart:async';
class MyFuture<T> implements Future<T> {
  MyFuture() {}
  MyFuture.value(T x) {}
  dynamic noSuchMethod(invocation);
  MyFuture/*<S>*/ then/*<S>*/(dynamic f(T x), {Function onError}) => null;
}

$downwards<int> g1(bool x) async {
  return x ? 42 : /*info:INFERRED_TYPE_ALLOCATION*/new $upwards.value(42); }
$downwards<int> g2(bool x) async =>
  x ? 42 : /*info:INFERRED_TYPE_ALLOCATION*/new $upwards.value(42);
$downwards<int> g3(bool x) async {
  var y = x ? 42 : /*info:INFERRED_TYPE_ALLOCATION*/new $upwards.value(42);
  return y;
}
    ''';
    checkFile(build(downwards: "Future", upwards: "Future"));
    checkFile(build(downwards: "Future", upwards: "MyFuture"));
  }

  void test_futureUnion_downwards() {
    String build({String declared, String downwards, String upwards}) {
      // TODO(leafp): The use of matchTypes in visitInstanceCreationExpression
      // in the resolver visitor isn't powerful enough to catch this for the
      // subclass.  See the TODO there.
      var allocInfo =
          (upwards == "Future") ? "/*info:INFERRED_TYPE_ALLOCATION*/" : "";
      return '''
import 'dart:async';
class MyFuture<T> implements Future<T> {
  MyFuture() {}
  MyFuture.value([T x]) {}
  dynamic noSuchMethod(invocation);
  MyFuture<S> then<S>(dynamic f(T x), {Function onError}) => null;
}

$declared f;
// Instantiates Future<int>
$downwards<int> t1 = f.then((_) =>
   ${allocInfo}new /*error:COULD_NOT_INFER*/$upwards.value(
     /*error:ARGUMENT_TYPE_NOT_ASSIGNABLE*/'hi'));

// Instantiates List<int>
$downwards<List<int>> t2 = f.then((_) => /*info:INFERRED_TYPE_LITERAL*/[3]);
$downwards<List<int>> g2() async { return /*info:INFERRED_TYPE_LITERAL*/[3]; }
$downwards<List<int>> g3() async {
  return /*info:INFERRED_TYPE_ALLOCATION*/new $upwards.value(
      /*info:INFERRED_TYPE_LITERAL*/[3]); }
''';
    }

    ;
    checkFile(
        build(declared: "MyFuture", downwards: "Future", upwards: "Future"));
    checkFile(
        build(declared: "MyFuture", downwards: "Future", upwards: "MyFuture"));
    checkFile(
        build(declared: "Future", downwards: "Future", upwards: "Future"));
    checkFile(
        build(declared: "Future", downwards: "Future", upwards: "MyFuture"));
  }

  void test_futureUnion_downwards_comment() {
    String build({String declared, String downwards, String upwards}) {
      // TODO(leafp): The use of matchTypes in visitInstanceCreationExpression
      // in the resolver visitor isn't powerful enough to catch this for the
      // subclass.  See the TODO there.
      var allocInfo =
          (upwards == "Future") ? "/*info:INFERRED_TYPE_ALLOCATION*/" : "";
      return '''
import 'dart:async';
class MyFuture<T> implements Future<T> {
  MyFuture() {}
  MyFuture.value([T x]) {}
  dynamic noSuchMethod(invocation);
  MyFuture/*<S>*/ then/*<S>*/(dynamic f(T x), {Function onError}) => null;
}

$declared f;
// Instantiates Future<int>
$downwards<int> t1 = f.then((_) =>
   ${allocInfo}new /*error:COULD_NOT_INFER*/$upwards.value(
     /*error:ARGUMENT_TYPE_NOT_ASSIGNABLE*/'hi'));

// Instantiates List<int>
$downwards<List<int>> t2 = f.then((_) => /*info:INFERRED_TYPE_LITERAL*/[3]);
$downwards<List<int>> g2() async { return /*info:INFERRED_TYPE_LITERAL*/[3]; }
$downwards<List<int>> g3() async {
  return /*info:INFERRED_TYPE_ALLOCATION*/new $upwards.value(
      /*info:INFERRED_TYPE_LITERAL*/[3]); }
''';
    }

    ;
    checkFile(
        build(declared: "MyFuture", downwards: "Future", upwards: "Future"));
    checkFile(
        build(declared: "MyFuture", downwards: "Future", upwards: "MyFuture"));
    checkFile(
        build(declared: "Future", downwards: "Future", upwards: "Future"));
    checkFile(
        build(declared: "Future", downwards: "Future", upwards: "MyFuture"));
  }

  void test_futureUnion_downwardsGenericMethodWithFutureReturn() {
    // Regression test for https://github.com/dart-lang/sdk/issues/27134
    //
    // We need to take a future union into account for both directions of
    // generic method inference.
    checkFile(r'''
import 'dart:async';

foo() async {
  Future<List<A>> f1 = null;
  Future<List<A>> f2 = null;
  List<List<A>> merged = await Future.wait(/*info:INFERRED_TYPE_LITERAL*/[f1, f2]);
}

class A {}
    ''');
  }

  void test_futureUnion_downwardsGenericMethodWithGenericReturn() {
    // Regression test for https://github.com/dart-lang/sdk/issues/27284
    checkFile(r'''
import 'dart:async';

T id<T>(T x) => x;

main() async {
  Future<String> f;
  String s = await id(f);
}
    ''');
  }

  void test_futureUnion_downwardsGenericMethodWithGenericReturn_comment() {
    // Regression test for https://github.com/dart-lang/sdk/issues/27284
    checkFile(r'''
import 'dart:async';

/*=T*/ id/*<T>*/(/*=T*/ x) => x;

main() async {
  Future<String> f;
  String s = await id(f);
}
    ''');
  }

  void test_futureUnion_upwardsGenericMethods() {
    // Regression test for https://github.com/dart-lang/sdk/issues/27151
    checkFile(r'''
import 'dart:async';

main() async {
  var b = new Future<B>.value(new B());
  var c = new Future<C>.value(new C());
  var lll = /*info:INFERRED_TYPE_LITERAL*/[b, c];
  var result = await Future.wait(lll);
  var result2 = await Future.wait(/*info:INFERRED_TYPE_LITERAL*/[b, c]);
  List<A> list = result;
  list = result2;
}

class A {}
class B extends A {}
class C extends A {}
    ''');
  }

  void test_genericFunctions_returnTypedef() {
    checkFile(r'''
typedef void ToValue<T>(T value);

main() {
  ToValue<T> f<T>(T x) => null;
  var x = f<int>(42);
  var y = f(42);
  ToValue<int> takesInt = x;
  takesInt = y;
}
    ''');
  }

  void test_genericFunctions_returnTypedef_comment() {
    checkFile(r'''
typedef void ToValue<T>(T value);

main() {
  ToValue/*<T>*/ f/*<T>*/(dynamic /*=T*/ x) => null;
  var x = f/*<int>*/(42);
  var y = f(42);
  ToValue<int> takesInt = x;
  takesInt = y;
}
    ''');
  }

  void test_genericMethods_basicDownwardInference() {
    checkFile(r'''
T f<S, T>(S s) => null;
main() {
  String x = f(42);
  String y = (f)(42);
}
''');
  }

  void test_genericMethods_basicDownwardInference_comment() {
    checkFile(r'''
/*=T*/ f/*<S, T>*/(/*=S*/ s) => null;
main() {
  String x = f(42);
  String y = (f)(42);
}
''');
  }

  void test_genericMethods_correctlyRecognizeGenericUpperBound() {
    // Regression test for https://github.com/dart-lang/sdk/issues/25740.
    checkFile(r'''
class Foo<T extends Pattern> {
  U method<U extends T>(U u) => u;
}
main() {
  String s;
  var a = new Foo().method<String>("str");
  s = a;
  new Foo();

  var b = new Foo<String>().method("str");
  s = b;
  var c = new Foo().method("str");
  s = c;

  new Foo<String>()./*error:COULD_NOT_INFER*/method(/*error:ARGUMENT_TYPE_NOT_ASSIGNABLE*/42);
}
''');
  }

  void test_genericMethods_correctlyRecognizeGenericUpperBound_comment() {
    // Regression test for https://github.com/dart-lang/sdk/issues/25740.
    checkFile(r'''
class Foo<T extends Pattern> {
  /*=U*/ method/*<U extends T>*/(/*=U*/ u) => u;
}
main() {
  String s;
  var a = new Foo().method/*<String>*/("str");
  s = a;
  new Foo();

  var b = new Foo<String>().method("str");
  s = b;
  var c = new Foo().method("str");
  s = c;

  new Foo<String>()./*error:COULD_NOT_INFER*/method(/*error:ARGUMENT_TYPE_NOT_ASSIGNABLE*/42);
}
''');
  }

  void test_genericMethods_dartMathMinMax() {
    checkFile('''
import 'dart:math';

void printInt(int x) => print(x);
void printDouble(double x) => print(x);

num myMax(num x, num y) => max(x, y);

main() {
  // Okay if static types match.
  printInt(max(1, 2));
  printInt(min(1, 2));
  printDouble(max(1.0, 2.0));
  printDouble(min(1.0, 2.0));

  // No help for user-defined functions from num->num->num.
  printInt(/*info:DOWN_CAST_IMPLICIT*/myMax(1, 2));
  printInt(myMax(1, 2) as int);

  // Mixing int and double means return type is num.
  printInt(/*error:COULD_NOT_INFER*/max(1, /*error:ARGUMENT_TYPE_NOT_ASSIGNABLE*/2.0));
  printInt(/*error:COULD_NOT_INFER*/min(1, /*error:ARGUMENT_TYPE_NOT_ASSIGNABLE*/2.0));
  printDouble(/*error:COULD_NOT_INFER*/max(/*error:ARGUMENT_TYPE_NOT_ASSIGNABLE*/1, 2.0));
  printDouble(/*error:COULD_NOT_INFER*/min(/*error:ARGUMENT_TYPE_NOT_ASSIGNABLE*/1, 2.0));

  // Types other than int and double are not accepted.
  printInt(
      /*error:COULD_NOT_INFER*/min(
          /*error:ARGUMENT_TYPE_NOT_ASSIGNABLE*/"hi",
          /*error:ARGUMENT_TYPE_NOT_ASSIGNABLE*/"there"));
}
''');
  }

  void test_genericMethods_doNotInferInvalidOverrideOfGenericMethod() {
    checkFile('''
class C {
T m<T>(T x) => x;
}
class D extends C {
/*error:INVALID_METHOD_OVERRIDE*/m(x) => x;
}
main() {
  int y = /*info:DYNAMIC_CAST*/new D()./*error:WRONG_NUMBER_OF_TYPE_ARGUMENTS_METHOD*/m<int>(42);
  print(y);
}
''');
  }

  void test_genericMethods_doNotInferInvalidOverrideOfGenericMethod_comment() {
    checkFile('''
class C {
/*=T*/ m/*<T>*/(/*=T*/ x) => x;
}
class D extends C {
/*error:INVALID_METHOD_OVERRIDE*/m(x) => x;
}
main() {
  int y = /*info:DYNAMIC_CAST*/new D()./*error:WRONG_NUMBER_OF_TYPE_ARGUMENTS_METHOD*/m/*<int>*/(42);
  print(y);
}
''');
  }

  void test_genericMethods_downwardsInferenceAffectsArguments() {
    checkFile(r'''
T f<T>(List<T> s) => null;
main() {
  String x = f(/*info:INFERRED_TYPE_LITERAL*/['hi']);
  String y = f(/*info:INFERRED_TYPE_LITERAL,error:COULD_NOT_INFER*/[/*error:LIST_ELEMENT_TYPE_NOT_ASSIGNABLE*/42]);
}
''');
  }

  void test_genericMethods_downwardsInferenceAffectsArguments_comment() {
    checkFile(r'''
/*=T*/ f/*<T>*/(List/*<T>*/ s) => null;
main() {
  String x = f(/*info:INFERRED_TYPE_LITERAL*/['hi']);
  String y = f(/*info:INFERRED_TYPE_LITERAL,error:COULD_NOT_INFER*/[/*error:LIST_ELEMENT_TYPE_NOT_ASSIGNABLE*/42]);
}
''');
  }

  void test_genericMethods_downwardsInferenceFold() {
    // Regression from https://github.com/dart-lang/sdk/issues/25491
    // The first example works now, but the latter requires a full solution to
    // https://github.com/dart-lang/sdk/issues/25490
    checkFile(r'''
void main() {
  List<int> o;
  int y = o.fold(0, /*info:INFERRED_TYPE_CLOSURE*/(x, y) => x + y);
  var z = o.fold(0, /*info:INFERRED_TYPE_CLOSURE*/(x, y) => /*info:DYNAMIC_INVOKE*/x + y);
  y = /*info:DYNAMIC_CAST*/z;
}
void functionExpressionInvocation() {
  List<int> o;
  int y = (o.fold)(0, /*info:INFERRED_TYPE_CLOSURE*/(x, y) => x + y);
  var z = (o.fold)(0, /*info:INFERRED_TYPE_CLOSURE*/(x, y) => /*info:DYNAMIC_INVOKE*/x + y);
  y = /*info:DYNAMIC_CAST*/z;
}
''');
  }

  void test_genericMethods_handleOverrideOfNonGenericWithGeneric() {
    // Regression test for crash when adding genericity
    checkFile('''
class C {
  m(x) => x;
  dynamic g(int x) => x;
}
class D extends C {
  T m<T>(T x) => x;
  T g<T>(T x) => x;
}
main() {
  int y = /*info:DYNAMIC_CAST*/(/*info:UNNECESSARY_CAST*/new D() as C).m(42);
  print(y);
}
  ''');
  }

  void test_genericMethods_handleOverrideOfNonGenericWithGeneric_comment() {
    // Regression test for crash when adding genericity
    checkFile('''
class C {
  m(x) => x;
  dynamic g(int x) => x;
}
class D extends C {
  /*=T*/ m/*<T>*/(/*=T*/ x) => x;
  /*=T*/ g/*<T>*/(/*=T*/ x) => x;
}
main() {
  int y = /*info:DYNAMIC_CAST*/(/*info:UNNECESSARY_CAST*/new D() as C).m(42);
  print(y);
}
  ''');
  }

  void test_genericMethods_inferenceError() {
    checkFile(r'''
main() {
  List<String> y;
  Iterable<String> x = y./*error:COULD_NOT_INFER*/map(/*error:ARGUMENT_TYPE_NOT_ASSIGNABLE*/(String z) => 1.0);
}
    ''');
  }

  void test_genericMethods_inferGenericFunctionParameterType() {
    var mainUnit = checkFile('''
class C<T> extends D<T> {
  f<U>(x) {}
}
class D<T> {
  F<U> f<U>(U u) => null;
}
typedef void F<V>(V v);
''');
    var f = mainUnit.getType('C').methods[0];
    expect(f.type.toString(), '<U>(U) → (U) → void');
  }

  void test_genericMethods_inferGenericFunctionParameterType2() {
    var mainUnit = checkFile('''
class C<T> extends D<T> {
  f<U>(g) => null;
}
abstract class D<T> {
  void f<U>(G<U> g);
}
typedef List<V> G<V>();
''');
    var f = mainUnit.getType('C').methods[0];
    expect(f.type.toString(), '<U>(() → List<U>) → void');
  }

  void test_genericMethods_inferGenericFunctionParameterType2_comment() {
    var mainUnit = checkFile('''
class C<T> extends D<T> {
  f/*<U>*/(g) => null;
}
abstract class D<T> {
  void f/*<U>*/(G/*<U>*/ g);
}
typedef List<V> G<V>();
''');
    var f = mainUnit.getType('C').methods[0];
    expect(f.type.toString(), '<U>(() → List<U>) → void');
  }

  void test_genericMethods_inferGenericFunctionParameterType_comment() {
    var mainUnit = checkFile('''
class C<T> extends D<T> {
  f/*<U>*/(x) {}
}
class D<T> {
  F/*<U>*/ f/*<U>*/(/*=U*/ u) => null;
}
typedef void F<V>(V v);
''');
    var f = mainUnit.getType('C').methods[0];
    expect(f.type.toString(), '<U>(U) → (U) → void');
  }

  void test_genericMethods_inferGenericFunctionReturnType() {
    var mainUnit = checkFile('''
class C<T> extends D<T> {
  f<U>(x) {}
}
class D<T> {
  F<U> f<U>(U u) => null;
}
typedef V F<V>();
''');
    var f = mainUnit.getType('C').methods[0];
    expect(f.type.toString(), '<U>(U) → () → U');
  }

  void test_genericMethods_inferGenericFunctionReturnType_comment() {
    var mainUnit = checkFile('''
class C<T> extends D<T> {
  f/*<U>*/(x) {}
}
class D<T> {
  F/*<U>*/ f/*<U>*/(/*=U*/ u) => null;
}
typedef V F<V>();
''');
    var f = mainUnit.getType('C').methods[0];
    expect(f.type.toString(), '<U>(U) → () → U');
  }

  void test_genericMethods_inferGenericInstantiation() {
    checkFile('''
import 'dart:math' as math;
import 'dart:math' show min;

class C {
T m<T extends num>(T x, T y) => null;
}

main() {
takeIII(math.max);
takeDDD(math.max);
takeNNN(math.max);
takeIDN(math.max);
takeDIN(math.max);
takeIIN(math.max);
takeDDN(math.max);
takeIIO(math.max);
takeDDO(math.max);

takeOOI(/*error:ARGUMENT_TYPE_NOT_ASSIGNABLE*/math.max);
takeIDI(/*error:ARGUMENT_TYPE_NOT_ASSIGNABLE*/math.max);
takeDID(/*error:ARGUMENT_TYPE_NOT_ASSIGNABLE*/math.max);
takeOON(/*error:ARGUMENT_TYPE_NOT_ASSIGNABLE*/math.max);
takeOOO(/*error:ARGUMENT_TYPE_NOT_ASSIGNABLE*/math.max);

// Also test SimpleIdentifier
takeIII(min);
takeDDD(min);
takeNNN(min);
takeIDN(min);
takeDIN(min);
takeIIN(min);
takeDDN(min);
takeIIO(min);
takeDDO(min);

takeOOI(/*error:ARGUMENT_TYPE_NOT_ASSIGNABLE*/min);
takeIDI(/*error:ARGUMENT_TYPE_NOT_ASSIGNABLE*/min);
takeDID(/*error:ARGUMENT_TYPE_NOT_ASSIGNABLE*/min);
takeOON(/*error:ARGUMENT_TYPE_NOT_ASSIGNABLE*/min);
takeOOO(/*error:ARGUMENT_TYPE_NOT_ASSIGNABLE*/min);

// Also PropertyAccess
takeIII(new C().m);
takeDDD(new C().m);
takeNNN(new C().m);
takeIDN(new C().m);
takeDIN(new C().m);
takeIIN(new C().m);
takeDDN(new C().m);
takeIIO(new C().m);
takeDDO(new C().m);

// Note: this is a warning because a downcast of a method tear-off could work
// (derived method can be a subtype):
//
//     class D extends C {
//       S m<S extends num>(Object x, Object y);
//     }
//
// That's legal because we're loosening parameter types.
//
takeOON(/*error:ARGUMENT_TYPE_NOT_ASSIGNABLE*/new C().m);
takeOOO(/*error:ARGUMENT_TYPE_NOT_ASSIGNABLE*/new C().m);

// Note: this is a warning because a downcast of a method tear-off could work
// in "normal" Dart, due to bivariance.
takeOOI(/*error:ARGUMENT_TYPE_NOT_ASSIGNABLE*/new C().m);
takeIDI(/*error:ARGUMENT_TYPE_NOT_ASSIGNABLE*/new C().m);
takeDID(/*error:ARGUMENT_TYPE_NOT_ASSIGNABLE*/new C().m);
}

void takeIII(int fn(int a, int b)) {}
void takeDDD(double fn(double a, double b)) {}
void takeIDI(int fn(double a, int b)) {}
void takeDID(double fn(int a, double b)) {}
void takeIDN(num fn(double a, int b)) {}
void takeDIN(num fn(int a, double b)) {}
void takeIIN(num fn(int a, int b)) {}
void takeDDN(num fn(double a, double b)) {}
void takeNNN(num fn(num a, num b)) {}
void takeOON(num fn(Object a, Object b)) {}
void takeOOO(num fn(Object a, Object b)) {}
void takeOOI(int fn(Object a, Object b)) {}
void takeIIO(Object fn(int a, int b)) {}
void takeDDO(Object fn(double a, double b)) {}
''');
  }

  void test_genericMethods_inferGenericInstantiation_comment() {
    checkFile('''
import 'dart:math' as math;
import 'dart:math' show min;

class C {
/*=T*/ m/*<T extends num>*/(/*=T*/ x, /*=T*/ y) => null;
}

main() {
takeIII(math.max);
takeDDD(math.max);
takeNNN(math.max);
takeIDN(math.max);
takeDIN(math.max);
takeIIN(math.max);
takeDDN(math.max);
takeIIO(math.max);
takeDDO(math.max);

takeOOI(/*error:ARGUMENT_TYPE_NOT_ASSIGNABLE*/math.max);
takeIDI(/*error:ARGUMENT_TYPE_NOT_ASSIGNABLE*/math.max);
takeDID(/*error:ARGUMENT_TYPE_NOT_ASSIGNABLE*/math.max);
takeOON(/*error:ARGUMENT_TYPE_NOT_ASSIGNABLE*/math.max);
takeOOO(/*error:ARGUMENT_TYPE_NOT_ASSIGNABLE*/math.max);

// Also test SimpleIdentifier
takeIII(min);
takeDDD(min);
takeNNN(min);
takeIDN(min);
takeDIN(min);
takeIIN(min);
takeDDN(min);
takeIIO(min);
takeDDO(min);

takeOOI(/*error:ARGUMENT_TYPE_NOT_ASSIGNABLE*/min);
takeIDI(/*error:ARGUMENT_TYPE_NOT_ASSIGNABLE*/min);
takeDID(/*error:ARGUMENT_TYPE_NOT_ASSIGNABLE*/min);
takeOON(/*error:ARGUMENT_TYPE_NOT_ASSIGNABLE*/min);
takeOOO(/*error:ARGUMENT_TYPE_NOT_ASSIGNABLE*/min);

// Also PropertyAccess
takeIII(new C().m);
takeDDD(new C().m);
takeNNN(new C().m);
takeIDN(new C().m);
takeDIN(new C().m);
takeIIN(new C().m);
takeDDN(new C().m);
takeIIO(new C().m);
takeDDO(new C().m);

// Note: this is a warning because a downcast of a method tear-off could work
// (derived method can be a subtype):
//
//     class D extends C {
//       S m<S extends num>(Object x, Object y);
//     }
//
// That's legal because we're loosening parameter types.
//
takeOON(/*error:ARGUMENT_TYPE_NOT_ASSIGNABLE*/new C().m);
takeOOO(/*error:ARGUMENT_TYPE_NOT_ASSIGNABLE*/new C().m);

// Note: this is a warning because a downcast of a method tear-off could work
// in "normal" Dart, due to bivariance.
takeOOI(/*error:ARGUMENT_TYPE_NOT_ASSIGNABLE*/new C().m);
takeIDI(/*error:ARGUMENT_TYPE_NOT_ASSIGNABLE*/new C().m);
takeDID(/*error:ARGUMENT_TYPE_NOT_ASSIGNABLE*/new C().m);
}

void takeIII(int fn(int a, int b)) {}
void takeDDD(double fn(double a, double b)) {}
void takeIDI(int fn(double a, int b)) {}
void takeDID(double fn(int a, double b)) {}
void takeIDN(num fn(double a, int b)) {}
void takeDIN(num fn(int a, double b)) {}
void takeIIN(num fn(int a, int b)) {}
void takeDDN(num fn(double a, double b)) {}
void takeNNN(num fn(num a, num b)) {}
void takeOON(num fn(Object a, Object b)) {}
void takeOOO(num fn(Object a, Object b)) {}
void takeOOI(int fn(Object a, Object b)) {}
void takeIIO(Object fn(int a, int b)) {}
void takeDDO(Object fn(double a, double b)) {}
''');
  }

  void test_genericMethods_inferGenericMethodType() {
    // Regression test for https://github.com/dart-lang/sdk/issues/25668
    checkFile('''
class C {
  T m<T>(T x) => x;
}
class D extends C {
  m<S>(x) => x;
}
main() {
  int y = new D().m<int>(42);
  print(y);
}
  ''');
  }

  void test_genericMethods_inferGenericMethodType_comment() {
    // Regression test for https://github.com/dart-lang/sdk/issues/25668
    checkFile('''
class C {
  /*=T*/ m/*<T>*/(/*=T*/ x) => x;
}
class D extends C {
  m/*<S>*/(x) => x;
}
main() {
  int y = new D().m/*<int>*/(42);
  print(y);
}
  ''');
  }

  void test_genericMethods_inferJSBuiltin() {
    // TODO(jmesserly): we should change how this inference works.
    // For now this test will cover what we use.
    checkFile('''
import 'dart:_foreign_helper' show JS;
main() {
  String x = /*error:INVALID_ASSIGNMENT*/JS('int', '42');
  var y = JS('String', '"hello"');
  y = "world";
  y = /*error:INVALID_ASSIGNMENT*/42;
}
''');
  }

  void test_genericMethods_IterableAndFuture() {
    checkFile('''
import 'dart:async';

Future<int> make(int x) => (/*info:INFERRED_TYPE_ALLOCATION*/new Future(() => x));

main() {
  Iterable<Future<int>> list = <int>[1, 2, 3].map(make);
  Future<List<int>> results = Future.wait(list);
  Future<String> results2 = results.then((List<int> list)
    => list.fold('', /*info:INFERRED_TYPE_CLOSURE*/(x, y) => x + y.toString()));
}
''');
  }

  void test_genericMethods_usesGreatestLowerBound() {
    var mainUnit = checkFile(r'''
typedef Iterable<num> F(int x);
typedef List<int> G(double x);

T generic<T>(a(T _), b(T _)) => null;

var v = generic((F f) => null, (G g) => null);
''');
    var v = mainUnit.topLevelVariables[0];
    expect(v.type.toString(), '(num) → List<int>');
  }

  void test_genericMethods_usesGreatestLowerBound_comment() {
    var mainUnit = checkFile(r'''
typedef Iterable<num> F(int x);
typedef List<int> G(double x);

/*=T*/ generic/*<T>*/(a(/*=T*/ _), b(/*=T*/ _)) => null;

var v = generic((F f) => null, (G g) => null);
''');
    var v = mainUnit.topLevelVariables[0];
    expect(v.type.toString(), '(num) → List<int>');
  }

  void test_infer_assignToIndex() {
    checkFile(r'''
List<double> a = <double>[];
var b = (a[0] = 1.0);
''');
  }

  void test_infer_assignToProperty() {
    checkFile(r'''
class A {
  int f;
}
var v_assign = (new A().f = 1);
var v_plus = (new A().f += 1);
var v_minus = (new A().f -= 1);
var v_multiply = (new A().f *= 1);
var v_prefix_pp = (++new A().f);
var v_prefix_mm = (--new A().f);
var v_postfix_pp = (new A().f++);
var v_postfix_mm = (new A().f--);
''');
  }

  void test_infer_assignToProperty_custom() {
    checkFile(r'''
class A {
  int operator +(other) => 1;
  double operator -(other) => 2.0;
}
class B {
  A a;
}
var v_prefix_pp = (++new B().a);
var v_prefix_mm = (--new B().a);
var v_postfix_pp = (new B().a++);
var v_postfix_mm = (new B().a--);
''');
  }

  void test_infer_assignToRef() {
    checkFile(r'''
class A {
  int f;
}
A a = new A();
var b = (a.f = 1);
var c = 0;
var d = (c = 1);
''');
  }

  void test_infer_binary_custom() {
    checkFile(r'''
class A {
  int operator +(other) => 1;
  double operator -(other) => 2.0;
}
var v_add = new A() + 'foo';
var v_minus = new A() - 'bar';
''');
  }

  void test_infer_binary_doubleDouble() {
    checkFile(r'''
var a_equal = 1.0 == 2.0;
var a_notEqual = 1.0 != 2.0;
var a_add = 1.0 + 2.0;
var a_subtract = 1.0 - 2.0;
var a_multiply = 1.0 * 2.0;
var a_divide = 1.0 / 2.0;
var a_floorDivide = 1.0 ~/ 2.0;
var a_greater = 1.0 > 2.0;
var a_less = 1.0 < 2.0;
var a_greaterEqual = 1.0 >= 2.0;
var a_lessEqual = 1.0 <= 2.0;
var a_modulo = 1.0 % 2.0;
''');
  }

  void test_infer_binary_doubleInt() {
    checkFile(r'''
var a_equal = 1.0 == 2;
var a_notEqual = 1.0 != 2;
var a_add = 1.0 + 2;
var a_subtract = 1.0 - 2;
var a_multiply = 1.0 * 2;
var a_divide = 1.0 / 2;
var a_floorDivide = 1.0 ~/ 2;
var a_greater = 1.0 > 2;
var a_less = 1.0 < 2;
var a_greaterEqual = 1.0 >= 2;
var a_lessEqual = 1.0 <= 2;
var a_modulo = 1.0 % 2;
''');
  }

  void test_infer_binary_intDouble() {
    checkFile(r'''
var a_equal = 1 == 2.0;
var a_notEqual = 1 != 2.0;
var a_add = 1 + 2.0;
var a_subtract = 1 - 2.0;
var a_multiply = 1 * 2.0;
var a_divide = 1 / 2.0;
var a_floorDivide = 1 ~/ 2.0;
var a_greater = 1 > 2.0;
var a_less = 1 < 2.0;
var a_greaterEqual = 1 >= 2.0;
var a_lessEqual = 1 <= 2.0;
var a_modulo = 1 % 2.0;
''');
  }

  void test_infer_binary_intInt() {
    checkFile(r'''
var a_equal = 1 == 2;
var a_notEqual = 1 != 2;
var a_bitXor = 1 ^ 2;
var a_bitAnd = 1 & 2;
var a_bitOr = 1 | 2;
var a_bitShiftRight = 1 >> 2;
var a_bitShiftLeft = 1 << 2;
var a_add = 1 + 2;
var a_subtract = 1 - 2;
var a_multiply = 1 * 2;
var a_divide = 1 / 2;
var a_floorDivide = 1 ~/ 2;
var a_greater = 1 > 2;
var a_less = 1 < 2;
var a_greaterEqual = 1 >= 2;
var a_lessEqual = 1 <= 2;
var a_modulo = 1 % 2;
''');
  }

  void test_infer_conditional() {
    checkFile(r'''
var a = 1 == 2 ? 1 : 2.0;
var b = 1 == 2 ? 1.0 : 2;
''');
  }

  void test_infer_prefixExpression() {
    checkFile(r'''
var a_not = !true;
var a_complement = ~1;
var a_negate = -1;
''');
  }

  void test_infer_prefixExpression_custom() {
    checkFile(r'''
class A {
  A();
  int operator ~() => 1;
  double operator -() => 2.0;
}
var a = new A();
var v_complement = ~a;
var v_negate = -a;
''');
  }

  void test_infer_throw() {
    checkFile(r'''
var t = true;
var a = (throw 0);
var b = (throw 0) ? 1 : 2;
var c = t ? (throw 1) : 2;
var d = t ? 1 : (throw 2);
''');
  }

  void test_infer_typeCast() {
    checkFile(r'''
class A<T> {}
class B<T> extends A<T> {
  foo() {}
}
A<num> a = new B<int>();
var b = (a as B<int>);
main() {
  b.foo();
}
''');
  }

  void test_infer_typedListLiteral() {
    checkFile(r'''
var a = <int>[];
var b = <double>[1.0, 2.0, 3.0];
var c = <List<int>>[];
var d = <dynamic>[1, 2.0, false];
''');
  }

  void test_infer_typedMapLiteral() {
    checkFile(r'''
var a = <int, String>{0: 'aaa', 1: 'bbb'};
var b = <double, int>{1.1: 1, 2.2: 2};
var c = <List<int>, Map<String, double>>{};
var d = <int, dynamic>{};
var e = <dynamic, int>{};
var f = <dynamic, dynamic>{};
''');
  }

  void test_infer_use_of_void() {
    checkFile('''
class B {
  void f() {}
}
class C extends B {
  f() {}
}
var x = new C()./*info:USE_OF_VOID_RESULT*/f();
''');
  }

  void test_inferConstsTransitively() {
    addFile(
        '''
const b1 = 2;
''',
        name: '/b.dart');
    addFile(
        '''
import 'main.dart';
import 'b.dart';
const a1 = m2;
const a2 = b1;
''',
        name: '/a.dart');
    checkFile('''
import 'a.dart';
const m1 = a1;
const m2 = a2;

foo() {
  int i;
  i = m1;
}
''');
  }

  void test_inferCorrectlyOnMultipleVariablesDeclaredTogether() {
    checkFile('''
class A {
  var x, y = 2, z = "hi";
}

class B implements A {
  var x = 2, y = 3, z, w = 2;
}

foo() {
  String s;
  int i;

  s = /*info:DYNAMIC_CAST*/new B().x;
  s = /*error:INVALID_ASSIGNMENT*/new B().y;
  s = new B().z;
  s = /*error:INVALID_ASSIGNMENT*/new B().w;

  i = /*info:DYNAMIC_CAST*/new B().x;
  i = new B().y;
  i = /*error:INVALID_ASSIGNMENT*/new B().z;
  i = new B().w;
}
''');
  }

  void test_inferedType_usesSyntheticFunctionType() {
    var mainUnit = checkFile('''
int f() => null;
String g() => null;
var v = /*info:INFERRED_TYPE_LITERAL*/[f, g];
''');
    var v = mainUnit.topLevelVariables[0];
    expect(v.type.toString(), 'List<() → Object>');
  }

  void test_inferedType_usesSyntheticFunctionType_functionTypedParam() {
    var mainUnit = checkFile('''
int f(int x(String y)) => null;
String g(int x(String y)) => null;
var v = /*info:INFERRED_TYPE_LITERAL*/[f, g];
''');
    var v = mainUnit.topLevelVariables[0];
    expect(v.type.toString(), 'List<((String) → int) → Object>');
  }

  void test_inferedType_usesSyntheticFunctionType_namedParam() {
    var mainUnit = checkFile('''
int f({int x}) => null;
String g({int x}) => null;
var v = /*info:INFERRED_TYPE_LITERAL*/[f, g];
''');
    var v = mainUnit.topLevelVariables[0];
    expect(v.type.toString(), 'List<({x: int}) → Object>');
  }

  void test_inferedType_usesSyntheticFunctionType_positionalParam() {
    var mainUnit = checkFile('''
int f([int x]) => null;
String g([int x]) => null;
var v = /*info:INFERRED_TYPE_LITERAL*/[f, g];
''');
    var v = mainUnit.topLevelVariables[0];
    expect(v.type.toString(), 'List<([int]) → Object>');
  }

  void test_inferedType_usesSyntheticFunctionType_requiredParam() {
    var mainUnit = checkFile('''
int f(int x) => null;
String g(int x) => null;
var v = /*info:INFERRED_TYPE_LITERAL*/[f, g];
''');
    var v = mainUnit.topLevelVariables[0];
    expect(v.type.toString(), 'List<(int) → Object>');
  }

  void test_inferenceInCyclesIsDeterministic() {
    addFile(
        '''
import 'b.dart';
class A {
  static final a1 = B.b1;
  final a2 = new B().b2;
}
''',
        name: '/a.dart');
    addFile(
        '''
class B {
  static final b1 = 1;
  final b2 = 1;
}
''',
        name: '/b.dart');
    addFile(
        '''
import "main.dart"; // creates a cycle

class C {
  static final c1 = 1;
  final c2 = 1;
}
''',
        name: '/c.dart');
    addFile(
        '''
library e;
import 'a.dart';
part 'e2.dart';

class E {
  static final e1 = 1;
  static final e2 = F.f1;
  static final e3 = A.a1;
  final e4 = 1;
  final e5 = new F().f2;
  final e6 = new A().a2;
}
''',
        name: '/e.dart');
    addFile(
        '''
part 'f2.dart';
''',
        name: '/f.dart');
    addFile(
        '''
part of e;
class F {
  static final f1 = 1;
  final f2 = 1;
}
''',
        name: '/e2.dart');
    checkFile('''
import "a.dart";
import "c.dart";
import "e.dart";

class D {
  static final d1 = A.a1 + 1;
  static final d2 = C.c1 + 1;
  final d3 = new A().a2;
  final d4 = new C().c2;
}

test1() {
  int x = 0;
  // inference in A works, it's not in a cycle
  x = A.a1;
  x = new A().a2;

  // Within a cycle we allow inference when the RHS is well known, but
  // not when it depends on other fields within the cycle
  x = C.c1;
  x = D.d1;
  x = D.d2;
  x = new C().c2;
  x = new D().d3;
  x = /*info:DYNAMIC_CAST*/new D().d4;


  // Similarly if the library contains parts.
  x = E.e1;
  x = E.e2;
  x = E.e3;
  x = new E().e4;
  x = /*info:DYNAMIC_CAST*/new E().e5;
  x = new E().e6;
  x = F.f1;
  x = new F().f2;
}
''');
  }

  void test_inferFromComplexExpressionsIfOuterMostValueIsPrecise() {
    checkFile('''
class A { int x; B operator+(other) => null; }
class B extends A { B(ignore); }
var a = new A();
// Note: it doesn't matter that some of these refer to 'x'.
var b = new B(/*error:UNDEFINED_IDENTIFIER*/x);  // allocations
var c1 = [/*error:UNDEFINED_IDENTIFIER*/x];      // list literals
var c2 = const [];
var d = <dynamic, dynamic>{'a': 'b'};     // map literals
var e = new A()..x = 3; // cascades
var f = 2 + 3;          // binary expressions are OK if the left operand
                        // is from a library in a different strongest
                        // conected component.
var g = -3;
var h = new A() + 3;
var i = /*error:UNDEFINED_OPERATOR,info:DYNAMIC_INVOKE*/- new A();
var j = /*info:UNNECESSARY_CAST*/null as B;

test1() {
  a = /*error:INVALID_ASSIGNMENT*/"hi";
  a = new B(3);
  b = /*error:INVALID_ASSIGNMENT*/"hi";
  b = new B(3);
  c1 = [];
  c1 = /*error:INVALID_ASSIGNMENT*/{};
  c2 = [];
  c2 = /*error:INVALID_ASSIGNMENT*/{};
  d = {};
  d = /*error:INVALID_ASSIGNMENT*/3;
  e = new A();
  e = /*error:INVALID_ASSIGNMENT*/{};
  f = 3;
  f = /*error:INVALID_ASSIGNMENT*/false;
  g = 1;
  g = /*error:INVALID_ASSIGNMENT*/false;
  h = /*error:INVALID_ASSIGNMENT*/false;
  h = new B('b');
  i = false;
  j = new B('b');
  j = /*error:INVALID_ASSIGNMENT*/false;
  j = /*error:INVALID_ASSIGNMENT*/[];
}
''');
  }

  void test_inferFromRhsOnlyIfItWontConflictWithOverriddenFields() {
    checkFile('''
class A {
  var x;
}

class B implements A {
  var x = 2;
}

foo() {
  String y = /*info:DYNAMIC_CAST*/new B().x;
  int z = /*info:DYNAMIC_CAST*/new B().x;
}
''');
  }

  void test_inferFromRhsOnlyIfItWontConflictWithOverriddenFields2() {
    checkFile('''
class A {
  final x = null;
}

class B implements A {
  final x = 2;
}

foo() {
  String y = /*error:INVALID_ASSIGNMENT*/new B().x;
  int z = new B().x;
}
''');
  }

  void test_inferFromVariablesInCycleLibsWhenFlagIsOn() {
    addFile(
        '''
import 'main.dart';
var x = 2; // ok to infer
''',
        name: '/a.dart');
    checkFile('''
import 'a.dart';
var y = x; // now ok :)

test1() {
  int t = 3;
  t = x;
  t = y;
}
''');
  }

  void test_inferFromVariablesInCycleLibsWhenFlagIsOn2() {
    addFile(
        '''
import 'main.dart';
class A { static var x = 2; }
''',
        name: '/a.dart');
    checkFile('''
import 'a.dart';
class B { static var y = A.x; }

test1() {
  int t = 3;
  t = A.x;
  t = B.y;
}
''');
  }

  void test_inferFromVariablesInNonCycleImportsWithFlag() {
    addFile(
        '''
var x = 2;
''',
        name: '/a.dart');
    checkFile('''
import 'a.dart';
var y = x;

test1() {
  x = /*error:INVALID_ASSIGNMENT*/"hi";
  y = /*error:INVALID_ASSIGNMENT*/"hi";
}
''');
  }

  void test_inferFromVariablesInNonCycleImportsWithFlag2() {
    addFile(
        '''
class A { static var x = 2; }
''',
        name: '/a.dart');
    checkFile('''
import 'a.dart';
class B { static var y = A.x; }

test1() {
  A.x = /*error:INVALID_ASSIGNMENT*/"hi";
  B.y = /*error:INVALID_ASSIGNMENT*/"hi";
}
''');
  }

  void test_inferGenericMethodType_named() {
    var unit = checkFile('''
class C {
  T m<T>(int a, {String b, T c}) => null;
}
var y = new C().m(1, b: 'bbb', c: 2.0);
  ''');
    expect(unit.topLevelVariables[0].type.toString(), 'double');
  }

  void test_inferGenericMethodType_named_comment() {
    var unit = checkFile('''
class C {
  /*=T*/ m/*<T>*/(int a, {String b, /*=T*/ c}) => null;
}
var y = new C().m(1, b: 'bbb', c: 2.0);
  ''');
    expect(unit.topLevelVariables[0].type.toString(), 'double');
  }

  void test_inferGenericMethodType_positional() {
    var unit = checkFile('''
class C {
  T m<T>(int a, [T b]) => null;
}
var y = new C().m(1, 2.0);
  ''');
    expect(unit.topLevelVariables[0].type.toString(), 'double');
  }

  void test_inferGenericMethodType_positional2() {
    var unit = checkFile('''
class C {
  T m<T>(int a, [String b, T c]) => null;
}
var y = new C().m(1, 'bbb', 2.0);
  ''');
    expect(unit.topLevelVariables[0].type.toString(), 'double');
  }

  void test_inferGenericMethodType_positional2_comment() {
    var unit = checkFile('''
class C {
  /*=T*/ m/*<T>*/(int a, [String b, /*=T*/ c]) => null;
}
var y = new C().m(1, 'bbb', 2.0);
  ''');
    expect(unit.topLevelVariables[0].type.toString(), 'double');
  }

  void test_inferGenericMethodType_positional_comment() {
    var unit = checkFile('''
class C {
  /*=T*/ m/*<T>*/(int a, [/*=T*/ b]) => null;
}
var y = new C().m(1, 2.0);
  ''');
    expect(unit.topLevelVariables[0].type.toString(), 'double');
  }

  void test_inferGenericMethodType_required() {
    var unit = checkFile('''
class C {
  T m<T>(T x) => x;
}
var y = new C().m(42);
  ''');
    expect(unit.topLevelVariables[0].type.toString(), 'int');
  }

  void test_inferGenericMethodType_required_comment() {
    var unit = checkFile('''
class C {
  /*=T*/ m/*<T>*/(/*=T*/ x) => x;
}
var y = new C().m(42);
  ''');
    expect(unit.topLevelVariables[0].type.toString(), 'int');
  }

  void test_inferIfComplexExpressionsReadPossibleInferredField() {
    // but flags can enable this behavior.
    addFile(
        '''
class A {
  var x = 3;
}
''',
        name: '/a.dart');
    checkFile('''
import 'a.dart';
class B {
  var y = 3;
}
final t1 = new A();
final t2 = new A().x;
final t3 = new B();
final t4 = new B().y;

test1() {
  int i = 0;
  A a;
  B b;
  a = t1;
  i = t2;
  b = t3;
  i = /*info:DYNAMIC_CAST*/t4;
  i = new B().y; // B.y was inferred though
}
''');
  }

  void test_inferListLiteralNestedInMapLiteral() {
    checkFile(r'''
class Resource {}
class Folder extends Resource {}

Resource getResource(String str) => null;

class Foo<T> {
  Foo(T t);
}

main() {
  // List inside map
  var map = <String, List<Folder>>{
    'pkgA': /*info:INFERRED_TYPE_LITERAL,error:COULD_NOT_INFER*/[/*info:DOWN_CAST_IMPLICIT*/getResource('/pkgA/lib/')],
    'pkgB': /*info:INFERRED_TYPE_LITERAL,error:COULD_NOT_INFER*/[/*info:DOWN_CAST_IMPLICIT*/getResource('/pkgB/lib/')]
  };
  // Also try map inside list
  var list = <Map<String, Folder>>[
    /*info:INFERRED_TYPE_LITERAL,error:COULD_NOT_INFER*/{ 'pkgA': /*info:DOWN_CAST_IMPLICIT*/getResource('/pkgA/lib/') },
    /*info:INFERRED_TYPE_LITERAL,error:COULD_NOT_INFER*/{ 'pkgB': /*info:DOWN_CAST_IMPLICIT*/getResource('/pkgB/lib/') },
  ];
  // Instance creation too
  var foo = new Foo<List<Folder>>(
    /*info:INFERRED_TYPE_LITERAL,error:COULD_NOT_INFER*/[/*info:DOWN_CAST_IMPLICIT*/getResource('/pkgA/lib/')]
  );
}
  ''');
  }

  void test_inferLocalFunctionReturnType() {
    // Regression test for https://github.com/dart-lang/sdk/issues/26414
    var unit = checkFile(r'''
main() {
  f0() => 42;
  f1() async => 42;

  f2 /*info:INFERRED_TYPE_CLOSURE*/() { return 42; }
  f3 /*info:INFERRED_TYPE_CLOSURE*/() async { return 42; }
  f4 /*info:INFERRED_TYPE_CLOSURE*/() sync* { yield 42; }
  f5 /*info:INFERRED_TYPE_CLOSURE*/() async* { yield 42; }

  num f6() => 42;

  f7() => f7();
  f8() => /*error:REFERENCED_BEFORE_DECLARATION*/f9();
  f9() => f5();
}
''');
    var fns = unit.functions[0].functions;
    expect(fns[0].type.toString(), '() → int');
    expect(fns[1].type.toString(), '() → Future<int>');

    expect(fns[2].type.toString(), '() → int');
    expect(fns[3].type.toString(), '() → Future<int>');
    expect(fns[4].type.toString(), '() → Iterable<int>');
    expect(fns[5].type.toString(), '() → Stream<int>');

    expect(fns[6].type.toString(), '() → num');

    // Recursive cases: these infer in declaration order.
    expect(fns[7].type.toString(), '() → dynamic');
    expect(fns[8].type.toString(), '() → dynamic');
    expect(fns[9].type.toString(), '() → Stream<int>');
  }

  void test_inferParameterType_setter_fromField() {
    var mainUnit = checkFile('''
class C extends D {
  /*error:INVALID_FIELD_OVERRIDE*/set foo(x) {}
}
class D {
  int foo;
}
''');
    var f = mainUnit.getType('C').accessors[0];
    expect(f.type.toString(), '(int) → void');
  }

  void test_inferParameterType_setter_fromSetter() {
    var mainUnit = checkFile('''
class C extends D {
  set foo(x) {}
}
class D {
  set foo(int x) {}
}
''');
    var f = mainUnit.getType('C').accessors[0];
    expect(f.type.toString(), '(int) → void');
  }

  void test_inferred_nonstatic_field_depends_on_static_field_complex() {
    var mainUnit = checkFile('''
class C {
  static var x = 'x';
  var y = /*info:INFERRED_TYPE_LITERAL*/{
    'a': /*info:INFERRED_TYPE_LITERAL*/{'b': 'c'},
    'd': /*info:INFERRED_TYPE_LITERAL*/{'e': x}
  };
}
''');
    var x = mainUnit.getType('C').fields[0];
    expect(x.name, 'x');
    expect(x.type.toString(), 'String');
    var y = mainUnit.getType('C').fields[1];
    expect(y.name, 'y');
    expect(y.type.toString(), 'Map<String, Map<String, String>>');
  }

  void test_inferred_nonstatic_field_depends_on_toplevel_var_simple() {
    var mainUnit = checkFile('''
var x = 'x';
class C {
  var y = x;
}
''');
    var x = mainUnit.topLevelVariables[0];
    expect(x.name, 'x');
    expect(x.type.toString(), 'String');
    var y = mainUnit.getType('C').fields[0];
    expect(y.name, 'y');
    expect(y.type.toString(), 'String');
  }

  void test_inferredInitializingFormalChecksDefaultValue() {
    checkFile('''
class Foo {
  var x = 1;
  Foo([this.x = /*error:INVALID_ASSIGNMENT*/"1"]);
}''');
  }

  void test_inferredType_blockBodiedClosure_noArguments() {
    var mainUnit = checkFile('''
class C {
  static final v = () {};
}
''');
    var v = mainUnit.getType('C').fields[0];
    expect(v.type.toString(), '() → dynamic');
  }

  void test_inferredType_blockClosure_noArgs_noReturn() {
    var mainUnit = checkFile('''
var f = () {};
''');
    var f = mainUnit.topLevelVariables[0];
    expect(f.type.toString(), '() → dynamic');
  }

  void test_inferredType_customBinaryOp() {
    var mainUnit = checkFile('''
class C {
  bool operator*(C other) => true;
}
C c;
var x = c*c;
''');
    var x = mainUnit.topLevelVariables[1];
    expect(x.name, 'x');
    expect(x.type.toString(), 'bool');
  }

  void test_inferredType_customBinaryOp_viaInterface() {
    var mainUnit = checkFile('''
class I {
  bool operator*(C other) => true;
}
abstract class C implements I {}
C c;
var x = c*c;
''');
    var x = mainUnit.topLevelVariables[1];
    expect(x.name, 'x');
    expect(x.type.toString(), 'bool');
  }

  void test_inferredType_customIndexOp() {
    var mainUnit = checkFile('''
class C {
  bool operator[](int index) => true;
}
C c;
var x = c[0];
''');
    var x = mainUnit.topLevelVariables[1];
    expect(x.name, 'x');
    expect(x.type.toString(), 'bool');
  }

  void test_inferredType_customIndexOp_viaInterface() {
    var mainUnit = checkFile('''
class I {
  bool operator[](int index) => true;
}
abstract class C implements I {}
C c;
var x = c[0];
''');
    var x = mainUnit.topLevelVariables[1];
    expect(x.name, 'x');
    expect(x.type.toString(), 'bool');
  }

  void test_inferredType_customUnaryOp() {
    var mainUnit = checkFile('''
class C {
  bool operator-() => true;
}
C c;
var x = -c;
''');
    var x = mainUnit.topLevelVariables[1];
    expect(x.name, 'x');
    expect(x.type.toString(), 'bool');
  }

  void test_inferredType_customUnaryOp_viaInterface() {
    var mainUnit = checkFile('''
class I {
  bool operator-() => true;
}
abstract class C implements I {}
C c;
var x = -c;
''');
    var x = mainUnit.topLevelVariables[1];
    expect(x.name, 'x');
    expect(x.type.toString(), 'bool');
  }

  void test_inferredType_extractMethodTearOff() {
    var mainUnit = checkFile('''
class C {
  bool g() => true;
}
C f() => null;
var x = f().g;
''');
    var x = mainUnit.topLevelVariables[0];
    expect(x.name, 'x');
    expect(x.type.toString(), '() → bool');
  }

  void test_inferredType_extractMethodTearOff_viaInterface() {
    var mainUnit = checkFile('''
class I {
  bool g() => true;
}
abstract class C implements I {}
C f() => null;
var x = f().g;
''');
    var x = mainUnit.topLevelVariables[0];
    expect(x.name, 'x');
    expect(x.type.toString(), '() → bool');
  }

  void test_inferredType_extractProperty() {
    var mainUnit = checkFile('''
class C {
  bool b;
}
C f() => null;
var x = f().b;
''');
    var x = mainUnit.topLevelVariables[0];
    expect(x.name, 'x');
    expect(x.type.toString(), 'bool');
  }

  void test_inferredType_extractProperty_prefixedIdentifier() {
    var mainUnit = checkFile('''
class C {
  bool b;
}
C c;
var x = c.b;
''');
    var x = mainUnit.topLevelVariables[1];
    expect(x.name, 'x');
    expect(x.type.toString(), 'bool');
  }

  void test_inferredType_extractProperty_prefixedIdentifier_viaInterface() {
    var mainUnit = checkFile('''
class I {
  bool b;
}
abstract class C implements I {}
C c;
var x = c.b;
''');
    var x = mainUnit.topLevelVariables[1];
    expect(x.name, 'x');
    expect(x.type.toString(), 'bool');
  }

  void test_inferredType_extractProperty_viaInterface() {
    var mainUnit = checkFile('''
class I {
  bool b;
}
abstract class C implements I {}
C f() => null;
var x = f().b;
''');
    var x = mainUnit.topLevelVariables[0];
    expect(x.name, 'x');
    expect(x.type.toString(), 'bool');
  }

  void test_inferredType_fromTopLevelExecutableTearoff() {
    var mainUnit = checkFile('''
var v = print;
''');
    var v = mainUnit.topLevelVariables[0];
    expect(v.type.toString(), '(Object) → void');
  }

  void test_inferredType_invokeMethod() {
    var mainUnit = checkFile('''
class C {
  bool g() => true;
}
C f() => null;
var x = f().g();
''');
    var x = mainUnit.topLevelVariables[0];
    expect(x.name, 'x');
    expect(x.type.toString(), 'bool');
  }

  void test_inferredType_invokeMethod_viaInterface() {
    var mainUnit = checkFile('''
class I {
  bool g() => true;
}
abstract class C implements I {}
C f() => null;
var x = f().g();
''');
    var x = mainUnit.topLevelVariables[0];
    expect(x.name, 'x');
    expect(x.type.toString(), 'bool');
  }

  void test_inferredType_isEnum() {
    var mainUnit = checkFile('''
enum E { v1 }
final x = E.v1;
''');
    var x = mainUnit.topLevelVariables[0];
    expect(x.type.toString(), 'E');
  }

  void test_inferredType_isEnumValues() {
    var mainUnit = checkFile('''
enum E { v1 }
final x = E.values;
''');
    var x = mainUnit.topLevelVariables[0];
    expect(x.type.toString(), 'List<E>');
  }

  void test_inferredType_isTypedef() {
    var mainUnit = checkFile('''
typedef void F();
final x = <String, F>{};
''');
    var x = mainUnit.topLevelVariables[0];
    expect(x.type.toString(), 'Map<String, () → void>');
  }

  void test_inferredType_isTypedef_parameterized() {
    var mainUnit = checkFile('''
typedef T F<T>();
final x = <String, F<int>>{};
''');
    var x = mainUnit.topLevelVariables[0];
    expect(x.type.toString(), 'Map<String, () → int>');
  }

  void test_inferredType_opAssignToProperty() {
    var mainUnit = checkFile('''
class C {
  num n;
}
C f() => null;
var x = (f().n *= null);
''');
    var x = mainUnit.topLevelVariables[0];
    expect(x.name, 'x');
    expect(x.type.toString(), 'num');
  }

  void test_inferredType_opAssignToProperty_prefixedIdentifier() {
    var mainUnit = checkFile('''
class C {
  num n;
}
C c;
var x = (c.n *= null);
''');
    var x = mainUnit.topLevelVariables[1];
    expect(x.name, 'x');
    expect(x.type.toString(), 'num');
  }

  void test_inferredType_opAssignToProperty_prefixedIdentifier_viaInterface() {
    var mainUnit = checkFile('''
class I {
  num n;
}
abstract class C implements I {}
C c;
var x = (c.n *= null);
''');
    var x = mainUnit.topLevelVariables[1];
    expect(x.name, 'x');
    expect(x.type.toString(), 'num');
  }

  void test_inferredType_opAssignToProperty_viaInterface() {
    var mainUnit = checkFile('''
class I {
  num n;
}
abstract class C implements I {}
C f() => null;
var x = (f().n *= null);
''');
    var x = mainUnit.topLevelVariables[0];
    expect(x.name, 'x');
    expect(x.type.toString(), 'num');
  }

  void test_inferredType_viaClosure_multipleLevelsOfNesting() {
    var mainUnit = checkFile('''
class C {
  static final f = (bool b) => (int i) => /*info:INFERRED_TYPE_LITERAL*/{i: b};
}
''');
    var f = mainUnit.getType('C').fields[0];
    expect(f.type.toString(), '(bool) → (int) → Map<int, bool>');
  }

  void test_inferredType_viaClosure_typeDependsOnArgs() {
    var mainUnit = checkFile('''
class C {
  static final f = (bool b) => b;
}
''');
    var f = mainUnit.getType('C').fields[0];
    expect(f.type.toString(), '(bool) → bool');
  }

  void test_inferredType_viaClosure_typeIndependentOfArgs_field() {
    var mainUnit = checkFile('''
class C {
  static final f = (bool b) => 1;
}
''');
    var f = mainUnit.getType('C').fields[0];
    expect(f.type.toString(), '(bool) → int');
  }

  void test_inferredType_viaClosure_typeIndependentOfArgs_topLevel() {
    var mainUnit = checkFile('final f = (bool b) => 1;');
    var f = mainUnit.topLevelVariables[0];
    expect(f.type.toString(), '(bool) → int');
  }

  void test_inferReturnOfStatementLambda() {
    // Regression test for https://github.com/dart-lang/sdk/issues/26139
    checkFile(r'''
List<String> strings() {
  var stuff = [].expand(/*info:INFERRED_TYPE_CLOSURE*/(i) {
    return <String>[];
  });
  return stuff.toList();
}
    ''');
  }

  void test_inferStaticsTransitively() {
    addFile(
        '''
final b1 = 2;
''',
        name: '/b.dart');
    addFile(
        '''
import 'main.dart';
import 'b.dart';
final a1 = m2;
class A {
  static final a2 = b1;
}
''',
        name: '/a.dart');
    checkFile('''
import 'a.dart';
final m1 = a1;
final m2 = A.a2;

foo() {
  int i;
  i = m1;
}
''');
  }

  void test_inferStaticsTransitively2() {
    checkFile('''
const x1 = 1;
final x2 = 1;
final y1 = x1;
final y2 = x2;

foo() {
  int i;
  i = y1;
  i = y2;
}
''');
  }

  void test_inferStaticsTransitively3() {
    addFile(
        '''
const a1 = 3;
const a2 = 4;
class A {
  static const a3 = null;
}
''',
        name: '/a.dart');
    checkFile('''
import 'a.dart' show a1, A;
import 'a.dart' as p show a2, A;
const t1 = 1;
const t2 = t1;
const t3 = a1;
const t4 = p.a2;
const t5 = A.a3;
const t6 = p.A.a3;

foo() {
  int i;
  i = t1;
  i = t2;
  i = t3;
  i = t4;
}
''');
  }

  void test_inferStaticsWithMethodInvocations() {
    addFile(
        '''
m3(String a, String b, [a1,a2]) {}
''',
        name: '/a.dart');
    checkFile('''
import 'a.dart';
class T {
  static final T foo = m1(m2(m3('', '')));
  static T m1(String m) { return null; }
  static String m2(e) { return ''; }
}
''');
  }

  void test_inferTypeOnOverriddenFields2() {
    checkFile('''
class A {
  int x = 2;
}

class B extends A {
  /*error:INVALID_FIELD_OVERRIDE*/get x => 3;
}

foo() {
  String y = /*error:INVALID_ASSIGNMENT*/new B().x;
  int z = new B().x;
}
''');
  }

  void test_inferTypeOnOverriddenFields4() {
    checkFile('''
class A {
  final int x = 2;
}

class B implements A {
  get x => 3;
}

foo() {
  String y = /*error:INVALID_ASSIGNMENT*/new B().x;
  int z = new B().x;
}
''');
  }

  void test_inferTypeOnVar() {
    // Error also expected when declared type is `int`.
    checkFile('''
test1() {
  int x = 3;
  x = /*error:INVALID_ASSIGNMENT*/"hi";
}
''');
  }

  void test_inferTypeOnVar2() {
    checkFile('''
test2() {
  var x = 3;
  x = /*error:INVALID_ASSIGNMENT*/"hi";
}
''');
  }

  void test_inferTypeOnVarFromField() {
    checkFile('''
class A {
  int x = 0;

  test1() {
    var a = x;
    a = /*error:INVALID_ASSIGNMENT*/"hi";
    a = 3;
    var b = y;
    b = /*error:INVALID_ASSIGNMENT*/"hi";
    b = 4;
    var c = z;
    c = /*error:INVALID_ASSIGNMENT*/"hi";
    c = 4;
  }

  int y; // field def after use
  final z = 42; // should infer `int`
}
''');
  }

  void test_inferTypeOnVarFromTopLevel() {
    checkFile('''
int x = 0;

test1() {
  var a = x;
  a = /*error:INVALID_ASSIGNMENT*/"hi";
  a = 3;
  var b = y;
  b = /*error:INVALID_ASSIGNMENT*/"hi";
  b = 4;
  var c = z;
  c = /*error:INVALID_ASSIGNMENT*/"hi";
  c = 4;
}

int y = 0; // field def after use
final z = 42; // should infer `int`
''');
  }

  void test_inferTypeRegardlessOfDeclarationOrderOrCycles() {
    addFile(
        '''
import 'main.dart';

class B extends A { }
''',
        name: '/b.dart');
    checkFile('''
import 'b.dart';
class C extends B {
  get x => null;
}
class A {
  int get x => 0;
}
foo() {
  int y = new C().x;
  String z = /*error:INVALID_ASSIGNMENT*/new C().x;
}
''');
  }

  void test_inferTypesOnGenericInstantiations_3() {
    checkFile('''
class A<T> {
  final T x = null;
  final T w = null;
}

class B implements A<int> {
  get x => 3;
  get w => /*error:RETURN_OF_INVALID_TYPE*/"hello";
}

foo() {
  String y = /*error:INVALID_ASSIGNMENT*/new B().x;
  int z = new B().x;
}
''');
  }

  void test_inferTypesOnGenericInstantiations_4() {
    checkFile('''
class A<T> {
  T x;
}

class B<E> extends A<E> {
  E y;
  /*error:INVALID_FIELD_OVERRIDE*/get x => y;
}

foo() {
  int y = /*error:INVALID_ASSIGNMENT*/new B<String>().x;
  String z = new B<String>().x;
}
''');
  }

  void test_inferTypesOnGenericInstantiations_5() {
    checkFile('''
abstract class I<E> {
  String m(a, String f(v, E e));
}

abstract class A<E> implements I<E> {
  const A();
  String m(a, String f(v, E e));
}

abstract class M {
  final int y = 0;
}

class B<E> extends A<E> implements M {
  const B();
  int get y => 0;

  m(a, f(v, E e)) {}
}

foo () {
  int y = /*error:INVALID_ASSIGNMENT*/new B().m(null, null);
  String z = new B().m(null, null);
}
''');
  }

  void test_inferTypesOnGenericInstantiations_infer() {
    checkFile('''
class A<T> {
  final T x = null;
}

class B implements A<int> {
  /*error:INVALID_METHOD_OVERRIDE*/dynamic get x => 3;
}

foo() {
  String y = /*info:DYNAMIC_CAST*/new B().x;
  int z = /*info:DYNAMIC_CAST*/new B().x;
}
''');
  }

  void test_inferTypesOnGenericInstantiationsInLibraryCycle() {
    // Note: this is a regression test for a non-deterministic behavior we used to
    // have with inference in library cycles. If you see this test flake out,
    // change `test` to `skip_test` and reopen bug #48.
    addFile(
        '''
import 'main.dart';
abstract class I<E> {
  A<E> m(a, String f(v, int e));
}
''',
        name: '/a.dart');
    checkFile('''
import 'a.dart';

abstract class A<E> implements I<E> {
  const A();

  final E value = null;
}

abstract class M {
  final int y = 0;
}

class B<E> extends A<E> implements M {
  const B();
  int get y => 0;

  m(a, f(v, int e)) {}
}

foo () {
  int y = /*error:INVALID_ASSIGNMENT*/new B<String>().m(null, null).value;
  String z = new B<String>().m(null, null).value;
}
''');
  }

  void test_inferTypesOnLoopIndices_forEachLoop() {
    checkFile('''
class Foo {
  int bar = 42;
}

class Bar<T extends Iterable<String>> {
  void foo(T t) {
    for (var i in t) {
      int x = /*error:INVALID_ASSIGNMENT*/i;
    }
  }
}

class Baz<T, E extends Iterable<T>, S extends E> {
  void foo(S t) {
    for (var i in t) {
      int x = /*error:INVALID_ASSIGNMENT*/i;
      T y = i;
    }
  }
}

test() {
  var list = <Foo>[];
  for (var x in list) {
    String y = /*error:INVALID_ASSIGNMENT*/x;
  }

  for (dynamic x in list) {
    String y = /*info:DYNAMIC_CAST*/x;
  }

  for (String x in /*error:FOR_IN_OF_INVALID_ELEMENT_TYPE*/list) {
    String y = x;
  }

  var z;
  for(z in list) {
    String y = /*info:DYNAMIC_CAST*/z;
  }

  Iterable iter = list;
  for (Foo /*info:DYNAMIC_CAST*/x in iter) {
    var y = x;
  }

  dynamic iter2 = list;
  for (Foo /*info:DYNAMIC_CAST*/x in /*info:DYNAMIC_CAST*/iter2) {
    var y = x;
  }

  var map = <String, Foo>{};
  // Error: map must be an Iterable.
  for (var x in /*error:FOR_IN_OF_INVALID_TYPE*/map) {
    String y = /*info:DYNAMIC_CAST*/x;
  }

  // We're not properly inferring that map.keys is an Iterable<String>
  // and that x is a String.
  for (var x in map.keys) {
    String y = x;
  }
}
''');
  }

  void test_inferTypesOnLoopIndices_forLoopWithInference() {
    checkFile('''
test() {
  for (var i = 0; i < 10; i++) {
    int j = i + 1;
  }
}
''');
  }

  void test_instanceField_basedOnInstanceField_betweenCycles() {
    // Verify that all instance fields in one library cycle are inferred before
    // an instance fields in a dependent library cycle.
    addFile(
        '''
import 'b.dart';
class A {
  var x = new B().y;
  var y = 0;
}
''',
        name: '/a.dart');
    addFile(
        '''
class B {
  var x = new B().y;
  var y = 0;
}
''',
        name: '/b.dart');
    checkFile('''
import 'a.dart';
import 'b.dart';
main() {
  new A().x = /*error:INVALID_ASSIGNMENT*/'foo';
  new B().x = 'foo';
}
''');
  }

  void test_instanceField_basedOnInstanceField_withinCycle() {
    // Verify that all instance field inferences that occur within the same
    // library cycle happen as though they occurred "all at once", so no
    // instance field in the library cycle can inherit its type from another
    // instance field in the same library cycle.
    addFile(
        '''
import 'b.dart';
class A {
  var x = new B().y;
  var y = 0;
}
''',
        name: '/a.dart');
    addFile(
        '''
import 'a.dart';
class B {
  var x = new A().y;
  var y = 0;
}
''',
        name: '/b.dart');
    checkFile('''
import 'a.dart';
import 'b.dart';
main() {
  new A().x = 'foo';
  new B().x = 'foo';
}
''');
  }

  void test_instantiateToBounds_generic2_hasBound_definedAfter() {
    var unit = checkFile(r'''
class B<T extends A> {}
class A<T extends int> {}
B v = null;
''');
    expect(unit.topLevelVariables[0].type.toString(), 'B<A<dynamic>>');
  }

  void test_instantiateToBounds_generic2_hasBound_definedBefore() {
    var unit = checkFile(r'''
class A<T extends int> {}
class B<T extends A> {}
B v = null;
''');
    expect(unit.topLevelVariables[0].type.toString(), 'B<A<dynamic>>');
  }

  void test_instantiateToBounds_generic2_noBound() {
    var unit = checkFile(r'''
class A<T> {}
class B<T extends A> {}
B v = null;
''');
    expect(unit.topLevelVariables[0].type.toString(), 'B<A<dynamic>>');
  }

  void test_instantiateToBounds_generic_hasBound_definedAfter() {
    var unit = checkFile(r'''
A v = null;
class A<T extends int> {}
''');
    expect(unit.topLevelVariables[0].type.toString(), 'A<int>');
  }

  void test_instantiateToBounds_generic_hasBound_definedBefore() {
    var unit = checkFile(r'''
class A<T extends int> {}
A v = null;
''');
    expect(unit.topLevelVariables[0].type.toString(), 'A<int>');
  }

  void test_instantiateToBounds_invokeConstructor_noBound() {
    var unit = checkFile('''
class C<T> {}
var x = new C();
''');
    expect(unit.topLevelVariables[0].type.toString(), 'C<dynamic>');
  }

  void test_instantiateToBounds_invokeConstructor_typeArgsExact() {
    var unit = checkFile('''
class C<T extends num> {}
var x = new C<int>();
''');
    expect(unit.topLevelVariables[0].type.toString(), 'C<int>');
  }

  void test_instantiateToBounds_notGeneric() {
    var unit = checkFile(r'''
class A {}
class B<T extends A> {}
B v = null;
''');
    expect(unit.topLevelVariables[0].type.toString(), 'B<A>');
  }

  void test_lambdaDoesNotHavePropagatedTypeHint() {
    checkFile(r'''
List<String> getListOfString() => const <String>[];

void foo() {
  List myList = getListOfString();
  myList.map((type) => 42);
}

void bar() {
  var list;
  try {
    list = <String>[];
  } catch (_) {
    return;
  }
  /*info:DYNAMIC_INVOKE*/list.map((value) => '$value');
}
    ''');
  }

  void test_listLiterals() {
    checkFile(r'''
test1() {
  var x = /*info:INFERRED_TYPE_LITERAL*/[1, 2, 3];
  x.add(/*error:ARGUMENT_TYPE_NOT_ASSIGNABLE*/'hi');
  x.add(/*error:ARGUMENT_TYPE_NOT_ASSIGNABLE*/4.0);
  x.add(4);
  List<num> y = x;
}
test2() {
  var x = /*info:INFERRED_TYPE_LITERAL*/[1, 2.0, 3];
  x.add(/*error:ARGUMENT_TYPE_NOT_ASSIGNABLE*/'hi');
  x.add(4.0);
  List<int> y = /*info:ASSIGNMENT_CAST*/x;
}
  ''');
  }

  void test_listLiterals_topLevel() {
    checkFile(r'''
var x1 = /*info:INFERRED_TYPE_LITERAL*/[1, 2, 3];
test1() {
  x1.add(/*error:ARGUMENT_TYPE_NOT_ASSIGNABLE*/'hi');
  x1.add(/*error:ARGUMENT_TYPE_NOT_ASSIGNABLE*/4.0);
  x1.add(4);
  List<num> y = x1;
}
var x2 = /*info:INFERRED_TYPE_LITERAL*/[1, 2.0, 3];
test2() {
  x2.add(/*error:ARGUMENT_TYPE_NOT_ASSIGNABLE*/'hi');
  x2.add(4.0);
  List<int> y = /*info:ASSIGNMENT_CAST*/x2;
}
  ''');
  }

  void test_listLiteralsShouldNotInferBottom() {
    if (!mayCheckTypesOfLocals) {
      return;
    }
    var unit = checkFile(r'''
test1() {
  var x = [null];
  x.add(42);
}
''');
    var x = unit.functions[0].localVariables[0];
    expect(x.type.toString(), 'List<dynamic>');
  }

  void test_listLiteralsShouldNotInferBottom_topLevel() {
    var unit = checkFile(r'''
var x = [null];
''');
    var x = unit.topLevelVariables[0];
    expect(x.type.toString(), 'List<dynamic>');
  }

  void test_mapLiterals() {
    checkFile(r'''
test1() {
  var x = /*info:INFERRED_TYPE_LITERAL*/{ 1: 'x', 2: 'y' };
  x[3] = 'z';
  x[/*error:ARGUMENT_TYPE_NOT_ASSIGNABLE*/'hi'] = 'w';
  x[/*error:ARGUMENT_TYPE_NOT_ASSIGNABLE*/4.0] = 'u';
  x[3] = /*error:INVALID_ASSIGNMENT*/42;
  Map<num, String> y = x;
}

test2() {
  var x = /*info:INFERRED_TYPE_LITERAL*/{ 1: 'x', 2: 'y', 3.0: new RegExp('.') };
  x[3] = 'z';
  x[/*error:ARGUMENT_TYPE_NOT_ASSIGNABLE*/'hi'] = 'w';
  x[4.0] = 'u';
  x[3] = /*error:INVALID_ASSIGNMENT*/42;
  Pattern p = null;
  x[2] = p;
  Map<int, String> y = /*info:ASSIGNMENT_CAST*/x;
}
  ''');
  }

  void test_mapLiterals_topLevel() {
    checkFile(r'''
var x1 = /*info:INFERRED_TYPE_LITERAL*/{ 1: 'x', 2: 'y' };
test1() {
  x1[3] = 'z';
  x1[/*error:ARGUMENT_TYPE_NOT_ASSIGNABLE*/'hi'] = 'w';
  x1[/*error:ARGUMENT_TYPE_NOT_ASSIGNABLE*/4.0] = 'u';
  x1[3] = /*error:INVALID_ASSIGNMENT*/42;
  Map<num, String> y = x1;
}

var x2 = /*info:INFERRED_TYPE_LITERAL*/{ 1: 'x', 2: 'y', 3.0: new RegExp('.') };
test2() {
  x2[3] = 'z';
  x2[/*error:ARGUMENT_TYPE_NOT_ASSIGNABLE*/'hi'] = 'w';
  x2[4.0] = 'u';
  x2[3] = /*error:INVALID_ASSIGNMENT*/42;
  Pattern p = null;
  x2[2] = p;
  Map<int, String> y = /*info:ASSIGNMENT_CAST*/x2;
}
  ''');
  }

  void test_mapLiteralsShouldNotInferBottom() {
    if (!mayCheckTypesOfLocals) {
      return;
    }
    var unit = checkFile(r'''
test1() {
  var x = { null: null };
  x[3] = 'z';
}
''');
    var x = unit.functions[0].localVariables[0];
    expect(x.type.toString(), 'Map<dynamic, dynamic>');
  }

  void test_mapLiteralsShouldNotInferBottom_topLevel() {
    var unit = checkFile(r'''
var x = { null: null };
''');
    var x = unit.topLevelVariables[0];
    expect(x.type.toString(), 'Map<dynamic, dynamic>');
  }

  void test_methodCall_withTypeArguments_instanceMethod() {
    var mainUnit = checkFile('''
class C {
  D<T> f<T>() => null;
}
class D<T> {}
var f = new C().f<int>();
''');
    var v = mainUnit.topLevelVariables[0];
    expect(v.type.toString(), 'D<int>');
  }

  void test_methodCall_withTypeArguments_instanceMethod_comment() {
    var mainUnit = checkFile('''
class C {
  D/*<T>*/ f/*<T>*/() => null;
}
class D<T> {}
var f = new C().f/*<int>*/();
''');
    var v = mainUnit.topLevelVariables[0];
    expect(v.type.toString(), 'D<int>');
  }

  void test_methodCall_withTypeArguments_instanceMethod_identifierSequence() {
    var mainUnit = checkFile('''
class C {
  D<T> f<T>() => null;
}
class D<T> {}
C c;
var f = c.f<int>();
''');
    var v = mainUnit.topLevelVariables[1];
    expect(v.name, 'f');
    expect(v.type.toString(), 'D<int>');
  }

  void
      test_methodCall_withTypeArguments_instanceMethod_identifierSequence_comment() {
    var mainUnit = checkFile('''
class C {
  D/*<T>*/ f/*<T>*/() => null;
}
class D<T> {}
C c;
var f = c.f/*<int>*/();
''');
    var v = mainUnit.topLevelVariables[1];
    expect(v.name, 'f');
    expect(v.type.toString(), 'D<int>');
  }

  void test_methodCall_withTypeArguments_staticMethod() {
    var mainUnit = checkFile('''
class C {
  static D<T> f<T>() => null;
}
class D<T> {}
var f = C.f<int>();
''');
    var v = mainUnit.topLevelVariables[0];
    expect(v.type.toString(), 'D<int>');
  }

  void test_methodCall_withTypeArguments_staticMethod_comment() {
    var mainUnit = checkFile('''
class C {
  static D/*<T>*/ f/*<T>*/() => null;
}
class D<T> {}
var f = C.f/*<int>*/();
''');
    var v = mainUnit.topLevelVariables[0];
    expect(v.type.toString(), 'D<int>');
  }

  void test_methodCall_withTypeArguments_topLevelFunction() {
    var mainUnit = checkFile('''
D<T> f<T>() => null;
class D<T> {}
var g = f<int>();
''');
    var v = mainUnit.topLevelVariables[0];
    expect(v.type.toString(), 'D<int>');
  }

  void test_methodCall_withTypeArguments_topLevelFunction_comment() {
    var mainUnit = checkFile('''
D/*<T>*/ f/*<T>*/() => null;
class D<T> {}
var g = f/*<int>*/();
''');
    var v = mainUnit.topLevelVariables[0];
    expect(v.type.toString(), 'D<int>');
  }

  void test_noErrorWhenDeclaredTypeIsNumAndAssignedNull() {
    checkFile('''
test1() {
  num x = 3;
  x = null;
}
''');
  }

  void test_nullCoalescingOperator() {
    // Regression test for https://github.com/dart-lang/sdk/issues/26552
    checkFile(r'''
List<int> x;
var y = x ?? /*info:INFERRED_TYPE_LITERAL*/[];
List<int> z = y;
    ''');
    // Don't do anything if we already have a context type.
    var unit = checkFile(r'''
List<int> x;
List<num> y = x ?? /*info:INFERRED_TYPE_LITERAL*/[];
    ''');

    expect(unit.topLevelVariables[1].initializer.returnType.toString(),
        'List<num>');
  }

  void test_nullLiteralShouldNotInferAsBottom() {
    // Regression test for https://github.com/dart-lang/dev_compiler/issues/47
    checkFile(r'''
var h = null;
void foo(int f(Object _)) {}

main() {
  var f = (Object x) => null;
  String y = /*info:DYNAMIC_CAST*/f(42);

  f = /*info:INFERRED_TYPE_CLOSURE*/(x) => 'hello';

  var g = null;
  g = 'hello';
  (/*info:DYNAMIC_INVOKE*/g.foo());

  h = 'hello';
  (/*info:DYNAMIC_INVOKE*/h.foo());

  foo(/*info:INFERRED_TYPE_CLOSURE*/(x) => null);
  foo(/*info:INFERRED_TYPE_CLOSURE*/(x) => throw "not implemented");
}
''');
  }

  void test_propagateInferenceToFieldInClass() {
    checkFile('''
class A {
  int x = 2;
}

test() {
  var a = new A();
  A b = a;                      // doesn't require down cast
  print(a.x);     // doesn't require dynamic invoke
  print(a.x + 2); // ok to use in bigger expression
}
''');
  }

  void test_propagateInferenceToFieldInClassDynamicWarnings() {
    checkFile('''
class A {
  int x = 2;
}

test() {
  dynamic a = new A();
  A b = /*info:DYNAMIC_CAST*/a;
  print(/*info:DYNAMIC_INVOKE*/a.x);
  print(/*info:DYNAMIC_INVOKE*/(/*info:DYNAMIC_INVOKE*/a.x) + 2);
}
''');
  }

  void test_propagateInferenceTransitively() {
    checkFile('''
class A {
  int x = 2;
}

test5() {
  var a1 = new A();
  a1.x = /*error:INVALID_ASSIGNMENT*/"hi";

  A a2 = new A();
  a2.x = /*error:INVALID_ASSIGNMENT*/"hi";
}
''');
  }

  void test_propagateInferenceTransitively2() {
    checkFile('''
class A {
  int x = 42;
}

class B {
  A a = new A();
}

class C {
  B b = new B();
}

class D {
  C c = new C();
}

void main() {
  var d1 = new D();
  print(d1.c.b.a.x);

  D d2 = new D();
  print(d2.c.b.a.x);
}
''');
  }

  void test_referenceToFieldOfStaticField() {
    var mainUnit = checkFile('''
class C {
  static D d;
}
class D {
  int i;
}
final x = C.d.i;
''');
    var x = mainUnit.topLevelVariables[0];
    expect(x.type.toString(), 'int');
  }

  void test_referenceToFieldOfStaticGetter() {
    var mainUnit = checkFile('''
class C {
  static D get d => null;
}
class D {
  int i;
}
final x = C.d.i;
''');
    var x = mainUnit.topLevelVariables[0];
    expect(x.type.toString(), 'int');
  }

  void test_referenceToTypedef() {
    var mainUnit = checkFile('''
typedef void F();
final x = F;
''');
    var x = mainUnit.topLevelVariables[0];
    expect(x.type.toString(), 'Type');
  }

  void test_refineBinaryExpressionType_typeParameter_T_double() {
    checkFile('''
class C<T extends num> {
  T a;

  void op(double b) {
    double r1 = a + b;
    double r2 = a - b;
    double r3 = a * b;
    double r4 = a / b;
  }
}
  ''');
  }

  void test_refineBinaryExpressionType_typeParameter_T_int() {
    checkFile('''
class C<T extends num> {
  T a;

  void op(int b) {
    T r1 = a + b;
    T r2 = a - b;
    T r3 = a * b;
  }

  void opEq(int b) {
    a += b;
    a -= b;
    a *= b;
  }
}
  ''');
  }

  void test_refineBinaryExpressionType_typeParameter_T_T() {
    checkFile('''
class C<T extends num> {
  T a;

  void op(T b) {
    T r1 = a + b;
    T r2 = a - b;
    T r3 = a * b;
  }

  void opEq(T b) {
    a += b;
    a -= b;
    a *= b;
  }
}
  ''');
  }

  void test_staticMethod_tearoff() {
    var mainUnit = checkFile('''
const v = C.f;
class C {
  static int f(String s) => null;
}
''');
    var v = mainUnit.topLevelVariables[0];
    expect(v.type.toString(), '(String) → int');
  }

  void test_staticRefersToNonStaticField_inOtherLibraryCycle() {
    addFile(
        '''
import 'b.dart';
var x = new C().f;
''',
        name: '/a.dart');
    addFile(
        '''
class C {
  var f = 0;
}
''',
        name: '/b.dart');
    checkFile('''
import 'a.dart';
test() {
  x = /*error:INVALID_ASSIGNMENT*/"hi";
}
''');
  }

  void test_staticRefersToNonstaticField_inSameLibraryCycle() {
    addFile(
        '''
import 'b.dart';
var x = new C().f;
class D {
  var f = 0;
}
''',
        name: '/a.dart');
    addFile(
        '''
import 'a.dart';
var y = new D().f;
class C {
  var f = 0;
}
''',
        name: '/b.dart');
    checkFile('''
import 'a.dart';
import 'b.dart';
test() {
  x = "hi";
  y = "hi";
}
''');
  }

  void test_typeInferenceDependency_staticVariable_inIdentifierSequence() {
    // Check that type inference dependencies are properly checked when a static
    // variable appears in the middle of a string of identifiers separated by
    // '.'.
    var mainUnit = checkFile('''
final a = /*info:DYNAMIC_INVOKE*/C.d.i;
class C {
  static final d = new D(a);
}
class D {
  D(_);
  int i;
}
''');
    // No type should be inferred for a because there is a circular reference
    // between a and C.d.
    var a = mainUnit.topLevelVariables[0];
    expect(a.type.toString(), 'dynamic');
  }

  void test_typeInferenceDependency_topLevelVariable_inIdentifierSequence() {
    // Check that type inference dependencies are properly checked when a top
    // level variable appears at the beginning of a string of identifiers
    // separated by '.'.
    checkFile('''
final a = /*info:DYNAMIC_INVOKE*/c.i;
final c = new C(a);
class C {
  C(_);
  int i;
}
''');
    // No type should be inferred for a because there is a circular reference
    // between a and c.
  }

  void test_unsafeBlockClosureInference_closureCall() {
    // Regression test for https://github.com/dart-lang/sdk/issues/26962
    var mainUnit = checkFile('''
var v = ((x) => 1.0)(/*info:INFERRED_TYPE_CLOSURE*/() { return 1; });
''');
    var v = mainUnit.topLevelVariables[0];
    expect(v.name, 'v');
    expect(v.type.toString(), 'double');
  }

  void test_unsafeBlockClosureInference_constructorCall_explicitDynamicParam() {
    var mainUnit = checkFile('''
class C<T> {
  C(T x());
}
var v = new C<dynamic>(/*info:INFERRED_TYPE_CLOSURE*/() { return 1; });
''');
    var v = mainUnit.topLevelVariables[0];
    expect(v.name, 'v');
    expect(v.type.toString(), 'C<dynamic>');
  }

  void test_unsafeBlockClosureInference_constructorCall_explicitTypeParam() {
    var mainUnit = checkFile('''
class C<T> {
  C(T x());
}
var v = new C<int>(/*info:INFERRED_TYPE_CLOSURE*/() { return 1; });
''');
    var v = mainUnit.topLevelVariables[0];
    expect(v.name, 'v');
    expect(v.type.toString(), 'C<int>');
  }

  void test_unsafeBlockClosureInference_constructorCall_implicitTypeParam() {
    var mainUnit = checkFile('''
class C<T> {
  C(T x());
}
var v = /*info:INFERRED_TYPE_ALLOCATION*/new C(
  /*info:INFERRED_TYPE_CLOSURE,warning:UNSAFE_BLOCK_CLOSURE_INFERENCE*/() {
    return 1;
  });
''');
    var v = mainUnit.topLevelVariables[0];
    expect(v.name, 'v');
    expect(v.type.toString(), 'C<int>');
  }

  void test_unsafeBlockClosureInference_constructorCall_noTypeParam() {
    var mainUnit = checkFile('''
class C {
  C(x());
}
var v = new C(/*info:INFERRED_TYPE_CLOSURE*/() { return 1; });
''');
    var v = mainUnit.topLevelVariables[0];
    expect(v.name, 'v');
    expect(v.type.toString(), 'C');
  }

  void test_unsafeBlockClosureInference_functionCall_explicitDynamicParam() {
    var mainUnit = checkFile('''
List<T> f<T>(T g()) => <T>[g()];
var v = f<dynamic>(/*info:INFERRED_TYPE_CLOSURE*/() { return 1; });
''');
    var v = mainUnit.topLevelVariables[0];
    expect(v.name, 'v');
    expect(v.type.toString(), 'List<dynamic>');
  }

  void
      test_unsafeBlockClosureInference_functionCall_explicitDynamicParam_comment() {
    var mainUnit = checkFile('''
dynamic /*=List<T>*/ f/*<T>*/(dynamic/*=T*/ g()) => <T>[g()];
var v = f/*<dynamic>*/(/*info:INFERRED_TYPE_CLOSURE*/() { return 1; });
''');
    var v = mainUnit.topLevelVariables[0];
    expect(v.name, 'v');
    expect(v.type.toString(), 'List<dynamic>');
  }

  @failingTest
  void
      test_unsafeBlockClosureInference_functionCall_explicitDynamicParam_viaExpr1() {
    // Note: (f/*<dynamic>*/) is nort properly resulting in an instantiated
    // function type due to dartbug.com/25824.
    var mainUnit = checkFile('''
List<T> f<T>(T g()) => <T>[g()];
var v = (f<dynamic>)(/*info:INFERRED_TYPE_CLOSURE*/() { return 1; });
''');
    var v = mainUnit.topLevelVariables[0];
    expect(v.name, 'v');
    expect(v.type.toString(), 'List<dynamic>');
  }

  @failingTest
  void
      test_unsafeBlockClosureInference_functionCall_explicitDynamicParam_viaExpr1_comment() {
    // Note: (f/*<dynamic>*/) is nort properly resulting in an instantiated
    // function type due to dartbug.com/25824.
    var mainUnit = checkFile('''
dynamic /*=List<T>*/ f/*<T>*/(dynamic/*=T*/ g()) => <T>[g()];
var v = (f/*<dynamic>*/)(/*info:INFERRED_TYPE_CLOSURE*/() { return 1; });
''');
    var v = mainUnit.topLevelVariables[0];
    expect(v.name, 'v');
    expect(v.type.toString(), 'List<dynamic>');
  }

  void
      test_unsafeBlockClosureInference_functionCall_explicitDynamicParam_viaExpr2() {
    var mainUnit = checkFile('''
List<T> f<T>(T g()) => <T>[g()];
var v = (f)<dynamic>(/*info:INFERRED_TYPE_CLOSURE*/() { return 1; });
''');
    var v = mainUnit.topLevelVariables[0];
    expect(v.name, 'v');
    expect(v.type.toString(), 'List<dynamic>');
  }

  void
      test_unsafeBlockClosureInference_functionCall_explicitDynamicParam_viaExpr2_comment() {
    var mainUnit = checkFile('''
dynamic /*=List<T>*/ f/*<T>*/(dynamic/*=T*/ g()) => <T>[g()];
var v = (f)/*<dynamic>*/(/*info:INFERRED_TYPE_CLOSURE*/() { return 1; });
''');
    var v = mainUnit.topLevelVariables[0];
    expect(v.name, 'v');
    expect(v.type.toString(), 'List<dynamic>');
  }

  void test_unsafeBlockClosureInference_functionCall_explicitTypeParam() {
    var mainUnit = checkFile('''
List<T> f<T>(T g()) => <T>[g()];
var v = f<int>(/*info:INFERRED_TYPE_CLOSURE*/() { return 1; });
''');
    var v = mainUnit.topLevelVariables[0];
    expect(v.name, 'v');
    expect(v.type.toString(), 'List<int>');
  }

  void
      test_unsafeBlockClosureInference_functionCall_explicitTypeParam_comment() {
    var mainUnit = checkFile('''
dynamic /*=List<T>*/ f/*<T>*/(dynamic/*=T*/ g()) => <T>[g()];
var v = f/*<int>*/(/*info:INFERRED_TYPE_CLOSURE*/() { return 1; });
''');
    var v = mainUnit.topLevelVariables[0];
    expect(v.name, 'v');
    expect(v.type.toString(), 'List<int>');
  }

  @failingTest
  void
      test_unsafeBlockClosureInference_functionCall_explicitTypeParam_viaExpr1() {
    // TODO(paulberry): for some reason (f/*<int>) is nort properly resulting
    // in an instantiated function type.
    var mainUnit = checkFile('''
List<T> f<T>(T g()) => <T>[g()];
var v = (f/int>)(/*info:INFERRED_TYPE_CLOSURE*/() { return 1; });
''');
    var v = mainUnit.topLevelVariables[0];
    expect(v.name, 'v');
    expect(v.type.toString(), 'List<int>');
  }

  @failingTest
  void
      test_unsafeBlockClosureInference_functionCall_explicitTypeParam_viaExpr1_comment() {
    // TODO(paulberry): for some reason (f/*<int>) is nort properly resulting
    // in an instantiated function type.
    var mainUnit = checkFile('''
dynamic /*=List<T>*/ f/*<T>*/(dynamic/*=T*/ g()) => <T>[g()];
var v = (f/*<int>*/)(/*info:INFERRED_TYPE_CLOSURE*/() { return 1; });
''');
    var v = mainUnit.topLevelVariables[0];
    expect(v.name, 'v');
    expect(v.type.toString(), 'List<int>');
  }

  void
      test_unsafeBlockClosureInference_functionCall_explicitTypeParam_viaExpr2() {
    var mainUnit = checkFile('''
List<T> f<T>(T g()) => <T>[g()];
var v = (f)<int>(/*info:INFERRED_TYPE_CLOSURE*/() { return 1; });
''');
    var v = mainUnit.topLevelVariables[0];
    expect(v.name, 'v');
    expect(v.type.toString(), 'List<int>');
  }

  void
      test_unsafeBlockClosureInference_functionCall_explicitTypeParam_viaExpr2_comment() {
    var mainUnit = checkFile('''
dynamic /*=List<T>*/ f/*<T>*/(dynamic/*=T*/ g()) => <T>[g()];
var v = (f)/*<int>*/(/*info:INFERRED_TYPE_CLOSURE*/() { return 1; });
''');
    var v = mainUnit.topLevelVariables[0];
    expect(v.name, 'v');
    expect(v.type.toString(), 'List<int>');
  }

  void test_unsafeBlockClosureInference_functionCall_implicitTypeParam() {
    var mainUnit = checkFile('''
List<T> f<T>(T g()) => <T>[g()];
var v = f(
  /*info:INFERRED_TYPE_CLOSURE,warning:UNSAFE_BLOCK_CLOSURE_INFERENCE*/() {
    return 1;
  });
''');
    var v = mainUnit.topLevelVariables[0];
    expect(v.name, 'v');
    expect(v.type.toString(), 'List<int>');
  }

  void
      test_unsafeBlockClosureInference_functionCall_implicitTypeParam_comment() {
    var mainUnit = checkFile('''
dynamic /*=List<T>*/ f/*<T>*/(dynamic/*=T*/ g()) => <T>[g()];
var v = f(
  /*info:INFERRED_TYPE_CLOSURE,warning:UNSAFE_BLOCK_CLOSURE_INFERENCE*/() {
    return 1;
  });
''');
    var v = mainUnit.topLevelVariables[0];
    expect(v.name, 'v');
    expect(v.type.toString(), 'List<int>');
  }

  void
      test_unsafeBlockClosureInference_functionCall_implicitTypeParam_viaExpr() {
    var mainUnit = checkFile('''
List<T> f<T>(T g()) => <T>[g()];
var v = (f)(
  /*info:INFERRED_TYPE_CLOSURE,warning:UNSAFE_BLOCK_CLOSURE_INFERENCE*/() {
    return 1;
  });
''');
    var v = mainUnit.topLevelVariables[0];
    expect(v.name, 'v');
    expect(v.type.toString(), 'List<int>');
  }

  void
      test_unsafeBlockClosureInference_functionCall_implicitTypeParam_viaExpr_comment() {
    var mainUnit = checkFile('''
dynamic /*=List<T>*/ f/*<T>*/(dynamic/*=T*/ g()) => <T>[g()];
var v = (f)(
  /*info:INFERRED_TYPE_CLOSURE,warning:UNSAFE_BLOCK_CLOSURE_INFERENCE*/() {
    return 1;
  });
''');
    var v = mainUnit.topLevelVariables[0];
    expect(v.name, 'v');
    expect(v.type.toString(), 'List<int>');
  }

  void test_unsafeBlockClosureInference_functionCall_noTypeParam() {
    var mainUnit = checkFile('''
double f(x) => 1.0;
var v = f(/*info:INFERRED_TYPE_CLOSURE*/() { return 1; });
''');
    var v = mainUnit.topLevelVariables[0];
    expect(v.name, 'v');
    expect(v.type.toString(), 'double');
  }

  void test_unsafeBlockClosureInference_functionCall_noTypeParam_viaExpr() {
    var mainUnit = checkFile('''
double f(x) => 1.0;
var v = (f)(/*info:INFERRED_TYPE_CLOSURE*/() { return 1; });
''');
    var v = mainUnit.topLevelVariables[0];
    expect(v.name, 'v');
    expect(v.type.toString(), 'double');
  }

  void test_unsafeBlockClosureInference_inList_dynamic() {
    var mainUnit = checkFile('''
var v = <dynamic>[/*info:INFERRED_TYPE_CLOSURE*/() { return 1; }];
''');
    var v = mainUnit.topLevelVariables[0];
    expect(v.name, 'v');
    expect(v.type.toString(), 'List<dynamic>');
  }

  void test_unsafeBlockClosureInference_inList_typed() {
    var mainUnit = checkFile('''
typedef int F();
var v = <F>[/*info:INFERRED_TYPE_CLOSURE*/() { return 1; }];
''');
    var v = mainUnit.topLevelVariables[0];
    expect(v.name, 'v');
    expect(v.type.toString(), 'List<() → int>');
  }

  void test_unsafeBlockClosureInference_inList_untyped() {
    var mainUnit = checkFile('''
var v = /*info:INFERRED_TYPE_LITERAL*/[
  /*info:INFERRED_TYPE_CLOSURE,warning:UNSAFE_BLOCK_CLOSURE_INFERENCE*/() {
    return 1;
  }];
''');
    var v = mainUnit.topLevelVariables[0];
    expect(v.name, 'v');
    expect(v.type.toString(), 'List<() → int>');
  }

  void test_unsafeBlockClosureInference_inMap_dynamic() {
    var mainUnit = checkFile('''
var v = <int, dynamic>{1: /*info:INFERRED_TYPE_CLOSURE*/() { return 1; }};
''');
    var v = mainUnit.topLevelVariables[0];
    expect(v.name, 'v');
    expect(v.type.toString(), 'Map<int, dynamic>');
  }

  void test_unsafeBlockClosureInference_inMap_typed() {
    var mainUnit = checkFile('''
typedef int F();
var v = <int, F>{1: /*info:INFERRED_TYPE_CLOSURE*/() { return 1; }};
''');
    var v = mainUnit.topLevelVariables[0];
    expect(v.name, 'v');
    expect(v.type.toString(), 'Map<int, () → int>');
  }

  void test_unsafeBlockClosureInference_inMap_untyped() {
    var mainUnit = checkFile('''
var v = /*info:INFERRED_TYPE_LITERAL*/{
  1: /*info:INFERRED_TYPE_CLOSURE,warning:UNSAFE_BLOCK_CLOSURE_INFERENCE*/() {
    return 1;
  }};
''');
    var v = mainUnit.topLevelVariables[0];
    expect(v.name, 'v');
    expect(v.type.toString(), 'Map<int, () → int>');
  }

  void test_unsafeBlockClosureInference_methodCall_explicitDynamicParam() {
    var mainUnit = checkFile('''
class C {
  List<T> f<T>(T g()) => <T>[g()];
}
var v = new C().f<dynamic>(/*info:INFERRED_TYPE_CLOSURE*/() { return 1; });
''');
    var v = mainUnit.topLevelVariables[0];
    expect(v.name, 'v');
    expect(v.type.toString(), 'List<dynamic>');
  }

  void
      test_unsafeBlockClosureInference_methodCall_explicitDynamicParam_comment() {
    var mainUnit = checkFile('''
class C {
  dynamic /*=List<T>*/ f/*<T>*/(dynamic/*=T*/ g()) => <T>[g()];
}
var v = new C().f/*<dynamic>*/(/*info:INFERRED_TYPE_CLOSURE*/() { return 1; });
''');
    var v = mainUnit.topLevelVariables[0];
    expect(v.name, 'v');
    expect(v.type.toString(), 'List<dynamic>');
  }

  void test_unsafeBlockClosureInference_methodCall_explicitTypeParam() {
    var mainUnit = checkFile('''
class C {
  List<T> f<T>(T g()) => <T>[g()];
}
var v = new C().f<int>(/*info:INFERRED_TYPE_CLOSURE*/() { return 1; });
''');
    var v = mainUnit.topLevelVariables[0];
    expect(v.name, 'v');
    expect(v.type.toString(), 'List<int>');
  }

  void test_unsafeBlockClosureInference_methodCall_explicitTypeParam_comment() {
    var mainUnit = checkFile('''
class C {
  dynamic /*=List<T>*/ f/*<T>*/(dynamic/*=T*/ g()) => <T>[g()];
}
var v = new C().f/*<int>*/(/*info:INFERRED_TYPE_CLOSURE*/() { return 1; });
''');
    var v = mainUnit.topLevelVariables[0];
    expect(v.name, 'v');
    expect(v.type.toString(), 'List<int>');
  }

  void test_unsafeBlockClosureInference_methodCall_implicitTypeParam() {
    var mainUnit = checkFile('''
class C {
  List<T> f<T>(T g()) => <T>[g()];
}
var v = new C().f(
  /*info:INFERRED_TYPE_CLOSURE,warning:UNSAFE_BLOCK_CLOSURE_INFERENCE*/() {
    return 1;
  });
''');
    var v = mainUnit.topLevelVariables[0];
    expect(v.name, 'v');
    expect(v.type.toString(), 'List<int>');
  }

  void test_unsafeBlockClosureInference_methodCall_implicitTypeParam_comment() {
    var mainUnit = checkFile('''
class C {
  dynamic /*=List<T>*/ f/*<T>*/(dynamic/*=T*/ g()) => <T>[g()];
}
var v = new C().f(
  /*info:INFERRED_TYPE_CLOSURE,warning:UNSAFE_BLOCK_CLOSURE_INFERENCE*/() {
    return 1;
  });
''');
    var v = mainUnit.topLevelVariables[0];
    expect(v.name, 'v');
    expect(v.type.toString(), 'List<int>');
  }

  void test_unsafeBlockClosureInference_methodCall_noTypeParam() {
    var mainUnit = checkFile('''
class C {
  double f(x) => 1.0;
}
var v = new C().f(/*info:INFERRED_TYPE_CLOSURE*/() { return 1; });
''');
    var v = mainUnit.topLevelVariables[0];
    expect(v.name, 'v');
    expect(v.type.toString(), 'double');
  }

  void test_voidReturnTypeSubtypesDynamic() {
    var unit = checkFile(r'''
T run<T>(T f()) {
  print("running");
  var t = f();
  print("done running");
  return t;
}


void printRunning() { print("running"); }
var x = run<dynamic>(printRunning);
var y = run(printRunning);

main() {
  void printRunning() { print("running"); }
  var x = run<dynamic>(printRunning);
  var y = run(printRunning);
  x = 123;
  x = 'hi';
  y = 123;
  y = 'hi';
}
    ''');

    var x = unit.topLevelVariables[0];
    var y = unit.topLevelVariables[1];
    expect(x.type.toString(), 'dynamic');
    expect(y.type.toString(), 'dynamic');
  }

  void test_voidReturnTypeSubtypesDynamic_comment() {
    var unit = checkFile(r'''
/*=T*/ run/*<T>*/(/*=T*/ f()) {
  print("running");
  var t = f();
  print("done running");
  return t;
}


void printRunning() { print("running"); }
var x = run/*<dynamic>*/(printRunning);
var y = run(printRunning);

main() {
  void printRunning() { print("running"); }
  var x = run/*<dynamic>*/(printRunning);
  var y = run(printRunning);
  x = 123;
  x = 'hi';
  y = 123;
  y = 'hi';
}
    ''');

    var x = unit.topLevelVariables[0];
    var y = unit.topLevelVariables[1];
    expect(x.type.toString(), 'dynamic');
    expect(y.type.toString(), 'dynamic');
  }
}

@reflectiveTest
class InferredTypeTest extends InferredTypeMixin {
  @override
  bool get mayCheckTypesOfLocals => true;

  /// Adds a file to check. The file should contain:
  ///
  ///   * all expected failures are listed in the source code using comments
  ///     immediately in front of the AST node that should contain the error.
  ///
  ///   * errors are formatted as a token `severity:ErrorCode`, where
  ///     `severity` is the ErrorSeverity the error would be reported at, and
  ///     `ErrorCode` is the error code's name.
  ///
  /// For example to check that an assignment produces a type error, you can
  /// create a file like:
  ///
  ///     addFile('''
  ///       String x = /*error:STATIC_TYPE_ERROR*/3;
  ///     ''');
  ///     check();
  ///
  /// For a single file, you may also use [checkFile].
  @override
  void addFile(String content, {String name: '/main.dart'}) {
    helper.addFile(content, name: name);
  }

  /// Adds a file using [helper.addFile] and calls [helper.check].
  ///
  /// Also returns the resolved compilation unit.
  @override
  CompilationUnitElement checkFile(String content) {
    return helper.checkFile(content).element;
  }

  void setUp() {
    helper.doSetUp();
  }

  void tearDown() {
    helper.doTearDown();
  }
}
