blob: d599b3ac40d2dcc145a0afd6d82d7abfc93571b1 [file] [log] [blame]
// Copyright (c) 2018, 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(ReferencesTest);
});
}
@reflectiveTest
class ReferencesTest extends AbstractLspAnalysisServerTest {
Future<void> test_acrossFiles_includeDeclaration() async {
var otherContent = '''
import 'main.dart';
void f() {
[!foo!]();
}
''';
var mainContent = '''
/// Ensure the function is on a line that
/// does not exist in the mainContents file
/// to ensure we're translating offsets to line/col
/// using the correct file's LineInfo
/// ...
/// ...
/// ...
/// ...
/// ...
[!^foo!]() {}
''';
await _checkRanges(
mainContent,
otherContent: otherContent,
includeDeclarations: true,
);
}
Future<void> test_acrossFiles_withoutDeclaration() async {
var otherContent = '''
import 'main.dart';
void f() {
[!foo!]();
}
''';
var mainContent = '''
/// Ensure the function is on a line that
/// does not exist in the mainContents file
/// to ensure we're translating offsets to line/col
/// using the correct file's LineInfo
/// ...
/// ...
/// ...
/// ...
/// ...
^foo() {}
''';
await _checkRanges(
mainContent,
otherContent: otherContent,
);
}
Future<void> test_field_decalaration_getterSetter() async {
var content = '''
class MyClass {
String field^ = '';
}
void f() {
MyClass()./*[0*/field/*0]*/ = '';
print(MyClass()./*[1*/field/*1]*/);
var myInstance = MyClass();
myInstance./*[2*/field/*2]*/ = '';
print(myInstance./*[3*/field/*3]*/);
myInstance./*[4*/field/*4]*/ += myInstance./*[5*/field/*5]*/;
}
''';
await _checkRanges(content);
}
Future<void> test_field_decalaration_initializingFormal() async {
// References on the field should find the initializing formal, the
// reference to the getter and the constructor argument.
var content = '''
class AAA {
final String? aa^a;
const AAA({this./*[0*/aaa/*0]*/});
}
class BBB extends AAA {
BBB({super./*[1*/aaa/*1]*/});
}
final a = AAA(/*[2*/aaa/*2]*/: '')./*[3*/aaa/*3]*/;
''';
await _checkRanges(content);
}
Future<void> test_forEachElement_blockBody() async {
var content = '''
void f(List<int> values) {
[for (final val^ue in values) [!value!] * 2];
}
''';
await _checkRanges(content);
}
Future<void> test_forEachElement_expressionBody() async {
var content = '''
Object f() => [for (final val^ue in []) [!value!] * 2];
''';
await _checkRanges(content);
}
Future<void> test_forEachElement_topLevel() async {
var content = '''
final a = [for (final val^ue in []) [!value!] * 2];
''';
await _checkRanges(content);
}
Future<void> test_function_startOfParameterList() async {
var content = '''
foo^() {
[!foo!]();
}
''';
await _checkRanges(content);
}
Future<void> test_function_startOfTypeParameterList() async {
var content = '''
foo^<T>() {
[!foo!]();
}
''';
await _checkRanges(content);
}
Future<void> test_getter_decalaration_getterSetter() async {
var content = '''
class MyClass {
String get field^ => '';
set field(String _) {}
}
void f() {
MyClass()./*[0*/field/*0]*/ = '';
print(MyClass()./*[1*/field/*1]*/);
var myInstance = MyClass();
myInstance./*[2*/field/*2]*/ = '';
print(myInstance./*[3*/field/*3]*/);
myInstance./*[4*/field/*4]*/ += myInstance./*[5*/field/*5]*/;
}
''';
await _checkRanges(content);
}
Future<void> test_getter_invocation_getterSetter() async {
var content = '''
class MyClass {
String get field => '';
set field(String _) {}
}
void f() {
MyClass()./*[0*/field/*0]*/ = '';
print(MyClass()./*[1*/fi^eld/*1]*/);
var myInstance = MyClass();
myInstance./*[2*/field/*2]*/ = '';
print(myInstance./*[3*/field/*3]*/);
myInstance./*[4*/field/*4]*/ += myInstance./*[5*/field/*5]*/;
}
''';
await _checkRanges(content);
}
Future<void> test_import_prefix() async {
var content = '''
imp^ort 'dart:async' as async;
/*[0*/async./*0]*/Future<String>? f() {}
/*[1*/async./*1]*/Future<String>? g() {}
''';
await _checkRanges(content);
}
Future<void> test_initializingFormal_argument_withDeclaration() async {
// Find references on an initializing formal argument should include
// all references to the field too.
var content = '''
class AAA {
String? /*[0*/aaa/*0]*/;
AAA({this./*[1*/aaa/*1]*/});
}
void f() {
final a = AAA(/*[2*/a^aa/*2]*/: '');
var x = a./*[3*/aaa/*3]*/;
a./*[4*/aaa/*4]*/ = '';
}
''';
await _checkRanges(content, includeDeclarations: true);
}
Future<void> test_initializingFormal_argument_withoutDeclaration() async {
// Find references on an initializing formal argument should include
// all references to the field too. The field is not included
// because we didn't request the declaration.
var content = '''
class AAA {
String? aaa;
AAA({this./*[0*/aaa/*0]*/});
}
void f() {
final a = AAA(/*[1*/a^aa/*1]*/: '');
var x = a./*[2*/aaa/*2]*/;
a./*[3*/aaa/*3]*/ = '';
}
''';
await _checkRanges(content);
}
Future<void> test_initializingFormal_parameter_withDeclaration() async {
// Find references on an initializing formal parameter should include
// all references to the field too.
var content = '''
class AAA {
String? /*[0*/aaa/*0]*/;
AAA({this./*[1*/aa^a/*1]*/});
}
void f() {
final a = AAA(/*[2*/aaa/*2]*/: '');
var x = a./*[3*/aaa/*3]*/;
a./*[4*/aaa/*4]*/ = '';
}
''';
await _checkRanges(content, includeDeclarations: true);
}
Future<void> test_initializingFormal_parameter_withoutDeclaration() async {
// Find references on an initializing formal parameter should include
// all references to the field too. The field is not included
// because we didn't request the declaration.
var content = '''
class AAA {
String? aaa;
AAA({this./*[0*/aa^a/*0]*/});
}
class BBB extends AAA {
BBB({super./*[1*/aaa/*1]*/});
}
void f() {
final a = AAA(/*[2*/aaa/*2]*/: '');
var x = a./*[3*/aaa/*3]*/;
a./*[4*/aaa/*4]*/ = '';
}
''';
await _checkRanges(content);
}
Future<void> test_method_startOfParameterList() async {
var content = '''
class A {
foo^() {
[!foo!]();
}
}
''';
await _checkRanges(content);
}
Future<void> test_method_startOfTypeParameterList() async {
var content = '''
class A {
foo^<T>() {
[!foo!]();
}
}
''';
await _checkRanges(content);
}
Future<void> test_nonDartFile() async {
newFile(pubspecFilePath, simplePubspecContent);
await initialize();
var res = await getReferences(pubspecFileUri, startOfDocPos);
expect(res, isEmpty);
}
Future<void> test_pattern_object_withDeclaration() async {
var content = '''
class A {
int get i => 0;
}
int f(Object o) {
switch (o) {
case A(:var /*[0*/^i/*0]*/):
return /*[1*/i/*1]*/;
}
return 0;
}
''';
await _checkRanges(content, includeDeclarations: true);
}
Future<void> test_pattern_object_withoutDeclaration() async {
var content = '''
class A {
int get i => 0;
}
int f(Object o) {
switch (o) {
case A(:var ^i):
return [!i!];
}
return 0;
}
''';
await _checkRanges(content);
}
Future<void> test_setter_decalaration_getterSetter() async {
var content = '''
class MyClass {
String get field => '';
set fie^ld(String _) {}
}
void f() {
MyClass()./*[0*/field/*0]*/ = '';
print(MyClass()./*[1*/field/*1]*/);
var myInstance = MyClass();
myInstance./*[2*/field/*2]*/ = '';
print(myInstance./*[3*/field/*3]*/);
myInstance./*[4*/field/*4]*/ += myInstance./*[5*/field/*5]*/;
}
''';
await _checkRanges(content);
}
Future<void> test_setter_invocation_getterSetter() async {
var content = '''
class MyClass {
String get field => '';
set field(String _) {}
}
void f() {
MyClass()./*[0*/fie^ld/*0]*/ = '';
print(MyClass()./*[1*/field/*1]*/);
var myInstance = MyClass();
myInstance./*[2*/field/*2]*/ = '';
print(myInstance./*[3*/field/*3]*/);
myInstance./*[4*/field/*4]*/ += myInstance./*[5*/field/*5]*/;
}
''';
await _checkRanges(content);
}
Future<void> test_singleFile_withoutDeclaration() async {
var content = '''
f^oo() {
[!foo!]();
}
''';
await _checkRanges(content);
}
Future<void> test_type() async {
var content = '''
class A^aa<T> {}
[!Aaa!]<String>? a;
''';
await _checkRanges(content);
}
Future<void> test_type_generic_end() async {
var content = '''
class Aaa^<T> {}
[!Aaa!]<String>? a;
''';
await _checkRanges(content);
}
Future<void> test_unopenFile() async {
var code = TestCode.parse('''
f^oo() {
[!foo!]();
}
''');
newFile(mainFilePath, code.code);
await initialize();
var res = await getReferences(mainFileUri, code.position.position);
var expected = [
for (final range in code.ranges)
Location(uri: mainFileUri, range: range.range),
];
expect(res, unorderedEquals(expected));
}
Future<void> _checkRanges(
String mainContent, {
String? otherContent,
bool includeDeclarations = false,
}) async {
var mainCode = TestCode.parse(mainContent);
var otherCode = otherContent != null ? TestCode.parse(otherContent) : null;
var otherFileUri = toUri(join(projectFolderPath, 'lib', 'other.dart'));
await initialize();
await openFile(mainFileUri, mainCode.code);
if (otherCode != null) {
await openFile(otherFileUri, otherCode.code);
}
var res = await getReferences(
mainFileUri,
mainCode.position.position,
includeDeclarations: includeDeclarations,
);
var expected = [
for (final range in mainCode.ranges)
Location(uri: mainFileUri, range: range.range),
if (otherCode != null)
for (final range in otherCode.ranges)
Location(uri: otherFileUri, range: range.range),
];
// Checking sets produces a better failure message than lists
// (it'll show which item is missing instead of just saying
// the lengths are different), so check that first.
expect(res.toSet(), expected.toSet());
// But also check the list in case there were unexpected duplicates.
expect(res, unorderedEquals(expected));
}
}