// 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:test_reflective_loader/test_reflective_loader.dart';

import 'context_collection_resolution.dart';

main() {
  defineReflectiveSuite(() {
    defineReflectiveTests(FieldFormalParameterResolutionTest);
  });
}

@reflectiveTest
class FieldFormalParameterResolutionTest extends PubPackageResolutionTest {
  test_class_functionTyped() async {
    await assertNoErrorsInCode(r'''
class A {
  Function f;
  A(void this.f(int a));
}
''');

    var node = findNode.singleFieldFormalParameter;
    assertResolvedNodeText(node, r'''
FieldFormalParameter
  type: NamedType
    name: void
    element: <null>
    type: void
  thisKeyword: this
  period: .
  name: f
  parameters: FormalParameterList
    leftParenthesis: (
    parameter: SimpleFormalParameter
      type: NamedType
        name: int
        element: dart:core::@class::int
        type: int
      name: a
      declaredFragment: <testLibraryFragment> a@44
        element: isPublic
          type: int
    rightParenthesis: )
  declaredFragment: <testLibraryFragment> f@38
    element: isFinal isPublic
      type: void Function(int)
      field: <testLibrary>::@class::A::@field::f
''');
  }

  /// There was a crash.
  /// https://github.com/dart-lang/sdk/issues/46968
  test_class_functionTyped_hasTypeParameters() async {
    await assertNoErrorsInCode(r'''
class A {
  T Function<T>(T) f;
  A(U this.f<U>(U a));
}
''');

    var node = findNode.singleFieldFormalParameter;
    assertResolvedNodeText(node, r'''
FieldFormalParameter
  type: NamedType
    name: U
    element: #E0 U
    type: U
  thisKeyword: this
  period: .
  name: f
  typeParameters: TypeParameterList
    leftBracket: <
    typeParameters
      TypeParameter
        name: U
        declaredFragment: <testLibraryFragment> U@45
          defaultType: null
    rightBracket: >
  parameters: FormalParameterList
    leftParenthesis: (
    parameter: SimpleFormalParameter
      type: NamedType
        name: U
        element: #E0 U
        type: U
      name: a
      declaredFragment: <testLibraryFragment> a@50
        element: isPublic
          type: U
    rightParenthesis: )
  declaredFragment: <testLibraryFragment> f@43
    element: isFinal isPublic
      type: U Function<U>(U)
      field: <testLibrary>::@class::A::@field::f
''');
  }

  test_class_functionTyped_hasTypeParameters2() async {
    await assertNoErrorsInCode(r'''
class A<V> {
  T Function<T, U>(U, V) f;
  A(T this.f<T, U>(U a, V b));
}
''');

    var node = findNode.singleFieldFormalParameter;
    assertResolvedNodeText(node, r'''
FieldFormalParameter
  type: NamedType
    name: T
    element: #E0 T
    type: T
  thisKeyword: this
  period: .
  name: f
  typeParameters: TypeParameterList
    leftBracket: <
    typeParameters
      TypeParameter
        name: T
        declaredFragment: <testLibraryFragment> T@54
          defaultType: null
      TypeParameter
        name: U
        declaredFragment: <testLibraryFragment> U@57
          defaultType: null
    rightBracket: >
  parameters: FormalParameterList
    leftParenthesis: (
    parameter: SimpleFormalParameter
      type: NamedType
        name: U
        element: #E1 U
        type: U
      name: a
      declaredFragment: <testLibraryFragment> a@62
        element: isPublic
          type: U
    parameter: SimpleFormalParameter
      type: NamedType
        name: V
        element: #E2 V
        type: V
      name: b
      declaredFragment: <testLibraryFragment> b@67
        element: isPublic
          type: V
    rightParenthesis: )
  declaredFragment: <testLibraryFragment> f@52
    element: isFinal isPublic
      type: T Function<T, U>(U, V)
      field: <testLibrary>::@class::A::@field::f
''');
  }

  test_class_simple() async {
    await assertNoErrorsInCode(r'''
class A {
  int f;
  A(this.f);
}
''');

    var node = findNode.singleFieldFormalParameter;
    assertResolvedNodeText(node, r'''
FieldFormalParameter
  thisKeyword: this
  period: .
  name: f
  declaredFragment: <testLibraryFragment> f@28
    element: hasImplicitType isFinal isPublic
      type: int
      field: <testLibrary>::@class::A::@field::f
''');
  }

  test_class_simple_typed() async {
    await assertNoErrorsInCode(r'''
class A {
  int f;
  A(int this.f);
}
''');

    var node = findNode.singleFieldFormalParameter;
    assertResolvedNodeText(node, r'''
FieldFormalParameter
  type: NamedType
    name: int
    element: dart:core::@class::int
    type: int
  thisKeyword: this
  period: .
  name: f
  declaredFragment: <testLibraryFragment> f@32
    element: isFinal isPublic
      type: int
      field: <testLibrary>::@class::A::@field::f
''');
  }

  test_enum() async {
    await assertNoErrorsInCode(r'''
enum E {
  v(0);
  final int f;
  const E(this.f);
}
''');

    var node = findNode.fieldFormalParameter('this.f');
    assertResolvedNodeText(node, r'''
FieldFormalParameter
  thisKeyword: this
  period: .
  name: f
  declaredFragment: <testLibraryFragment> f@47
    element: hasImplicitType isFinal isPublic
      type: int
      field: <testLibrary>::@enum::E::@field::f
''');
  }
}
