// Copyright (c) 2020, 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/element/element.dart';
import 'package:analyzer/file_system/file_system.dart';
import 'package:analyzer/source/line_info.dart';
import 'package:analyzer/src/dart/analysis/file_state.dart';
import 'package:analyzer/src/dart/error/syntactic_errors.dart';
import 'package:analyzer/src/dart/micro/resolve_file.dart';
import 'package:analyzer/src/dart/micro/utils.dart';
import 'package:analyzer/src/error/codes.dart';
import 'package:linter/src/lint_codes.dart';
import 'package:test/test.dart';
import 'package:test_reflective_loader/test_reflective_loader.dart';

import '../resolution/node_text_expectations.dart';
import 'file_resolution.dart';

main() {
  defineReflectiveSuite(() {
    defineReflectiveTests(FileResolver_changeFiles_Test);
    defineReflectiveTests(FileResolverTest);
    defineReflectiveTests(UpdateNodeTextExpectations);
  });
}

@reflectiveTest
class FileResolver_changeFiles_Test extends FileResolutionTest {
  test_changeFile_refreshedFiles() async {
    var a = newFile('$testPackageLibPath/a.dart', r'''
class A {}
''');

    newFile('$testPackageLibPath/b.dart', r'''
class B {}
''');

    var c = newFile('$testPackageLibPath/c.dart', r'''
import 'a.dart';
import 'b.dart';
''');

    // First time we refresh everything.
    await resolveFile(c);

    assertStateString(r'''
files
  /workspace/dart/test/lib/a.dart
    uri: package:dart.test/a.dart
    current
      id: file_0
      kind: library_0
        libraryImports
          library_3 dart:core synthetic
        fileKinds: library_0
        cycle_0
          dependencies: dart:core
          libraries: library_0
          apiSignature_0
          users: cycle_2
      referencingFiles: file_2
      unlinkedKey: k00
    unlinkedGet: []
    unlinkedPut: [k00]
  /workspace/dart/test/lib/b.dart
    uri: package:dart.test/b.dart
    current
      id: file_1
      kind: library_1
        libraryImports
          library_3 dart:core synthetic
        fileKinds: library_1
        cycle_1
          dependencies: dart:core
          libraries: library_1
          apiSignature_1
          users: cycle_2
      referencingFiles: file_2
      unlinkedKey: k01
    unlinkedGet: []
    unlinkedPut: [k01]
  /workspace/dart/test/lib/c.dart
    uri: package:dart.test/c.dart
    current
      id: file_2
      kind: library_2
        libraryImports
          library_0
          library_1
          library_3 dart:core synthetic
        fileKinds: library_2
        cycle_2
          dependencies: cycle_0 cycle_1 dart:core
          libraries: library_2
          apiSignature_2
      unlinkedKey: k02
    unlinkedGet: []
    unlinkedPut: [k02]
libraryCycles
  /workspace/dart/test/lib/a.dart
    current: cycle_0
      key: k03
    get: []
    put: [k03]
  /workspace/dart/test/lib/b.dart
    current: cycle_1
      key: k04
    get: []
    put: [k04]
  /workspace/dart/test/lib/c.dart
    current: cycle_2
      key: k05
    get: []
    put: [k05]
elementFactory
  hasElement
    package:dart.test/a.dart
    package:dart.test/b.dart
    package:dart.test/c.dart
unlinkedUnitStore
  1: [k00, k01, k02, k06, k07, k08, k09, k10]
byteStore
  1: [k00, k01, k02, k03, k04, k05, k06, k07, k08, k09, k10, k11]
''');

    // Without changes we refresh nothing.
    await resolveFile(c);
    assertStateString(r'''
files
  /workspace/dart/test/lib/a.dart
    uri: package:dart.test/a.dart
    current
      id: file_0
      kind: library_0
        libraryImports
          library_3 dart:core synthetic
        fileKinds: library_0
        cycle_0
          dependencies: dart:core
          libraries: library_0
          apiSignature_0
          users: cycle_2
      referencingFiles: file_2
      unlinkedKey: k00
    unlinkedGet: []
    unlinkedPut: [k00]
  /workspace/dart/test/lib/b.dart
    uri: package:dart.test/b.dart
    current
      id: file_1
      kind: library_1
        libraryImports
          library_3 dart:core synthetic
        fileKinds: library_1
        cycle_1
          dependencies: dart:core
          libraries: library_1
          apiSignature_1
          users: cycle_2
      referencingFiles: file_2
      unlinkedKey: k01
    unlinkedGet: []
    unlinkedPut: [k01]
  /workspace/dart/test/lib/c.dart
    uri: package:dart.test/c.dart
    current
      id: file_2
      kind: library_2
        libraryImports
          library_0
          library_1
          library_3 dart:core synthetic
        fileKinds: library_2
        cycle_2
          dependencies: cycle_0 cycle_1 dart:core
          libraries: library_2
          apiSignature_2
      unlinkedKey: k02
    unlinkedGet: []
    unlinkedPut: [k02]
libraryCycles
  /workspace/dart/test/lib/a.dart
    current: cycle_0
      key: k03
    get: []
    put: [k03]
  /workspace/dart/test/lib/b.dart
    current: cycle_1
      key: k04
    get: []
    put: [k04]
  /workspace/dart/test/lib/c.dart
    current: cycle_2
      key: k05
    get: []
    put: [k05]
elementFactory
  hasElement
    package:dart.test/a.dart
    package:dart.test/b.dart
    package:dart.test/c.dart
unlinkedUnitStore
  1: [k00, k01, k02, k06, k07, k08, k09, k10]
byteStore
  1: [k00, k01, k02, k03, k04, k05, k06, k07, k08, k09, k10, k11]
''');

    // We already know a.dart, refresh nothing.
    await resolveFile(a);
    assertStateString(r'''
files
  /workspace/dart/test/lib/a.dart
    uri: package:dart.test/a.dart
    current
      id: file_0
      kind: library_0
        libraryImports
          library_3 dart:core synthetic
        fileKinds: library_0
        cycle_0
          dependencies: dart:core
          libraries: library_0
          apiSignature_0
          users: cycle_2
      referencingFiles: file_2
      unlinkedKey: k00
    unlinkedGet: []
    unlinkedPut: [k00]
  /workspace/dart/test/lib/b.dart
    uri: package:dart.test/b.dart
    current
      id: file_1
      kind: library_1
        libraryImports
          library_3 dart:core synthetic
        fileKinds: library_1
        cycle_1
          dependencies: dart:core
          libraries: library_1
          apiSignature_1
          users: cycle_2
      referencingFiles: file_2
      unlinkedKey: k01
    unlinkedGet: []
    unlinkedPut: [k01]
  /workspace/dart/test/lib/c.dart
    uri: package:dart.test/c.dart
    current
      id: file_2
      kind: library_2
        libraryImports
          library_0
          library_1
          library_3 dart:core synthetic
        fileKinds: library_2
        cycle_2
          dependencies: cycle_0 cycle_1 dart:core
          libraries: library_2
          apiSignature_2
      unlinkedKey: k02
    unlinkedGet: []
    unlinkedPut: [k02]
libraryCycles
  /workspace/dart/test/lib/a.dart
    current: cycle_0
      key: k03
    get: []
    put: [k03]
  /workspace/dart/test/lib/b.dart
    current: cycle_1
      key: k04
    get: []
    put: [k04]
  /workspace/dart/test/lib/c.dart
    current: cycle_2
      key: k05
    get: []
    put: [k05]
elementFactory
  hasElement
    package:dart.test/a.dart
    package:dart.test/b.dart
    package:dart.test/c.dart
unlinkedUnitStore
  1: [k00, k01, k02, k06, k07, k08, k09, k10]
byteStore
  1: [k00, k01, k02, k03, k04, k05, k06, k07, k08, k09, k10, k11]
''');

    // Change a.dart, discard data for a.dart and c.dart, but not b.dart
    fileResolver.changeFiles([a.path]);
    assertStateString(r'''
files
  /workspace/dart/test/lib/a.dart
    uri: package:dart.test/a.dart
    unlinkedGet: []
    unlinkedPut: [k00]
  /workspace/dart/test/lib/b.dart
    uri: package:dart.test/b.dart
    current
      id: file_1
      kind: library_1
        libraryImports
          library_3 dart:core synthetic
        fileKinds: library_1
        cycle_1
          dependencies: dart:core
          libraries: library_1
          apiSignature_1
      unlinkedKey: k01
    unlinkedGet: []
    unlinkedPut: [k01]
  /workspace/dart/test/lib/c.dart
    uri: package:dart.test/c.dart
    unlinkedGet: []
    unlinkedPut: [k02]
libraryCycles
  /workspace/dart/test/lib/a.dart
    get: []
    put: [k03]
  /workspace/dart/test/lib/b.dart
    current: cycle_1
      key: k04
    get: []
    put: [k04]
  /workspace/dart/test/lib/c.dart
    get: []
    put: [k05]
elementFactory
  hasElement
    package:dart.test/b.dart
unlinkedUnitStore
  1: [k01, k06, k07, k08, k09, k10]
byteStore
  1: [k01, k04, k06, k07, k08, k09, k10, k11]
''');

    // Resolve, read again a.dart and c.dart
    await resolveFile(c);
    assertStateString(r'''
files
  /workspace/dart/test/lib/a.dart
    uri: package:dart.test/a.dart
    current
      id: file_8
      kind: library_8
        libraryImports
          library_3 dart:core synthetic
        fileKinds: library_8
        cycle_4
          dependencies: dart:core
          libraries: library_8
          apiSignature_0
          users: cycle_5
      referencingFiles: file_9
      unlinkedKey: k00
    unlinkedGet: []
    unlinkedPut: [k00, k00]
  /workspace/dart/test/lib/b.dart
    uri: package:dart.test/b.dart
    current
      id: file_1
      kind: library_1
        libraryImports
          library_3 dart:core synthetic
        fileKinds: library_1
        cycle_1
          dependencies: dart:core
          libraries: library_1
          apiSignature_1
          users: cycle_5
      referencingFiles: file_9
      unlinkedKey: k01
    unlinkedGet: []
    unlinkedPut: [k01]
  /workspace/dart/test/lib/c.dart
    uri: package:dart.test/c.dart
    current
      id: file_9
      kind: library_9
        libraryImports
          library_8
          library_1
          library_3 dart:core synthetic
        fileKinds: library_9
        cycle_5
          dependencies: cycle_1 cycle_4 dart:core
          libraries: library_9
          apiSignature_2
      unlinkedKey: k02
    unlinkedGet: []
    unlinkedPut: [k02, k02]
libraryCycles
  /workspace/dart/test/lib/a.dart
    current: cycle_4
      key: k03
    get: []
    put: [k03, k03]
  /workspace/dart/test/lib/b.dart
    current: cycle_1
      key: k04
    get: []
    put: [k04]
  /workspace/dart/test/lib/c.dart
    current: cycle_5
      key: k05
    get: []
    put: [k05, k05]
elementFactory
  hasElement
    package:dart.test/a.dart
    package:dart.test/b.dart
    package:dart.test/c.dart
unlinkedUnitStore
  1: [k00, k01, k02, k06, k07, k08, k09, k10]
byteStore
  1: [k00, k01, k02, k03, k04, k05, k06, k07, k08, k09, k10, k11]
''');
  }

