[wildcards] fix scoping for local functions

See: https://github.com/dart-lang/sdk/issues/55680

Change-Id: I74e41257e1537ce371d10d4bb7aa24afbb911b6f
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/376024
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
Commit-Queue: Phil Quitslund <pquitslund@google.com>
diff --git a/pkg/analyzer/lib/src/dart/element/extensions.dart b/pkg/analyzer/lib/src/dart/element/extensions.dart
index 5d452a7..47882fe 100644
--- a/pkg/analyzer/lib/src/dart/element/extensions.dart
+++ b/pkg/analyzer/lib/src/dart/element/extensions.dart
@@ -119,15 +119,16 @@
   }
 
   /// Return true if this element is a wildcard variable.
-  bool get isWildcardVariable =>
-      name == '_' &&
-      (this is LocalVariableElement ||
-          this is PrefixElement ||
-          this is TypeParameterElement ||
-          (this is ParameterElement &&
-              this is! FieldFormalParameterElement &&
-              this is! SuperFormalParameterElement)) &&
-      (library?.featureSet.isEnabled(Feature.wildcard_variables) ?? false);
+  bool get isWildcardVariable {
+    return name == '_' &&
+        (this is LocalVariableElement ||
+            this is PrefixElement ||
+            this is TypeParameterElement ||
+            (this is ParameterElement &&
+                this is! FieldFormalParameterElement &&
+                this is! SuperFormalParameterElement)) &&
+        library.hasWildcardVariablesFeatureEnabled;
+  }
 }
 
 extension ExecutableElementExtension on ExecutableElement {
@@ -154,6 +155,13 @@
   }
 }
 
+extension LibraryExtension on LibraryElement? {
+  bool get hasWildcardVariablesFeatureEnabled {
+    var self = this;
+    return self?.featureSet.isEnabled(Feature.wildcard_variables) ?? false;
+  }
+}
+
 extension ParameterElementExtensions on ParameterElement {
   /// Return [ParameterElement] with the specified properties replaced.
   ParameterElement copyWith({
diff --git a/pkg/analyzer/lib/src/dart/resolver/resolution_visitor.dart b/pkg/analyzer/lib/src/dart/resolver/resolution_visitor.dart
index cdfee73..6074607 100644
--- a/pkg/analyzer/lib/src/dart/resolver/resolution_visitor.dart
+++ b/pkg/analyzer/lib/src/dart/resolver/resolution_visitor.dart
@@ -16,6 +16,7 @@
 import 'package:analyzer/src/dart/ast/ast.dart';
 import 'package:analyzer/src/dart/ast/extensions.dart';
 import 'package:analyzer/src/dart/element/element.dart';
+import 'package:analyzer/src/dart/element/extensions.dart';
 import 'package:analyzer/src/dart/element/scope.dart';
 import 'package:analyzer/src/dart/element/type.dart';
 import 'package:analyzer/src/dart/element/type_constraint_gatherer.dart';
@@ -1494,7 +1495,10 @@
     var nameToken = node.name;
     var element = FunctionElementImpl(nameToken.lexeme, nameToken.offset);
     node.declaredElement = element;
-    _define(element);
+    if (nameToken.lexeme != '_' ||
+        !_libraryElement.hasWildcardVariablesFeatureEnabled) {
+      _define(element);
+    }
     _elementHolder.enclose(element);
   }
 
diff --git a/pkg/analyzer/lib/src/generated/resolver.dart b/pkg/analyzer/lib/src/generated/resolver.dart
index d68385c..a1cf286 100644
--- a/pkg/analyzer/lib/src/generated/resolver.dart
+++ b/pkg/analyzer/lib/src/generated/resolver.dart
@@ -5307,7 +5307,11 @@
     var outerScope = nameScope;
     try {
       var enclosedScope = LocalScope(nameScope);
-      BlockScope.elementsInStatements(statements).forEach(enclosedScope.add);
+      for (var statement in BlockScope.elementsInStatements(statements)) {
+        if (!statement.isWildcardFunction) {
+          enclosedScope.add(statement);
+        }
+      }
 
       nameScope = enclosedScope;
       node.nameScope = nameScope;
@@ -5607,3 +5611,10 @@
         url: NonPromotionDocumentationLink.fieldPromotionUnavailable.url);
   }
 }
