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

import 'package:analyzer/src/dart/error/syntactic_errors.dart';
import 'package:analyzer/src/error/codes.dart';
import 'package:test_reflective_loader/test_reflective_loader.dart';

import 'context_collection_resolution.dart';

main() {
  defineReflectiveSuite(() {
    defineReflectiveTests(ConstructorReferenceResolutionTest);
    defineReflectiveTests(ConstructorReferenceResolutionTest_TypeArgs);
    defineReflectiveTests(
        ConstructorReferenceResolutionTest_WithoutConstructorTearoffs);
  });
}

@reflectiveTest
class ConstructorReferenceResolutionTest extends PubPackageResolutionTest {
  test_abstractClass_factory() async {
    await assertNoErrorsInCode('''
abstract class A {
  factory A() => A2();
}

class A2 implements A {}

foo() {
  A.new;
}
''');

    var node = findNode.constructorReference('A.new;');
    assertResolvedNodeText(node, r'''
ConstructorReference
  constructorName: ConstructorName
    type: NamedType
      name: A
      element2: <testLibrary>::@class::A
      type: null
    period: .
    name: SimpleIdentifier
      token: new
      element: <testLibraryFragment>::@class::A::@constructor::new#element
      staticType: null
    element: <testLibraryFragment>::@class::A::@constructor::new#element
  staticType: A Function()
''');
  }

  test_abstractClass_generative() async {
    await assertErrorsInCode('''
abstract class A {
  A();
}

foo() {
  A.new;
}
''', [
      error(
          CompileTimeErrorCode
              .TEAROFF_OF_GENERATIVE_CONSTRUCTOR_OF_ABSTRACT_CLASS,
          39,
          5),
    ]);

    var node = findNode.constructorReference('A.new;');
    assertResolvedNodeText(node, r'''
ConstructorReference
  constructorName: ConstructorName
    type: NamedType
      name: A
      element2: <testLibrary>::@class::A
      type: null
    period: .
    name: SimpleIdentifier
      token: new
      element: <testLibraryFragment>::@class::A::@constructor::new#element
      staticType: null
    element: <testLibraryFragment>::@class::A::@constructor::new#element
  staticType: A Function()
''');
  }

  test_abstractClass_redirecting() async {
    await assertErrorsInCode('''
abstract class A {
  A(): this.two();

  A.two();
}

foo() {
  A.new;
}
''', [
      error(
          CompileTimeErrorCode
              .TEAROFF_OF_GENERATIVE_CONSTRUCTOR_OF_ABSTRACT_CLASS,
          63,
          5),
    ]);

    var node = findNode.constructorReference('A.new;');
    assertResolvedNodeText(node, r'''
ConstructorReference
  constructorName: ConstructorName
    type: NamedType
      name: A
      element2: <testLibrary>::@class::A
      type: null
    period: .
    name: SimpleIdentifier
      token: new
      element: <testLibraryFragment>::@class::A::@constructor::new#element
      staticType: null
    element: <testLibraryFragment>::@class::A::@constructor::new#element
  staticType: A Function()
''');
  }

  test_class_generic_inferFromContext_badTypeArgument() async {
    await assertErrorsInCode('''
class A<T extends num> {
  A.foo();
}

A<String> Function() bar() {
  return A.foo;
}
''', [
      error(CompileTimeErrorCode.TYPE_ARGUMENT_NOT_MATCHING_BOUNDS, 41, 6,
          contextMessages: [message(testFile, 39, 9)]),
    ]);

    var node = findNode.constructorReference('A.foo;');
    assertResolvedNodeText(node, r'''
ConstructorReference
  constructorName: ConstructorName
    type: NamedType
      name: A
      element2: <testLibrary>::@class::A
      type: null
    period: .
    name: SimpleIdentifier
      token: foo
      element: <testLibraryFragment>::@class::A::@constructor::foo#element
      staticType: null
      tearOffTypeArgumentTypes
        Never
    element: <testLibraryFragment>::@class::A::@constructor::foo#element
  staticType: A<Never> Function()
''');
  }

  test_class_generic_named_inferTypeFromContext() async {
    await assertNoErrorsInCode('''
class A<T> {
  A.foo();
}

A<int> Function() bar() {
  return A.foo;
}
''');

    var node = findNode.constructorReference('A.foo;');
    assertResolvedNodeText(node, r'''
ConstructorReference
  constructorName: ConstructorName
    type: NamedType
      name: A
      element2: <testLibrary>::@class::A
      type: null
    period: .
    name: SimpleIdentifier
      token: foo
      element: <testLibraryFragment>::@class::A::@constructor::foo#element
      staticType: null
      tearOffTypeArgumentTypes
        int
    element: <testLibraryFragment>::@class::A::@constructor::foo#element
  staticType: A<int> Function()
''');
  }

  test_class_generic_named_uninstantiated() async {
    await assertNoErrorsInCode('''
class A<T> {
  A.foo();
}

void bar() {
  A.foo;
}
''');

    var node = findNode.constructorReference('A.foo;');
    assertResolvedNodeText(node, r'''
ConstructorReference
  constructorName: ConstructorName
    type: NamedType
      name: A
      element2: <testLibrary>::@class::A
      type: null
    period: .
    name: SimpleIdentifier
      token: foo
      element: <testLibraryFragment>::@class::A::@constructor::foo#element
      staticType: null
    element: <testLibraryFragment>::@class::A::@constructor::foo#element
  staticType: A<T> Function<T>()
''');
  }

