// Copyright (c) 2025, 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/dart/ast/ast.dart';
import 'package:analyzer/dart/element/element2.dart';
import 'package:analyzer/src/error/codes.dart';
import 'package:analyzer/src/generated/parser.dart' show ParserErrorCode;
import 'package:test/test.dart';
import 'package:test_reflective_loader/test_reflective_loader.dart';

import '../../src/dart/resolution/context_collection_resolution.dart';

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

@reflectiveTest
class FragmentOffsetTest extends PubPackageResolutionTest {
  void checkOffset<F extends Fragment>(
      AstNode declaration, Fragment fragment, int expectedOffset) {
    var offset = checkOffsetInRange<F>(declaration, fragment);
    expect(offset, expectedOffset);
  }

  /// Checks that the offset is in the range of the declaration, but doesn't
  /// check the precise offset.
  ///
  /// Used in the case of error recovery, where the precise offset is
  /// unspecified.
  int checkOffsetInRange<F extends Fragment>(
      AstNode declaration, Fragment fragment) {
    expect(fragment, isA<F>());
    var offset = fragment.offset;
    expect(offset, greaterThanOrEqualTo(declaration.offset));
    expect(offset, lessThanOrEqualTo(declaration.end));
    return offset;
  }

  test_bindPatternVariableFragment() async {
    await assertNoErrorsInCode(r'''
void f() {
  // ignore: unused_local_variable
  var (int i,) = (0,);
}
''');
    var declaredVariablePattern = findNode.declaredVariablePattern('int i');
    checkOffset<BindPatternVariableFragment>(
        declaredVariablePattern,
        declaredVariablePattern.declaredFragment!,
        declaredVariablePattern.name.offset);
  }

  test_classFragment() async {
    await assertNoErrorsInCode(r'''
class C {}
''');
    var classDeclaration = findNode.classDeclaration('C');
    checkOffset<ClassFragment>(classDeclaration,
        classDeclaration.declaredFragment!, classDeclaration.name.offset);
  }

  test_classFragment_classTypeAlias() async {
    await assertNoErrorsInCode(r'''
mixin M {}
class C = Object with M;
''');
    var classTypeAlias = findNode.classTypeAlias('C');
    checkOffset<ClassFragment>(classTypeAlias, classTypeAlias.declaredFragment!,
        classTypeAlias.name.offset);
  }

  test_classFragment_classTypeAlias_missingName() async {
    await assertErrorsInCode(r'''
mixin M {}
class = Object with M;
''', [
      error(ParserErrorCode.MISSING_IDENTIFIER, 17, 1),
    ]);
    var classTypeAlias = findNode.classTypeAlias('Object with M');
    checkOffsetInRange<ClassFragment>(
        classTypeAlias, classTypeAlias.declaredFragment!);
  }

  test_classFragment_missingName() async {
    await assertErrorsInCode(r'''
library; // Ensures that the class declaration isn't at offset 0

class {}
''', [
      error(ParserErrorCode.MISSING_IDENTIFIER, 72, 1),
    ]);
    var classDeclaration = findNode.classDeclaration('class {}');
    checkOffsetInRange<ClassFragment>(
        classDeclaration, classDeclaration.declaredFragment!);
  }

  test_constructorFragment_implicit() async {
    await assertNoErrorsInCode(r'''
class C {}
''');
    var classDeclaration = findNode.classDeclaration('C');
    checkOffset<ConstructorFragment>(
        classDeclaration,
        classDeclaration
            .declaredFragment!.element.unnamedConstructor2!.firstFragment,
        classDeclaration.name.offset);
  }

  test_constructorFragment_missingName() async {
    await assertErrorsInCode(r'''
class C {
  C.();
}
''', [
      error(ParserErrorCode.MISSING_IDENTIFIER, 14, 1),
    ]);
    var constructorDeclaration = findNode.constructor('C.()');
    checkOffsetInRange<ConstructorFragment>(
        constructorDeclaration, constructorDeclaration.declaredFragment!);
  }

  test_constructorFragment_named() async {
    await assertNoErrorsInCode(r'''
class C {
  C.foo();
}
''');
    var constructorDeclaration = findNode.constructor('foo');
    checkOffset<ConstructorFragment>(
        constructorDeclaration,
        constructorDeclaration.declaredFragment!,
        constructorDeclaration.name!.offset);
  }

