blob: ae4ce03536f40d01090754b956bbf5292a18afbc [file] [log] [blame]
// Copyright (c) 2024, 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:analysis_server/lsp_protocol/protocol.dart';
import 'package:analyzer/src/test_utilities/test_code_format.dart';
import 'package:test/test.dart';
import 'package:test_reflective_loader/test_reflective_loader.dart';
import '../utils/test_code_extensions.dart';
import 'server_abstract.dart';
void main() {
defineReflectiveSuite(() {
defineReflectiveTests(ImportTest);
});
}
@reflectiveTest
class ImportTest extends AbstractLspAnalysisServerTest {
/// A helper for creating files for multi-level-parts tests.
({Uri uri, String filePath, TestCode code}) createFile(
String filename,
String content,
) {
assert(filename.endsWith('.dart'));
var filePath = join(projectFolderPath, 'lib', filename);
var code = TestCode.parse(content);
newFile(filePath, code.code);
return (uri: toUri(filePath), filePath: filePath, code: code);
}
Future<void> test_constant() async {
await _verifyGoToImports(
TestCode.parse('''
[!import 'dart:math';!]
void foo() {
print(p^i);
}
'''),
);
}
Future<void> test_constant_alias() async {
await _verifyGoToImports(
TestCode.parse('''
[!import 'dart:math' as math;!]
void foo() {
print(math.p^i);
}
'''),
);
}
Future<void> test_extension_getter() async {
newFile(join(projectFolderPath, 'lib', 'other.dart'), '''
extension E on int {
int get bar => 42;
}
''');
await _verifyGoToImports(
TestCode.parse('''
[!import 'other.dart';!]
void f(int i) {
i.ba^r;
}
'''),
);
}
Future<void> test_extension_nestedInvocations() async {
newFile(join(projectFolderPath, 'lib', 'other.dart'), '''
extension E on int {
void bar() {}
}
''');
await _verifyGoToImports(
TestCode.parse('''
[!import 'other.dart';!]
var a = 1.abs().ba^r();
'''),
);
}
Future<void> test_extension_setter() async {
newFile(join(projectFolderPath, 'lib', 'other.dart'), '''
extension E on int {
set foo(int _) {}
}
''');
await _verifyGoToImports(
TestCode.parse('''
[!import 'other.dart';!]
void f(int i) {
i.fo^o = 0;
}
'''),
);
}
Future<void> test_extensionOverride_getter() async {
newFile(join(projectFolderPath, 'lib', 'other.dart'), '''
extension E on int {
int get bar => 42;
}
''');
await _verifyGoToImports(
TestCode.parse('''
[!import 'other.dart';!]
void f(int i) {
E(i).ba^r;
}
'''),
);
}
Future<void> test_extensionOverride_method() async {
newFile(join(projectFolderPath, 'lib', 'other.dart'), '''
extension E on int {
void bar() {}
}
''');
await _verifyGoToImports(
TestCode.parse('''
[!import 'other.dart';!]
var a = E(1).ba^r();
'''),
);
}
Future<void> test_extensionOverride_setter() async {
newFile(join(projectFolderPath, 'lib', 'other.dart'), '''
extension E on int {
set foo(int _) {}
}
''');
await _verifyGoToImports(
TestCode.parse('''
[!import 'other.dart';!]
void f(int i) {
E(i).fo^o = 0;
}
'''),
);
}
Future<void> test_function() async {
await _verifyGoToImports(
TestCode.parse('''
[!import 'dart:math';!]
void foo() {
ma^x(1, 2);
}
'''),
);
}
Future<void> test_function_alias() async {
await _verifyGoToImports(
TestCode.parse('''
[!import 'dart:math' as math;!]
void foo() {
math.ma^x(1, 2);
}
'''),
);
}
Future<void> test_import_double() async {
newFile(join(projectFolderPath, 'lib', 'other.dart'), '''
export 'dart:math';
''');
await _verifyGoToImports(
TestCode.parse('''
/*[0*/import 'dart:math';/*0]*/
/*[1*/import 'other.dart';/*1]*/
Rando^m? r;
'''),
);
}
Future<void> test_import_double_ambiguous() async {
newFile(join(projectFolderPath, 'lib', 'a1.dart'), '''
class A {}
''');
newFile(join(projectFolderPath, 'lib', 'a2.dart'), '''
class A {}
''');
await _verifyGoToImports(
TestCode.parse('''
/*[0*/import 'a1.dart';/*0]*/
/*[1*/import 'a2.dart';/*1]*/
// ignore: ambiguous_import
^A? r;
'''),
);
}
Future<void> test_import_double_hide() async {
newFile(join(projectFolderPath, 'lib', 'a1.dart'), '''
class A {}
''');
newFile(join(projectFolderPath, 'lib', 'a2.dart'), '''
class A {}
''');
await _verifyGoToImports(
TestCode.parse('''
import 'a1.dart' hide A;
[!import 'a2.dart';!]
^A? r;
'''),
);
}
Future<void> test_import_double_same_different_alias() async {
newFile(join(projectFolderPath, 'lib', 'other.dart'), '''
export 'dart:math';
''');
await _verifyGoToImports(
TestCode.parse('''
import 'dart:math' as math;
[!import 'other.dart' as other;!]
other.Rando^m? r;
'''),
);
}
Future<void> test_import_double_same_different_alias_prefix() async {
newFile(join(projectFolderPath, 'lib', 'other.dart'), '''
export 'dart:math';
''');
await _verifyGoToImports(
TestCode.parse('''
import 'dart:math' as math;
[!import 'other.dart' as other;!]
other.Ran^dom? r;
'''),
);
}
Future<void> test_import_double_show() async {
newFile(join(projectFolderPath, 'lib', 'a1.dart'), '''
class A {}
class B {}
''');
newFile(join(projectFolderPath, 'lib', 'a2.dart'), '''
class A {}
''');
await _verifyGoToImports(
TestCode.parse('''
import 'a1.dart' show B;
[!import 'a2.dart' show A;!]
^A? r;
'''),
);
}
Future<void> test_import_double_unambiguous_aliased() async {
newFile(join(projectFolderPath, 'lib', 'a1.dart'), '''
class A {}
''');
newFile(join(projectFolderPath, 'lib', 'a2.dart'), '''
class A {}
''');
await _verifyGoToImports(
TestCode.parse('''
[!import 'a1.dart' as a;!]
import 'a2.dart' as b;
// ignore: ambiguous_import
a.A^? r;
'''),
);
}
Future<void> test_import_multiLevelParts() async {
// Create a tree of files that all import 'dart:math' and ensure we find
// only the import from the parent (not a grandparent, sibling, or child).
//
//
// - root has import
// - level1_other has import
// - level1 has import, is the used reference
// - level2_other has import
// - level2 has reference
// - level3_other has import
createFile('root.dart', '''
import 'dart:math';
part 'level1_other.dart';
part 'level1.dart';
''');
createFile('level1_other.dart', '''
part of 'root.dart';
import 'dart:math';
''');
var level1 = createFile('level1.dart', '''
part of 'root.dart';
[!import 'dart:math';!]
part 'level2_other.dart';
part 'level2.dart';
''');
createFile('level2_other.dart', '''
part of 'level1.dart';
import 'dart:math';
''');
var level2 = createFile('level2.dart', '''
part of 'level1.dart';
part 'level3_other.dart';
Rando^m? r;
''');
createFile('level3_other.dart', '''
part of 'level2.dart';
import 'dart:math';
''');
await _verifyGoToImports(
// Test the position in level2.
level2.code,
fileUri: level2.uri,
// Expect only the nearest parent import (level1).
expecting: (level1.uri, level1.code.ranges),
);
}
Future<void> test_import_multiLevelParts_findsInGrandParent() async {
var root = createFile('root.dart', '''
[!import 'dart:math';!]
part 'level1.dart';
''');
createFile('level1.dart', '''
part of 'root.dart';
part 'level2.dart';
''');
var level2 = createFile('level2.dart', '''
part of 'level1.dart';
Rando^m? r;''');
await _verifyGoToImports(
// Test the position in level2, expect reference in root.
level2.code,
fileUri: level2.uri,
expecting: (root.uri, root.code.ranges),
);
}
Future<void> test_import_multiLevelParts_findsInParent() async {
createFile('root.dart', '''
part 'level1.dart';
''');
var level1 = createFile('level1.dart', '''
part of 'root.dart';
[!import 'dart:math';!]
part 'level2.dart';
''');
var level2 = createFile('level2.dart', '''
part of 'level1.dart';
Rando^m? r;''');
await _verifyGoToImports(
// Test the position in level2, expect reference in level1.
level2.code,
fileUri: level2.uri,
expecting: (level1.uri, level1.code.ranges),
);
}
Future<void> test_import_multiLevelParts_findsInSelf() async {
createFile('root.dart', '''
part 'level1.dart';
''');
createFile('level1.dart', '''
part of 'root.dart';
part 'level2.dart';
''');
var level2 = createFile('level2.dart', '''
part of 'level1.dart';
[!import 'dart:math';!]
Rando^m? r;''');
await _verifyGoToImports(
// Test the position in level2, expect reference in same.
level2.code,
fileUri: level2.uri,
expecting: (level2.uri, level2.code.ranges),
);
}
Future<void> test_import_multiple() async {
await _verifyGoToImports(
TestCode.parse('''
import 'dart:async' as import1;
import 'dart:async' as import2;
/*[0*/import 'dart:async';/*0]*/
/*[1*/import 'dart:core';/*1]*/
import 'dart:math';
Futur^e<void>? f;
'''),
);
}
Future<void> test_import_part() async {
var otherFileUri = Uri.file(join(projectFolderPath, 'lib', 'other.dart'));
var main = TestCode.parse('''
[!import 'dart:math';!]
part '$otherFileUri';
''');
newFile(mainFilePath, main.code);
await _verifyGoToImports(
TestCode.parse('''
part of '$mainFileUri';
Rando^m? r;
'''),
fileUri: otherFileUri,
expecting: (mainFileUri, main.ranges),
);
}
Future<void> test_import_single() async {
await _verifyGoToImports(
TestCode.parse('''
[!import 'dart:math';!]
Rando^m? r;
'''),
);
}
Future<void> test_import_single_aliased() async {
await _verifyGoToImports(
TestCode.parse('''
[!import 'dart:math' as math;!]
math.Rando^m? r;
'''),
);
}
Future<void> test_import_single_exported() async {
newFile(join(projectFolderPath, 'lib', 'other.dart'), '''
export 'dart:math';
''');
await _verifyGoToImports(
TestCode.parse('''
[!import 'other.dart';!]
Rando^m? r;
'''),
);
}
Future<void> test_local_no_result() async {
await _verifyGoToImports(
TestCode.parse('''
import 'dart:math';
Random? r;
L^ocalClass? l;
class LocalClass {}
'''),
);
}
Future<void> test_nestedInvocations() async {
newFile(join(projectFolderPath, 'lib', 'other.dart'), '''
class A {
const A();
A foo() => A();
void bar() {}
}
''');
await _verifyGoToImports(
TestCode.parse('''
import 'other.dart';
var a = A().foo().ba^r();
'''),
);
}
Future<void> test_staticDeclarations() async {
newFile(join(projectFolderPath, 'lib', 'other.dart'), '''
class A {
static const a = 1;
}
''');
await _verifyGoToImports(
TestCode.parse('''
[!import 'other.dart';!]
var a = A^.a;
'''),
);
}
Future<void> test_staticDeclarations_prefixed() async {
newFile(join(projectFolderPath, 'lib', 'other.dart'), '''
class A {
static const a = 1;
}
''');
await _verifyGoToImports(
TestCode.parse('''
[!import 'other.dart' as o;!]
var a = o.A^.a;
'''),
);
}
Future<void> _verifyGoToImports(
TestCode code, {
Uri? fileUri,
(Uri, List<TestCodeRange>)? expecting,
}) async {
expecting ??= (fileUri ?? mainFileUri, code.ranges);
var (expectedUri, expectedRanges) = expecting;
var expectedLocations = [
for (var range in expectedRanges)
Location(uri: expectedUri, range: range.range),
];
newFile(fromUri(fileUri ?? mainFileUri), code.code);
await initialize();
await initialAnalysis;
var res = await getImports(fileUri ?? mainFileUri, code.position.position);
expect(res ?? [], expectedLocations);
}
}