  test_changeFile_resolution() async {
    var a = newFile('/workspace/dart/test/lib/a.dart', r'''
class A {}
''');

    var b = newFile('/workspace/dart/test/lib/b.dart', r'''
import 'a.dart';
void f(A a, B b) {}
''');

    result = await resolveFile(b);
    assertErrorsInResolvedUnit(result, [
      error(CompileTimeErrorCode.undefinedClass, 29, 1),
    ]);

    newFile(a.path, r'''
class A {}
class B {}
''');
    fileResolver.changeFiles([a.path]);

    result = await resolveFile(b);
    assertErrorsInResolvedUnit(result, []);
  }

  test_changeFile_resolution_flushInheritanceManager() async {
    var a = newFile('/workspace/dart/test/lib/a.dart', r'''
class A {
  final int foo = 0;
}
''');

    var b = newFile('/workspace/dart/test/lib/b.dart', r'''
import 'a.dart';

void f(A a) {
  a.foo = 1;
}
''');

    result = await resolveFile(b);
    assertErrorsInResolvedUnit(result, [
      error(CompileTimeErrorCode.assignmentToFinal, 36, 3),
    ]);

    newFile(a.path, r'''
class A {
  int foo = 0;
}
''');
    fileResolver.changeFiles([a.path]);

    result = await resolveFile(b);
    assertErrorsInResolvedUnit(result, []);
  }

  test_changeFile_resolution_missingChangeFileForPart() async {
    var a = newFile('/workspace/dart/test/lib/a.dart', r'''
part 'b.dart';

var b = B(0);
''');

    var b = newFile('/workspace/dart/test/lib/b.dart', r'''
part of 'a.dart';
''');

    result = await resolveFile(a);
    assertErrorsInResolvedUnit(result, [
      error(CompileTimeErrorCode.undefinedFunction, 24, 1),
    ]);

    // Update a.dart, and notify the resolver. We need this to have at least
    // one change, so that we decided to rebuild the library summary.
    newFile(a.path, r'''
part 'b.dart';

var b = B(1);
''');
    fileResolver.changeFiles([a.path]);

    // Update b.dart, but do not notify the resolver.
    // If we try to read it now, it will throw.
    newFile(b.path, r'''
part of 'a.dart';

class B {
  B(int _);
}
''');

    try {
      await resolveFile(a);
      fail('Expected StateError');
    } on StateError {
      // OK
    }

    // Notify the resolver about b.dart, it is OK now.
    fileResolver.changeFiles([b.path]);
    result = await resolveFile(a);
    assertErrorsInResolvedUnit(result, []);
  }

  test_changePartFile_refreshedFiles() async {
    newFile('/workspace/dart/test/lib/a.dart', r'''
part 'b.dart';

class A {}
''');

    var b = newFile('/workspace/dart/test/lib/b.dart', r'''
part of 'a.dart';

class B extends A {}
''');

    // First time we refresh everything.
    await resolveFile(b);
    assertStateString(r'''
files
  /workspace/dart/test/lib/a.dart
    uri: package:dart.test/a.dart
    current
      id: file_0
      kind: library_0
        libraryImports
          library_2 dart:core synthetic
        partIncludes
          partOfUriKnown_1
        fileKinds: library_0 partOfUriKnown_1
        cycle_0
          dependencies: dart:core
          libraries: library_0
          apiSignature_0
      unlinkedKey: k00
    unlinkedGet: []
    unlinkedPut: [k00]
  /workspace/dart/test/lib/b.dart
    uri: package:dart.test/b.dart
    current
      id: file_1
      kind: partOfUriKnown_1
        uriFile: file_0
        library: library_0
      referencingFiles: file_0
      unlinkedKey: k01
    unlinkedGet: []
    unlinkedPut: [k01]
libraryCycles
  /workspace/dart/test/lib/a.dart
    current: cycle_0
      key: k02
    get: []
    put: [k02]
elementFactory
  hasElement
    package:dart.test/a.dart
unlinkedUnitStore
  1: [k00, k01, k03, k04, k05, k06, k07]
byteStore
  1: [k00, k01, k02, k03, k04, k05, k06, k07, k08]
''');

    // Change b.dart, discard both b.dart and a.dart
    fileResolver.changeFiles([b.path]);
    assertStateString(r'''
files
  /workspace/dart/test/lib/a.dart
    uri: package:dart.test/a.dart
    unlinkedGet: []
    unlinkedPut: [k00]
  /workspace/dart/test/lib/b.dart
    uri: package:dart.test/b.dart
    unlinkedGet: []
    unlinkedPut: [k01]
libraryCycles
  /workspace/dart/test/lib/a.dart
    get: []
    put: [k02]
elementFactory
unlinkedUnitStore
  1: [k03, k04, k05, k06, k07]
byteStore
  1: [k03, k04, k05, k06, k07, k08]
''');

    // Resolve, read a.dart and b.dart
    await resolveFile(b);
    assertStateString(r'''
files
  /workspace/dart/test/lib/a.dart
    uri: package:dart.test/a.dart
    current
      id: file_7
      kind: library_7
        libraryImports
          library_2 dart:core synthetic
        partIncludes
          partOfUriKnown_8
        fileKinds: library_7 partOfUriKnown_8
        cycle_2
          dependencies: dart:core
          libraries: library_7
          apiSignature_0
      unlinkedKey: k00
    unlinkedGet: []
    unlinkedPut: [k00, k00]
  /workspace/dart/test/lib/b.dart
    uri: package:dart.test/b.dart
    current
      id: file_8
      kind: partOfUriKnown_8
        uriFile: file_7
        library: library_7
      referencingFiles: file_7
      unlinkedKey: k01
    unlinkedGet: []
    unlinkedPut: [k01, k01]
libraryCycles
  /workspace/dart/test/lib/a.dart
    current: cycle_2
      key: k02
    get: []
    put: [k02, k02]
elementFactory
  hasElement
    package:dart.test/a.dart
unlinkedUnitStore
  1: [k00, k01, k03, k04, k05, k06, k07]
byteStore
  1: [k00, k01, k02, k03, k04, k05, k06, k07, k08]
''');
  }