  test_constructorFragment_unnamed() async {
    await assertNoErrorsInCode(r'''
class C {
  C();
}
''');
    var constructorDeclaration = findNode.constructor('C()');
    checkOffset<ConstructorFragment>(
        constructorDeclaration,
        constructorDeclaration.declaredFragment!,
        constructorDeclaration.returnType.offset);
  }

  test_dynamicFragment() async {
    await assertNoErrorsInCode(r'''
dynamic d;
''');
    var namedType = findNode
        .topLevelVariableDeclaration('dynamic d')
        .variables
        .type as NamedType;
    expect(namedType.element2!.kind, ElementKind.DYNAMIC);
    // `dynamic` isn't defined in the source code anywhere, so its offset is 0.
    expect(namedType.element2!.firstFragment.offset, 0);
  }

  test_enumFragment() async {
    await assertNoErrorsInCode(r'''
enum E { e1 }
''');
    var enumDeclaration = findNode.enumDeclaration('E');
    checkOffset<EnumFragment>(enumDeclaration,
        enumDeclaration.declaredFragment!, enumDeclaration.name.offset);
  }

  test_enumFragment_missingName() async {
    await assertErrorsInCode(r'''
library; // Ensures that the enum declaration isn't at offset 0

enum { e1 }
''', [
      error(ParserErrorCode.MISSING_IDENTIFIER, 70, 1),
    ]);
    var enumDeclaration = findNode.enumDeclaration('enum { e1 }');
    checkOffsetInRange<EnumFragment>(
        enumDeclaration, enumDeclaration.declaredFragment!);
  }

  test_extensionFragment_named() async {
    await assertNoErrorsInCode(r'''
library; // Ensures that the extension declaration isn't at offset 0

/// Documentation comment
extension E on int {}
''');
    var extensionDeclaration = findNode.extensionDeclaration('on int');
    checkOffset<ExtensionFragment>(
        extensionDeclaration,
        extensionDeclaration.declaredFragment!,
        extensionDeclaration.name!.offset);
  }

  test_extensionFragment_unnamed() async {
    await assertNoErrorsInCode(r'''
extension on int {}
''');
    var extensionDeclaration = findNode.extensionDeclaration('on int');
    checkOffset<ExtensionFragment>(
        extensionDeclaration,
        extensionDeclaration.declaredFragment!,
        extensionDeclaration.extensionKeyword.offset);
  }

  test_extensionTypeFragment() async {
    await assertNoErrorsInCode(r'''
extension type E(int i) {}
''');
    var extensionTypeDeclaration = findNode.extensionTypeDeclaration('E');
    checkOffset<ExtensionTypeFragment>(
        extensionTypeDeclaration,
        extensionTypeDeclaration.declaredFragment!,
        extensionTypeDeclaration.name.offset);
  }

  test_extensionTypeFragment_missingName() async {
    await assertErrorsInCode(r'''
library; // Ensures that the extension type declaration isn't at offset 0

extension type(int i) {}
''', [
      error(ParserErrorCode.MISSING_IDENTIFIER, 89, 1),
    ]);
    var extensionTypeDeclaration =
        findNode.extensionTypeDeclaration('extension type(int i)');
    checkOffsetInRange<ExtensionTypeFragment>(
        extensionTypeDeclaration, extensionTypeDeclaration.declaredFragment!);
  }

  test_fieldFormalParameterFragment() async {
    await assertNoErrorsInCode(r'''
class C {
  final int i;
  C(this.i);
}
''');
    var parameter = findNode.fieldFormalParameter('this.i');
    checkOffset<FieldFormalParameterFragment>(
        parameter, parameter.declaredFragment!, parameter.name.offset);
  }

  test_fieldFormalParameterFragment_missingName() async {
    await assertErrorsInCode(r'''
class C {
  int? x;
  C(this.);
}
''', [
      error(CompileTimeErrorCode.INITIALIZING_FORMAL_FOR_NON_EXISTENT_FIELD, 24,
          5),
      error(ParserErrorCode.MISSING_IDENTIFIER, 29, 1),
    ]);
    var parameter = findNode.fieldFormalParameter('this.');
    checkOffsetInRange<FieldFormalParameterFragment>(
        parameter, parameter.declaredFragment!);
  }