  test_class_generic_named_uninstantiated_bound() async {
    await assertNoErrorsInCode('''
class A<T extends num> {
  A.foo();
}

void bar() {
  A.foo;
}
''');

    var node = findNode.constructorReference('A.foo;');
    assertResolvedNodeText(node, r'''
ConstructorReference
  constructorName: ConstructorName
    type: NamedType
      name: A
      element2: <testLibrary>::@class::A
      type: null
    period: .
    name: SimpleIdentifier
      token: foo
      element: <testLibraryFragment>::@class::A::@constructor::foo#element
      staticType: null
    element: <testLibraryFragment>::@class::A::@constructor::foo#element
  staticType: A<T> Function<T extends num>()
''');
  }

  test_class_nonGeneric_const() async {
    await assertNoErrorsInCode('''
class A {
  const A();
}

const a1 = A.new;
''');

    var node = findNode.constructorReference('A.new;');
    assertResolvedNodeText(node, r'''
ConstructorReference
  constructorName: ConstructorName
    type: NamedType
      name: A
      element2: <testLibrary>::@class::A
      type: null
    period: .
    name: SimpleIdentifier
      token: new
      element: <testLibraryFragment>::@class::A::@constructor::new#element
      staticType: null
    element: <testLibraryFragment>::@class::A::@constructor::new#element
  staticType: A Function()
''');
  }

  test_class_nonGeneric_named() async {
    await assertNoErrorsInCode('''
class A {
  A.foo();
}

void bar() {
  A.foo;
}
''');

    var node = findNode.constructorReference('A.foo;');
    assertResolvedNodeText(node, r'''
ConstructorReference
  constructorName: ConstructorName
    type: NamedType
      name: A
      element2: <testLibrary>::@class::A
      type: null
    period: .
    name: SimpleIdentifier
      token: foo
      element: <testLibraryFragment>::@class::A::@constructor::foo#element
      staticType: null
    element: <testLibraryFragment>::@class::A::@constructor::foo#element
  staticType: A Function()
''');
  }

  test_class_nonGeneric_unnamed() async {
    await assertNoErrorsInCode('''
class A {
  A();
}

bar() {
  A.new;
}
''');

    var node = findNode.constructorReference('A.new;');
    assertResolvedNodeText(node, r'''
ConstructorReference
  constructorName: ConstructorName
    type: NamedType
      name: A
      element2: <testLibrary>::@class::A
      type: null
    period: .
    name: SimpleIdentifier
      token: new
      element: <testLibraryFragment>::@class::A::@constructor::new#element
      staticType: null
    element: <testLibraryFragment>::@class::A::@constructor::new#element
  staticType: A Function()
''');
  }

  test_prefixedAlias_nonGeneric_named() async {
    newFile('$testPackageLibPath/a.dart', '''
class A {
  A.foo();
}
typedef TA = A;
''');
    await assertNoErrorsInCode('''
import 'a.dart' as a;
bar() {
  a.TA.foo;
}
''');

    var node = findNode.constructorReference('a.TA.foo;');
    assertResolvedNodeText(node, r'''
ConstructorReference
  constructorName: ConstructorName
    type: NamedType
      importPrefix: ImportPrefixReference
        name: a
        period: .
        element2: <testLibraryFragment>::@prefix2::a
      name: TA
      element2: package:test/a.dart::@typeAlias::TA
      type: null
    period: .
    name: SimpleIdentifier
      token: foo
      element: package:test/a.dart::<fragment>::@class::A::@constructor::foo#element
      staticType: null
    element: package:test/a.dart::<fragment>::@class::A::@constructor::foo#element
  staticType: A Function()
''');
  }

  test_prefixedAlias_nonGeneric_unnamed() async {
    newFile('$testPackageLibPath/a.dart', '''
class A {
  A();
}
typedef TA = A;
''');
    await assertNoErrorsInCode('''
import 'a.dart' as a;
bar() {
  a.TA.new;
}
''');

    var node = findNode.constructorReference('a.TA.new;');
    assertResolvedNodeText(node, r'''
ConstructorReference
  constructorName: ConstructorName
    type: NamedType
      importPrefix: ImportPrefixReference
        name: a
        period: .
        element2: <testLibraryFragment>::@prefix2::a
      name: TA
      element2: package:test/a.dart::@typeAlias::TA
      type: null
    period: .
    name: SimpleIdentifier
      token: new
      element: package:test/a.dart::<fragment>::@class::A::@constructor::new#element
      staticType: null
    element: package:test/a.dart::<fragment>::@class::A::@constructor::new#element
  staticType: A Function()
''');
  }