  test_changePartFile_refreshedFiles_transitive() async {
    newFile('/workspace/dart/test/lib/a.dart', r'''
part 'b.dart';

class A {}
''');

    var b = newFile('/workspace/dart/test/lib/b.dart', r'''
part of 'a.dart';

class B extends A {}
''');

    var c = newFile('/workspace/dart/test/lib/c.dart', r'''
import 'a.dart';
''');

    await resolveFile(c);
    assertStateString(r'''
files
  /workspace/dart/test/lib/a.dart
    uri: package:dart.test/a.dart
    current
      id: file_0
      kind: library_0
        libraryImports
          library_3 dart:core synthetic
        partIncludes
          partOfUriKnown_1
        fileKinds: library_0 partOfUriKnown_1
        cycle_0
          dependencies: dart:core
          libraries: library_0
          apiSignature_0
          users: cycle_1
      referencingFiles: file_2
      unlinkedKey: k00
    unlinkedGet: []
    unlinkedPut: [k00]
  /workspace/dart/test/lib/b.dart
    uri: package:dart.test/b.dart
    current
      id: file_1
      kind: partOfUriKnown_1
        uriFile: file_0
        library: library_0
      referencingFiles: file_0
      unlinkedKey: k01
    unlinkedGet: []
    unlinkedPut: [k01]
  /workspace/dart/test/lib/c.dart
    uri: package:dart.test/c.dart
    current
      id: file_2
      kind: library_2
        libraryImports
          library_0
          library_3 dart:core synthetic
        fileKinds: library_2
        cycle_1
          dependencies: cycle_0 dart:core
          libraries: library_2
          apiSignature_1
      unlinkedKey: k02
    unlinkedGet: []
    unlinkedPut: [k02]
libraryCycles
  /workspace/dart/test/lib/a.dart
    current: cycle_0
      key: k03
    get: []
    put: [k03]
  /workspace/dart/test/lib/c.dart
    current: cycle_1
      key: k04
    get: []
    put: [k04]
elementFactory
  hasElement
    package:dart.test/a.dart
    package:dart.test/c.dart
unlinkedUnitStore
  1: [k00, k01, k02, k05, k06, k07, k08, k09]
byteStore
  1: [k00, k01, k02, k03, k04, k05, k06, k07, k08, k09, k10]
''');

    // Should invalidate a.dart, b.dart, c.dart
    fileResolver.changeFiles([b.path]);
    assertStateString(r'''
files
  /workspace/dart/test/lib/a.dart
    uri: package:dart.test/a.dart
    unlinkedGet: []
    unlinkedPut: [k00]
  /workspace/dart/test/lib/b.dart
    uri: package:dart.test/b.dart
    unlinkedGet: []
    unlinkedPut: [k01]
  /workspace/dart/test/lib/c.dart
    uri: package:dart.test/c.dart
    unlinkedGet: []
    unlinkedPut: [k02]
libraryCycles
  /workspace/dart/test/lib/a.dart
    get: []
    put: [k03]
  /workspace/dart/test/lib/c.dart
    get: []
    put: [k04]
elementFactory
unlinkedUnitStore
  1: [k05, k06, k07, k08, k09]
byteStore
  1: [k05, k06, k07, k08, k09, k10]
''');

    // Read again a.dart, b.dart, c.dart
    await resolveFile(c);
    assertStateString(r'''
files
  /workspace/dart/test/lib/a.dart
    uri: package:dart.test/a.dart
    current
      id: file_8
      kind: library_8
        libraryImports
          library_3 dart:core synthetic
        partIncludes
          partOfUriKnown_9
        fileKinds: library_8 partOfUriKnown_9
        cycle_3
          dependencies: dart:core
          libraries: library_8
          apiSignature_0
          users: cycle_4
      referencingFiles: file_10
      unlinkedKey: k00
    unlinkedGet: []
    unlinkedPut: [k00, k00]
  /workspace/dart/test/lib/b.dart
    uri: package:dart.test/b.dart
    current
      id: file_9
      kind: partOfUriKnown_9
        uriFile: file_8
        library: library_8
      referencingFiles: file_8
      unlinkedKey: k01
    unlinkedGet: []
    unlinkedPut: [k01, k01]
  /workspace/dart/test/lib/c.dart
    uri: package:dart.test/c.dart
    current
      id: file_10
      kind: library_10
        libraryImports
          library_8
          library_3 dart:core synthetic
        fileKinds: library_10
        cycle_4
          dependencies: cycle_3 dart:core
          libraries: library_10
          apiSignature_1
      unlinkedKey: k02
    unlinkedGet: []
    unlinkedPut: [k02, k02]
libraryCycles
  /workspace/dart/test/lib/a.dart
    current: cycle_3
      key: k03
    get: []
    put: [k03, k03]
  /workspace/dart/test/lib/c.dart
    current: cycle_4
      key: k04
    get: []
    put: [k04, k04]
elementFactory
  hasElement
    package:dart.test/a.dart
    package:dart.test/c.dart
unlinkedUnitStore
  1: [k00, k01, k02, k05, k06, k07, k08, k09]
byteStore
  1: [k00, k01, k02, k03, k04, k05, k06, k07, k08, k09, k10]
''');
  }
}

@reflectiveTest
class FileResolverTest extends FileResolutionTest {
  test_analysisOptions_default_fromPackageUri() async {
    newFile('/workspace/dart/analysis_options/lib/default.yaml', r'''
analyzer:
  strong-mode:
    implicit-casts: false
''');

    await assertErrorsInCode(
      r'''
num a = 0;
int b = a;
''',
      [error(CompileTimeErrorCode.invalidAssignment, 19, 1)],
    );
  }

  test_analysisOptions_file_inPackage() async {
    newAnalysisOptionsYamlFile('/workspace/dart/test', r'''
analyzer:
  strong-mode:
    implicit-casts: false
''');

    await assertErrorsInCode(
      r'''
num a = 0;
int b = a;
''',
      [error(CompileTimeErrorCode.invalidAssignment, 19, 1)],
    );
  }

  test_analysisOptions_file_inThirdParty() async {
    newFile('/workspace/dart/analysis_options/lib/third_party.yaml', r'''
analyzer:
  strong-mode:
    implicit-casts: false
''');

    newAnalysisOptionsYamlFile('/workspace/third_party/dart/aaa', r'''
analyzer:
  strong-mode:
    implicit-casts: true
''');

    var aPath = convertPath('/workspace/third_party/dart/aaa/lib/a.dart');
    await assertErrorsInFile(
      aPath,
      r'''
num a = 0;
int b = a;
''',
      [error(CompileTimeErrorCode.invalidAssignment, 19, 1)],
    );
  }

  test_analysisOptions_file_inThirdPartyDartLang() async {
    newFile('/workspace/dart/analysis_options/lib/third_party.yaml', r'''
analyzer:
  strong-mode:
    implicit-casts: false
''');

    newAnalysisOptionsYamlFile('/workspace/third_party/dart_lang/aaa', r'''
analyzer:
  strong-mode:
    implicit-casts: true
''');

    var aPath = convertPath('/workspace/third_party/dart_lang/aaa/lib/a.dart');
    await assertErrorsInFile(
      aPath,
      r'''
num a = 0;
int b = a;
''',
      [error(CompileTimeErrorCode.invalidAssignment, 19, 1)],
    );
  }

  test_analysisOptions_lints() async {
    newFile('/workspace/dart/analysis_options/lib/default.yaml', r'''
linter:
  rules:
    - omit_local_variable_types
''');

    await assertErrorsInCode(
      r'''
main() {
  int a = 0;
  a;
}
''',
      [error(LinterLintCode.omitLocalVariableTypes, 11, 3)],
    );
  }

  test_basic() async {
    await assertNoErrorsInCode(r'''
int a = 0;
var b = 1 + 2;
''');
    assertType(findElement2.topVar('a').type, 'int');
    assertElement(findNode.namedType('int a').element, declaration: intElement);

    assertType(findElement2.topVar('b').type, 'int');
  }