  test_fieldFormalParameterFragment_withDefaultValue() async {
    await assertNoErrorsInCode(r'''
class C {
  final int i;
  C({this.i = 0});
}
''');
    var parameter = findNode.fieldFormalParameter('this.i');
    checkOffset<FieldFormalParameterFragment>(
        parameter, parameter.declaredFragment!, parameter.name.offset);
  }

  test_fieldFragment() async {
    await assertNoErrorsInCode(r'''
class C {
  int? x;
}
''');
    var fieldDeclaration = findNode.fieldDeclaration('x').fields.variables[0];
    checkOffset<FieldFragment>(fieldDeclaration,
        fieldDeclaration.declaredFragment!, fieldDeclaration.name.offset);
  }

  test_fieldFragment_const() async {
    await assertNoErrorsInCode(r'''
class C {
  static const int x = 0;
}
''');
    var fieldDeclaration = findNode.fieldDeclaration('x').fields.variables[0];
    checkOffset<FieldFragment>(fieldDeclaration,
        fieldDeclaration.declaredFragment!, fieldDeclaration.name.offset);
  }

  test_fieldFragment_enum_constant() async {
    await assertNoErrorsInCode(r'''
enum E { e1 }
''');
    var enumConstantDeclaration = findNode.enumConstantDeclaration('e1');
    checkOffset<FieldFragment>(
        enumConstantDeclaration,
        enumConstantDeclaration.declaredFragment!,
        enumConstantDeclaration.name.offset);
  }

  test_fieldFragment_enum_values() async {
    await assertNoErrorsInCode(r'''
enum E { e1 }
''');
    var enumDeclaration = findNode.enumDeclaration('E');
    checkOffset<FieldFragment>(
        enumDeclaration,
        enumDeclaration.declaredFragment!.element
            .getField2('values')!
            .firstFragment,
        enumDeclaration.name.offset);
  }

  test_fieldFragment_extensionTypeRepresentationField() async {
    await assertNoErrorsInCode(r'''
extension type E(int i) {}
''');
    var representationDeclaration =
        findNode.extensionTypeDeclaration('int i').representation;
    checkOffset<FieldFragment>(
        representationDeclaration,
        representationDeclaration.fieldFragment!,
        representationDeclaration.fieldName.offset);
  }

  test_formalParameterFragment() async {
    await assertNoErrorsInCode(r'''
void f(int x) {}
''');
    var parameter = findNode.simpleFormalParameter('x');
    checkOffset<FormalParameterFragment>(
        parameter, parameter.declaredFragment!, parameter.name!.offset);
  }

  test_formalParameterFragment_missingName() async {
    await assertErrorsInCode(r'''
void f((int x)) {}
''', [
      error(ParserErrorCode.MISSING_IDENTIFIER, 7, 1),
    ]);
    var function = findNode.functionDeclaration('f(');
    var parameter = function.functionExpression.parameters!.parameters[0]
        as FunctionTypedFormalParameter;
    expect(parameter.name.isSynthetic, true);
    checkOffsetInRange<FormalParameterFragment>(
        parameter, parameter.declaredFragment!);
  }

  test_formalParameterFragment_missingName2() async {
    await assertErrorsInCode(r'''
void f(void (int x)) {}
''', [
      error(ParserErrorCode.MISSING_IDENTIFIER, 12, 1),
    ]);
    var function = findNode.functionDeclaration('f(');
    var parameter = function.functionExpression.parameters!.parameters[0]
        as FunctionTypedFormalParameter;
    expect(parameter.name.isSynthetic, true);
    checkOffsetInRange<FormalParameterFragment>(
        parameter, parameter.declaredFragment!);
  }

  test_formalParameterFragment_ofImplicitSetter_member_implicit() async {
    await assertNoErrorsInCode(r'''
class C {
  int? x;
}
''');
    var fieldDeclaration = findNode.fieldDeclaration('x').fields.variables[0];
    checkOffset<FormalParameterFragment>(
        fieldDeclaration,
        (fieldDeclaration.declaredFragment as FieldFragment)
            .element
            .setter2!
            .formalParameters
            .single
            .firstFragment,
        fieldDeclaration.name.offset);
  }

