[DAS] Fixes type completion for switch expression, function declaration and async function body
Fixes: https://github.com/dart-lang/sdk/issues/60808
Change-Id: Ia6cc2cbad3206f62c9149a5e326dc8333aca556f
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/434124
Reviewed-by: Samuel Rawlins <srawlins@google.com>
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
Commit-Queue: Samuel Rawlins <srawlins@google.com>
Auto-Submit: Felipe Morschel <git@fmorschel.dev>
diff --git a/pkg/analysis_server/lib/src/services/completion/dart/feature_computer.dart b/pkg/analysis_server/lib/src/services/completion/dart/feature_computer.dart
index e46ff8c..60c479e 100644
--- a/pkg/analysis_server/lib/src/services/completion/dart/feature_computer.dart
+++ b/pkg/analysis_server/lib/src/services/completion/dart/feature_computer.dart
@@ -706,11 +706,12 @@
} else if (parent is FunctionExpressionImpl) {
// If the surrounding function has a context type, use it.
var functionContextType = parent.body.bodyContext?.contextType;
- if (functionContextType != null) {
+ if (functionContextType != null &&
+ functionContextType is! InvalidType) {
return functionContextType;
- } else if (parent.parent is FunctionDeclaration) {
+ } else if (parent.parent case FunctionDeclaration(:var returnType)) {
// Don't walk up past the function declaration.
- return null;
+ return returnType?.type;
}
return _visitParent(parent);
}
@@ -1082,7 +1083,14 @@
if (node.returnKeyword.end < offset) {
var functionBody = node.thisOrAncestorOfType<FunctionBodyImpl>();
if (functionBody != null) {
- return functionBody.bodyContext?.contextType;
+ AstNode? invocationParent;
+ if (functionBody.parent?.parent case FunctionExpressionInvocation(
+ :var parent,
+ )) {
+ invocationParent = parent;
+ }
+ return functionBody.bodyContext?.contextType ??
+ invocationParent?.accept(this);
}
}
return null;
@@ -1145,6 +1153,14 @@
}
@override
+ DartType? visitSwitchExpressionCase(SwitchExpressionCase node) {
+ if (node.parent case SwitchExpression(:var parent?)) {
+ return parent.accept(this);
+ }
+ return super.visitSwitchExpressionCase(node);
+ }
+
+ @override
DartType? visitTopLevelVariableDeclaration(TopLevelVariableDeclaration node) {
if (node.variables.contains(offset)) {
return node.variables.accept(this);
diff --git a/pkg/analysis_server/lib/src/services/completion/dart/in_scope_completion_pass.dart b/pkg/analysis_server/lib/src/services/completion/dart/in_scope_completion_pass.dart
index 3eea6f2..c3f7636 100644
--- a/pkg/analysis_server/lib/src/services/completion/dart/in_scope_completion_pass.dart
+++ b/pkg/analysis_server/lib/src/services/completion/dart/in_scope_completion_pass.dart
@@ -4040,7 +4040,13 @@
required RecordLiteral? recordLiteral,
required bool isNewField,
}) {
- if (contextType is! RecordType) {
+ if (contextType != null && (contextType.isDartAsyncFutureOr)) {
+ contextType = (contextType as InterfaceType).typeArguments.firstOrNull;
+ }
+ RecordType recordType;
+ if (contextType is RecordType) {
+ recordType = contextType;
+ } else {
return;
}
@@ -4058,7 +4064,7 @@
.toSet();
}
- for (var field in contextType.namedFields) {
+ for (var field in recordType.namedFields) {
if (!includedNames.contains(field.name)) {
var matcherScore = state.matcher.score(field.name);
if (matcherScore != -1) {
diff --git a/pkg/analysis_server/test/services/completion/dart/location/record_literal_test.dart b/pkg/analysis_server/test/services/completion/dart/location/record_literal_test.dart
index 2264a98..c38b815 100644
--- a/pkg/analysis_server/test/services/completion/dart/location/record_literal_test.dart
+++ b/pkg/analysis_server/test/services/completion/dart/location/record_literal_test.dart
@@ -435,4 +435,36 @@
kind: topLevelVariable
''');
}
+
+ Future<void> test_futureAsync_suggests_fieldNames() async {
+ await computeSuggestions('''
+Future<({int foo01, String foo02})> f() async => (foo0^);
+''');
+ assertResponse(r'''
+replacement
+ left: 4
+suggestions
+ |foo01: |
+ kind: namedArgument
+ |foo02: |
+ kind: namedArgument
+''');
+ }
+
+ Future<void> test_futureOr_suggests_fieldNames() async {
+ await computeSuggestions('''
+import 'dart:async';
+
+FutureOr<({int foo01, String foo02})> f() => (foo0^);
+''');
+ assertResponse(r'''
+replacement
+ left: 4
+suggestions
+ |foo01: |
+ kind: namedArgument
+ |foo02: |
+ kind: namedArgument
+''');
+ }
}
diff --git a/pkg/analysis_server/test/services/completion/dart/location/switch_expression_test.dart b/pkg/analysis_server/test/services/completion/dart/location/switch_expression_test.dart
index 1e1e996..8f3457a 100644
--- a/pkg/analysis_server/test/services/completion/dart/location/switch_expression_test.dart
+++ b/pkg/analysis_server/test/services/completion/dart/location/switch_expression_test.dart
@@ -29,16 +29,16 @@
suggestions
p01
kind: parameter
- const
- kind: keyword
false
kind: keyword
null
kind: keyword
- switch
- kind: keyword
true
kind: keyword
+ const
+ kind: keyword
+ switch
+ kind: keyword
''');
}
diff --git a/pkg/analysis_server/test/src/services/completion/dart/feature_computer_test.dart b/pkg/analysis_server/test/src/services/completion/dart/feature_computer_test.dart
index 6382389..3f9f09c 100644
--- a/pkg/analysis_server/test/src/services/completion/dart/feature_computer_test.dart
+++ b/pkg/analysis_server/test/src/services/completion/dart/feature_computer_test.dart
@@ -474,6 +474,26 @@
''', 'int');
}
+ Future<void> test_closureCallReturn() async {
+ await assertContextType('''
+void f() {
+ String f = () {
+ return ^;
+ }();
+}
+''', 'String');
+ }
+
+ Future<void> test_closureReturn() async {
+ await assertContextType('''
+void f() {
+ String Function() f = () {
+ return ^;
+ };
+}
+''', 'String');
+ }
+
Future<void> test_fieldDeclaration_int() async {
await assertContextType('''
class Foo {
@@ -578,6 +598,42 @@
''', 'bool');
}
+ Future<void> test_functionExpressionCallReturn() async {
+ await assertContextType('''
+void f() {
+ String f = (() => ^)();
+}
+''', 'String');
+ }
+
+ Future<void> test_functionExpressionReturn() async {
+ await assertContextType('''
+void f() {
+ String Function() f = (() => ^);
+}
+''', 'String');
+ }
+
+ Future<void> test_futureOrRecord() async {
+ await assertContextType('''
+import 'dart:async';
+
+FutureOr<({int field})> f() => ^;
+''', 'FutureOr<({int field})>');
+ }
+
+ Future<void> test_futureType_async() async {
+ await assertContextType('''
+Future<String> f() async => ^;
+''', 'FutureOr<String>');
+ }
+
+ Future<void> test_futureType_noAsync() async {
+ await assertContextType('''
+Future<String> f() => ^;
+''', 'Future<String>');
+ }
+
Future<void> test_ifElement() async {
await assertContextType('''
void f(bool b, int e) {
@@ -1052,6 +1108,15 @@
''');
}
+ Future<void> test_switchExpression() async {
+ await assertContextType('''
+String f(num n) => switch (n) {
+ double() => ^,
+ int() => '',
+};
+''', 'String');
+ }
+
Future<void> test_topLevelVariableDeclaration_int() async {
await assertContextType('''
int i=^;