  test_dispose() async {
    var a = newFile('$testPackageLibPath/a.dart', r'''
class A {}
''');

    // After resolution the byte store contains unlinked data for files,
    // and linked data for loaded bundles.
    await resolveFile(a);
    assertStateString(r'''
files
  /workspace/dart/test/lib/a.dart
    uri: package:dart.test/a.dart
    current
      id: file_0
      kind: library_0
        libraryImports
          library_1 dart:core synthetic
        fileKinds: library_0
        cycle_0
          dependencies: dart:core
          libraries: library_0
          apiSignature_0
      unlinkedKey: k00
    unlinkedGet: []
    unlinkedPut: [k00]
libraryCycles
  /workspace/dart/test/lib/a.dart
    current: cycle_0
      key: k01
    get: []
    put: [k01]
elementFactory
  hasElement
    package:dart.test/a.dart
unlinkedUnitStore
  1: [k00, k02, k03, k04, k05, k06]
byteStore
  1: [k00, k01, k02, k03, k04, k05, k06, k07]
''');

    fileResolver.dispose();

    // After dispose() we don't have any loaded libraries or files.
    // The byte store is empty - no unlinked or linked data.
    assertStateString(r'''
files
  /workspace/dart/test/lib/a.dart
    uri: package:dart.test/a.dart
    unlinkedGet: []
    unlinkedPut: [k00]
libraryCycles
  /workspace/dart/test/lib/a.dart
    get: []
    put: [k01]
elementFactory
unlinkedUnitStore
  1: [k00, k02, k03, k04, k05, k06]
byteStore
''');
  }

  test_elements_export_dartCoreDynamic() async {
    var a = newFile('/workspace/dart/test/lib/a.dart', r'''
export 'dart:core' show dynamic;
''');

    // Analyze so that `dart:core` is linked.
    var a_result = await resolveFile(a);

    // Touch `dart:core` so that its element model is discarded.
    var dartCorePath = a_result.session.uriConverter.uriToPath(
      Uri.parse('dart:core'),
    )!;
    fileResolver.changeFiles([dartCorePath]);

    // Analyze, this will read the element model for `dart:core`.
    // There was a bug that `root::dart:core::dynamic` had no element set.
    await assertNoErrorsInCode(r'''
import 'a.dart' as p;
p.dynamic f() {}
''');
  }

  Future<void> test_errors_hasNullSuffix() async {
    await assertErrorsInCode(
      r'''
String f(Map<int, String> a) {
  return a[0];
}
''',
      [
        error(
          CompileTimeErrorCode.returnOfInvalidTypeFromFunction,
          40,
          4,
          messageContains: ["'String'", 'String?'],
        ),
      ],
    );
  }

  test_findReferences_class() async {
    var a = newFile('/workspace/dart/test/lib/a.dart', r'''
class A {
  int foo;
}
''');

    var b = newFile('/workspace/dart/test/lib/b.dart', r'''
import 'a.dart';

void func() {
  var a = A();
  print(a.foo);
}
''');

    await resolveFile(b);
    var element = await _findElement(6, a);
    var result = await fileResolver.findReferences(element);
    var expected = <CiderSearchMatch>[
      CiderSearchMatch(b.path, [
        CiderSearchInfo(CharacterLocation(4, 11), 1, MatchKind.REFERENCE),
      ]),
    ];
    expect(result, unorderedEquals(expected));
  }

  test_findReferences_field() async {
    var a = newFile('/workspace/dart/test/lib/a.dart', r'''
class A {
  int foo = 0;

  void func(int bar) {
    foo = bar;
 }
}
''');

    await resolveFile(a);
    var element = await _findElement(16, a);
    var result = await fileResolver.findReferences(element);
    var expected = <CiderSearchMatch>[
      CiderSearchMatch(a.path, [
        CiderSearchInfo(CharacterLocation(5, 5), 3, MatchKind.WRITE),
      ]),
    ];
    expect(result, expected);
  }

  test_findReferences_function() async {
    var a = newFile('/workspace/dart/test/lib/a.dart', r'''
main() {
  foo('Hello');
}

foo(String str) {}
''');

    await resolveFile(a);
    var element = await _findElement(11, a);
    var result = await fileResolver.findReferences(element);
    var expected = <CiderSearchMatch>[
      CiderSearchMatch(a.path, [
        CiderSearchInfo(CharacterLocation(2, 3), 3, MatchKind.REFERENCE),
      ]),
    ];
    expect(result, unorderedEquals(expected));
  }

  test_findReferences_getter() async {
    var a = newFile('/workspace/dart/test/lib/a.dart', r'''
class A {
  int get foo => 6;
}
''');
    var b = newFile('/workspace/dart/test/lib/b.dart', r'''
import 'a.dart';

main() {
  var a = A();
  var bar = a.foo;
}
''');

    await resolveFile(b);
    var element = await _findElement(20, a);
    var result = await fileResolver.findReferences(element);
    var expected = <CiderSearchMatch>[
      CiderSearchMatch(b.path, [
        CiderSearchInfo(CharacterLocation(5, 15), 3, MatchKind.REFERENCE),
      ]),
    ];
    expect(result, unorderedEquals(expected));
  }

  test_findReferences_local_variable() async {
    var aPath = convertPath('/workspace/dart/test/lib/a.dart');
    var a = newFile(aPath, r'''
class A {
  void func(int n) {
    var foo = bar+1;
    print(foo);
 }
}
''');
    await resolveFile(a);
    var element = await _findElement(39, a);
    var result = await fileResolver.findReferences(element);
    var expected = <CiderSearchMatch>[
      CiderSearchMatch(a.path, [
        CiderSearchInfo(CharacterLocation(4, 11), 3, MatchKind.REFERENCE),
      ]),
    ];
    expect(result, unorderedEquals(expected));
  }

  test_findReferences_method() async {
    var a = newFile('/workspace/dart/test/lib/a.dart', r'''
class A {
  void func() {
   print('hello');
 }

 void func2() {
   func();
 }
}
''');

    var b = newFile('/workspace/dart/test/lib/b.dart', r'''
import 'a.dart';

main() {
  var a = A();
  a.func();
}
''');

    await resolveFile(a);
    await resolveFile(b);

    var element = await _findElement(17, a);
    var result = await fileResolver.findReferences(element);
    var expected = <CiderSearchMatch>[
      CiderSearchMatch(b.path, [
        CiderSearchInfo(CharacterLocation(5, 5), 4, MatchKind.REFERENCE),
      ]),
      CiderSearchMatch(a.path, [
        CiderSearchInfo(CharacterLocation(7, 4), 4, MatchKind.REFERENCE),
      ]),
    ];
    expect(result, unorderedEquals(expected));
  }

  test_findReferences_setter() async {
    var a = newFile('/workspace/dart/test/lib/a.dart', r'''
class A {
  void set value(int m){ };
}
''');
    var b = newFile('/workspace/dart/test/lib/b.dart', r'''
import 'a.dart';

main() {
  var a = A();
  a.value = 6;
}
''');

    await resolveFile(b);
    var element = await _findElement(21, a);
    var result = await fileResolver.findReferences(element);
    var expected = <CiderSearchMatch>[
      CiderSearchMatch(b.path, [
        CiderSearchInfo(CharacterLocation(5, 5), 5, MatchKind.WRITE),
      ]),
    ];
    expect(result, unorderedEquals(expected));
  }

  test_findReferences_top_level_getter() async {
    var a = newFile('/workspace/dart/test/lib/a.dart', r'''
int _foo;

int get foo => _foo;
''');

    var b = newFile('/workspace/dart/test/lib/b.dart', r'''
import 'a.dart';

main() {
  var bar = foo;
}
''');

    await resolveFile(b);
    var element = await _findElement(19, a);
    var result = await fileResolver.findReferences(element);
    var expected = <CiderSearchMatch>[
      CiderSearchMatch(b.path, [
        CiderSearchInfo(CharacterLocation(4, 13), 3, MatchKind.REFERENCE),
      ]),
    ];
    expect(result, unorderedEquals(expected));
  }

  test_findReferences_top_level_setter() async {
    var a = newFile('/workspace/dart/test/lib/a.dart', r'''
int _foo;

void set foo(int bar) { _foo = bar; }
''');

    var b = newFile('/workspace/dart/test/lib/b.dart', r'''
import 'a.dart';

main() {
  foo = 6;
}
''');

    await resolveFile(b);
    var element = await _findElement(20, a);
    var result = await fileResolver.findReferences(element);
    var expected = <CiderSearchMatch>[
      CiderSearchMatch(b.path, [
        CiderSearchInfo(CharacterLocation(4, 3), 3, MatchKind.WRITE),
      ]),
    ];
    expect(result, unorderedEquals(expected));
  }

  test_findReferences_top_level_variable() async {
    var a = newFile('/workspace/dart/test/lib/a.dart', r'''
const int C = 42;

void func() {
    print(C);
}
''');

    await resolveFile(a);
    var element = await _findElement(10, a);
    var result = await fileResolver.findReferences(element);
    var expected = <CiderSearchMatch>[
      CiderSearchMatch(a.path, [
        CiderSearchInfo(CharacterLocation(4, 11), 1, MatchKind.READ),
      ]),
    ];
    expect(result, unorderedEquals(expected));
  }