  test_formalParameterFragment_ofImplicitSetter_topLevel_implicit() async {
    await assertNoErrorsInCode(r'''
int? x;
''');
    var topLevelVariableDeclaration =
        findNode.topLevelVariableDeclaration('x').variables.variables[0];
    checkOffset<FormalParameterFragment>(
        topLevelVariableDeclaration,
        (topLevelVariableDeclaration.declaredFragment
                as TopLevelVariableFragment)
            .element
            .setter2!
            .formalParameters
            .single
            .firstFragment,
        topLevelVariableDeclaration.name.offset);
  }

  test_formalParameterFragment_withDefaultValue() async {
    await assertNoErrorsInCode(r'''
void f({int x = 0}) {}
''');
    var parameter = findNode.simpleFormalParameter('x');
    checkOffset<FormalParameterFragment>(
        parameter, parameter.declaredFragment!, parameter.name!.offset);
  }

  test_genericFunctionTypeFragment() async {
    await assertNoErrorsInCode(r'''
library; // Ensures that the generic function type isn't at offset 0

void Function(int x)? f;
''');
    var genericFunctionType =
        findNode.genericFunctionType('void Function(int x)?');
    checkOffset<GenericFunctionTypeFragment>(genericFunctionType,
        genericFunctionType.declaredFragment!, genericFunctionType.offset);
  }

  test_getterFragment_member() async {
    await assertNoErrorsInCode(r'''
class C {
  int get foo => 0;
}
''');
    var getterDeclaration = findNode.methodDeclaration('foo');
    checkOffset<GetterFragment>(getterDeclaration,
        getterDeclaration.declaredFragment!, getterDeclaration.name.offset);
  }

  test_getterFragment_member_implicit() async {
    await assertNoErrorsInCode(r'''
class C {
  int? x;
}
''');
    var fieldDeclaration = findNode.fieldDeclaration('x').fields.variables[0];
    checkOffset<GetterFragment>(
        fieldDeclaration,
        (fieldDeclaration.declaredFragment as FieldFragment)
            .element
            .getter2!
            .firstFragment,
        fieldDeclaration.name.offset);
  }

  test_getterFragment_topLevel() async {
    await assertNoErrorsInCode(r'''
int get foo => 0;
''');
    var getterDeclaration = findNode.functionDeclaration('foo');
    checkOffset<GetterFragment>(getterDeclaration,
        getterDeclaration.declaredFragment!, getterDeclaration.name.offset);
  }

  test_getterFragment_topLevel_implicit() async {
    await assertNoErrorsInCode(r'''
int? x;
''');
    var topLevelVariableDeclaration =
        findNode.topLevelVariableDeclaration('x').variables.variables[0];
    checkOffset<GetterFragment>(
        topLevelVariableDeclaration,
        (topLevelVariableDeclaration.declaredFragment
                as TopLevelVariableFragment)
            .element
            .getter2!
            .firstFragment,
        topLevelVariableDeclaration.name.offset);
  }

  test_joinPatternVariableFragment() async {
    await assertNoErrorsInCode(r'''
void f() {
  switch ((0,) as dynamic) {
    // ignore: unused_local_variable
    case (var i,) || (:var i,):
      break;
  }
}
''');
    var firstDeclaredVariablePattern =
        findNode.declaredVariablePattern('var i,) ||');
    checkOffset<JoinPatternVariableFragment>(
        firstDeclaredVariablePattern,
        firstDeclaredVariablePattern.declaredFragment!.join2!,
        firstDeclaredVariablePattern.name.offset);
  }

  test_labelFragment() async {
    await assertNoErrorsInCode(r'''
void f() {
  // ignore: unused_label
  L: while(true) {}
}
''');
    var label = findNode.label('L');
    checkOffset<LabelFragment>(
        label, label.declaredFragment!, label.label.offset);
  }

