From 2ca588fce8bcde6437123070bac9cff187221a4e Mon Sep 17 00:00:00 2001 From: Brian Wilkerson Date: Tue, 24 Jan 2023 00:22:32 +0000 Subject: [PATCH] Support code completion in cast patterns Change-Id: I429957a44bdc09a0936c10123b58b3514b23e016 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/279563 Reviewed-by: Konstantin Shcheglov Commit-Queue: Brian Wilkerson --- .../test/client/completion_driver_test.dart | 12 ++- .../local_reference_contributor_test.dart | 1 - .../dart/location/cast_pattern_test.dart | 81 +++++++++++++++++++ .../location/switch_pattern_case_test.dart | 78 +++++++++++++++--- .../completion/dart/location/test_all.dart | 2 + .../lib/src/utilities/completion/optype.dart | 27 +++++++ 6 files changed, 183 insertions(+), 18 deletions(-) create mode 100644 pkg/analysis_server/test/services/completion/dart/location/cast_pattern_test.dart diff --git a/pkg/analysis_server/test/client/completion_driver_test.dart b/pkg/analysis_server/test/client/completion_driver_test.dart index 7beb3bd9585..f8153bd07bd 100644 --- a/pkg/analysis_server/test/client/completion_driver_test.dart +++ b/pkg/analysis_server/test/client/completion_driver_test.dart @@ -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 diff --git a/pkg/analysis_server/test/services/completion/dart/local_reference_contributor_test.dart b/pkg/analysis_server/test/services/completion/dart/local_reference_contributor_test.dart index 9d313d7f646..c7c12215eff 100644 --- a/pkg/analysis_server/test/services/completion/dart/local_reference_contributor_test.dart +++ b/pkg/analysis_server/test/services/completion/dart/local_reference_contributor_test.dart @@ -5849,7 +5849,6 @@ class C { assertNotSuggested('String'); } - @FailingTest(issue: 'https://github.com/dart-lang/sdk/issues/49759') Future test_SwitchStatement_case_var() async { // SwitchStatement Block BlockFunctionBody MethodDeclaration addTestSource(''' diff --git a/pkg/analysis_server/test/services/completion/dart/location/cast_pattern_test.dart b/pkg/analysis_server/test/services/completion/dart/location/cast_pattern_test.dart new file mode 100644 index 00000000000..d417bb39ca2 --- /dev/null +++ b/pkg/analysis_server/test/services/completion/dart/location/cast_pattern_test.dart @@ -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 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 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 +'''); + } + } +} diff --git a/pkg/analysis_server/test/services/completion/dart/location/switch_pattern_case_test.dart b/pkg/analysis_server/test/services/completion/dart/location/switch_pattern_case_test.dart index f9564d983e8..52c93b19f19 100644 --- a/pkg/analysis_server/test/services/completion/dart/location/switch_pattern_case_test.dart +++ b/pkg/analysis_server/test/services/completion/dart/location/switch_pattern_case_test.dart @@ -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 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 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 '''); } } diff --git a/pkg/analysis_server/test/services/completion/dart/location/test_all.dart b/pkg/analysis_server/test/services/completion/dart/location/test_all.dart index 6091af3ad5b..e7c8e93d2ca 100644 --- a/pkg/analysis_server/test/services/completion/dart/location/test_all.dart +++ b/pkg/analysis_server/test/services/completion/dart/location/test_all.dart @@ -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(); diff --git a/pkg/analyzer_plugin/lib/src/utilities/completion/optype.dart b/pkg/analyzer_plugin/lib/src/utilities/completion/optype.dart index 74f86078a5c..6cfa6ef4af2 100644 --- a/pkg/analyzer_plugin/lib/src/utilities/completion/optype.dart +++ b/pkg/analyzer_plugin/lib/src/utilities/completion/optype.dart @@ -350,6 +350,14 @@ class _OpTypeAstVisitor extends GeneralizingAstVisitor { } } + @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 { } } + @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)) {