Support code completion in cast patterns

Change-Id: I429957a44bdc09a0936c10123b58b3514b23e016
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/279563
Reviewed-by: Konstantin Shcheglov <scheglov@google.com>
Commit-Queue: Brian Wilkerson <brianwilkerson@google.com>
This commit is contained in:
Brian Wilkerson 2023-01-24 00:22:32 +00:00 committed by Commit Queue
parent b2c26d0aae
commit 2ca588fce8
6 changed files with 183 additions and 18 deletions

View file

@ -96,12 +96,16 @@ abstract class AbstractCompletionDriverTest
final actual = buffer.toString();
if (actual != expected) {
// TODO(brianwilkerson) Improve the output to make it easier to debug. For
// example, print the type of the covering node and the `entity` used to
// compute the suggestions.
var target = driver.server.server.completionState.currentRequest?.target;
var where = '';
if (target != null) {
var containingNode = target.containingNode.runtimeType;
var entity = target.entity;
where = ' (containingNode = $containingNode, entity = $entity)';
}
TextExpectationsCollector.add(actual);
fail('''
The actual suggestions do not match the expected suggestions.
The actual suggestions do not match the expected suggestions$where.
To accept the current state change the expectation to

View file

@ -5849,7 +5849,6 @@ class C<T> {
assertNotSuggested('String');
}
@FailingTest(issue: 'https://github.com/dart-lang/sdk/issues/49759')
Future<void> test_SwitchStatement_case_var() async {
// SwitchStatement Block BlockFunctionBody MethodDeclaration
addTestSource('''

View file

@ -0,0 +1,81 @@
// Copyright (c) 2023, 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/src/protocol_server.dart';
import 'package:test_reflective_loader/test_reflective_loader.dart';
import '../../../../client/completion_driver_test.dart';
import '../completion_printer.dart' as printer;
void main() {
defineReflectiveSuite(() {
defineReflectiveTests(CastPatternTest1);
defineReflectiveTests(CastPatternTest2);
});
}
@reflectiveTest
class CastPatternTest1 extends AbstractCompletionDriverTest
with CastPatternTestCases {
@override
TestingCompletionProtocol get protocol => TestingCompletionProtocol.version1;
}
@reflectiveTest
class CastPatternTest2 extends AbstractCompletionDriverTest
with CastPatternTestCases {
@override
TestingCompletionProtocol get protocol => TestingCompletionProtocol.version2;
}
mixin CastPatternTestCases on AbstractCompletionDriverTest {
@override
Future<void> setUp() async {
await super.setUp();
printerConfiguration = printer.Configuration(
filter: (suggestion) {
final completion = suggestion.completion;
return suggestion.kind == CompletionSuggestionKind.KEYWORD ||
['A0', 'B0'].any(completion.startsWith);
},
);
}
Future<void> test_partialType() async {
await computeSuggestions('''
void f(Object x) {
switch (x) {
case i as A^
}
}
class A01 {}
class A02 {}
class B01 {}
''');
if (isProtocolVersion2) {
assertResponse('''
replacement
left: 1
suggestions
A01
kind: class
A02
kind: class
''');
} else {
assertResponse('''
replacement
left: 1
suggestions
A01
kind: class
A02
kind: class
B01
kind: class
''');
}
}
}

View file

@ -37,7 +37,9 @@ mixin SwitchPatternCaseTestCases on AbstractCompletionDriverTest {
// TODO(brianwilkerson) Include more than keywords in these tests.
printerConfiguration = printer.Configuration(
filter: (suggestion) {
return suggestion.kind == CompletionSuggestionKind.KEYWORD;
final completion = suggestion.completion;
return suggestion.kind == CompletionSuggestionKind.KEYWORD ||
['A0', 'B0'].any(completion.startsWith);
},
);
}
@ -50,33 +52,38 @@ void f(Object o) {
return;
}
}
class A01 {}
''');
assertResponse('''
suggestions
assert
kind: keyword
break
kind: keyword
return
kind: keyword
if
kind: keyword
A01
kind: class
final
kind: keyword
for
kind: keyword
throw
kind: keyword
A01
kind: constructorInvocation
assert
kind: keyword
const
kind: keyword
do
kind: keyword
dynamic
kind: keyword
final
kind: keyword
for
kind: keyword
if
kind: keyword
late
kind: keyword
return
kind: keyword
switch
kind: keyword
throw
kind: keyword
try
kind: keyword
var
@ -296,6 +303,51 @@ suggestions
kind: keyword
when
kind: keyword
''');
}
Future<void> test_noColon_afterDeclarationAndAs() async {
await computeSuggestions('''
void f(Object x) {
switch (x) {
case var i as ^
}
}
class A01 {}
class A02 {}
class B01 {}
''');
assertResponse('''
suggestions
A01
kind: class
A02
kind: class
B01
kind: class
''');
}
Future<void> test_noColon_afterReferenceAndAs() async {
await computeSuggestions('''
void f(Object x) {
const i = 0;
switch (x) {
case i as ^
}
}
class A01 {}
class A02 {}
class B01 {}
''');
assertResponse('''
suggestions
A01
kind: class
A02
kind: class
B01
kind: class
''');
}
}

View file

@ -6,6 +6,7 @@ import 'package:test_reflective_loader/test_reflective_loader.dart';
import 'block_test.dart' as block;
import 'case_clause_test.dart' as case_clause;
import 'cast_pattern_test.dart' as cast_pattern;
import 'class_body_test.dart' as class_body;
import 'compilation_unit_test.dart' as compilation_unit;
import 'directive_uri_test.dart' as directive_uri;
@ -26,6 +27,7 @@ void main() {
defineReflectiveSuite(() {
block.main();
case_clause.main();
cast_pattern.main();
class_body.main();
compilation_unit.main();
directive_uri.main();

View file

@ -350,6 +350,14 @@ class _OpTypeAstVisitor extends GeneralizingAstVisitor<void> {
}
}
@override
void visitCastPattern(CastPattern node) {
if (identical(entity, node.type)) {
optype.completionLocation = 'CastPattern_type';
optype.includeTypeNameSuggestions = true;
}
}
@override
void visitCatchClause(CatchClause node) {
if (identical(entity, node.exceptionType)) {
@ -1317,6 +1325,25 @@ class _OpTypeAstVisitor extends GeneralizingAstVisitor<void> {
}
}
@override
void visitSwitchPatternCase(SwitchPatternCase node) {
if (identical(entity, node.colon)) {
var guardedPattern = node.guardedPattern;
var pattern = guardedPattern.pattern;
if (guardedPattern.whenClause == null &&
pattern is DeclaredVariablePattern &&
pattern.name.lexeme == 'as') {
optype.completionLocation = 'CastPattern_type';
optype.includeTypeNameSuggestions = true;
}
} else if (node.statements.contains(entity)) {
optype.completionLocation = 'SwitchMember_statement';
optype.includeReturnValueSuggestions = true;
optype.includeTypeNameSuggestions = true;
optype.includeVoidReturnSuggestions = true;
}
}
@override
void visitSwitchStatement(SwitchStatement node) {
if (identical(entity, node.expression)) {