[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()
+''');
+ }
}