  test_libraryFragment_first_named() async {
    await assertNoErrorsInCode(r'''
// Comment to ensure that the library declaration isn't at offset 0
library L;

class C {}
''');
    var unit = findNode.unit;
    checkOffset<LibraryFragment>(
        unit, unit.declaredFragment!, findNode.library('L').name2!.offset);
  }

  test_libraryFragment_first_unnamed_withLibraryDeclaration() async {
    await assertNoErrorsInCode(r'''
// Comment to ensure that the library declaration isn't at offset 0
library;

class C {}
''');
    var unit = findNode.unit;
    checkOffset<LibraryFragment>(unit, unit.declaredFragment!, 0);
  }

  test_libraryFragment_first_unnamed_withoutLibraryDeclaration() async {
    await assertNoErrorsInCode(r'''
// Comment to ensure that the class declaration isn't at offset 0
class C {}
''');
    var unit = findNode.unit;
    checkOffset<LibraryFragment>(unit, unit.declaredFragment!, 0);
  }

  test_libraryFragment_notFirst_named() async {
    newFile('$testPackageLibPath/lib.dart', r'''
library L;

part 'part.dart';
''');
    newFile('$testPackageLibPath/part.dart', r'''
// Comment to ensure that the "part of" declaration isn't at offset 0
part of 'lib.dart';

class C {}
''');
    await resolveFile2(getFile('$testPackageLibPath/part.dart'));
    assertErrorsInResolvedUnit(result, const []);

    var unit = findNode.unit;
    checkOffset<LibraryFragment>(unit, unit.declaredFragment!, 0);
  }

  test_libraryFragment_notFirst_unnamed() async {
    newFile('$testPackageLibPath/lib.dart', r'''
part 'part.dart';
''');
    newFile('$testPackageLibPath/part.dart', r'''
// Comment to ensure that the "part of" declaration isn't at offset 0
part of 'lib.dart';

class C {}
''');
    await resolveFile2(getFile('$testPackageLibPath/part.dart'));
    assertErrorsInResolvedUnit(result, const []);

    var unit = findNode.unit;
    checkOffset<LibraryFragment>(unit, unit.declaredFragment!, 0);
  }

  test_local_variable_fragment() async {
    await assertNoErrorsInCode(r'''
void f() {
  // ignore: unused_local_variable
  int i = 0;
}
''');
    var localVariable = findNode.variableDeclaration('i = 0');
    checkOffset<LocalVariableFragment>(localVariable,
        localVariable.declaredFragment!, localVariable.name.offset);
  }

  test_local_variable_fragment_const() async {
    await assertNoErrorsInCode(r'''
void f() {
  // ignore: unused_local_variable
  const int i = 0;
}
''');
    var localVariable = findNode.variableDeclaration('i = 0');
    checkOffset<LocalVariableFragment>(localVariable,
        localVariable.declaredFragment!, localVariable.name.offset);
  }

  test_localFunctionFragment_named() async {
    await assertNoErrorsInCode(r'''
void f() {
  // ignore: unused_element
  void g() {}
}
''');
    var localFunction =
        findNode.functionDeclarationStatement('g()').functionDeclaration;
    checkOffset<LocalFunctionFragment>(localFunction,
        localFunction.declaredFragment!, localFunction.name.offset);
  }

  test_localFunctionFragment_unnamed() async {
    await assertNoErrorsInCode(r'''
dynamic f() {
  return () {};
}
''');
    var localFunction = findNode.functionExpression('() {}');
    checkOffset<LocalFunctionFragment>(
        localFunction,
        localFunction.declaredFragment!,
        localFunction.parameters!.leftParenthesis.offset);
  }

  test_methodFragment() async {
    await assertNoErrorsInCode(r'''
class C {
  void foo() {}
}
''');
    var methodDeclaration = findNode.methodDeclaration('foo');
    checkOffset<MethodFragment>(methodDeclaration,
        methodDeclaration.declaredFragment!, methodDeclaration.name.offset);
  }

  test_mixinFragment() async {
    await assertNoErrorsInCode(r'''
mixin M {}
''');
    var mixinDeclaration = findNode.mixinDeclaration('M');
    checkOffset<MixinFragment>(mixinDeclaration,
        mixinDeclaration.declaredFragment!, mixinDeclaration.name.offset);
  }