  test_findReferences_type_parameter() async {
    var a = newFile('/workspace/dart/test/lib/a.dart', r'''
class Foo<T> {
  List<T> l;

  void bar(T t) {}
}
''');
    await resolveFile(a);
    var element = await _findElement(10, a);
    var result = await fileResolver.findReferences(element);
    var expected = <CiderSearchMatch>[
      CiderSearchMatch(a.path, [
        CiderSearchInfo(CharacterLocation(2, 8), 1, MatchKind.REFERENCE),
        CiderSearchInfo(CharacterLocation(4, 12), 1, MatchKind.REFERENCE),
      ]),
    ];
    expect(result, expected);
  }

  test_findReferences_typedef() async {
    var a = newFile('/workspace/dart/test/lib/a.dart', r'''
typedef func = int Function(int);

''');
    var b = newFile('/workspace/dart/test/lib/b.dart', r'''
import 'a.dart';

void f(func o) {}
''');

    await resolveFile(b);
    var element = await _findElement(8, a);
    var result = await fileResolver.findReferences(element);
    var expected = <CiderSearchMatch>[
      CiderSearchMatch(b.path, [
        CiderSearchInfo(CharacterLocation(3, 8), 4, MatchKind.REFERENCE),
      ]),
    ];
    expect(result, unorderedEquals(expected));
  }

  test_formalParameter_promotion() async {
    await assertNoErrorsInCode(r'''
void f(int? a) {
  if (a != null) {
    a.isEven;
  }
}
''');

    assertType(findElement2.parameter('a').type, 'int?');
  }

  test_getErrors() async {
    addTestFile(r'''
var a = b;
var foo = 0;
''');

    var result = await getTestErrors();
    expect(result.path, convertPath('/workspace/dart/test/lib/test.dart'));
    expect(result.uri.toString(), 'package:dart.test/test.dart');
    assertErrorsInList(result.diagnostics, [
      error(CompileTimeErrorCode.undefinedIdentifier, 8, 1),
    ]);
    expect(result.lineInfo.lineStarts, [0, 11, 24]);
  }

  test_getErrors_docImports() async {
    newFile('$testPackageLibPath/a.dart', '');

    var b = newFile('$testPackageLibPath/b.dart', r'''
/// @docImport 'a.dart';
library;
''');

    var errorsResult = await fileResolver.getErrors2(path: b.path);
    assertErrorsInList(errorsResult.diagnostics, []);
  }

  test_getErrors_library() async {
    var a = newFile('$testPackageLibPath/a.dart', r'''
var a = 42
''');

    var errorsResult = await fileResolver.getErrors2(path: a.path);
    assertErrorsInList(errorsResult.diagnostics, [
      error(ParserErrorCode.expectedToken, 8, 2),
    ]);
  }

  test_getErrors_part_hasLibrary() async {
    newFile('$testPackageLibPath/a.dart', r'''
part 'b.dart';
''');

    var b = newFile('$testPackageLibPath/b.dart', r'''
part of 'a.dart';
var a = 42
''');

    var errorsResult = await fileResolver.getErrors2(path: b.path);
    assertErrorsInList(errorsResult.diagnostics, [
      error(ParserErrorCode.expectedToken, 26, 2),
    ]);
  }

  test_getErrors_reuse() async {
    addTestFile('var a = b;');

    // No resolved files yet.
    _assertResolvedFiles([]);

    // No cached, will resolve once.
    expect((await getTestErrors()).diagnostics, hasLength(1));
    _assertResolvedFiles([testFile]);

    // Has cached, will be not resolved again.
    expect((await getTestErrors()).diagnostics, hasLength(1));
    _assertResolvedFiles([]);

    // Change the file, will be resolved again.
    addTestFile('var a = c;');
    fileResolver.changeFiles([testFile.path]);
    expect((await getTestErrors()).diagnostics, hasLength(1));
    _assertResolvedFiles([testFile]);
  }

  test_getErrors_reuse_changeDependency() async {
    var a = newFile('/workspace/dart/test/lib/a.dart', r'''
var a = 0;
''');

    addTestFile(r'''
import 'a.dart';
var b = a.foo;
''');

    // No resolved files yet.
    _assertResolvedFiles([]);

    // No cached, will resolve once.
    expect((await getTestErrors()).diagnostics, hasLength(1));
    _assertResolvedFiles([testFile]);

    // Has cached, will be not resolved again.
    expect((await getTestErrors()).diagnostics, hasLength(1));
    _assertResolvedFiles([]);

    // Change the dependency.
    // The signature of the result is different.
    // The previously cached result cannot be used.
    newFile(a.path, r'''
var a = 4.2;
''');
    fileResolver.changeFiles([a.path]);
    expect((await getTestErrors()).diagnostics, hasLength(1));
    _assertResolvedFiles([testFile]);
  }

  test_getFilesWithTopLevelDeclarations_cached() async {
    await assertNoErrorsInCode(r'''
int a = 0;
var b = 1 + 2;
''');

    void assertHasOneVariable() {
      var files = fileResolver.getFilesWithTopLevelDeclarations('a');
      expect(files, hasLength(1));
      var file = files.single;
      expect(file.path, result.path);
    }

    // Ask to check that it works when parsed.
    assertHasOneVariable();

    // Create a new resolved, but reuse the cache.
    createFileResolver();

    await resolveTestFile();

    // Ask again, when unlinked information is read from the cache.
    assertHasOneVariable();
  }

  test_getLibraryByUri() async {
    newFile('/workspace/dart/my/lib/a.dart', r'''
class A {}
''');

    var element = await fileResolver.getLibraryByUri2(
      uriStr: 'package:dart.my/a.dart',
    );
    expect(element.classes, hasLength(1));
  }

  test_getLibraryByUri_notExistingFile() async {
    var element = await fileResolver.getLibraryByUri2(
      uriStr: 'package:dart.my/a.dart',
    );
    expect(element.classes, isEmpty);
  }

  test_getLibraryByUri_partOf() async {
    newFile('/workspace/dart/my/lib/a.dart', r'''
part of 'b.dart';
''');

    expect(() async {
      await fileResolver.getLibraryByUri2(uriStr: 'package:dart.my/a.dart');
    }, throwsArgumentError);
  }

  test_getLibraryByUri_unresolvedUri() async {
    expect(() async {
      await fileResolver.getLibraryByUri2(uriStr: 'my:unresolved');
    }, throwsArgumentError);
  }

  test_hint_in_third_party() async {
    var a = newFile('/workspace/third_party/dart/aaa/lib/a.dart', r'''
import 'dart:math';
''');
    await resolveFile(a);
    assertNoErrorsInResult();
  }

