blob: d7b654c7b807e5d9606a90cdbcdd5352fb8bf195 [file] [log] [blame]
// 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/source/line_info.dart';
import 'package:analyzer/src/dart/ast/utilities.dart';
import 'package:analyzer/src/dart/error/syntactic_errors.dart';
import 'package:analyzer/src/dart/micro/cider_byte_store.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:analyzer/src/lint/registry.dart';
import 'package:test/test.dart';
import 'package:test_reflective_loader/test_reflective_loader.dart';
import 'file_resolution.dart';
main() {
defineReflectiveSuite(() {
defineReflectiveTests(FileResolver_changeFile_Test);
defineReflectiveTests(FileResolverTest);
});
}
@reflectiveTest
class FileResolver_changeFile_Test extends FileResolutionTest {
late final String aPath;
late final String bPath;
late final String cPath;
@override
void setUp() {
super.setUp();
aPath = convertPath('/workspace/dart/test/lib/a.dart');
bPath = convertPath('/workspace/dart/test/lib/b.dart');
cPath = convertPath('/workspace/dart/test/lib/c.dart');
}
test_changeFile_refreshedFiles() async {
newFile2(aPath, r'''
class A {}
''');
newFile2(bPath, r'''
class B {}
''');
newFile2(cPath, r'''
import 'a.dart';
import 'b.dart';
''');
// First time we refresh everything.
await resolveFile(cPath);
_assertRefreshedFiles([aPath, bPath, cPath], withSdk: true);
// Without changes we refresh nothing.
await resolveFile(cPath);
_assertRefreshedFiles([]);
// We already know a.dart, refresh nothing.
await resolveFile(aPath);
_assertRefreshedFiles([]);
// Change a.dart, refresh a.dart and c.dart, but not b.dart
fileResolver.changeFile(aPath);
await resolveFile(cPath);
_assertRefreshedFiles([aPath, cPath]);
}
test_changeFile_resolution() async {
newFile2(aPath, r'''
class A {}
''');
newFile2(bPath, r'''
import 'a.dart';
void f(A a, B b) {}
''');
result = await resolveFile(bPath);
assertErrorsInResolvedUnit(result, [
error(CompileTimeErrorCode.UNDEFINED_CLASS, 29, 1),
]);
newFile2(aPath, r'''
class A {}
class B {}
''');
fileResolver.changeFile(aPath);
result = await resolveFile(bPath);
assertErrorsInResolvedUnit(result, []);
}
test_changeFile_resolution_flushInheritanceManager() async {
newFile2(aPath, r'''
class A {
final int foo = 0;
}
''');
newFile2(bPath, r'''
import 'a.dart';
void f(A a) {
a.foo = 1;
}
''');
result = await resolveFile(bPath);
assertErrorsInResolvedUnit(result, [
error(CompileTimeErrorCode.ASSIGNMENT_TO_FINAL, 36, 3),
]);
newFile2(aPath, r'''
class A {
int foo = 0;
}
''');
fileResolver.changeFile(aPath);
result = await resolveFile(bPath);
assertErrorsInResolvedUnit(result, []);
}
test_changeFile_resolution_missingChangeFileForPart() async {
newFile2(aPath, r'''
part 'b.dart';
var b = B(0);
''');
result = await resolveFile(aPath);
assertErrorsInResolvedUnit(result, [
error(CompileTimeErrorCode.URI_DOES_NOT_EXIST, 5, 8),
error(CompileTimeErrorCode.UNDEFINED_FUNCTION, 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.
newFile2(aPath, r'''
part 'b.dart';
var b = B(1);
''');
fileResolver.changeFile(aPath);
// Update b.dart, but do not notify the resolver.
// If we try to read it now, it will throw.
newFile2(bPath, r'''
part of 'a.dart';
class B {
B(int _);
}
''');
expect(() async {
await resolveFile(aPath);
}, throwsStateError);
// Notify the resolver about b.dart, it is OK now.
fileResolver.changeFile(bPath);
result = await resolveFile(aPath);
assertErrorsInResolvedUnit(result, []);
}
test_changePartFile_refreshedFiles() async {
newFile2(aPath, r'''
part 'b.dart';
class A {}
''');
newFile2(bPath, r'''
part of 'a.dart';
class B extends A {}
''');
newFile2(cPath, r'''
import 'a.dart';
''');
// First time we refresh everything.
await resolveFile(bPath);
_assertRefreshedFiles([aPath, bPath], withSdk: true);
// Change b.dart, refresh a.dart
fileResolver.changeFile(bPath);
await resolveFile(bPath);
_assertRefreshedFiles([aPath, bPath]);
// now with c.dart
await resolveFile(cPath);
_assertRefreshedFiles([cPath]);
fileResolver.changeFile(bPath);
await resolveFile(cPath);
_assertRefreshedFiles([aPath, bPath, cPath]);
}
void _assertRefreshedFiles(List<String> expected, {bool withSdk = false}) {
var expectedPlusSdk = expected.toSet();
if (withSdk) {
expectedPlusSdk
..add(convertPath('/sdk/lib/_internal/internal.dart'))
..add(convertPath('/sdk/lib/async/async.dart'))
..add(convertPath('/sdk/lib/async/stream.dart'))
..add(convertPath('/sdk/lib/core/core.dart'))
..add(convertPath('/sdk/lib/math/math.dart'));
}
var refreshedFiles = fileResolver.fsState!.testView.refreshedFiles;
expect(refreshedFiles, unorderedEquals(expectedPlusSdk));
refreshedFiles.clear();
}
}
@reflectiveTest
class FileResolverTest extends FileResolutionTest {
@override
bool get isNullSafetyEnabled => true;
test_analysisOptions_default_fromPackageUri() async {
newFile2('/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.INVALID_ASSIGNMENT, 19, 1),
]);
}
test_analysisOptions_file_inPackage() async {
newAnalysisOptionsYamlFile2('/workspace/dart/test', r'''
analyzer:
strong-mode:
implicit-casts: false
''');
await assertErrorsInCode(r'''
num a = 0;
int b = a;
''', [
error(CompileTimeErrorCode.INVALID_ASSIGNMENT, 19, 1),
]);
}
test_analysisOptions_file_inThirdParty() async {
newFile2('/workspace/dart/analysis_options/lib/third_party.yaml', r'''
analyzer:
strong-mode:
implicit-casts: false
''');
newAnalysisOptionsYamlFile2('/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.INVALID_ASSIGNMENT, 19, 1),
]);
}
test_analysisOptions_file_inThirdPartyDartLang() async {
newFile2('/workspace/dart/analysis_options/lib/third_party.yaml', r'''
analyzer:
strong-mode:
implicit-casts: false
''');
newAnalysisOptionsYamlFile2('/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.INVALID_ASSIGNMENT, 19, 1),
]);
}
test_analysisOptions_lints() async {
newFile2('/workspace/dart/analysis_options/lib/default.yaml', r'''
linter:
rules:
- omit_local_variable_types
''');
var rule = Registry.ruleRegistry.getRule('omit_local_variable_types')!;
await assertErrorsInCode(r'''
main() {
int a = 0;
a;
}
''', [
error(rule.lintCode, 11, 9),
]);
}
test_basic() async {
await assertNoErrorsInCode(r'''
int a = 0;
var b = 1 + 2;
''');
assertType(findElement.topVar('a').type, 'int');
assertElement(findNode.simple('int a'), intElement);
assertType(findElement.topVar('b').type, 'int');
}
test_collectSharedDataIdentifiers() async {
var aPath = convertPath('/workspace/third_party/dart/aaa/lib/a.dart');
newFile2(aPath, r'''
class A {}
''');
await resolveFile(aPath);
fileResolver.collectSharedDataIdentifiers();
expect(fileResolver.removedCacheIds.length,
(fileResolver.byteStore as CiderCachedByteStore).testView!.length);
}
test_elements_export_dartCoreDynamic() async {
var a_path = convertPath('/workspace/dart/test/lib/a.dart');
newFile2(a_path, r'''
export 'dart:core' show dynamic;
''');
// Analyze so that `dart:core` is linked.
var a_result = await resolveFile(a_path);
// Touch `dart:core` so that its element model is discarded.
var dartCorePath = a_result.session.uriConverter.uriToPath(
Uri.parse('dart:core'),
)!;
fileResolver.changeFile(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() {}
''');
}
test_errors_hasNullSuffix() {
assertErrorsInCode(r'''
String f(Map<int, String> a) {
return a[0];
}
''', [
error(CompileTimeErrorCode.RETURN_OF_INVALID_TYPE_FROM_FUNCTION, 40, 4,
messageContains: ["'String'", 'String?']),
]);
}
test_findReferences_class() async {
var aPath = convertPath('/workspace/dart/test/lib/a.dart');
newFile2(aPath, r'''
class A {
int foo;
}
''');
var bPath = convertPath('/workspace/dart/test/lib/b.dart');
newFile2(bPath, r'''
import 'a.dart';
void func() {
var a = A();
print(a.foo);
}
''');
await resolveFile(bPath);
var result = fileResolver.findReferences(_findElement(6, aPath));
var expected = <CiderSearchMatch>[
CiderSearchMatch(bPath, [CharacterLocation(4, 11)]),
CiderSearchMatch(aPath, [CharacterLocation(1, 7)])
];
expect(result, unorderedEquals(expected));
}
test_findReferences_field() async {
var aPath = convertPath('/workspace/dart/test/lib/a.dart');
newFile2(aPath, r'''
class A {
int foo = 0;
void func(int bar) {
foo = bar;
}
}
''');
await resolveFile(aPath);
var result = fileResolver.findReferences(_findElement(16, aPath));
var expected = <CiderSearchMatch>[
CiderSearchMatch(
aPath, [CharacterLocation(2, 7), CharacterLocation(5, 5)])
];
expect(result, unorderedEquals(expected));
}
test_findReferences_function() async {
var aPath = convertPath('/workspace/dart/test/lib/a.dart');
newFile2(aPath, r'''
main() {
foo('Hello');
}
foo(String str) {}
''');
await resolveFile(aPath);
var result = fileResolver.findReferences(_findElement(11, aPath));
var expected = <CiderSearchMatch>[
CiderSearchMatch(
aPath, [CharacterLocation(2, 3), CharacterLocation(5, 1)])
];
expect(result, unorderedEquals(expected));
}
test_findReferences_getter() async {
var aPath = convertPath('/workspace/dart/test/lib/a.dart');
newFile2(aPath, r'''
class A {
int get foo => 6;
}
''');
var bPath = convertPath('/workspace/dart/test/lib/b.dart');
newFile2(bPath, r'''
import 'a.dart';
main() {
var a = A();
var bar = a.foo;
}
''');
await resolveFile(bPath);
var result = fileResolver.findReferences(_findElement(20, aPath));
var expected = <CiderSearchMatch>[
CiderSearchMatch(bPath, [CharacterLocation(5, 15)]),
CiderSearchMatch(aPath, [CharacterLocation(2, 11)])
];
expect(result, unorderedEquals(expected));
}
test_findReferences_local_variable() async {
var aPath = convertPath('/workspace/dart/test/lib/a.dart');
newFile2(aPath, r'''
class A {
void func(int n) {
var foo = bar+1;
print(foo);
}
}
''');
await resolveFile(aPath);
var result = fileResolver.findReferences(_findElement(39, aPath));
var expected = <CiderSearchMatch>[
CiderSearchMatch(
aPath, [CharacterLocation(3, 9), CharacterLocation(4, 11)])
];
expect(result, unorderedEquals(expected));
}
test_findReferences_method() async {
var aPath = convertPath('/workspace/dart/test/lib/a.dart');
newFile2(aPath, r'''
class A {
void func() {
print('hello');
}
void func2() {
func();
}
}
''');
var bPath = convertPath('/workspace/dart/test/lib/b.dart');
newFile2(bPath, r'''
import 'a.dart';
main() {
var a = A();
a.func();
}
''');
await resolveFile(bPath);
var result = fileResolver.findReferences(_findElement(17, aPath));
var expected = <CiderSearchMatch>[
CiderSearchMatch(bPath, [CharacterLocation(5, 5)]),
CiderSearchMatch(
aPath, [CharacterLocation(2, 8), CharacterLocation(7, 4)])
];
expect(result, unorderedEquals(expected));
}
test_findReferences_setter() async {
var aPath = convertPath('/workspace/dart/test/lib/a.dart');
newFile2(aPath, r'''
class A {
void set value(int m){ };
}
''');
var bPath = convertPath('/workspace/dart/test/lib/b.dart');
newFile2(bPath, r'''
import 'a.dart';
main() {
var a = A();
a.value = 6;
}
''');
await resolveFile(bPath);
var result = fileResolver.findReferences(_findElement(21, aPath));
var expected = <CiderSearchMatch>[
CiderSearchMatch(bPath, [CharacterLocation(5, 5)]),
CiderSearchMatch(aPath, [CharacterLocation(2, 12)])
];
expect(result, unorderedEquals(expected));
}
test_findReferences_top_level_getter() async {
var aPath = convertPath('/workspace/dart/test/lib/a.dart');
newFile2(aPath, r'''
int _foo;
int get foo => _foo;
''');
var bPath = convertPath('/workspace/dart/test/lib/b.dart');
newFile2(bPath, r'''
import 'a.dart';
main() {
var bar = foo;
}
''');
await resolveFile(bPath);
var result = fileResolver.findReferences(_findElement(19, aPath));
var expected = <CiderSearchMatch>[
CiderSearchMatch(bPath, [CharacterLocation(4, 13)]),
CiderSearchMatch(aPath, [CharacterLocation(3, 9)])
];
expect(result, unorderedEquals(expected));
}
test_findReferences_top_level_setter() async {
var aPath = convertPath('/workspace/dart/test/lib/a.dart');
newFile2(aPath, r'''
int _foo;
void set foo(int bar) { _foo = bar; }
''');
var bPath = convertPath('/workspace/dart/test/lib/b.dart');
newFile2(bPath, r'''
import 'a.dart';
main() {
foo = 6;
}
''');
await resolveFile(bPath);
var result = fileResolver.findReferences(_findElement(20, aPath));
var expected = <CiderSearchMatch>[
CiderSearchMatch(bPath, [CharacterLocation(4, 3)]),
CiderSearchMatch(aPath, [CharacterLocation(3, 10)])
];
expect(result, unorderedEquals(expected));
}
test_findReferences_top_level_variable() async {
var aPath = convertPath('/workspace/dart/test/lib/a.dart');
newFile2(aPath, r'''
const int C = 42;
void func() {
print(C);
}
''');
await resolveFile(aPath);
var result = fileResolver.findReferences(_findElement(10, aPath));
var expected = <CiderSearchMatch>[
CiderSearchMatch(
aPath, [CharacterLocation(1, 11), CharacterLocation(4, 11)])
];
expect(result, unorderedEquals(expected));
}
test_findReferences_type_parameter() async {
var aPath = convertPath('/workspace/dart/test/lib/a.dart');
newFile2(aPath, r'''
class Foo<T> {
List<T> l;
void bar(T t) {}
}
''');
await resolveFile(aPath);
var result = fileResolver.findReferences(_findElement(10, aPath));
var expected = <CiderSearchMatch>[
CiderSearchMatch(aPath, [
CharacterLocation(1, 11),
CharacterLocation(2, 8),
CharacterLocation(4, 12)
])
];
expect(result.map((e) => e.path),
unorderedEquals(expected.map((e) => e.path)));
expect(result.map((e) => e.startPositions),
unorderedEquals(expected.map((e) => e.startPositions)));
}
test_findReferences_typedef() async {
var aPath = convertPath('/workspace/dart/test/lib/a.dart');
newFile2(aPath, r'''
typedef func = int Function(int);
''');
var bPath = convertPath('/workspace/dart/test/lib/b.dart');
newFile2(bPath, r'''
import 'a.dart';
void f(func o) {}
''');
await resolveFile(bPath);
var result = fileResolver.findReferences(_findElement(8, aPath));
var expected = <CiderSearchMatch>[
CiderSearchMatch(bPath, [CharacterLocation(3, 8)]),
CiderSearchMatch(aPath, [CharacterLocation(1, 9)])
];
expect(result, unorderedEquals(expected));
}
test_getErrors() {
addTestFile(r'''
var a = b;
var foo = 0;
''');
var result = getTestErrors();
expect(result.path, convertPath('/workspace/dart/test/lib/test.dart'));
expect(result.uri.toString(), 'package:dart.test/test.dart');
assertErrorsInList(result.errors, [
error(CompileTimeErrorCode.UNDEFINED_IDENTIFIER, 8, 1),
]);
expect(result.lineInfo.lineStarts, [0, 11, 24]);
}
test_getErrors_reuse() {
addTestFile('var a = b;');
var path = convertPath('/workspace/dart/test/lib/test.dart');
// No resolved files yet.
expect(fileResolver.testView!.resolvedLibraries, isEmpty);
// No cached, will resolve once.
expect(getTestErrors().errors, hasLength(1));
expect(fileResolver.testView!.resolvedLibraries, [path]);
// Has cached, will be not resolved again.
expect(getTestErrors().errors, hasLength(1));
expect(fileResolver.testView!.resolvedLibraries, [path]);
// New resolver.
// Still has cached, will be not resolved.
createFileResolver();
expect(getTestErrors().errors, hasLength(1));
expect(fileResolver.testView!.resolvedLibraries, <Object>[]);
// Change the file, new resolver.
// With changed file the previously cached result cannot be used.
addTestFile('var a = c;');
createFileResolver();
expect(getTestErrors().errors, hasLength(1));
expect(fileResolver.testView!.resolvedLibraries, [path]);
// New resolver.
// Still has cached, will be not resolved.
createFileResolver();
expect(getTestErrors().errors, hasLength(1));
expect(fileResolver.testView!.resolvedLibraries, <Object>[]);
}
test_getErrors_reuse_changeDependency() {
newFile2('/workspace/dart/test/lib/a.dart', r'''
var a = 0;
''');
addTestFile(r'''
import 'a.dart';
var b = a.foo;
''');
var path = convertPath('/workspace/dart/test/lib/test.dart');
// No resolved files yet.
expect(fileResolver.testView!.resolvedLibraries, isEmpty);
// No cached, will resolve once.
expect(getTestErrors().errors, hasLength(1));
expect(fileResolver.testView!.resolvedLibraries, [path]);
// Has cached, will be not resolved again.
expect(getTestErrors().errors, hasLength(1));
expect(fileResolver.testView!.resolvedLibraries, [path]);
// Change the dependency, new resolver.
// The signature of the result is different.
// The previously cached result cannot be used.
newFile2('/workspace/dart/test/lib/a.dart', r'''
var a = 4.2;
''');
createFileResolver();
expect(getTestErrors().errors, hasLength(1));
expect(fileResolver.testView!.resolvedLibraries, [path]);
// New resolver.
// Still has cached, will be not resolved.
createFileResolver();
expect(getTestErrors().errors, hasLength(1));
expect(fileResolver.testView!.resolvedLibraries, <Object>[]);
}
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() {
newFile2('/workspace/dart/my/lib/a.dart', r'''
class A {}
''');
var element = fileResolver.getLibraryByUri(
uriStr: 'package:dart.my/a.dart',
);
expect(element.definingCompilationUnit.classes, hasLength(1));
}
test_getLibraryByUri_notExistingFile() {
var element = fileResolver.getLibraryByUri(
uriStr: 'package:dart.my/a.dart',
);
expect(element.definingCompilationUnit.classes, isEmpty);
}
test_getLibraryByUri_partOf() {
newFile2('/workspace/dart/my/lib/a.dart', r'''
part of 'b.dart';
''');
expect(() {
fileResolver.getLibraryByUri(
uriStr: 'package:dart.my/a.dart',
);
}, throwsArgumentError);
}
test_getLibraryByUri_unresolvedUri() {
expect(() {
fileResolver.getLibraryByUri(uriStr: 'my:unresolved');
}, throwsArgumentError);
}
test_hint() async {
await assertErrorsInCode(r'''
import 'dart:math';
''', [
error(HintCode.UNUSED_IMPORT, 7, 11),
]);
}
test_hint_in_third_party() async {
var aPath = convertPath('/workspace/third_party/dart/aaa/lib/a.dart');
newFile2(aPath, r'''
import 'dart:math';
''');
await resolveFile(aPath);
assertNoErrorsInResult();
}
test_linkLibraries_getErrors() {
addTestFile(r'''
var a = b;
var foo = 0;
''');
var path = convertPath('/workspace/dart/test/lib/test.dart');
fileResolver.linkLibraries(path: path);
var result = getTestErrors();
expect(result.path, path);
expect(result.uri.toString(), 'package:dart.test/test.dart');
assertErrorsInList(result.errors, [
error(CompileTimeErrorCode.UNDEFINED_IDENTIFIER, 8, 1),
]);
expect(result.lineInfo.lineStarts, [0, 11, 24]);
}
test_nameOffset_class_method_fromBytes() async {
newFile2('/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();').staticElement!;
expect(element.nameOffset, 17);
}
// New resolver.
// Element models will be loaded from the cache.
createFileResolver();
await resolveTestFile();
{
var element = findNode.simple('foo();').staticElement!;
expect(element.nameOffset, 17);
}
}
test_nameOffset_unit_variable_fromBytes() async {
newFile2('/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;').staticElement!;
expect(element.nonSynthetic.nameOffset, 4);
}
// New resolver.
// Element models will be loaded from the cache.
createFileResolver();
await resolveTestFile();
{
var element = findNode.simple('a;').staticElement!;
expect(element.nonSynthetic.nameOffset, 4);
}
}
test_nullSafety_enabled() async {
await assertNoErrorsInCode(r'''
void f(int? a) {
if (a != null) {
a.isEven;
}
}
''');
assertType(
findElement.parameter('a').type,
'int?',
);
}
test_nullSafety_notEnabled() async {
newFile2('/workspace/dart/test/BUILD', '');
await assertErrorsInCode(r'''
void f(int? a) {}
''', [
error(ParserErrorCode.EXPERIMENT_NOT_ENABLED, 10, 1),
]);
assertType(
findElement.parameter('a').type,
'int*',
);
}
test_part_notInLibrary_libraryDoesNotExist() async {
// TODO(scheglov) Should report CompileTimeErrorCode.URI_DOES_NOT_EXIST
await assertNoErrorsInCode(r'''
part of 'a.dart';
''');
}
test_removeFilesNotNecessaryForAnalysisOf() async {
var aPath = convertPath('/workspace/dart/aaa/lib/a.dart');
var bPath = convertPath('/workspace/dart/aaa/lib/b.dart');
var cPath = convertPath('/workspace/dart/aaa/lib/c.dart');
newFile2(aPath, r'''
class A {}
''');
newFile2(bPath, r'''
import 'a.dart';
''');
newFile2(cPath, r'''
import 'a.dart';
''');
await resolveFile(bPath);
await resolveFile(cPath);
fileResolver.removeFilesNotNecessaryForAnalysisOf([cPath]);
_assertRemovedPaths(unorderedEquals([bPath]));
}
test_removeFilesNotNecessaryForAnalysisOf_multiple() async {
var bPath = convertPath('/workspace/dart/aaa/lib/b.dart');
var dPath = convertPath('/workspace/dart/aaa/lib/d.dart');
var ePath = convertPath('/workspace/dart/aaa/lib/e.dart');
var fPath = convertPath('/workspace/dart/aaa/lib/f.dart');
newFile2('/workspace/dart/aaa/lib/a.dart', r'''
class A {}
''');
newFile2(bPath, r'''
class B {}
''');
newFile2('/workspace/dart/aaa/lib/c.dart', r'''
class C {}
''');
newFile2(dPath, r'''
import 'a.dart';
''');
newFile2(ePath, r'''
import 'a.dart';
import 'b.dart';
''');
newFile2(fPath, r'''
import 'c.dart';
''');
await resolveFile(dPath);
await resolveFile(ePath);
await resolveFile(fPath);
fileResolver.removeFilesNotNecessaryForAnalysisOf([dPath, fPath]);
_assertRemovedPaths(unorderedEquals([bPath, ePath]));
}
test_removeFilesNotNecessaryForAnalysisOf_unknown() async {
var aPath = convertPath('/workspace/dart/aaa/lib/a.dart');
var bPath = convertPath('/workspace/dart/aaa/lib/b.dart');
newFile2(aPath, r'''
class A {}
''');
await resolveFile(aPath);
fileResolver.removeFilesNotNecessaryForAnalysisOf([aPath, bPath]);
_assertRemovedPaths(isEmpty);
}
test_resolve_libraryWithPart_noLibraryDiscovery() async {
var partPath = '/workspace/dart/test/lib/a.dart';
newFile2(partPath, 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.
_assertDiscoveredLibraryForParts([]);
}
test_resolve_part_of_name() async {
newFile2('/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);
}
''');
_assertDiscoveredLibraryForParts([result.path]);
}
test_resolve_part_of_uri() async {
newFile2('/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);
}
''');
_assertDiscoveredLibraryForParts([result.path]);
}
test_resolveFile_cache() async {
var path = convertPath('/workspace/dart/test/lib/test.dart');
newFile2(path, 'var a = 0;');
// No resolved files yet.
var testView = fileResolver.testView!;
expect(testView.resolvedLibraries, isEmpty);
await resolveFile2(path);
var result1 = result;
// The file was resolved.
expect(testView.resolvedLibraries, [path]);
// The result is cached.
expect(fileResolver.cachedResults, contains(path));
// Ask again, no changes, not resolved.
await resolveFile2(path);
expect(testView.resolvedLibraries, [path]);
// The same result was returned.
expect(result, same(result1));
// Change a file.
var a_path = convertPath('/workspace/dart/test/lib/a.dart');
fileResolver.changeFile(a_path);
// The was a change to a file, no matter which, resolve again.
await resolveFile2(path);
expect(testView.resolvedLibraries, [path, path]);
// Get should get a new result.
expect(result, isNot(same(result1)));
}
test_resolveFile_dontCache_whenForCompletion() async {
var a_path = convertPath('/workspace/dart/test/lib/a.dart');
newFile2(a_path, r'''
part 'b.dart';
''');
var b_path = convertPath('/workspace/dart/test/lib/b.dart');
newFile2(b_path, r'''
part of 'a.dart';
''');
// No resolved files yet.
var testView = fileResolver.testView!;
expect(testView.resolvedLibraries, isEmpty);
fileResolver.resolve(
path: b_path,
completionLine: 0,
completionColumn: 0,
);
// The file was resolved.
expect(testView.resolvedLibraries, [a_path]);
// The completion location was set, so not units are resolved.
// So, the result should not be cached.
expect(fileResolver.cachedResults, isEmpty);
}
test_resolveLibrary() async {
var aPath = convertPath('/workspace/dart/test/lib/a.dart');
newFile2(aPath, r'''
part 'test.dart';
class A {
int m;
}
''');
newFile2('/workspace/dart/test/lib/test.dart', r'''
part of 'a.dart';
void func() {
var a = A();
print(a.m);
}
''');
var result = fileResolver.resolveLibrary(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 {
newFile2('/workspace/dart/aaa/BUILD', '');
newFile2('/workspace/dart/bbb/BUILD', '');
var aPath = '/workspace/dart/aaa/lib/a.dart';
var aResult = await assertErrorsInFile(aPath, r'''
num a = 0;
int b = a;
''', []);
var bPath = '/workspace/dart/bbb/lib/a.dart';
var bResult = await assertErrorsInFile(bPath, r'''
num a = 0;
int b = a;
''', []);
// Both files use the same (default) analysis options.
// So, when we resolve 'bbb', we can reuse the context after 'aaa'.
expect(
aResult.libraryElement.context,
same(bResult.libraryElement.context),
);
}
test_reuse_incompatibleOptions_implicitCasts() async {
newFile2('/workspace/dart/aaa/BUILD', '');
newAnalysisOptionsYamlFile2('/workspace/dart/aaa', r'''
analyzer:
strong-mode:
implicit-casts: false
''');
newFile2('/workspace/dart/bbb/BUILD', '');
newAnalysisOptionsYamlFile2('/workspace/dart/bbb', r'''
analyzer:
strong-mode:
implicit-casts: true
''');
// Implicit casts are disabled in 'aaa'.
var aPath = '/workspace/dart/aaa/lib/a.dart';
await assertErrorsInFile(aPath, r'''
num a = 0;
int b = a;
''', [
error(CompileTimeErrorCode.INVALID_ASSIGNMENT, 19, 1),
]);
// Implicit casts are enabled in 'bbb'.
var bPath = '/workspace/dart/bbb/lib/a.dart';
await assertErrorsInFile(bPath, r'''
num a = 0;
int b = a;
''', []);
// Implicit casts are still disabled in 'aaa'.
await assertErrorsInFile(aPath, r'''
num a = 0;
int b = a;
''', [
error(CompileTimeErrorCode.INVALID_ASSIGNMENT, 19, 1),
]);
}
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_unknown_uri() async {
await assertErrorsInCode(r'''
import 'foo:bar';
''', [
error(CompileTimeErrorCode.URI_DOES_NOT_EXIST, 7, 9),
]);
}
void _assertDiscoveredLibraryForParts(List<String> expected) {
expect(fileResolver.fsState!.testView.partsDiscoveredLibraries, expected);
}
void _assertRemovedPaths(Matcher matcher) {
expect(fileResolver.fsState!.testView.removedPaths, matcher);
}
Element _findElement(int offset, String filePath) {
var resolvedUnit = fileResolver.resolve(path: filePath);
var node = NodeLocator(offset).searchWithin(resolvedUnit.unit);
var element = getElementOfNode(node);
return element!;
}
}