  test_mixinFragment_missingName() async {
    await assertErrorsInCode(r'''
library; // Ensures that the mixin declaration isn't at offset 0

mixin {}
''', [
      error(ParserErrorCode.MISSING_IDENTIFIER, 72, 1),
    ]);
    var mixinDeclaration = findNode.mixinDeclaration('mixin {}');
    checkOffsetInRange<MixinFragment>(
        mixinDeclaration, mixinDeclaration.declaredFragment!);
  }

  test_neverFragment() async {
    await assertNoErrorsInCode(r'''
Never n = throw '';
''');
    var namedType = findNode
        .topLevelVariableDeclaration('Never n')
        .variables
        .type as NamedType;
    expect(namedType.element2!.kind, ElementKind.NEVER);
    // `Never` isn't defined in the source code anywhere, so its offset is 0.
    expect(namedType.element2!.firstFragment.offset, 0);
  }

  test_prefixFragment() async {
    await assertNoErrorsInCode(r'''
// ignore: unused_import
import 'dart:async' as a;
''');
    var importDirective = findNode.import('as a');
    checkOffset<PrefixFragment>(
        importDirective,
        importDirective.libraryImport!.prefix2!,
        importDirective.prefix!.offset);
  }

  test_prefixFragment_inMultipleImports() async {
    await assertNoErrorsInCode(r'''
// ignore: unused_import
import 'dart:async' as a; // first
// ignore: unused_import
import 'dart:math' as a; // second
''');
    var firstImportDirective = findNode.import('as a; // first');
    checkOffset<PrefixFragment>(
        firstImportDirective,
        firstImportDirective.libraryImport!.prefix2!,
        firstImportDirective.prefix!.offset);
  }

  test_prefixFragment_missingName() async {
    await assertErrorsInCode(r'''
// ignore: unused_import
import 'dart:async' as;
''', [
      error(ParserErrorCode.MISSING_IDENTIFIER, 47, 1),
    ]);
    var importDirective = findNode.import('as;');
    checkOffsetInRange<PrefixFragment>(
        importDirective, importDirective.libraryImport!.prefix2!);
  }

  test_setterFragment_member() async {
    await assertNoErrorsInCode(r'''
class C {
  set foo(int value) {}
}
''');
    var setterDeclaration = findNode.methodDeclaration('foo');
    checkOffset<SetterFragment>(setterDeclaration,
        setterDeclaration.declaredFragment!, setterDeclaration.name.offset);
  }

  test_setterFragment_member_implicit() async {
    await assertNoErrorsInCode(r'''
class C {
  int? x;
}
''');
    var fieldDeclaration = findNode.fieldDeclaration('x').fields.variables[0];
    checkOffset<SetterFragment>(
        fieldDeclaration,
        (fieldDeclaration.declaredFragment as FieldFragment)
            .element
            .setter2!
            .firstFragment,
        fieldDeclaration.name.offset);
  }

  test_setterFragment_topLevel() async {
    await assertNoErrorsInCode(r'''
set foo(int value) {}
''');
    var setterDeclaration = findNode.functionDeclaration('foo');
    checkOffset<SetterFragment>(setterDeclaration,
        setterDeclaration.declaredFragment!, setterDeclaration.name.offset);
  }

  test_setterFragment_topLevel_implicit() async {
    await assertNoErrorsInCode(r'''
int? x;
''');
    var topLevelVariableDeclaration =
        findNode.topLevelVariableDeclaration('x').variables.variables[0];
    checkOffset<SetterFragment>(
        topLevelVariableDeclaration,
        (topLevelVariableDeclaration.declaredFragment
                as TopLevelVariableFragment)
            .element
            .setter2!
            .firstFragment,
        topLevelVariableDeclaration.name.offset);
  }

  test_superFormalParameterFragment() async {
    await assertNoErrorsInCode(r'''
class B {
  B(int i);
}

class C extends B {
  C(super.i);
}
''');
    var parameter = findNode.superFormalParameter('super.i');
    checkOffset<SuperFormalParameterFragment>(
        parameter, parameter.declaredFragment!, parameter.name.offset);
  }

