blob: c9003f85c967ecea79fb6931b177596556b90de3 [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:analyzer/dart/ast/token.dart';
import 'package:analyzer_utilities/check/check.dart';
import 'package:test_reflective_loader/test_reflective_loader.dart';
import '../../../../client/completion_driver_test.dart';
import '../completion_check.dart';
void main() {
defineReflectiveSuite(() {
defineReflectiveTests(ClassBodyTest1);
defineReflectiveTests(ClassBodyTest2);
});
}
@reflectiveTest
class ClassBodyTest1 extends AbstractCompletionDriverTest
with ClassBodyTestCases, OverrideTestCases {
@override
TestingCompletionProtocol get protocol => TestingCompletionProtocol.version1;
}
@reflectiveTest
class ClassBodyTest2 extends AbstractCompletionDriverTest
with ClassBodyTestCases, OverrideTestCases {
@override
TestingCompletionProtocol get protocol => TestingCompletionProtocol.version2;
}
mixin ClassBodyTestCases on AbstractCompletionDriverTest {
/// It does not really matter which classes we list here, in this test
/// suite we only need to know that we suggest classes at all.
List<CompletionSuggestionChecker> get sampleClassChecks {
return const {
'Object',
}.map((name) {
return (CompletionSuggestionTarget suggestion) {
suggestion
..completion.isEqualTo(name)
..isClass;
};
}).toList();
}
Future<void> test_nothing_x() async {
await _checkContainers(
line: '^',
validator: (context, response) {
check(response).suggestions
..withKindKeyword.matchesInAnyOrder(
{
// TODO(scheglov) Not quite right, without static.
Keyword.CONST,
if (context.isClass || context.isMixin) Keyword.COVARIANT,
Keyword.DYNAMIC,
// TODO(scheglov) This does not look right, mixin.
if (context.isClass || context.isMixin) Keyword.FACTORY,
Keyword.FINAL,
Keyword.GET,
Keyword.LATE,
Keyword.OPERATOR,
Keyword.SET,
Keyword.STATIC,
Keyword.VAR,
Keyword.VOID,
}.asKeywordChecks,
)
..includesAll(sampleClassChecks);
},
);
}
Future<void> test_static_const_x() async {
await _checkContainers(
line: 'static const ^',
validator: (context, response) {
check(response).suggestions
..withKindKeyword.matchesInAnyOrder(
{
Keyword.DYNAMIC,
Keyword.VOID,
}.asKeywordChecks,
)
..includesAll(sampleClassChecks);
},
);
}
Future<void> test_static_final_Ox() async {
await _checkContainers(
line: 'static final O^',
validator: (context, response) {
if (isProtocolVersion2) {
check(response).suggestions
..withKindKeyword.isEmpty
..includesAll(sampleClassChecks);
} else {
check(response).suggestions
..withKindKeyword.matchesInAnyOrder(
{
Keyword.DYNAMIC,
Keyword.VOID,
}.asKeywordChecks,
)
..includesAll(sampleClassChecks);
}
},
);
}
Future<void> test_static_final_x() async {
await _checkContainers(
line: 'static final ^',
validator: (context, response) {
check(response).suggestions
..withKindKeyword.matchesInAnyOrder(
{
Keyword.DYNAMIC,
Keyword.VOID,
}.asKeywordChecks,
)
..includesAll(sampleClassChecks);
},
);
}
Future<void> test_static_fx() async {
await _checkContainers(
line: 'static f^',
validator: (context, response) {
if (isProtocolVersion2) {
check(response).suggestions
..withKindKeyword.matchesInAnyOrder(
{
Keyword.FINAL,
}.asKeywordChecks,
)
..includesAll([
(suggestion) => suggestion
..completion.isEqualTo('FutureOr')
..isClass,
]);
} else {
check(response).suggestions
..withKindKeyword.matchesInAnyOrder(
{
Keyword.ABSTRACT,
Keyword.CONST,
Keyword.COVARIANT,
Keyword.DYNAMIC,
Keyword.EXTERNAL,
Keyword.FINAL,
Keyword.LATE,
}.asKeywordChecks,
)
..includesAll(sampleClassChecks);
}
},
);
}
Future<void> test_static_late_x() async {
await _checkContainers(
line: 'static late ^',
validator: (context, response) {
check(response).suggestions
..withKindKeyword.matchesInAnyOrder(
{
Keyword.DYNAMIC,
Keyword.FINAL,
}.asKeywordChecks,
)
..includesAll(sampleClassChecks);
},
);
}
Future<void> test_static_x() async {
await _checkContainers(
line: 'static ^',
validator: (context, response) {
check(response).suggestions
..withKindKeyword.matchesInAnyOrder(
{
Keyword.CONST,
Keyword.DYNAMIC,
Keyword.FINAL,
Keyword.LATE,
}.asKeywordChecks,
)
..includesAll(sampleClassChecks);
},
);
}
Future<void> test_static_x_name_eq() async {
await _checkContainers(
line: 'static ^ name = 0;',
validator: (context, response) {
check(response).suggestions
..withKindKeyword.matchesInAnyOrder(
{
// TODO(scheglov) This does not look right.
Keyword.ABSTRACT,
Keyword.CONST,
// TODO(scheglov) This does not look right.
Keyword.COVARIANT,
Keyword.DYNAMIC,
// TODO(scheglov) This does not look right.
Keyword.EXTERNAL,
Keyword.FINAL,
Keyword.LATE,
}.asKeywordChecks,
)
..includesAll(sampleClassChecks);
},
);
}
Future<void> test_sx() async {
await _checkContainers(
line: 's^',
validator: (context, response) {
if (isProtocolVersion2) {
check(response).suggestions
..withKindKeyword.matchesInAnyOrder(
{
Keyword.SET,
Keyword.STATIC,
}.asKeywordChecks,
)
..includesAll([
(suggestion) => suggestion
..completion.isEqualTo('String')
..isClass,
]);
} else {
check(response).suggestions
..withKindKeyword.matchesInAnyOrder(
{
// TODO(scheglov) Not quite right, without static.
Keyword.CONST,
if (context.isClass || context.isMixin) Keyword.COVARIANT,
Keyword.DYNAMIC,
// TODO(scheglov) This does not look right, mixin.
if (context.isClass || context.isMixin) Keyword.FACTORY,
Keyword.FINAL,
Keyword.GET,
Keyword.LATE,
Keyword.OPERATOR,
Keyword.SET,
Keyword.STATIC,
Keyword.VAR,
Keyword.VOID,
}.asKeywordChecks,
)
..includesAll(sampleClassChecks);
}
},
);
}
Future<void> _checkContainers({
required String line,
required void Function(
_Context context,
CompletionResponseForTesting response,
)
validator,
}) async {
// class
{
var response = await getTestCodeSuggestions('''
class A {
$line
}
''');
validator(_Context(isClass: true), response);
}
// enum
{
var response = await getTestCodeSuggestions('''
enum E {
v;
$line
}
''');
validator(_Context(), response);
}
// extension
{
var response = await getTestCodeSuggestions('''
extension on Object {
$line
}
''');
validator(_Context(), response);
}
// mixin
{
var response = await getTestCodeSuggestions('''
mixin M {
$line
}
''');
validator(_Context(isMixin: true), response);
}
}
}
mixin OverrideTestCases on AbstractCompletionDriverTest {
Future<void> test_class_inComment() async {
final response = await getTestCodeSuggestions('''
class A {
void foo01() {}
}
class B extends A {
// foo^
void bar() {}
}
''');
check(response).suggestions.overrides.isEmpty;
}
Future<void> test_class_inComment_dartdoc() async {
final response = await getTestCodeSuggestions('''
class A {
void foo01() {}
}
class B extends A {
/// foo^
void bar() {}
}
''');
check(response).suggestions.overrides.isEmpty;
}
Future<void> test_class_inComment_reference() async {
final response = await getTestCodeSuggestions('''
class A {
void foo01() {}
}
class B extends A {
/// [foo^]
void bar() {}
}
''');
check(response).suggestions.overrides.isEmpty;
}
Future<void> test_class_method_alreadyOverridden() async {
final response = await getTestCodeSuggestions('''
class A {
void foo01() {}
void foo02() {}
}
class B extends A {
void foo02() {}
foo^
}
''');
check(response).suggestions.overrides
..includesAll([
_isOverrideWithSuper_foo01,
])
..excludesAll([
(suggestion) => suggestion.completion.contains('foo02'),
]);
}
Future<void> test_class_method_beforeField() async {
final response = await getTestCodeSuggestions('''
class A {
void foo01() {}
}
class B extends A {
foo^
int bar = 0;
}
''');
check(response).suggestions.overrides.includesAll([
_isOverrideWithSuper_foo01,
]);
}
Future<void> test_class_method_beforeMethod() async {
final response = await getTestCodeSuggestions('''
class A {
void foo01() {}
}
class B extends A {
foo^
void bar() {}
}
''');
check(response).suggestions.overrides.includesAll([
_isOverrideWithSuper_foo01,
]);
}
Future<void> test_class_method_fromExtends() async {
final response = await getTestCodeSuggestions('''
class A {
void foo01() {}
}
class B extends A {
foo^
}
''');
check(response).suggestions.overrides.includesAll([
_isOverrideWithSuper_foo01,
]);
}
Future<void> test_class_method_fromExtends_fromPart() async {
newFile('$testPackageLibPath/a.dart', r'''
part of 'test.dart';
class A {
void foo01() {}
}
''');
final response = await getTestCodeSuggestions('''
part 'a.dart';
class B extends A {
foo^
}
''');
check(response).suggestions.overrides.includesAll([
_isOverrideWithSuper_foo01,
]);
}
Future<void> test_class_method_fromExtends_multiple() async {
final response = await getTestCodeSuggestions('''
class A {
void foo01() {}
}
class B extends A {
void foo02() {}
}
class C extends B {
foo^
}
''');
check(response).suggestions.overrides.includesAll([
_isOverrideWithSuper_foo01,
_isOverrideWithSuper_foo02,
]);
}
Future<void> test_class_method_fromExtends_private_otherLibrary() async {
newFile('$testPackageLibPath/a.dart', r'''
class A {
// ignore:unused_element
void _foo01() {}
void foo02() {}
}
''');
final response = await getTestCodeSuggestions('''
import 'a.dart';
class B extends A {
foo^
}
''');
check(response).suggestions.overrides
..includesAll([
_isOverrideWithSuper_foo02,
])
..excludesAll([
_isOverrideWithSuper_private_foo01,
]);
}
Future<void> test_class_method_fromExtends_private_thisLibrary() async {
final response = await getTestCodeSuggestions('''
class A {
void _foo01() {}
void foo02() {}
}
class B extends A {
foo^
}
''');
check(response).suggestions.overrides.includesAll([
_isOverrideWithSuper_private_foo01,
_isOverrideWithSuper_foo02,
]);
}
Future<void> test_class_method_fromExtends_withOverride() async {
final response = await getTestCodeSuggestions('''
class A {
void foo01() {}
}
class B extends A {
@override
foo^
}
''');
check(response).suggestions.overrides.includesAll([
(suggestion) => suggestion
..displayText.isEqualTo('foo01() { … }')
..hasSelection(offset: 48, length: 14)
..completion.isEqualTo(r'''
void foo01() {
// TODO: implement foo01
super.foo01();
}'''),
]);
}
Future<void> test_class_method_fromImplements() async {
final response = await getTestCodeSuggestions('''
class A {
void foo01() {}
}
class B implements A {
foo^
}
''');
check(response).suggestions.overrides.includesAll([
_isOverrideWithoutSuper_foo01,
]);
}
Future<void> test_class_method_fromWith() async {
final response = await getTestCodeSuggestions('''
mixin M {
void foo01() {}
}
class A with M {
foo^
}
''');
check(response).suggestions.overrides.includesAll([
_isOverrideWithSuper_foo01,
]);
}
Future<void> test_class_operator_eqEq() async {
final response = await getTestCodeSuggestions('''
class A {
opera^
}
''');
check(response).suggestions.overrides.includesAll([
(suggestion) => suggestion
..displayText.isEqualTo('==(Object other) { … }')
..hasSelection(offset: 75, length: 22)
..completion.isEqualTo(r'''
@override
bool operator ==(Object other) {
// TODO: implement ==
return super == other;
}'''),
]);
}
Future<void> test_class_operator_plus() async {
final response = await getTestCodeSuggestions('''
class A {
int operator +(int other) { }
}
class B extends A {
opera^
}
''');
check(response).suggestions.overrides.includesAll([
(suggestion) => suggestion
..displayText.isEqualTo('+(int other) { … }')
..hasSelection(offset: 69, length: 21)
..completion.isEqualTo(r'''
@override
int operator +(int other) {
// TODO: implement +
return super + other;
}'''),
]);
}
Future<void> test_extension_method() async {
final response = await getTestCodeSuggestions('''
class A {
void foo01() {}
}
extension E on A {
foo^
}
''');
check(response).suggestions.overrides.isEmpty;
}
Future<void> test_mixin_method_fromConstraints_alreadyOverridden() async {
final response = await getTestCodeSuggestions('''
class A {
void foo01() {}
void foo02() {}
}
mixin M on A {
void foo02() {}
foo^
}
''');
check(response).suggestions.overrides
..includesAll([
_isOverrideWithSuper_foo01,
])
..excludesAll([
(suggestion) => suggestion.completion.contains('foo02'),
]);
}
Future<void> test_mixin_method_fromImplements() async {
final response = await getTestCodeSuggestions('''
class A {
void foo01() {}
}
mixin M implements A {
foo^
}
''');
check(response).suggestions.overrides.includesAll([
_isOverrideWithoutSuper_foo01,
]);
}
Future<void> test_mixin_method_fromSuperclassConstraint() async {
final response = await getTestCodeSuggestions('''
class A {
void foo01() {}
}
mixin M on A {
foo^
}
''');
check(response).suggestions.overrides.includesAll([
_isOverrideWithSuper_foo01,
]);
}
static void _isOverrideWithoutSuper_foo01(
CheckTarget<CompletionSuggestionForTesting> suggestion,
) {
suggestion
..displayText.isEqualTo('foo01() { … }')
..hasSelection(offset: 55)
..completion.isEqualTo(r'''
@override
void foo01() {
// TODO: implement foo01
}''');
}
static void _isOverrideWithSuper_foo01(
CheckTarget<CompletionSuggestionForTesting> suggestion,
) {
suggestion
..displayText.isEqualTo('foo01() { … }')
..hasSelection(offset: 60, length: 14)
..completion.isEqualTo(r'''
@override
void foo01() {
// TODO: implement foo01
super.foo01();
}''');
}
static void _isOverrideWithSuper_foo02(
CheckTarget<CompletionSuggestionForTesting> suggestion,
) {
suggestion
..displayText.isEqualTo('foo02() { … }')
..hasSelection(offset: 60, length: 14)
..completion.isEqualTo(r'''
@override
void foo02() {
// TODO: implement foo02
super.foo02();
}''');
}
static void _isOverrideWithSuper_private_foo01(
CheckTarget<CompletionSuggestionForTesting> suggestion,
) {
suggestion
..displayText.isEqualTo('_foo01() { … }')
..hasSelection(offset: 62, length: 15)
..completion.isEqualTo(r'''
@override
void _foo01() {
// TODO: implement _foo01
super._foo01();
}''');
}
}
class _Context {
final bool isClass;
final bool isMixin;
_Context({
this.isClass = false,
this.isMixin = false,
});
}