  test_linkLibraries() async {
    var a = newFile('$testPackageLibPath/a.dart', r'''
final a = 0;
''');

    var b = newFile('$testPackageLibPath/b.dart', r'''
import 'a.dart';
final b = a;
''');

    await fileResolver.linkLibraries2(path: a.path);
    assertStateString(r'''
files
  /workspace/dart/test/lib/a.dart
    uri: package:dart.test/a.dart
    current
      id: file_0
      kind: library_0
        libraryImports
          library_1 dart:core synthetic
        fileKinds: library_0
        cycle_0
          dependencies: dart:core
          libraries: library_0
          apiSignature_0
      unlinkedKey: k00
    unlinkedGet: []
    unlinkedPut: [k00]
libraryCycles
  /workspace/dart/test/lib/a.dart
    current: cycle_0
      key: k01
    get: [k01]
    put: [k01]
elementFactory
  hasReader
    package:dart.test/a.dart
unlinkedUnitStore
  1: [k00, k02, k03, k04, k05, k06]
byteStore
  1: [k00, k02, k03, k04, k05, k06]
''');

    await fileResolver.getLibraryByUri2(uriStr: 'package:dart.test/a.dart');
    assertStateString(r'''
files
  /workspace/dart/test/lib/a.dart
    uri: package:dart.test/a.dart
    current
      id: file_0
      kind: library_0
        libraryImports
          library_1 dart:core synthetic
        fileKinds: library_0
        cycle_0
          dependencies: dart:core
          libraries: library_0
          apiSignature_0
      unlinkedKey: k00
    unlinkedGet: []
    unlinkedPut: [k00]
libraryCycles
  /workspace/dart/test/lib/a.dart
    current: cycle_0
      key: k01
    get: [k01]
    put: [k01]
elementFactory
  hasElement
    package:dart.test/a.dart
  hasReader
    package:dart.test/a.dart
unlinkedUnitStore
  1: [k00, k02, k03, k04, k05, k06]
byteStore
  1: [k00, k02, k03, k04, k05, k06]
''');

    await fileResolver.linkLibraries2(path: b.path);

    // We discarded all libraries, so each one has `get` and `put`.
    // We did not discard files, so only `unlinkedPut`.
    // The reference count for each data is exactly `1`.
    assertStateString(r'''
files
  /workspace/dart/test/lib/a.dart
    uri: package:dart.test/a.dart
    current
      id: file_0
      kind: library_0
        libraryImports
          library_1 dart:core synthetic
        fileKinds: library_0
        cycle_0
          dependencies: dart:core
          libraries: library_0
          apiSignature_0
          users: cycle_2
      referencingFiles: file_6
      unlinkedKey: k00
    unlinkedGet: []
    unlinkedPut: [k00]
  /workspace/dart/test/lib/b.dart
    uri: package:dart.test/b.dart
    current
      id: file_6
      kind: library_6
        libraryImports
          library_0
          library_1 dart:core synthetic
        fileKinds: library_6
        cycle_2
          dependencies: cycle_0 dart:core
          libraries: library_6
          apiSignature_1
      unlinkedKey: k07
    unlinkedGet: []
    unlinkedPut: [k07]
libraryCycles
  /workspace/dart/test/lib/a.dart
    current: cycle_0
      key: k01
    get: [k01]
    put: [k01, k01]
  /workspace/dart/test/lib/b.dart
    current: cycle_2
      key: k08
    get: [k08]
    put: [k08]
elementFactory
  hasElement
    package:dart.test/a.dart
  hasReader
    package:dart.test/b.dart
unlinkedUnitStore
  1: [k00, k02, k03, k04, k05, k06, k07]
byteStore
  1: [k00, k02, k03, k04, k05, k06, k07]
''');

    var b_library = await fileResolver.getLibraryByUri2(
      uriStr: 'package:dart.test/b.dart',
    );

    // Ask types for top-level variables.
    for (var topLevelVariable in b_library.topLevelVariables) {
      topLevelVariable.type;
    }

    // All types are stored in the bundle for b.dart itself, we don't need to
    // read a.dart to access them, so we keep it as a reader.
    assertStateString(r'''
files
  /workspace/dart/test/lib/a.dart
    uri: package:dart.test/a.dart
    current
      id: file_0
      kind: library_0
        libraryImports
          library_1 dart:core synthetic
        fileKinds: library_0
        cycle_0
          dependencies: dart:core
          libraries: library_0
          apiSignature_0
          users: cycle_2
      referencingFiles: file_6
      unlinkedKey: k00
    unlinkedGet: []
    unlinkedPut: [k00]
  /workspace/dart/test/lib/b.dart
    uri: package:dart.test/b.dart
    current
      id: file_6
      kind: library_6
        libraryImports
          library_0
          library_1 dart:core synthetic
        fileKinds: library_6
        cycle_2
          dependencies: cycle_0 dart:core
          libraries: library_6
          apiSignature_1
      unlinkedKey: k07
    unlinkedGet: []
    unlinkedPut: [k07]
libraryCycles
  /workspace/dart/test/lib/a.dart
    current: cycle_0
      key: k01
    get: [k01]
    put: [k01, k01]
  /workspace/dart/test/lib/b.dart
    current: cycle_2
      key: k08
    get: [k08]
    put: [k08]
elementFactory
  hasElement
    package:dart.test/a.dart
    package:dart.test/b.dart
  hasReader
    package:dart.test/b.dart
unlinkedUnitStore
  1: [k00, k02, k03, k04, k05, k06, k07]
byteStore
  1: [k00, k02, k03, k04, k05, k06, k07]
''');
  }

  test_linkLibraries_getErrors() async {
    addTestFile(r'''
var a = b;
var foo = 0;
''');

    await fileResolver.linkLibraries2(path: testFile.path);

    // We discarded all libraries, so each one has `get` and `put`.
    // We did not discard files, so only `unlinkedPut`.
    // The library for the test file has reader, but not the element yet.
    // `dart:core` and `dart` have element because of `TypeProvider`.
    // The reference count for each data is exactly `1`.
    assertStateString(r'''
files
  /workspace/dart/test/lib/test.dart
    uri: package:dart.test/test.dart
    current
      id: file_0
      kind: library_0
        libraryImports
          library_1 dart:core synthetic
        fileKinds: library_0
        cycle_0
          dependencies: dart:core
          libraries: library_0
          apiSignature_0
      unlinkedKey: k00
    unlinkedGet: []
    unlinkedPut: [k00]
libraryCycles
  /workspace/dart/test/lib/test.dart
    current: cycle_0
      key: k01
    get: [k01]
    put: [k01]
elementFactory
  hasReader
    package:dart.test/test.dart
unlinkedUnitStore
  1: [k00, k02, k03, k04, k05, k06]
byteStore
  1: [k00, k02, k03, k04, k05, k06]
''');

    var result = await getTestErrors();
    expect(result.path, testFile.path);
    expect(result.uri.toString(), 'package:dart.test/test.dart');
    assertErrorsInList(result.diagnostics, [
      error(CompileTimeErrorCode.undefinedIdentifier, 8, 1),
    ]);
    expect(result.lineInfo.lineStarts, [0, 11, 24]);

    // We created the library element for the test file, using the reader.
    assertStateString(r'''
files
  /workspace/dart/test/lib/test.dart
    uri: package:dart.test/test.dart
    current
      id: file_0
      kind: library_0
        libraryImports
          library_1 dart:core synthetic
        fileKinds: library_0
        cycle_0
          dependencies: dart:core
          libraries: library_0
          apiSignature_0
      unlinkedKey: k00
    unlinkedGet: []
    unlinkedPut: [k00]
libraryCycles
  /workspace/dart/test/lib/test.dart
    current: cycle_0
      key: k01
    get: [k01]
    put: [k01]
elementFactory
  hasElement
    package:dart.test/test.dart
  hasReader
    package:dart.test/test.dart
unlinkedUnitStore
  1: [k00, k02, k03, k04, k05, k06]
byteStore
  1: [k00, k02, k03, k04, k05, k06]
''');
  }

  test_nameOffset_class_method_fromBytes() async {
    newFile('/workspace/dart/test/lib/a.dart', r'''
class A {
  void foo() {}
}
''');

    addTestFile(r'''
import 'a.dart';

void f(A a) {
  a.foo();
}
''');

    await resolveTestFile();
    {
      var element = findNode.simple('foo();').element!;
      expect(element.firstFragment.nameOffset, 17);
    }

    // New resolver.
    // Element models will be loaded from the cache.
    createFileResolver();
    await resolveTestFile();
    {
      var element = findNode.simple('foo();').element!;
      expect(element.firstFragment.nameOffset, 17);
    }
  }

  test_nameOffset_unit_variable_fromBytes() async {
    newFile('/workspace/dart/test/lib/a.dart', r'''
var a = 0;
''');

    addTestFile(r'''
import 'a.dart';
var b = a;
''');

    await resolveTestFile();
    {
      var element = findNode.simple('a;').element!;
      expect(element.nonSynthetic.firstFragment.nameOffset, 4);
    }

    // New resolver.
    // Element models will be loaded from the cache.
    createFileResolver();
    await resolveTestFile();
    {
      var element = findNode.simple('a;').element!;
      expect(element.nonSynthetic.firstFragment.nameOffset, 4);
    }
  }

  test_part_notInLibrary_libraryDoesNotExist() async {
    // TODO(scheglov): Should report CompileTimeErrorCode.URI_DOES_NOT_EXIST
    await assertNoErrorsInCode(r'''
part of 'a.dart';
''');
  }

