blob: 8719596a474735f99d434a94dc9bc511eee72134 [file] [log] [blame]
// Copyright (c) 2022, 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(TypeDefinitionTest);
});
}
@reflectiveTest
class TypeDefinitionTest extends AbstractLspAnalysisServerTest {
Uri get sdkCoreUri {
var sdkCorePath = convertPath('/sdk/lib/core/core.dart');
return pathContext.toUri(sdkCorePath);
}
@override
void setUp() {
super.setUp();
setLocationLinkSupport();
}
Future<void> test_currentFile() async {
var code = TestCode.parse('''
class /*[0*/A/*0]*/ {}
final /*[1*/a^/*1]*/ = A();
''');
var ranges = code.ranges.ranges;
var targetRange = ranges[0];
var originRange = ranges[1];
var result = await _getResult(code);
expect(result.originSelectionRange, originRange);
expect(result.targetUri, mainFileUri);
expect(result.targetSelectionRange, targetRange);
expect(result.targetRange, rangeOfString(code, 'class A {}'));
}
Future<void> test_doubleLiteral() async {
var code = TestCode.parse('''
const a = [!12^.3!];
''');
var result = await _getResult(code);
expect(result.originSelectionRange, code.range.range);
_expectSdkCoreType(result, 'double');
}
Future<void> test_getter() async {
var code = TestCode.parse('''
class A {
String get aaa => '';
}
void f() {
final a = A();
print(a.[!a^aa!]);
}
''');
var result = await _getResult(code);
expect(result.originSelectionRange, code.range.range);
_expectSdkCoreType(result, 'String');
}
Future<void> test_intLiteral() async {
var code = TestCode.parse('''
const a = [!12^3!];
''');
var result = await _getResult(code);
expect(result.originSelectionRange, code.range.range);
_expectSdkCoreType(result, 'int');
}
/// Checks a result when the client does not support [LocationLink], only
/// the original LSP [Location].
Future<void> test_location() async {
setLocationLinkSupport(false);
var code = TestCode.parse('''
const a^ = 'test string';
''');
var result = await _getLocationResult(code);
expect(result.uri, sdkCoreUri);
_expectNameRange(result.range, 'String');
}
Future<void> test_nonDartFile() async {
var code = TestCode.parse('''
const a = '^';
''');
newFile(pubspecFilePath, code.code);
await initialize();
var results =
await getTypeDefinitionAsLocation(mainFileUri, code.position.position);
expect(results, isEmpty);
}
Future<void> test_otherFile() async {
var otherFilePath = join(projectFolderPath, 'lib', 'other.dart');
var otherFileUri = pathContext.toUri(otherFilePath);
var code = TestCode.parse('''
import 'other.dart';
final [!a^!] = A();
''');
var otherCode = TestCode.parse('''
class [!A!] {}
''');
newFile(otherFilePath, otherCode.code);
var result = await _getResult(code);
expect(result.originSelectionRange, code.range.range);
expect(result.targetUri, otherFileUri);
expect(result.targetSelectionRange, otherCode.range.range);
expect(result.targetRange, rangeOfString(otherCode, 'class A {}'));
}
Future<void> test_parameter() async {
var code = TestCode.parse('''
void f(String a) {
f([!'te^st'!]);
}
''');
var result = await _getResult(code);
expect(result.originSelectionRange, code.range.range);
_expectSdkCoreType(result, 'String');
}
Future<void> test_parameterName() async {
var code = TestCode.parse('''
void f({String? a}) {
f([!a^!]: 'test');
}
''');
var result = await _getResult(code);
expect(result.originSelectionRange, code.range.range);
_expectSdkCoreType(result, 'String');
}
Future<void> test_setter() async {
var code = TestCode.parse('''
class A {
set aaa(String value) {}
}
void f() {
final a = A();
a.[!a^aa!] = '';
}
''');
var result = await _getResult(code);
expect(result.originSelectionRange, code.range.range);
_expectSdkCoreType(result, 'String');
}
Future<void> test_stringLiteral() async {
var code = TestCode.parse('''
const a = [!'te^st string'!];
''');
var result = await _getResult(code);
expect(result.originSelectionRange, code.range.range);
_expectSdkCoreType(result, 'String');
}
Future<void> test_type() async {
var code = TestCode.parse('''
[!St^ring!] a = '';
''');
var result = await _getResult(code);
expect(result.originSelectionRange, code.range.range);
_expectSdkCoreType(result, 'String');
}
Future<void> test_unopenedFile() async {
var code = TestCode.parse('''
const a = [!'^'!];
''');
newFile(mainFilePath, code.code);
var result = await _getResult(code, inOpenFile: false);
expect(result.originSelectionRange, code.range.range);
_expectSdkCoreType(result, 'String');
}
Future<void> test_variableDeclaration() async {
var code = TestCode.parse('''
const [!a^!] = 'test string';
''');
var result = await _getResult(code);
expect(result.originSelectionRange, code.range.range);
_expectSdkCoreType(result, 'String');
}
Future<void> test_variableDeclaration_forInLoop() async {
var code = TestCode.parse('''
void f() {
for (final [!a^!] in ['']) {
}
}
''');
var result = await _getResult(code);
expect(result.originSelectionRange, code.range.range);
_expectSdkCoreType(result, 'String');
}
Future<void> test_variableDeclaration_forLoop() async {
var code = TestCode.parse('''
void f() {
for (var [!i^!] = 0; i < 1; i++) {
}
}
''');
var result = await _getResult(code);
expect(result.originSelectionRange, code.range.range);
_expectSdkCoreType(result, 'int');
}
Future<void> test_variableDeclaration_inferredType() async {
var code = TestCode.parse('''
var [!a^!] = 'test string';
''');
var result = await _getResult(code);
expect(result.originSelectionRange, code.range.range);
_expectSdkCoreType(result, 'String');
}
Future<void> test_variableReference() async {
var code = TestCode.parse('''
void f() {
const a = 'test string';
print([!a^!]);
}
''');
var result = await _getResult(code);
expect(result.originSelectionRange, code.range.range);
_expectSdkCoreType(result, 'String');
}
Future<void> test_variableReference_forInLoop() async {
var code = TestCode.parse('''
void f() {
for (final a in ['']) {
print([!a^!]);
}
}
''');
var result = await _getResult(code);
expect(result.originSelectionRange, code.range.range);
_expectSdkCoreType(result, 'String');
}
Future<void> test_variableReference_forLoop() async {
var code = TestCode.parse('''
void f() {
for (var i = 0; i < 1; i++) {
print([!i^!]);
}
}
''');
var result = await _getResult(code);
expect(result.originSelectionRange, code.range.range);
_expectSdkCoreType(result, 'int');
}
Future<void> test_variableReference_inferredType() async {
var code = TestCode.parse('''
void f() {
var a = 'test string';
print([!a^!]);
}
''');
var result = await _getResult(code);
expect(result.originSelectionRange, code.range.range);
_expectSdkCoreType(result, 'String');
}
/// Expects [range] looks consistent with a range of an elements code.
///
/// This is used for SDK sources where the exact location is not known to the
/// test.
void _expectCodeRange(Range range) {
expect(range.start.line, isPositive);
expect(range.end.line, isPositive);
// And a range that spans multiple lines.
expect(range.start.line, lessThan(range.end.line));
}
/// Expects [range] looks consistent with a range of an elements name.
///
/// This is used for SDK sources where the exact location is not known to the
/// test.
void _expectNameRange(Range range, String name) {
expect(range.start.line, isPositive);
expect(range.end.line, isPositive);
// Expect a single line, with the length matching `name`.
expect(range.start.line, range.end.line);
expect(
range.end.character - range.start.character,
name.length,
);
}
/// Expects [range] looks consistent with a range of an elements code.
///
/// This is used for SDK sources where the exact location is not known to the
/// test.
void _expectSdkCoreType(LocationLink result, String typeName) {
expect(result.targetUri, sdkCoreUri);
_expectNameRange(result.targetSelectionRange, typeName);
_expectCodeRange(result.targetRange);
}
/// Gets the type definition as an LSP Location object.
Future<Location> _getLocationResult(TestCode code) async {
await initialize();
await openFile(mainFileUri, code.code);
var results =
await getTypeDefinitionAsLocation(mainFileUri, code.position.position);
return results.single;
}
/// Advertises support for the LSP LocationLink type and gets the type
/// definition using that.
Future<LocationLink> _getResult(TestCode code,
{Uri? fileUri, bool inOpenFile = true}) async {
fileUri ??= mainFileUri;
await initialize();
if (inOpenFile) {
await openFile(fileUri, code.code);
}
var results = await getTypeDefinitionAsLocationLinks(
mainFileUri,
code.position.position,
);
return results.single;
}
}