  test_prefixedClass_nonGeneric_named() async {
    newFile('$testPackageLibPath/a.dart', '''
class A {
  A.foo();
}
''');
    await assertNoErrorsInCode('''
import 'a.dart' as a;
bar() {
  a.A.foo;
}
''');

    var node = findNode.constructorReference('a.A.foo;');
    assertResolvedNodeText(node, r'''
ConstructorReference
  constructorName: ConstructorName
    type: NamedType
      importPrefix: ImportPrefixReference
        name: a
        period: .
        element2: <testLibraryFragment>::@prefix2::a
      name: A
      element2: package:test/a.dart::@class::A
      type: null
    period: .
    name: SimpleIdentifier
      token: foo
      element: package:test/a.dart::<fragment>::@class::A::@constructor::foo#element
      staticType: null
    element: package:test/a.dart::<fragment>::@class::A::@constructor::foo#element
  staticType: A Function()
''');
  }

  test_prefixedClass_nonGeneric_unnamed() async {
    newFile('$testPackageLibPath/a.dart', '''
class A {
  A();
}
''');
    await assertNoErrorsInCode('''
import 'a.dart' as a;
bar() {
  a.A.new;
}
''');

    var node = findNode.constructorReference('a.A.new;');
    assertResolvedNodeText(node, r'''
ConstructorReference
  constructorName: ConstructorName
    type: NamedType
      importPrefix: ImportPrefixReference
        name: a
        period: .
        element2: <testLibraryFragment>::@prefix2::a
      name: A
      element2: package:test/a.dart::@class::A
      type: null
    period: .
    name: SimpleIdentifier
      token: new
      element: package:test/a.dart::<fragment>::@class::A::@constructor::new#element
      staticType: null
    element: package:test/a.dart::<fragment>::@class::A::@constructor::new#element
  staticType: A Function()
''');
  }

  test_typeAlias_generic_const() async {
    await assertNoErrorsInCode('''
class A<T> {
  const A();
}
typedef TA<T> = A<T>;

const a = TA.new;
''');

    var node = findNode.constructorReference('TA.new;');
    assertResolvedNodeText(node, r'''
ConstructorReference
  constructorName: ConstructorName
    type: NamedType
      name: TA
      element2: <testLibrary>::@typeAlias::TA
      type: null
    period: .
    name: SimpleIdentifier
      token: new
      element: <testLibraryFragment>::@class::A::@constructor::new#element
      staticType: null
    element: <testLibraryFragment>::@class::A::@constructor::new#element
  staticType: A<T> Function<T>()
''');
  }

  test_typeAlias_generic_named_uninstantiated() async {
    await assertNoErrorsInCode('''
class A<T, U> {
  A.foo();
}
typedef TA<U> = A<String, U>;

bar() {
  TA.foo;
}
''');

    var node = findNode.constructorReference('TA.foo;');
    assertResolvedNodeText(node, r'''
ConstructorReference
  constructorName: ConstructorName
    type: NamedType
      name: TA
      element2: <testLibrary>::@typeAlias::TA
      type: null
    period: .
    name: SimpleIdentifier
      token: foo
      element: <testLibraryFragment>::@class::A::@constructor::foo#element
      staticType: null
    element: <testLibraryFragment>::@class::A::@constructor::foo#element
  staticType: A<String, U> Function<U>()
''');
  }

  test_typeAlias_instantiated_const() async {
    await assertNoErrorsInCode('''
class A<T> {
  const A();
}
typedef TA = A<int>;

const a = TA.new;
''');

    var node = findNode.constructorReference('TA.new;');
    assertResolvedNodeText(node, r'''
ConstructorReference
  constructorName: ConstructorName
    type: NamedType
      name: TA
      element2: <testLibrary>::@typeAlias::TA
      type: null
    period: .
    name: SimpleIdentifier
      token: new
      element: ConstructorMember
        baseElement: <testLibraryFragment>::@class::A::@constructor::new#element
        substitution: {T: int}
      staticType: null
    element: ConstructorMember
      baseElement: <testLibraryFragment>::@class::A::@constructor::new#element
      substitution: {T: int}
  staticType: A<int> Function()
''');
  }

  test_typeAlias_instantiated_named() async {
    await assertNoErrorsInCode('''
class A<T> {
  A.foo();
}
typedef TA = A<int>;

bar() {
  TA.foo;
}
''');

    var node = findNode.constructorReference('TA.foo;');
    assertResolvedNodeText(node, r'''
ConstructorReference
  constructorName: ConstructorName
    type: NamedType
      name: TA
      element2: <testLibrary>::@typeAlias::TA
      type: null
    period: .
    name: SimpleIdentifier
      token: foo
      element: ConstructorMember
        baseElement: <testLibraryFragment>::@class::A::@constructor::foo#element
        substitution: {T: int}
      staticType: null
    element: ConstructorMember
      baseElement: <testLibraryFragment>::@class::A::@constructor::foo#element
      substitution: {T: int}
  staticType: A<int> Function()
''');
  }
}