  test_removeFilesNotNecessaryForAnalysisOf() async {
    newFile('/workspace/dart/aaa/lib/a.dart', r'''
class A {}
''');

    var b = newFile('/workspace/dart/aaa/lib/b.dart', r'''
import 'a.dart';
class B {}
''');

    var c = newFile('/workspace/dart/aaa/lib/c.dart', r'''
import 'a.dart';
class C {}
''');

    await resolveFile(b);
    await resolveFile(c);
    assertStateString(r'''
files
  /workspace/dart/aaa/lib/a.dart
    uri: package:dart.aaa/a.dart
    current
      id: file_0
      kind: library_0
        libraryImports
          library_3 dart:core synthetic
        fileKinds: library_0
        cycle_0
          dependencies: dart:core
          libraries: library_0
          apiSignature_0
          users: cycle_1 cycle_2
      referencingFiles: file_1 file_2
      unlinkedKey: k00
    unlinkedGet: []
    unlinkedPut: [k00]
  /workspace/dart/aaa/lib/b.dart
    uri: package:dart.aaa/b.dart
    current
      id: file_1
      kind: library_1
        libraryImports
          library_0
          library_3 dart:core synthetic
        fileKinds: library_1
        cycle_1
          dependencies: cycle_0 dart:core
          libraries: library_1
          apiSignature_1
      unlinkedKey: k01
    unlinkedGet: []
    unlinkedPut: [k01]
  /workspace/dart/aaa/lib/c.dart
    uri: package:dart.aaa/c.dart
    current
      id: file_2
      kind: library_2
        libraryImports
          library_0
          library_3 dart:core synthetic
        fileKinds: library_2
        cycle_2
          dependencies: cycle_0 dart:core
          libraries: library_2
          apiSignature_2
      unlinkedKey: k02
    unlinkedGet: []
    unlinkedPut: [k02]
libraryCycles
  /workspace/dart/aaa/lib/a.dart
    current: cycle_0
      key: k03
    get: []
    put: [k03]
  /workspace/dart/aaa/lib/b.dart
    current: cycle_1
      key: k04
    get: []
    put: [k04]
  /workspace/dart/aaa/lib/c.dart
    current: cycle_2
      key: k05
    get: []
    put: [k05]
elementFactory
  hasElement
    package:dart.aaa/a.dart
    package:dart.aaa/b.dart
    package:dart.aaa/c.dart
unlinkedUnitStore
  1: [k00, k01, k02, k06, k07, k08, k09, k10]
byteStore
  1: [k00, k01, k02, k03, k04, k05, k06, k07, k08, k09, k10, k11]
''');

    fileResolver.removeFilesNotNecessaryForAnalysisOf([c.path]);

    // No data for b.dart anymore.
    assertStateString(r'''
files
  /workspace/dart/aaa/lib/a.dart
    uri: package:dart.aaa/a.dart
    current
      id: file_0
      kind: library_0
        libraryImports
          library_3 dart:core synthetic
        fileKinds: library_0
        cycle_0
          dependencies: dart:core
          libraries: library_0
          apiSignature_0
          users: cycle_2
      referencingFiles: file_2
      unlinkedKey: k00
    unlinkedGet: []
    unlinkedPut: [k00]
  /workspace/dart/aaa/lib/b.dart
    uri: package:dart.aaa/b.dart
    unlinkedGet: []
    unlinkedPut: [k01]
  /workspace/dart/aaa/lib/c.dart
    uri: package:dart.aaa/c.dart
    current
      id: file_2
      kind: library_2
        libraryImports
          library_0
          library_3 dart:core synthetic
        fileKinds: library_2
        cycle_2
          dependencies: cycle_0 dart:core
          libraries: library_2
          apiSignature_2
      unlinkedKey: k02
    unlinkedGet: []
    unlinkedPut: [k02]
libraryCycles
  /workspace/dart/aaa/lib/a.dart
    current: cycle_0
      key: k03
    get: []
    put: [k03]
  /workspace/dart/aaa/lib/b.dart
    get: []
    put: [k04]
  /workspace/dart/aaa/lib/c.dart
    current: cycle_2
      key: k05
    get: []
    put: [k05]
elementFactory
  hasElement
    package:dart.aaa/a.dart
    package:dart.aaa/c.dart
unlinkedUnitStore
  1: [k00, k02, k06, k07, k08, k09, k10]
byteStore
  1: [k00, k02, k03, k05, k06, k07, k08, k09, k10, k11]
''');
  }

  test_removeFilesNotNecessaryForAnalysisOf_multiple() async {
    newFile('/workspace/dart/aaa/lib/a.dart', r'''
class A {}
''');

    newFile('/workspace/dart/aaa/lib/b.dart', r'''
class B {}
''');

    newFile('/workspace/dart/aaa/lib/c.dart', r'''
class C {}
''');

    var d = newFile('/workspace/dart/aaa/lib/d.dart', r'''
import 'a.dart';
''');

    var e = newFile('/workspace/dart/aaa/lib/e.dart', r'''
import 'a.dart';
import 'b.dart';
''');

    var f = newFile('/workspace/dart/aaa/lib/f.dart', r'''
import 'c.dart';
 ''');

    await resolveFile(d);
    await resolveFile(e);
    await resolveFile(f);
    assertStateString(r'''
files
  /workspace/dart/aaa/lib/a.dart
    uri: package:dart.aaa/a.dart
    current
      id: file_0
      kind: library_0
        libraryImports
          library_6 dart:core synthetic
        fileKinds: library_0
        cycle_0
          dependencies: dart:core
          libraries: library_0
          apiSignature_0
          users: cycle_3 cycle_4
      referencingFiles: file_3 file_4
      unlinkedKey: k00
    unlinkedGet: []
    unlinkedPut: [k00]
  /workspace/dart/aaa/lib/b.dart
    uri: package:dart.aaa/b.dart
    current
      id: file_1
      kind: library_1
        libraryImports
          library_6 dart:core synthetic
        fileKinds: library_1
        cycle_1
          dependencies: dart:core
          libraries: library_1
          apiSignature_1
          users: cycle_4
      referencingFiles: file_4
      unlinkedKey: k01
    unlinkedGet: []
    unlinkedPut: [k01]
  /workspace/dart/aaa/lib/c.dart
    uri: package:dart.aaa/c.dart
    current
      id: file_2
      kind: library_2
        libraryImports
          library_6 dart:core synthetic
        fileKinds: library_2
        cycle_2
          dependencies: dart:core
          libraries: library_2
          apiSignature_2
          users: cycle_5
      referencingFiles: file_5
      unlinkedKey: k02
    unlinkedGet: []
    unlinkedPut: [k02]
  /workspace/dart/aaa/lib/d.dart
    uri: package:dart.aaa/d.dart
    current
      id: file_3
      kind: library_3
        libraryImports
          library_0
          library_6 dart:core synthetic
        fileKinds: library_3
        cycle_3
          dependencies: cycle_0 dart:core
          libraries: library_3
          apiSignature_3
      unlinkedKey: k03
    unlinkedGet: []
    unlinkedPut: [k03]
  /workspace/dart/aaa/lib/e.dart
    uri: package:dart.aaa/e.dart
    current
      id: file_4
      kind: library_4
        libraryImports
          library_0
          library_1
          library_6 dart:core synthetic
        fileKinds: library_4
        cycle_4
          dependencies: cycle_0 cycle_1 dart:core
          libraries: library_4
          apiSignature_4
      unlinkedKey: k04
    unlinkedGet: []
    unlinkedPut: [k04]
  /workspace/dart/aaa/lib/f.dart
    uri: package:dart.aaa/f.dart
    current
      id: file_5
      kind: library_5
        libraryImports
          library_2
          library_6 dart:core synthetic
        fileKinds: library_5
        cycle_5
          dependencies: cycle_2 dart:core
          libraries: library_5
          apiSignature_5
      unlinkedKey: k05
    unlinkedGet: []
    unlinkedPut: [k05]
libraryCycles
  /workspace/dart/aaa/lib/a.dart
    current: cycle_0
      key: k06
    get: []
    put: [k06]
  /workspace/dart/aaa/lib/b.dart
    current: cycle_1
      key: k07
    get: []
    put: [k07]
  /workspace/dart/aaa/lib/c.dart
    current: cycle_2
      key: k08
    get: []
    put: [k08]
  /workspace/dart/aaa/lib/d.dart
    current: cycle_3
      key: k09
    get: []
    put: [k09]
  /workspace/dart/aaa/lib/e.dart
    current: cycle_4
      key: k10
    get: []
    put: [k10]
  /workspace/dart/aaa/lib/f.dart
    current: cycle_5
      key: k11
    get: []
    put: [k11]
elementFactory
  hasElement
    package:dart.aaa/a.dart
    package:dart.aaa/b.dart
    package:dart.aaa/c.dart
    package:dart.aaa/d.dart
    package:dart.aaa/e.dart
    package:dart.aaa/f.dart
unlinkedUnitStore
  1: [k00, k01, k02, k03, k04, k05, k12, k13, k14, k15, k16]
byteStore
  1: [k00, k01, k02, k03, k04, k05, k06, k07, k08, k09, k10, k11, k12, k13, k14, k15, k16, k17]
''');

    fileResolver.removeFilesNotNecessaryForAnalysisOf([d.path, f.path]);
    // No data for b.dart and e.dart anymore.
    assertStateString(r'''
files
  /workspace/dart/aaa/lib/a.dart
    uri: package:dart.aaa/a.dart
    current
      id: file_0
      kind: library_0
        libraryImports
          library_6 dart:core synthetic
        fileKinds: library_0
        cycle_0
          dependencies: dart:core
          libraries: library_0
          apiSignature_0
          users: cycle_3
      referencingFiles: file_3
      unlinkedKey: k00
    unlinkedGet: []
    unlinkedPut: [k00]
  /workspace/dart/aaa/lib/b.dart
    uri: package:dart.aaa/b.dart
    unlinkedGet: []
    unlinkedPut: [k01]
  /workspace/dart/aaa/lib/c.dart
    uri: package:dart.aaa/c.dart
    current
      id: file_2
      kind: library_2
        libraryImports
          library_6 dart:core synthetic
        fileKinds: library_2
        cycle_2
          dependencies: dart:core
          libraries: library_2
          apiSignature_2
          users: cycle_5
      referencingFiles: file_5
      unlinkedKey: k02
    unlinkedGet: []
    unlinkedPut: [k02]
  /workspace/dart/aaa/lib/d.dart
    uri: package:dart.aaa/d.dart
    current
      id: file_3
      kind: library_3
        libraryImports
          library_0
          library_6 dart:core synthetic
        fileKinds: library_3
        cycle_3
          dependencies: cycle_0 dart:core
          libraries: library_3
          apiSignature_3
      unlinkedKey: k03
    unlinkedGet: []
    unlinkedPut: [k03]
  /workspace/dart/aaa/lib/e.dart
    uri: package:dart.aaa/e.dart
    unlinkedGet: []
    unlinkedPut: [k04]
  /workspace/dart/aaa/lib/f.dart
    uri: package:dart.aaa/f.dart
    current
      id: file_5
      kind: library_5
        libraryImports
          library_2
          library_6 dart:core synthetic
        fileKinds: library_5
        cycle_5
          dependencies: cycle_2 dart:core
          libraries: library_5
          apiSignature_5
      unlinkedKey: k05
    unlinkedGet: []
    unlinkedPut: [k05]
libraryCycles
  /workspace/dart/aaa/lib/a.dart
    current: cycle_0
      key: k06
    get: []
    put: [k06]
  /workspace/dart/aaa/lib/b.dart
    get: []
    put: [k07]
  /workspace/dart/aaa/lib/c.dart
    current: cycle_2
      key: k08
    get: []
    put: [k08]
  /workspace/dart/aaa/lib/d.dart
    current: cycle_3
      key: k09
    get: []
    put: [k09]
  /workspace/dart/aaa/lib/e.dart
    get: []
    put: [k10]
  /workspace/dart/aaa/lib/f.dart
    current: cycle_5
      key: k11
    get: []
    put: [k11]
elementFactory
  hasElement
    package:dart.aaa/a.dart
    package:dart.aaa/c.dart
    package:dart.aaa/d.dart
    package:dart.aaa/f.dart
unlinkedUnitStore
  1: [k00, k02, k03, k05, k12, k13, k14, k15, k16]
byteStore
  1: [k00, k02, k03, k05, k06, k08, k09, k11, k12, k13, k14, k15, k16, k17]
''');
  }