  test_superFormalParameterFragment_missingName() async {
    await assertErrorsInCode(r'''
class B {
  B([int? i]);
}

class C extends B {
  C(super.);
}
''', [
      error(ParserErrorCode.MISSING_IDENTIFIER, 58, 1),
    ]);
    var parameter = findNode.superFormalParameter('super.');
    checkOffsetInRange<SuperFormalParameterFragment>(
        parameter, parameter.declaredFragment!);
  }

  test_superFormalParameterFragment_withDefaultValue() async {
    await assertNoErrorsInCode(r'''
class B {
  B({int? i});
}

class C extends B {
  C({super.i = 0});
}
''');
    var parameter = findNode.superFormalParameter('super.i');
    checkOffset<SuperFormalParameterFragment>(
        parameter, parameter.declaredFragment!, parameter.name.offset);
  }

  test_topLevelFunctionFragment() async {
    await assertNoErrorsInCode(r'''
void foo() {}
''');
    var topLevelFunctionDeclaration = findNode.functionDeclaration('foo');
    checkOffset<TopLevelFunctionFragment>(
        topLevelFunctionDeclaration,
        topLevelFunctionDeclaration.declaredFragment!,
        topLevelFunctionDeclaration.name.offset);
  }

  test_topLevelVariableFragment() async {
    await assertNoErrorsInCode(r'''
int? x;
''');
    var topLevelVariableDeclaration =
        findNode.topLevelVariableDeclaration('x').variables.variables[0];
    checkOffset<TopLevelVariableFragment>(
        topLevelVariableDeclaration,
        topLevelVariableDeclaration.declaredFragment!,
        topLevelVariableDeclaration.name.offset);
  }

  test_topLevelVariableFragment_const() async {
    await assertNoErrorsInCode(r'''
const int x = 0;
''');
    var topLevelVariableDeclaration =
        findNode.topLevelVariableDeclaration('x').variables.variables[0];
    checkOffset<TopLevelVariableFragment>(
        topLevelVariableDeclaration,
        topLevelVariableDeclaration.declaredFragment!,
        topLevelVariableDeclaration.name.offset);
  }

  test_typeAliasFragment_functionTypeAlias() async {
    await assertNoErrorsInCode(r'''
typedef void F();
''');
    var functionTypeAlias = findNode.functionTypeAlias('F');
    checkOffset<TypeAliasFragment>(functionTypeAlias,
        functionTypeAlias.declaredFragment!, functionTypeAlias.name.offset);
  }

  test_typeAliasFragment_functionTypeAlias_missingName() async {
    await assertErrorsInCode(r'''
library; // Ensures that the function type alias declaration isn't at offset 0

typedef void();
''', [
      error(WarningCode.UNUSED_ELEMENT, 0, 0),
      error(ParserErrorCode.MISSING_IDENTIFIER, 92, 1),
    ]);
    var functionTypeAlias = findNode.functionTypeAlias('void()');
    checkOffsetInRange<TypeAliasFragment>(
        functionTypeAlias, functionTypeAlias.declaredFragment!);
  }

  test_typeAliasFragment_genericTypeAlias() async {
    await assertNoErrorsInCode(r'''
typedef T = int;
''');
    var genericTypeAlias = findNode.genericTypeAlias('T');
    checkOffset<TypeAliasFragment>(genericTypeAlias,
        genericTypeAlias.declaredFragment!, genericTypeAlias.name.offset);
  }

  test_typeAliasFragment_genericTypeAlias_missingName() async {
    await assertErrorsInCode(r'''
library; // Ensures that the generic type alias declaration isn't at offset 0

typedef = int;
''', [
      error(WarningCode.UNUSED_ELEMENT, 0, 0),
      error(ParserErrorCode.MISSING_IDENTIFIER, 87, 1),
    ]);
    var genericTypeAlias = findNode.genericTypeAlias('= int');
    checkOffsetInRange<TypeAliasFragment>(
        genericTypeAlias, genericTypeAlias.declaredFragment!);
  }

  test_typeParameterFragment() async {
    await assertNoErrorsInCode(r'''
class C<T> {}
''');
    var typeParameter = findNode.typeParameter('T');
    checkOffset<TypeParameterFragment>(typeParameter,
        typeParameter.declaredFragment!, typeParameter.name.offset);
  }
}