@reflectiveTest
class ConstructorReferenceResolutionTest_TypeArgs
    extends PubPackageResolutionTest {
  test_alias_generic_const() async {
    await assertNoErrorsInCode('''
class A<T, U> {
  const A.foo();
}
typedef TA<T, U> = A<U, T>;

const a = TA<int, String>.foo;
''');

    var node = findNode.constructorReference('TA<int, String>.foo;');
    assertResolvedNodeText(node, r'''
ConstructorReference
  constructorName: ConstructorName
    type: NamedType
      name: TA
      typeArguments: TypeArgumentList
        leftBracket: <
        arguments
          NamedType
            name: int
            element2: dart:core::@class::int
            type: int
          NamedType
            name: String
            element2: dart:core::@class::String
            type: String
        rightBracket: >
      element2: <testLibrary>::@typeAlias::TA
      type: null
    period: .
    name: SimpleIdentifier
      token: foo
      element: ConstructorMember
        baseElement: <testLibraryFragment>::@class::A::@constructor::foo#element
        substitution: {T: String, U: int}
      staticType: null
    element: ConstructorMember
      baseElement: <testLibraryFragment>::@class::A::@constructor::foo#element
      substitution: {T: String, U: int}
  staticType: A<String, int> Function()
''');
  }

  test_alias_generic_const_differingNumberOfTypeParameters() async {
    await assertNoErrorsInCode('''
class A<T, U> {
  A.foo() {}
}
typedef TA<T> = A<T, String>;

const x = TA<int>.foo;
''');

    var node = findNode.constructorReference('TA<int>.foo;');
    assertResolvedNodeText(node, r'''
ConstructorReference
  constructorName: ConstructorName
    type: NamedType
      name: TA
      typeArguments: TypeArgumentList
        leftBracket: <
        arguments
          NamedType
            name: int
            element2: dart:core::@class::int
            type: int
        rightBracket: >
      element2: <testLibrary>::@typeAlias::TA
      type: null
    period: .
    name: SimpleIdentifier
      token: foo
      element: ConstructorMember
        baseElement: <testLibraryFragment>::@class::A::@constructor::foo#element
        substitution: {T: int, U: String}
      staticType: null
    element: ConstructorMember
      baseElement: <testLibraryFragment>::@class::A::@constructor::foo#element
      substitution: {T: int, U: String}
  staticType: A<int, String> Function()
''');
  }

  test_alias_generic_named() async {
    await assertNoErrorsInCode('''
class A<T, U> {
  A.foo();
}
typedef TA<T, U> = A<U, T>;

void bar() {
  TA<int, String>.foo;
}
''');

    var node = findNode.constructorReference('TA<int, String>.foo;');
    assertResolvedNodeText(node, r'''
ConstructorReference
  constructorName: ConstructorName
    type: NamedType
      name: TA
      typeArguments: TypeArgumentList
        leftBracket: <
        arguments
          NamedType
            name: int
            element2: dart:core::@class::int
            type: int
          NamedType
            name: String
            element2: dart:core::@class::String
            type: String
        rightBracket: >
      element2: <testLibrary>::@typeAlias::TA
      type: null
    period: .
    name: SimpleIdentifier
      token: foo
      element: ConstructorMember
        baseElement: <testLibraryFragment>::@class::A::@constructor::foo#element
        substitution: {T: String, U: int}
      staticType: null
    element: ConstructorMember
      baseElement: <testLibraryFragment>::@class::A::@constructor::foo#element
      substitution: {T: String, U: int}
  staticType: A<String, int> Function()
''');
  }

  test_alias_generic_uninstantiated_const() async {
    await assertNoErrorsInCode('''
class A<T, U> {
  const A.foo();
}
typedef TA<T, U> = A<U, T>;

const a = TA.foo;
''');

    var node = findNode.constructorReference('TA.foo;');
    assertResolvedNodeText(node, r'''
ConstructorReference
  constructorName: ConstructorName
    type: NamedType
      name: TA
      element2: <testLibrary>::@typeAlias::TA
      type: null
    period: .
    name: SimpleIdentifier
      token: foo
      element: <testLibraryFragment>::@class::A::@constructor::foo#element
      staticType: null
    element: <testLibraryFragment>::@class::A::@constructor::foo#element
  staticType: A<U, T> Function<T, U>()
''');
  }

  test_alias_generic_unnamed() async {
    await assertNoErrorsInCode('''
class A<T> {
  A();
}
typedef TA<T> = A<T>;

void bar() {
  TA<int>.new;
}
''');

    var node = findNode.constructorReference('TA<int>.new;');
    assertResolvedNodeText(node, r'''
ConstructorReference
  constructorName: ConstructorName
    type: NamedType
      name: TA
      typeArguments: TypeArgumentList
        leftBracket: <
        arguments
          NamedType
            name: int
            element2: dart:core::@class::int
            type: int
        rightBracket: >
      element2: <testLibrary>::@typeAlias::TA
      type: null
    period: .
    name: SimpleIdentifier
      token: new
      element: ConstructorMember
        baseElement: <testLibraryFragment>::@class::A::@constructor::new#element
        substitution: {T: int}
      staticType: null
    element: ConstructorMember
      baseElement: <testLibraryFragment>::@class::A::@constructor::new#element
      substitution: {T: int}
  staticType: A<int> Function()
''');
  }

  test_alias_generic_with_inferred_type_parameter() async {
    await assertErrorsInCode('''
class C<T> {
  final T x;
  C(this.x);
}
typedef Direct<T> = C<T>;
void main() {
  var x = const <C<int> Function(int)>[Direct.new];
}
''', [
      error(WarningCode.UNUSED_LOCAL_VARIABLE, 87, 1),
    ]);
  }

  test_alias_genericWithBound_unnamed() async {
    await assertNoErrorsInCode('''
class A<T> {
  A();
}
typedef TA<T extends num> = A<T>;

void bar() {
  TA<int>.new;
}
''');

    var node = findNode.constructorReference('TA<int>.new;');
    assertResolvedNodeText(node, r'''
ConstructorReference
  constructorName: ConstructorName
    type: NamedType
      name: TA
      typeArguments: TypeArgumentList
        leftBracket: <
        arguments
          NamedType
            name: int
            element2: dart:core::@class::int
            type: int
        rightBracket: >
      element2: <testLibrary>::@typeAlias::TA
      type: null
    period: .
    name: SimpleIdentifier
      token: new
      element: ConstructorMember
        baseElement: <testLibraryFragment>::@class::A::@constructor::new#element
        substitution: {T: int}
      staticType: null
    element: ConstructorMember
      baseElement: <testLibraryFragment>::@class::A::@constructor::new#element
      substitution: {T: int}
  staticType: A<int> Function()
''');
  }

  test_alias_genericWithBound_unnamed_badBound() async {
    await assertErrorsInCode('''
class A<T> {
  A();
}
typedef TA<T extends num> = A<T>;

void bar() {
  TA<String>.new;
}
''', [
      error(CompileTimeErrorCode.TYPE_ARGUMENT_NOT_MATCHING_BOUNDS, 75, 6),
    ]);

    var node = findNode.constructorReference('TA<String>.new;');
    assertResolvedNodeText(node, r'''
ConstructorReference
  constructorName: ConstructorName
    type: NamedType
      name: TA
      typeArguments: TypeArgumentList
        leftBracket: <
        arguments
          NamedType
            name: String
            element2: dart:core::@class::String
            type: String
        rightBracket: >
      element2: <testLibrary>::@typeAlias::TA
      type: null
    period: .
    name: SimpleIdentifier
      token: new
      element: ConstructorMember
        baseElement: <testLibraryFragment>::@class::A::@constructor::new#element
        substitution: {T: String}
      staticType: null
    element: ConstructorMember
      baseElement: <testLibraryFragment>::@class::A::@constructor::new#element
      substitution: {T: String}
  staticType: A<String> Function()
''');
  }

  test_class_generic_const() async {
    await assertNoErrorsInCode('''
class A<T> {
  const A();
}

const a = A<int>.new;
''');

    var node = findNode.constructorReference('A<int>.new;');
    assertResolvedNodeText(node, r'''
ConstructorReference
  constructorName: ConstructorName
    type: NamedType
      name: A
      typeArguments: TypeArgumentList
        leftBracket: <
        arguments
          NamedType
            name: int
            element2: dart:core::@class::int
            type: int
        rightBracket: >
      element2: <testLibrary>::@class::A
      type: null
    period: .
    name: SimpleIdentifier
      token: new
      element: ConstructorMember
        baseElement: <testLibraryFragment>::@class::A::@constructor::new#element
        substitution: {T: int}
      staticType: null
    element: ConstructorMember
      baseElement: <testLibraryFragment>::@class::A::@constructor::new#element
      substitution: {T: int}
  staticType: A<int> Function()
''');
  }

  test_class_generic_named() async {
    await assertNoErrorsInCode('''
class A<T> {
  A.foo();
}

void bar() {
  A<int>.foo;
}
''');

    var node = findNode.constructorReference('A<int>.foo;');
    assertResolvedNodeText(node, r'''
ConstructorReference
  constructorName: ConstructorName
    type: NamedType
      name: A
      typeArguments: TypeArgumentList
        leftBracket: <
        arguments
          NamedType
            name: int
            element2: dart:core::@class::int
            type: int
        rightBracket: >
      element2: <testLibrary>::@class::A
      type: null
    period: .
    name: SimpleIdentifier
      token: foo
      element: ConstructorMember
        baseElement: <testLibraryFragment>::@class::A::@constructor::foo#element
        substitution: {T: int}
      staticType: null
    element: ConstructorMember
      baseElement: <testLibraryFragment>::@class::A::@constructor::foo#element
      substitution: {T: int}
  staticType: A<int> Function()
''');
  }

  test_class_generic_named_cascade() async {
    await assertErrorsInCode('''
class A<T> {
  A.foo();
}

void bar() {
  A<int>..foo;
}
''', [
      error(CompileTimeErrorCode.UNDEFINED_OPERATOR, 43, 1),
      error(ParserErrorCode.EQUALITY_CANNOT_BE_EQUALITY_OPERAND, 47, 1),
      error(ParserErrorCode.MISSING_IDENTIFIER, 48, 2),
    ]);
    // The parser produces nonsense here because the `<` disambiguates as a
    // relational operator, so no need to assert anything about analysis
    // results.
  }

  test_class_generic_named_nullAware() async {
    await assertErrorsInCode('''
class A<T> {
  A.foo();
}

void bar() {
  A<int>?.foo;
}
''', [
      error(CompileTimeErrorCode.UNDEFINED_OPERATOR, 43, 1),
      error(ParserErrorCode.EQUALITY_CANNOT_BE_EQUALITY_OPERAND, 47, 1),
      error(ParserErrorCode.MISSING_IDENTIFIER, 48, 2),
    ]);
    // The parser produces nonsense here because the `<` disambiguates as a
    // relational operator, so no need to assert anything about analysis
    // results.
  }

  test_class_generic_named_typeArgs() async {
    await assertErrorsInCode('''
class A<T> {
  A.foo();
}

void bar() {
  A<int>.foo<int>;
}
''', [
      error(CompileTimeErrorCode.WRONG_NUMBER_OF_TYPE_ARGUMENTS_CONSTRUCTOR, 52,
          5,
          messageContains: ["The constructor 'A.foo'"]),
    ]);

    var node = findNode.constructorReference('A<int>.foo<int>;');
    assertResolvedNodeText(node, r'''
ConstructorReference
  constructorName: ConstructorName
    type: NamedType
      name: A
      typeArguments: TypeArgumentList
        leftBracket: <
        arguments
          NamedType
            name: int
            element2: dart:core::@class::int
            type: int
        rightBracket: >
      element2: <testLibrary>::@class::A
      type: null
    period: .
    name: SimpleIdentifier
      token: foo
      element: ConstructorMember
        baseElement: <testLibraryFragment>::@class::A::@constructor::foo#element
        substitution: {T: int}
      staticType: null
    element: ConstructorMember
      baseElement: <testLibraryFragment>::@class::A::@constructor::foo#element
      substitution: {T: int}
  staticType: A<int> Function()
''');
  }

  test_class_generic_new_typeArgs() async {
    await assertErrorsInCode('''
class A<T> {
  A.new();
}

void bar() {
  A<int>.new<int>;
}
''', [
      error(CompileTimeErrorCode.WRONG_NUMBER_OF_TYPE_ARGUMENTS_CONSTRUCTOR, 52,
          5,
          messageContains: ["The constructor 'A.new'"]),
    ]);

    var node = findNode.constructorReference('A<int>.new<int>;');
    assertResolvedNodeText(node, r'''
ConstructorReference
  constructorName: ConstructorName
    type: NamedType
      name: A
      typeArguments: TypeArgumentList
        leftBracket: <
        arguments
          NamedType
            name: int
            element2: dart:core::@class::int
            type: int
        rightBracket: >
      element2: <testLibrary>::@class::A
      type: null
    period: .
    name: SimpleIdentifier
      token: new
      element: ConstructorMember
        baseElement: <testLibraryFragment>::@class::A::@constructor::new#element
        substitution: {T: int}
      staticType: null
    element: ConstructorMember
      baseElement: <testLibraryFragment>::@class::A::@constructor::new#element
      substitution: {T: int}
  staticType: A<int> Function()
''');
  }

  test_class_generic_nonConstructor() async {
    await assertErrorsInCode('''
class A<T> {
  static int i = 1;
}

void bar() {
  A<int>.i;
}
''', [
      error(CompileTimeErrorCode.CLASS_INSTANTIATION_ACCESS_TO_STATIC_MEMBER,
          51, 8),
    ]);

    var node = findNode.constructorReference('A<int>.i;');
    assertResolvedNodeText(node, r'''
ConstructorReference
  constructorName: ConstructorName
    type: NamedType
      name: A
      typeArguments: TypeArgumentList
        leftBracket: <
        arguments
          NamedType
            name: int
            element2: dart:core::@class::int
            type: int
        rightBracket: >
      element2: <testLibrary>::@class::A
      type: null
    period: .
    name: SimpleIdentifier
      token: i
      element: <null>
      staticType: null
    element: <null>
  staticType: InvalidType
''');
  }

  test_class_generic_nothing_hasNamedConstructor() async {
    await assertErrorsInCode('''
class A<T> {
  A.foo();
}

void bar() {
  A<int>.;
}
''', [
      error(ParserErrorCode.MISSING_IDENTIFIER, 49, 1),
    ]);

    var node = findNode.constructorReference('A<int>.;');
    assertResolvedNodeText(node, r'''
ConstructorReference
  constructorName: ConstructorName
    type: NamedType
      name: A
      typeArguments: TypeArgumentList
        leftBracket: <
        arguments
          NamedType
            name: int
            element2: dart:core::@class::int
            type: int
        rightBracket: >
      element2: <testLibrary>::@class::A
      type: null
    period: .
    name: SimpleIdentifier
      token: <empty> <synthetic>
      element: <null>
      staticType: null
    element: <null>
  staticType: InvalidType
''');
  }

  test_class_generic_unnamed() async {
    await assertNoErrorsInCode('''
class A<T> {
  A();
}

void bar() {
  A<int>.new;
}
''');

    var node = findNode.constructorReference('A<int>.new;');
    assertResolvedNodeText(node, r'''
ConstructorReference
  constructorName: ConstructorName
    type: NamedType
      name: A
      typeArguments: TypeArgumentList
        leftBracket: <
        arguments
          NamedType
            name: int
            element2: dart:core::@class::int
            type: int
        rightBracket: >
      element2: <testLibrary>::@class::A
      type: null
    period: .
    name: SimpleIdentifier
      token: new
      element: ConstructorMember
        baseElement: <testLibraryFragment>::@class::A::@constructor::new#element
        substitution: {T: int}
      staticType: null
    element: ConstructorMember
      baseElement: <testLibraryFragment>::@class::A::@constructor::new#element
      substitution: {T: int}
  staticType: A<int> Function()
''');
  }

  test_class_generic_unnamed_partOfPropertyAccess() async {
    await assertNoErrorsInCode('''
class A<T> {
  A();
}

void bar() {
  A<int>.new.runtimeType;
}
''');

    var node = findNode.constructorReference('A<int>.new');
    assertResolvedNodeText(node, r'''
ConstructorReference
  constructorName: ConstructorName
    type: NamedType
      name: A
      typeArguments: TypeArgumentList
        leftBracket: <
        arguments
          NamedType
            name: int
            element2: dart:core::@class::int
            type: int
        rightBracket: >
      element2: <testLibrary>::@class::A
      type: null
    period: .
    name: SimpleIdentifier
      token: new
      element: ConstructorMember
        baseElement: <testLibraryFragment>::@class::A::@constructor::new#element
        substitution: {T: int}
      staticType: null
    element: ConstructorMember
      baseElement: <testLibraryFragment>::@class::A::@constructor::new#element
      substitution: {T: int}
  staticType: A<int> Function()
''');
  }

  test_class_genericWithBound_unnamed() async {
    await assertNoErrorsInCode('''
class A<T extends num> {
  A();
}

void bar() {
  A<int>.new;
}
''');

    var node = findNode.constructorReference('A<int>.new;');
    assertResolvedNodeText(node, r'''
ConstructorReference
  constructorName: ConstructorName
    type: NamedType
      name: A
      typeArguments: TypeArgumentList
        leftBracket: <
        arguments
          NamedType
            name: int
            element2: dart:core::@class::int
            type: int
        rightBracket: >
      element2: <testLibrary>::@class::A
      type: null
    period: .
    name: SimpleIdentifier
      token: new
      element: ConstructorMember
        baseElement: <testLibraryFragment>::@class::A::@constructor::new#element
        substitution: {T: int}
      staticType: null
    element: ConstructorMember
      baseElement: <testLibraryFragment>::@class::A::@constructor::new#element
      substitution: {T: int}
  staticType: A<int> Function()
''');
  }

  test_class_genericWithBound_unnamed_badBound() async {
    await assertErrorsInCode('''
class A<T extends num> {
  A();
}

void bar() {
  A<String>.new;
}
''', [
      error(CompileTimeErrorCode.TYPE_ARGUMENT_NOT_MATCHING_BOUNDS, 52, 6),
    ]);

    var node = findNode.constructorReference('A<String>.new;');
    assertResolvedNodeText(node, r'''
ConstructorReference
  constructorName: ConstructorName
    type: NamedType
      name: A
      typeArguments: TypeArgumentList
        leftBracket: <
        arguments
          NamedType
            name: String
            element2: dart:core::@class::String
            type: String
        rightBracket: >
      element2: <testLibrary>::@class::A
      type: null
    period: .
    name: SimpleIdentifier
      token: new
      element: ConstructorMember
        baseElement: <testLibraryFragment>::@class::A::@constructor::new#element
        substitution: {T: String}
      staticType: null
    element: ConstructorMember
      baseElement: <testLibraryFragment>::@class::A::@constructor::new#element
      substitution: {T: String}
  staticType: A<String> Function()
''');
  }

  test_prefixedAlias_generic_unnamed() async {
    newFile('$testPackageLibPath/a.dart', '''
class A<T> {
  A();
}
typedef TA<T> = A<T>;
''');
    await assertNoErrorsInCode('''
import 'a.dart' as a;
void bar() {
  a.TA<int>.new;
}
''');

    var node = findNode.constructorReference('a.TA<int>.new;');
    assertResolvedNodeText(node, r'''
ConstructorReference
  constructorName: ConstructorName
    type: NamedType
      importPrefix: ImportPrefixReference
        name: a
        period: .
        element2: <testLibraryFragment>::@prefix2::a
      name: TA
      typeArguments: TypeArgumentList
        leftBracket: <
        arguments
          NamedType
            name: int
            element2: dart:core::@class::int
            type: int
        rightBracket: >
      element2: package:test/a.dart::@typeAlias::TA
      type: null
    period: .
    name: SimpleIdentifier
      token: new
      element: ConstructorMember
        baseElement: package:test/a.dart::<fragment>::@class::A::@constructor::new#element
        substitution: {T: int}
      staticType: null
    element: ConstructorMember
      baseElement: package:test/a.dart::<fragment>::@class::A::@constructor::new#element
      substitution: {T: int}
  staticType: A<int> Function()
''');
  }

  test_prefixedClass_generic_named() async {
    newFile('$testPackageLibPath/a.dart', '''
class A<T> {
  A.foo();
}
''');
    await assertNoErrorsInCode('''
import 'a.dart' as a;
void bar() {
  a.A<int>.foo;
}
''');

    var node = findNode.constructorReference('a.A<int>.foo;');
    assertResolvedNodeText(node, r'''
ConstructorReference
  constructorName: ConstructorName
    type: NamedType
      importPrefix: ImportPrefixReference
        name: a
        period: .
        element2: <testLibraryFragment>::@prefix2::a
      name: A
      typeArguments: TypeArgumentList
        leftBracket: <
        arguments
          NamedType
            name: int
            element2: dart:core::@class::int
            type: int
        rightBracket: >
      element2: package:test/a.dart::@class::A
      type: null
    period: .
    name: SimpleIdentifier
      token: foo
      element: ConstructorMember
        baseElement: package:test/a.dart::<fragment>::@class::A::@constructor::foo#element
        substitution: {T: int}
      staticType: null
    element: ConstructorMember
      baseElement: package:test/a.dart::<fragment>::@class::A::@constructor::foo#element
      substitution: {T: int}
  staticType: A<int> Function()
''');
  }

  test_prefixedClass_generic_targetOfFunctionCall() async {
    newFile('$testPackageLibPath/a.dart', '''
class A<T> {
  A();
}
''');
    await assertNoErrorsInCode('''
import 'a.dart' as a;
extension on Function {
  void m() {}
}
void bar() {
  a.A<int>.new.m();
}
''');

    var node = findNode.constructorReference('a.A<int>.new');
    assertResolvedNodeText(node, r'''
ConstructorReference
  constructorName: ConstructorName
    type: NamedType
      importPrefix: ImportPrefixReference
        name: a
        period: .
        element2: <testLibraryFragment>::@prefix2::a
      name: A
      typeArguments: TypeArgumentList
        leftBracket: <
        arguments
          NamedType
            name: int
            element2: dart:core::@class::int
            type: int
        rightBracket: >
      element2: package:test/a.dart::@class::A
      type: null
    period: .
    name: SimpleIdentifier
      token: new
      element: ConstructorMember
        baseElement: package:test/a.dart::<fragment>::@class::A::@constructor::new#element
        substitution: {T: int}
      staticType: null
    element: ConstructorMember
      baseElement: package:test/a.dart::<fragment>::@class::A::@constructor::new#element
      substitution: {T: int}
  staticType: A<int> Function()
''');
  }

  test_prefixedClass_generic_unnamed() async {
    newFile('$testPackageLibPath/a.dart', '''
class A<T> {
  A();
}
''');
    await assertNoErrorsInCode('''
import 'a.dart' as a;
void bar() {
  a.A<int>.new;
}
''');

    var node = findNode.constructorReference('a.A<int>.new;');
    assertResolvedNodeText(node, r'''
ConstructorReference
  constructorName: ConstructorName
    type: NamedType
      importPrefix: ImportPrefixReference
        name: a
        period: .
        element2: <testLibraryFragment>::@prefix2::a
      name: A
      typeArguments: TypeArgumentList
        leftBracket: <
        arguments
          NamedType
            name: int
            element2: dart:core::@class::int
            type: int
        rightBracket: >
      element2: package:test/a.dart::@class::A
      type: null
    period: .
    name: SimpleIdentifier
      token: new
      element: ConstructorMember
        baseElement: package:test/a.dart::<fragment>::@class::A::@constructor::new#element
        substitution: {T: int}
      staticType: null
    element: ConstructorMember
      baseElement: package:test/a.dart::<fragment>::@class::A::@constructor::new#element
      substitution: {T: int}
  staticType: A<int> Function()
''');
  }
}

