blob: f664d5702d28e599c0397d3c6ff191039dcbf8d9 [file]
// 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_constructor_inBody_default() async {
var content = '''
class A^aa {
/*[0*/Aaa/*0]*/();
}
void f() {
/*[1*/Aaa/*1]*/? value = /*[2*/Aaa/*2]*/();
}
''';
await _checkRanges(content);
}
Future<void> test_constructor_inBody_factory_constructorName() async {
var content = '''
class Aaa {
new _();
fa^ctory() => Aaa._();
}
void f() {
Aaa value = Aaa/*[0*/.new/*0]*/();
Aaa/*[1*/.new/*1]*/();
}
''';
await _checkRanges(content);
}
Future<void> test_constructor_inBody_factoryNamed_constructorName() async {
var content = '''
class Aaa {
new ();
factory na^med() => Aaa();
}
void f() {
Aaa value = Aaa/*[0*/.named/*0]*/();
Aaa/*[1*/.named/*1]*/();
}
''';
await _checkRanges(content);
}
Future<void> test_constructor_inBody_named_constructorName() async {
var content = '''
class Aaa {
Aaa.na^med();
}
void f() {
Aaa value = Aaa/*[0*/.named/*0]*/();
Aaa/*[1*/.named/*1]*/();
}
''';
await _checkRanges(content);
}
Future<void> test_constructor_inBody_named_typeName() async {
var content = '''
class A^aa {
/*[0*/Aaa/*0]*/.named();
}
void f() {
/*[1*/Aaa/*1]*/? value = /*[2*/Aaa/*2]*/.named();
}
''';
await _checkRanges(content);
}
Future<void> test_constructor_inBody_new_constructorName() async {
var content = '''
class Aaa {
ne^w();
}
void f() {
Aaa value = Aaa/*[0*/.new/*0]*/();
Aaa/*[1*/.new/*1]*/();
}
''';
await _checkRanges(content);
}
Future<void> test_constructor_inBody_new_typeName() async {
var content = '''
class A^aa {
new();
}
void f() {
/*[0*/Aaa/*0]*/? value = /*[1*/Aaa/*1]*/.new();
/*[2*/Aaa/*2]*/.new();
}
''';
await _checkRanges(content);
}
Future<void> test_constructor_inBody_newNamed_constructorName() async {
var content = '''
class Aaa {
new na^med();
}
void f() {
Aaa value = Aaa/*[0*/.named/*0]*/();
Aaa/*[1*/.named/*1]*/();
}
''';
await _checkRanges(content);
}
Future<void> test_constructor_inBody_newNamed_typeName() async {
var content = '''
class A^aa {
new named();
}
void f() {
/*[0*/Aaa/*0]*/? value = /*[1*/Aaa/*1]*/.named();
/*[2*/Aaa/*2]*/.named();
}
''';
await _checkRanges(content);
}
Future<void> test_constructor_primary_default() async {
var content = '''
class A^aa(int value);
void f() {
/*[0*/Aaa/*0]*/? value = /*[1*/Aaa/*1]*/(1);
/*[2*/Aaa/*2]*/(2);
}
''';
await _checkRanges(content);
}
Future<void> test_constructor_primary_named_constructorName() async {
var content = '''
class Aaa.na^med(int value);
void f() {
Aaa value = Aaa/*[0*/.named/*0]*/(1);
Aaa/*[1*/.named/*1]*/(2);
}
''';
await _checkRanges(content);
}
Future<void> test_constructor_primary_named_typeName() async {
var content = '''
class A^aa.named(int value);
void f() {
/*[0*/Aaa/*0]*/? value = /*[1*/Aaa/*1]*/.named(1);
/*[2*/Aaa/*2]*/.named(2);
}
''';
await _checkRanges(content);
}
Future<void> test_constructor_primary_parameter_declaring_final() async {
var content = '''
class A({required final int a}) {
this {
print(/*[0*/a^/*0]*/);
}
}
void f() {
var value = A(/*[1*/a/*1]*/: 1);
print(value./*[2*/a/*2]*/);
}
''';
await _checkRanges(content);
}
Future<void> test_constructor_primary_parameter_declaring_var() async {
var content = '''
class A({required var int a});
void f() {
var value = A(/*[0*/a/*0]*/: 1);
value./*[1*/a^/*1]*/ = 2;
print(value./*[2*/a/*2]*/);
}
''';
await _checkRanges(content);
}
Future<void> test_constructor_primary_parameter_nonDeclaring() async {
var content = '''
class A({required int b}) {
final int c;
this : c = /*[0*/b^/*0]*/ {
print(/*[1*/b/*1]*/);
}
}
void f() {
A(/*[2*/b/*2]*/: 1);
}
''';
await _checkRanges(content);
}
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_declaration() async {
var content = '''
import 'dart:async' as asy^nc;
/*[0*/async/*0]*/.Future<String>? f() {}
/*[1*/async/*1]*/.Future<String>? g() {}
''';
await _checkRanges(content);
}
Future<void> test_import_prefix_reference() async {
var content = '''
import '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_operator_binary_minus() async {
var content = '''
class A {
void operator ^-(A _) {}
void operator -() {} // Not checked, here to ensure it's excluded
}
void f(A a) {
a /*[0*/-/*0]*/ a;
a /*[1*/-/*1]*/ a;
-a;
}
''';
await _checkRanges(content);
}
Future<void> test_operator_binary_plus() async {
var content = '''
class A {
void operator ^+(A _) {}
}
void f(A a) {
a /*[0*/+/*0]*/ a;
a /*[1*/+/*1]*/ a;
}
''';
await _checkRanges(content);
}
Future<void> test_operator_unary_minus() async {
var content = '''
class A {
void operator ^-() {}
void operator -(A _) {} // Not checked, here to ensure it's excluded
}
void f(A a) {
/*[0*/-/*0]*/a;
/*[1*/-/*1]*/a;
a - a;
}
''';
await _checkRanges(content);
}
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));
}
}