[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=^;