@reflectiveTest
class ConstructorReferenceResolutionTest_WithoutConstructorTearoffs
    extends PubPackageResolutionTest with WithoutConstructorTearoffsMixin {
  test_class_generic_nonConstructor() async {
    await assertErrorsInCode('''
class A<T> {
  static int i = 1;
}

void bar() {
  A<int>.i;
}
''', [
      error(ParserErrorCode.EXPERIMENT_NOT_ENABLED, 52, 5),
    ]);

    var node = findNode.constructorReference('A<int>.i;');
    assertResolvedNodeText(node, r'''
ConstructorReference
  constructorName: ConstructorName
    type: NamedType
      name: A
      typeArguments: TypeArgumentList
        leftBracket: <
        arguments
          NamedType
            name: int
            element2: dart:core::@class::int
            type: int
        rightBracket: >
      element2: <testLibrary>::@class::A
      type: null
    period: .
    name: SimpleIdentifier
      token: i
      element: <null>
      staticType: null
    element: <null>
  staticType: InvalidType
''');
  }

  test_constructorTearoff() async {
    await assertErrorsInCode('''
class A {
  A.foo();
}

void bar() {
  A.foo;
}
''', [
      error(WarningCode.SDK_VERSION_CONSTRUCTOR_TEAROFFS, 39, 5),
    ]);

    var node = findNode.constructorReference('A.foo;');
    assertResolvedNodeText(node, r'''
ConstructorReference
  constructorName: ConstructorName
    type: NamedType
      name: A
      element2: <testLibrary>::@class::A
      type: null
    period: .
    name: SimpleIdentifier
      token: foo
      element: <testLibraryFragment>::@class::A::@constructor::foo#element
      staticType: null
    element: <testLibraryFragment>::@class::A::@constructor::foo#element
  staticType: A Function()
''');
  }
}