  test_removeFilesNotNecessaryForAnalysisOf_unknown() async {
    var a = newFile('/workspace/dart/aaa/lib/a.dart', r'''
class A {}
''');

    var b = getFile('/workspace/dart/aaa/lib/b.dart');

    await resolveFile(a);
    fileResolver.removeFilesNotNecessaryForAnalysisOf([a.path, b.path]);

    // No b.dart anywhere.
    assertStateString(r'''
files
  /workspace/dart/aaa/lib/a.dart
    uri: package:dart.aaa/a.dart
    current
      id: file_0
      kind: library_0
        libraryImports
          library_1 dart:core synthetic
        fileKinds: library_0
        cycle_0
          dependencies: dart:core
          libraries: library_0
          apiSignature_0
      unlinkedKey: k00
    unlinkedGet: []
    unlinkedPut: [k00]
libraryCycles
  /workspace/dart/aaa/lib/a.dart
    current: cycle_0
      key: k01
    get: []
    put: [k01]
elementFactory
  hasElement
    package:dart.aaa/a.dart
unlinkedUnitStore
  1: [k00, k02, k03, k04, k05, k06]
byteStore
  1: [k00, k01, k02, k03, k04, k05, k06, k07]
''');
  }

  test_resolve_libraryWithPart_noLibraryDiscovery() async {
    newFile('/workspace/dart/test/lib/a.dart', r'''
part of 'test.dart';

class A {}
''');

    await assertNoErrorsInCode(r'''
part 'a.dart';

void f(A a) {}
''');

    // We started resolution from the library, and then followed to the part.
    // So, the part knows its library, there is no need to discover it.
    // TODO(scheglov): Use textual dump
    // _assertDiscoveredLibraryForParts([]);
  }

  test_resolve_part_of_name() async {
    var a = newFile('/workspace/dart/test/lib/a.dart', r'''
library my.lib;

part 'test.dart';

class A {
  int m;
}
''');

    await assertNoErrorsInCode(r'''
part of my.lib;

void func() {
  var a = A();
  print(a.m);
}
''');

    // TODO(scheglov): Use textual dump
    var fsState = fileResolver.fsState!;
    var testState = fsState.getExisting(testFile)!;
    var testKind = testState.kind as PartFileKind;
    expect(testKind.library?.file, fsState.getExisting(a));
  }

  test_resolve_part_of_uri() async {
    var a = newFile('/workspace/dart/test/lib/a.dart', r'''
part 'test.dart';

class A {
  int m;
}
''');

    await assertNoErrorsInCode(r'''
part of 'a.dart';

void func() {
  var a = A();
  print(a.m);
}
''');

    // TODO(scheglov): Use textual dump
    var fsState = fileResolver.fsState!;
    var testState = fsState.getExisting(testFile)!;
    var testKind = testState.kind as PartFileKind;
    expect(testKind.library?.file, fsState.getExisting(a));
  }

  test_resolveFile_cache() async {
    addTestFile('var a = 0;');

    // No resolved files yet.
    _assertResolvedFiles([]);

    await resolveFile2(testFile);
    var result1 = result;

    // The file was resolved.
    _assertResolvedFiles([testFile]);

    // The result is cached.
    expect(fileResolver.cachedResults, contains(testFile.path));

    // Ask again, no changes, not resolved.
    await resolveFile2(testFile);
    _assertResolvedFiles([]);

    // The same result was returned.
    expect(result, same(result1));

    // Change a file.
    var a_path = convertPath('/workspace/dart/test/lib/a.dart');
    fileResolver.changeFiles([a_path]);

    // The was a change to a file, no matter which, resolve again.
    await resolveFile2(testFile);
    _assertResolvedFiles([testFile]);

    // Get should get a new result.
    expect(result, isNot(same(result1)));
  }

  test_resolveLibrary() async {
    var aPath = convertPath('/workspace/dart/test/lib/a.dart');
    newFile(aPath, r'''
part 'test.dart';

class A {
  int m;
}
''');

    newFile('/workspace/dart/test/lib/test.dart', r'''
part of 'a.dart';

void func() {
  var a = A();
  print(a.m);
}
''');

    var result = await fileResolver.resolveLibrary2(path: aPath);
    expect(result.units.length, 2);
    expect(result.units[0].path, aPath);
    expect(result.units[0].uri, Uri.parse('package:dart.test/a.dart'));
  }

  test_reuse_compatibleOptions() async {
    newFile('/workspace/dart/aaa/BUILD', '');
    newFile('/workspace/dart/bbb/BUILD', '');

    var aPath = '/workspace/dart/aaa/lib/a.dart';
    var aResult = await assertErrorsInFile(aPath, '', []);

    var bPath = '/workspace/dart/bbb/lib/a.dart';
    var bResult = await assertErrorsInFile(bPath, '', []);

    // Both files use the same (default) analysis options.
    // So, when we resolve 'bbb', we can reuse the context after 'aaa'.
    expect(
      aResult.libraryElement.session,
      same(bResult.libraryElement.session),
    );
  }

  test_switchCase_implementsEquals_enum() async {
    await assertNoErrorsInCode(r'''
enum MyEnum {a, b, c}

void f(MyEnum myEnum) {
  switch (myEnum) {
    case MyEnum.a:
      break;
    default:
      break;
  }
}
''');
  }

  test_switchCase_implementsEquals_enum_language219() async {
    await assertNoErrorsInCode(r'''
// @dart = 2.19
enum MyEnum {a, b, c}

void f(MyEnum myEnum) {
  switch (myEnum) {
    case MyEnum.a:
      break;
    default:
      break;
  }
}
''');
  }

  test_unknown_uri() async {
    await assertErrorsInCode(
      r'''
import 'foo:bar';
''',
      [error(CompileTimeErrorCode.uriDoesNotExist, 7, 9)],
    );
  }

  test_warning() async {
    await assertErrorsInCode(
      r'''
import 'dart:math';
''',
      [error(WarningCode.unusedImport, 7, 11)],
    );
  }

  void _assertResolvedFiles(List<File> expected, {bool andClear = true}) {
    var actual = fileResolver.testData!.resolvedLibraries;
    expect(actual, expected.map((e) => e.path).toList());
    if (andClear) {
      actual.clear();
    }
  }

  Future<Element> _findElement(int offset, File file) async {
    var resolvedUnit = await fileResolver.resolve(path: file.path);
    var node = resolvedUnit.unit.nodeCovering(offset: offset);
    var element = getElementOfNode2(node);
    return element!;
  }
}