+
+extension on Element {
+  bool get isWildcardFunction =>
+      this is FunctionElement &&
+      name == '_' &&
+      library.hasWildcardVariablesFeatureEnabled;
+}
diff --git a/pkg/analyzer/test/src/dart/resolution/function_declaration_test.dart b/pkg/analyzer/test/src/dart/resolution/function_declaration_test.dart
index 720f62f..d7b5b59 100644
--- a/pkg/analyzer/test/src/dart/resolution/function_declaration_test.dart
+++ b/pkg/analyzer/test/src/dart/resolution/function_declaration_test.dart
@@ -281,4 +281,61 @@
     type: Iterable<int> Function()
 ''');
   }
+
+  test_wildCardFunction() async {
+    await assertErrorsInCode('''
+_() {}
+''', [
+      error(WarningCode.UNUSED_ELEMENT, 0, 1),
+    ]);
+
+    var node = findNode.singleFunctionDeclaration;
+    assertResolvedNodeText(node, r'''
+FunctionDeclaration
+  name: _
+  functionExpression: FunctionExpression
+    parameters: FormalParameterList
+      leftParenthesis: (
+      rightParenthesis: )
+    body: BlockFunctionBody
+      block: Block
+        leftBracket: {
+        rightBracket: }
+    declaredElement: self::@function::_
+      type: dynamic Function()
+    staticType: dynamic Function()
+  declaredElement: self::@function::_
+    type: dynamic Function()
+''');
+  }
+
+  test_wildCardFunction_preWildCards() async {
+    await assertErrorsInCode('''
+// @dart = 3.4
+// (pre wildcard-variables)
+
+_() {}
+''', [
+      error(WarningCode.UNUSED_ELEMENT, 44, 1),
+    ]);
+
+    var node = findNode.singleFunctionDeclaration;
+    assertResolvedNodeText(node, r'''
+FunctionDeclaration
+  name: _
+  functionExpression: FunctionExpression
+    parameters: FormalParameterList
+      leftParenthesis: (
+      rightParenthesis: )
+    body: BlockFunctionBody
+      block: Block
+        leftBracket: {
+        rightBracket: }
+    declaredElement: self::@function::_
+      type: dynamic Function()
+    staticType: dynamic Function()
+  declaredElement: self::@function::_
+    type: dynamic Function()
+''');
+  }
 }
diff --git a/pkg/analyzer/test/src/dart/resolution/local_variable_test.dart b/pkg/analyzer/test/src/dart/resolution/local_variable_test.dart
index 7246f5b..ceed699 100644
--- a/pkg/analyzer/test/src/dart/resolution/local_variable_test.dart
+++ b/pkg/analyzer/test/src/dart/resolution/local_variable_test.dart
@@ -134,6 +134,38 @@
     expect(x.isStatic, isFalse);
   }
 
+  test_localVariable_wildcardFunction() async {
+    await assertErrorsInCode('''
+f() {
+  _() {}
+  _();
+}
+''', [
+      error(WarningCode.DEAD_CODE, 8, 6),
+      error(CompileTimeErrorCode.UNDEFINED_FUNCTION, 17, 1),
+    ]);
+  }
+
+  test_localVariable_wildcardFunction_preWildcards() async {
+    await assertNoErrorsInCode('''
+// @dart = 3.4
+// (pre wildcard-variables)
+
+f() {
+  _() {}
+  _();
+}
+''');
+
+    var node = findNode.simple('_();');
+    assertResolvedNodeText(node, r'''
+SimpleIdentifier
+  token: _
+  staticElement: _@52
+  staticType: Null Function()
+''');
+  }
+
   test_localVariable_wildcardVariable_field() async {
     await assertNoErrorsInCode('''
 class C {
diff --git a/pkg/analyzer/test/src/dart/resolution/method_declaration_test.dart b/pkg/analyzer/test/src/dart/resolution/method_declaration_test.dart
index 49e8b20..ce5844a 100644
--- a/pkg/analyzer/test/src/dart/resolution/method_declaration_test.dart
+++ b/pkg/analyzer/test/src/dart/resolution/method_declaration_test.dart
@@ -2,6 +2,7 @@
 // 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/src/error/codes.dart';
 import 'package:test_reflective_loader/test_reflective_loader.dart';
 
 import 'context_collection_resolution.dart';
@@ -70,4 +71,57 @@
   staticType: int
 ''');
   }
+
+  test_wildCardMethod() async {
+    await assertErrorsInCode('''
+class C {
+  _() {}
+}
+''', [
+      error(WarningCode.UNUSED_ELEMENT, 12, 1),
+    ]);
+
+    var node = findNode.methodDeclaration('_');
+    assertResolvedNodeText(node, r'''
+MethodDeclaration
+  name: _
+  parameters: FormalParameterList
+    leftParenthesis: (
+    rightParenthesis: )
+  body: BlockFunctionBody
+    block: Block
+      leftBracket: {
+      rightBracket: }
+  declaredElement: self::@class::C::@method::_
+    type: dynamic Function()
+''');
+  }
+
+  test_wildCardMethod_preWildCards() async {
+    await assertErrorsInCode('''
+// @dart = 3.4
+// (pre wildcard-variables)
+
+class C {
+  _() {}
+}
+''', [
+      error(WarningCode.UNUSED_ELEMENT, 56, 1),
+    ]);
+
+    var node = findNode.methodDeclaration('_');
+    assertResolvedNodeText(node, r'''
+MethodDeclaration
+  name: _
+  parameters: FormalParameterList
+    leftParenthesis: (
+    rightParenthesis: )
+  body: BlockFunctionBody
+    block: Block
+      leftBracket: {
+      rightBracket: }
+  declaredElement: self::@class::C::@method::_
+    type: dynamic Function()
+''');
+  }
 }