diff --git a/pkg/_fe_analyzer_shared/lib/src/parser/forwarding_listener.dart b/pkg/_fe_analyzer_shared/lib/src/parser/forwarding_listener.dart index 59d81bb7112..0c08b0f78e5 100644 --- a/pkg/_fe_analyzer_shared/lib/src/parser/forwarding_listener.dart +++ b/pkg/_fe_analyzer_shared/lib/src/parser/forwarding_listener.dart @@ -966,8 +966,9 @@ class ForwardingListener implements Listener { } @override - void endFunctionName(Token beginToken, Token token) { - listener?.endFunctionName(beginToken, token); + void endFunctionName( + Token beginToken, Token token, bool isFunctionExpression) { + listener?.endFunctionName(beginToken, token, isFunctionExpression); } @override diff --git a/pkg/_fe_analyzer_shared/lib/src/parser/listener.dart b/pkg/_fe_analyzer_shared/lib/src/parser/listener.dart index 187726a9b74..08b9b6573c3 100644 --- a/pkg/_fe_analyzer_shared/lib/src/parser/listener.dart +++ b/pkg/_fe_analyzer_shared/lib/src/parser/listener.dart @@ -812,7 +812,23 @@ class Listener implements UnescapeErrorListener { void beginFunctionName(Token token) {} - void endFunctionName(Token beginToken, Token token) { + /// The end of the function name in either a local function declaration, like + /// 'local' in: + /// + /// void m() { + /// void local() {} + /// } + /// + /// or an erroneous function expression, like 'local' in: + /// + /// void m() { + /// var f = void local() {}; + /// } + /// + /// The boolean [isFunctionExpression] indicates that we are in the latter + /// case. + void endFunctionName( + Token beginToken, Token token, bool isFunctionExpression) { logEvent("FunctionName"); } diff --git a/pkg/_fe_analyzer_shared/lib/src/parser/parser_impl.dart b/pkg/_fe_analyzer_shared/lib/src/parser/parser_impl.dart index 7654aeb6c17..814b3fc0f97 100644 --- a/pkg/_fe_analyzer_shared/lib/src/parser/parser_impl.dart +++ b/pkg/_fe_analyzer_shared/lib/src/parser/parser_impl.dart @@ -5222,7 +5222,7 @@ class Parser { reportRecoverableError( beforeName.next!, codes.messageNamedFunctionExpression); } - listener.endFunctionName(begin, token); + listener.endFunctionName(begin, token, isFunctionExpression); token = parseFormalParametersRequiredOpt(formals, MemberKind.Local); token = parseInitializersOpt(token); token = parseAsyncOptBody( diff --git a/pkg/analyzer/lib/src/fasta/ast_builder.dart b/pkg/analyzer/lib/src/fasta/ast_builder.dart index 60032f26de0..19529e63da4 100644 --- a/pkg/analyzer/lib/src/fasta/ast_builder.dart +++ b/pkg/analyzer/lib/src/fasta/ast_builder.dart @@ -2105,7 +2105,8 @@ class AstBuilder extends StackListener { } @override - void endFunctionName(Token beginToken, Token token) { + void endFunctionName( + Token beginToken, Token token, bool isFunctionExpression) { debugEvent("FunctionName"); } diff --git a/pkg/analyzer/test/generated/parser_fasta_listener.dart b/pkg/analyzer/test/generated/parser_fasta_listener.dart index a2f7b13fb2a..ecb79ffb98c 100644 --- a/pkg/analyzer/test/generated/parser_fasta_listener.dart +++ b/pkg/analyzer/test/generated/parser_fasta_listener.dart @@ -981,9 +981,9 @@ class ForwardingTestListener extends ForwardingListener { } @override - void endFunctionName(Token beginToken, Token token) { + void endFunctionName(Token beginToken, Token token, bool isFunctionExpression) { end('FunctionName'); - super.endFunctionName(beginToken, token); + super.endFunctionName(beginToken, token, isFunctionExpression); } @override diff --git a/pkg/front_end/lib/src/base/local_scope.dart b/pkg/front_end/lib/src/base/local_scope.dart index 8e0e30f49da..cca9d925b26 100644 --- a/pkg/front_end/lib/src/base/local_scope.dart +++ b/pkg/front_end/lib/src/base/local_scope.dart @@ -4,6 +4,7 @@ import '../builder/builder.dart'; import '../builder/declaration_builders.dart'; +import '../builder/variable_builder.dart'; import 'scope.dart'; abstract class LocalScope implements LookupScope { @@ -27,9 +28,7 @@ abstract class LocalScope implements LookupScope { /// If name was used previously in this scope, this method returns the read /// offsets which can be used for reporting a compile-time error about /// [name] being used before its declared. - List? declare(String name, Builder builder); - - void addLocalVariable(String name, Builder builder); + List? declare(String name, VariableBuilder builder); @override Builder? lookupGetable(String name, int charOffset, Uri fileUri); @@ -117,7 +116,7 @@ final class LocalScopeImpl extends BaseLocalScope /// Names declared in this scope. @override - Map? _local; + Map? _local; @override Map>? usedNames; @@ -128,12 +127,7 @@ final class LocalScopeImpl extends BaseLocalScope LocalScopeImpl(this._parent, this.kind, this.classNameOrDebugName); @override - void addLocalVariable(String name, Builder builder) { - (_local ??= {})[name] = builder; - } - - @override - List? declare(String name, Builder builder) { + List? declare(String name, VariableBuilder builder) { List? previousOffsets = usedNames?[name]; if (previousOffsets != null && previousOffsets.isNotEmpty) { return previousOffsets; @@ -157,12 +151,7 @@ final class LocalScopeImpl extends BaseLocalScope mixin ImmutableLocalScopeMixin implements LocalScope { @override - void addLocalVariable(String name, Builder builder) { - throw new UnsupportedError('$runtimeType($kind).addLocalMember'); - } - - @override - List? declare(String name, Builder builder) { + List? declare(String name, VariableBuilder builder) { throw new UnsupportedError('$runtimeType($kind).declare'); } @@ -171,6 +160,34 @@ mixin ImmutableLocalScopeMixin implements LocalScope { Map>? get usedNames => null; } +final class LocalTypeParameterScope extends BaseLocalScope + with LookupScopeMixin, ImmutableLocalScopeMixin, LocalScopeMixin { + @override + final LocalScope? _parent; + @override + final ScopeKind kind; + @override + final Map? _local; + + final String _debugName; + + LocalTypeParameterScope( + {required this.kind, + LocalScope? parent, + Map? local, + required String debugName}) + : _parent = parent, + _local = local, + _debugName = debugName; + + @override + String get classNameOrDebugName => _debugName; + + @override + String toString() => + "$runtimeType(${kind}, $classNameOrDebugName, ${_local?.keys})"; +} + final class FixedLocalScope extends BaseLocalScope with LookupScopeMixin, ImmutableLocalScopeMixin, LocalScopeMixin { @override diff --git a/pkg/front_end/lib/src/kernel/body_builder.dart b/pkg/front_end/lib/src/kernel/body_builder.dart index 0b98cade7de..d3e9e9828f4 100644 --- a/pkg/front_end/lib/src/kernel/body_builder.dart +++ b/pkg/front_end/lib/src/kernel/body_builder.dart @@ -5085,41 +5085,49 @@ class BodyBuilder extends StackListenerImpl void enterNominalVariablesScope( List? nominalVariableBuilders) { debugEvent("enterNominalVariableScope"); - enterLocalScope(_localScope.createNestedScope( - debugName: "function-type scope", kind: ScopeKind.typeParameters)); + Map typeParameters = {}; if (nominalVariableBuilders != null) { for (NominalParameterBuilder builder in nominalVariableBuilders) { if (builder.isWildcard) continue; String name = builder.name; - Builder? existing = _localScope.lookupLocalVariable(name); + TypeParameterBuilder? existing = typeParameters[name]; if (existing == null) { - _localScope.addLocalVariable(name, builder); + typeParameters[name] = builder; } else { // Coverage-ignore-block(suite): Not run. reportDuplicatedDeclaration(existing, name, builder.fileOffset); } } } + enterLocalScope(new LocalTypeParameterScope( + local: typeParameters, + parent: _localScope, + debugName: "local function type parameter scope", + kind: ScopeKind.typeParameters)); } void enterStructuralVariablesScope( List? structuralVariableBuilders) { debugEvent("enterStructuralVariableScope"); - enterLocalScope(_localScope.createNestedScope( - debugName: "function-type scope", kind: ScopeKind.typeParameters)); + Map typeParameters = {}; if (structuralVariableBuilders != null) { for (StructuralParameterBuilder builder in structuralVariableBuilders) { if (builder.isWildcard) continue; String name = builder.name; - Builder? existing = _localScope.lookupLocalVariable(name); + TypeParameterBuilder? existing = typeParameters[name]; if (existing == null) { - _localScope.addLocalVariable(name, builder); + typeParameters[name] = builder; } else { // Coverage-ignore-block(suite): Not run. reportDuplicatedDeclaration(existing, name, builder.fileOffset); } } } + enterLocalScope(new LocalTypeParameterScope( + local: typeParameters, + parent: _localScope, + debugName: "function-type scope", + kind: ScopeKind.typeParameters)); } @override @@ -7273,7 +7281,8 @@ class BodyBuilder extends StackListenerImpl void handleNamedRecordField(Token colon) => handleNamedArgument(colon); @override - void endFunctionName(Token beginToken, Token token) { + void endFunctionName( + Token beginToken, Token token, bool isFunctionExpression) { debugEvent("FunctionName"); Identifier name = pop() as Identifier; Token nameToken = name.token; @@ -7290,20 +7299,24 @@ class BodyBuilder extends StackListenerImpl isLocalFunction: true, isWildcard: isWildcard) ..fileOffset = name.nameOffset; - // TODO(ahe): Why are we looking up in local scope, but declaring in parent - // scope? - Builder? existing = _localScope.lookupLocalVariable(name.name); - if (existing != null) { - // Coverage-ignore-block(suite): Not run. - reportDuplicatedDeclaration(existing, name.name, name.nameOffset); - } push(new FunctionDeclarationImpl( variable, // The real function node is created later. dummyFunctionNode) ..fileOffset = beginToken.charOffset); if (!(libraryFeatures.wildcardVariables.isEnabled && variable.isWildcard)) { - declareVariable(variable, _localScopes.previous); + // The local scope stack contains a type parameter scope for the local + // function on top of the scope for the block in which the local function + // declaration occurs. So for a local function declaration, we add the + // declaration to the previous scope, i.e. the block scope. + // + // For a named function expression, a nested scope is created to hold the + // name, so that it doesn't pollute the block scope (the named function + // expression is erroneous and should introduce the name in the scope) and + // we therefore use the current scope in this case. + LocalScope scope = + isFunctionExpression ? _localScope : _localScopes.previous; + declareVariable(variable, scope); } } diff --git a/pkg/front_end/lib/src/util/parser_ast_helper.dart b/pkg/front_end/lib/src/util/parser_ast_helper.dart index a5296cd2731..3f57e3e5bd7 100644 --- a/pkg/front_end/lib/src/util/parser_ast_helper.dart +++ b/pkg/front_end/lib/src/util/parser_ast_helper.dart @@ -1054,9 +1054,12 @@ abstract class AbstractParserAstListener implements Listener { } @override - void endFunctionName(Token beginToken, Token token) { + void endFunctionName( + Token beginToken, Token token, bool isFunctionExpression) { FunctionNameEnd data = new FunctionNameEnd(ParserAstType.END, - beginToken: beginToken, token: token); + beginToken: beginToken, + token: token, + isFunctionExpression: isFunctionExpression); seen(data); } @@ -5373,15 +5376,19 @@ class FunctionNameBegin extends ParserAstNode { class FunctionNameEnd extends ParserAstNode { final Token beginToken; final Token token; + final bool isFunctionExpression; FunctionNameEnd(ParserAstType type, - {required this.beginToken, required this.token}) + {required this.beginToken, + required this.token, + required this.isFunctionExpression}) : super("FunctionName", type); @override Map get deprecatedArguments => { "beginToken": beginToken, "token": token, + "isFunctionExpression": isFunctionExpression, }; @override diff --git a/pkg/front_end/parser_testcases/error_recovery/issue_49116.dart.expect b/pkg/front_end/parser_testcases/error_recovery/issue_49116.dart.expect index ae68e6431a3..f431d5234ad 100644 --- a/pkg/front_end/parser_testcases/error_recovery/issue_49116.dart.expect +++ b/pkg/front_end/parser_testcases/error_recovery/issue_49116.dart.expect @@ -818,7 +818,7 @@ beginCompilationUnit(Future) handleType(await, null) beginFunctionName(foo) handleIdentifier(foo, localFunctionDeclaration) - endFunctionName(await, () + endFunctionName(await, (, false) beginFormalParameters((, MemberKind.Local) beginMetadataStar(int) endMetadataStar(0) @@ -855,7 +855,7 @@ beginCompilationUnit(Future) handleType(await, null) beginFunctionName(bar) handleIdentifier(bar, localFunctionDeclaration) - endFunctionName(await, () + endFunctionName(await, (, false) beginFormalParameters((, MemberKind.Local) beginMetadataStar(await) endMetadataStar(0) diff --git a/pkg/front_end/parser_testcases/error_recovery/issue_49116.dart.intertwined.expect b/pkg/front_end/parser_testcases/error_recovery/issue_49116.dart.intertwined.expect index 9256ebcfc6b..f7ca385a72e 100644 --- a/pkg/front_end/parser_testcases/error_recovery/issue_49116.dart.intertwined.expect +++ b/pkg/front_end/parser_testcases/error_recovery/issue_49116.dart.intertwined.expect @@ -2019,7 +2019,7 @@ parseUnit(Future) listener: beginFunctionName(foo) ensureIdentifier(await, localFunctionDeclaration) listener: handleIdentifier(foo, localFunctionDeclaration) - listener: endFunctionName(await, () + listener: endFunctionName(await, (, false) parseFormalParametersRequiredOpt(foo, MemberKind.Local) parseFormalParametersRest((, MemberKind.Local) listener: beginFormalParameters((, MemberKind.Local) @@ -2096,7 +2096,7 @@ parseUnit(Future) listener: beginFunctionName(bar) ensureIdentifier(await, localFunctionDeclaration) listener: handleIdentifier(bar, localFunctionDeclaration) - listener: endFunctionName(await, () + listener: endFunctionName(await, (, false) parseFormalParametersRequiredOpt(bar, MemberKind.Local) parseFormalParametersRest((, MemberKind.Local) listener: beginFormalParameters((, MemberKind.Local) diff --git a/pkg/front_end/parser_testcases/error_recovery/issue_49477.dart.expect b/pkg/front_end/parser_testcases/error_recovery/issue_49477.dart.expect index 5b0b74c1a08..7ca74e75502 100644 --- a/pkg/front_end/parser_testcases/error_recovery/issue_49477.dart.expect +++ b/pkg/front_end/parser_testcases/error_recovery/issue_49477.dart.expect @@ -96,7 +96,7 @@ beginCompilationUnit(import) handleVoidKeyword(void) beginFunctionName(writeMessage) handleIdentifier(writeMessage, localFunctionDeclaration) - endFunctionName(void, () + endFunctionName(void, (, false) beginFormalParameters((, MemberKind.Local) beginMetadataStar(String) endMetadataStar(0) diff --git a/pkg/front_end/parser_testcases/error_recovery/issue_49477.dart.intertwined.expect b/pkg/front_end/parser_testcases/error_recovery/issue_49477.dart.intertwined.expect index b6c710bd222..f88c864e3bb 100644 --- a/pkg/front_end/parser_testcases/error_recovery/issue_49477.dart.intertwined.expect +++ b/pkg/front_end/parser_testcases/error_recovery/issue_49477.dart.intertwined.expect @@ -200,7 +200,7 @@ parseUnit(import) listener: beginFunctionName(writeMessage) ensureIdentifier(void, localFunctionDeclaration) listener: handleIdentifier(writeMessage, localFunctionDeclaration) - listener: endFunctionName(void, () + listener: endFunctionName(void, (, false) parseFormalParametersRequiredOpt(writeMessage, MemberKind.Local) parseFormalParametersRest((, MemberKind.Local) listener: beginFormalParameters((, MemberKind.Local) diff --git a/pkg/front_end/parser_testcases/error_recovery/issue_49477_prime.dart.expect b/pkg/front_end/parser_testcases/error_recovery/issue_49477_prime.dart.expect index 610bad00350..7b4daa8cee4 100644 --- a/pkg/front_end/parser_testcases/error_recovery/issue_49477_prime.dart.expect +++ b/pkg/front_end/parser_testcases/error_recovery/issue_49477_prime.dart.expect @@ -91,7 +91,7 @@ beginCompilationUnit(import) handleNoType(;) beginFunctionName(getNumber) handleIdentifier(getNumber, localFunctionDeclaration) - endFunctionName(getNumber, () + endFunctionName(getNumber, (, false) beginFormalParameters((, MemberKind.Local) endFormalParameters(0, (, ), MemberKind.Local) handleNoInitializers() diff --git a/pkg/front_end/parser_testcases/error_recovery/issue_49477_prime.dart.intertwined.expect b/pkg/front_end/parser_testcases/error_recovery/issue_49477_prime.dart.intertwined.expect index c9bd30b1efd..ee4bb27b5b0 100644 --- a/pkg/front_end/parser_testcases/error_recovery/issue_49477_prime.dart.intertwined.expect +++ b/pkg/front_end/parser_testcases/error_recovery/issue_49477_prime.dart.intertwined.expect @@ -196,7 +196,7 @@ parseUnit(import) listener: beginFunctionName(getNumber) ensureIdentifier(;, localFunctionDeclaration) listener: handleIdentifier(getNumber, localFunctionDeclaration) - listener: endFunctionName(getNumber, () + listener: endFunctionName(getNumber, (, false) parseFormalParametersRequiredOpt(getNumber, MemberKind.Local) parseFormalParametersRest((, MemberKind.Local) listener: beginFormalParameters((, MemberKind.Local) diff --git a/pkg/front_end/parser_testcases/general/call_on_after_try_block2_prime.dart.expect b/pkg/front_end/parser_testcases/general/call_on_after_try_block2_prime.dart.expect index 0aaa65f899e..dcd04bf8299 100644 --- a/pkg/front_end/parser_testcases/general/call_on_after_try_block2_prime.dart.expect +++ b/pkg/front_end/parser_testcases/general/call_on_after_try_block2_prime.dart.expect @@ -46,7 +46,7 @@ beginCompilationUnit(void) handleNoType(}) beginFunctionName(onX) handleIdentifier(onX, localFunctionDeclaration) - endFunctionName(onX, () + endFunctionName(onX, (, false) beginFormalParameters((, MemberKind.Local) beginMetadataStar(e) endMetadataStar(0) diff --git a/pkg/front_end/parser_testcases/general/call_on_after_try_block2_prime.dart.intertwined.expect b/pkg/front_end/parser_testcases/general/call_on_after_try_block2_prime.dart.intertwined.expect index a4c01361c19..6ed3d52b3f7 100644 --- a/pkg/front_end/parser_testcases/general/call_on_after_try_block2_prime.dart.intertwined.expect +++ b/pkg/front_end/parser_testcases/general/call_on_after_try_block2_prime.dart.intertwined.expect @@ -98,7 +98,7 @@ parseUnit(void) listener: beginFunctionName(onX) ensureIdentifier(}, localFunctionDeclaration) listener: handleIdentifier(onX, localFunctionDeclaration) - listener: endFunctionName(onX, () + listener: endFunctionName(onX, (, false) parseFormalParametersRequiredOpt(onX, MemberKind.Local) parseFormalParametersRest((, MemberKind.Local) listener: beginFormalParameters((, MemberKind.Local) diff --git a/pkg/front_end/parser_testcases/general/function_declaration.dart.expect b/pkg/front_end/parser_testcases/general/function_declaration.dart.expect index 0aba26097e0..5c237ec7640 100644 --- a/pkg/front_end/parser_testcases/general/function_declaration.dart.expect +++ b/pkg/front_end/parser_testcases/general/function_declaration.dart.expect @@ -17,7 +17,7 @@ beginCompilationUnit(main) handleNoType({) beginFunctionName(local) handleIdentifier(local, localFunctionDeclaration) - endFunctionName(local, () + endFunctionName(local, (, false) beginFormalParameters((, MemberKind.Local) endFormalParameters(0, (, ), MemberKind.Local) handleNoInitializers() diff --git a/pkg/front_end/parser_testcases/general/function_declaration.dart.intertwined.expect b/pkg/front_end/parser_testcases/general/function_declaration.dart.intertwined.expect index abf2b048780..401fa78a8c1 100644 --- a/pkg/front_end/parser_testcases/general/function_declaration.dart.intertwined.expect +++ b/pkg/front_end/parser_testcases/general/function_declaration.dart.intertwined.expect @@ -40,7 +40,7 @@ parseUnit(main) listener: beginFunctionName(local) ensureIdentifier({, localFunctionDeclaration) listener: handleIdentifier(local, localFunctionDeclaration) - listener: endFunctionName(local, () + listener: endFunctionName(local, (, false) parseFormalParametersRequiredOpt(local, MemberKind.Local) parseFormalParametersRest((, MemberKind.Local) listener: beginFormalParameters((, MemberKind.Local) diff --git a/pkg/front_end/parser_testcases/patterns/const_patterns.dart.expect b/pkg/front_end/parser_testcases/patterns/const_patterns.dart.expect index 73ee566327a..1a50caa05fa 100644 --- a/pkg/front_end/parser_testcases/patterns/const_patterns.dart.expect +++ b/pkg/front_end/parser_testcases/patterns/const_patterns.dart.expect @@ -931,7 +931,7 @@ beginCompilationUnit(import) beginFunctionName(fun) handleIdentifier(fun, localFunctionDeclaration) handleRecoverableError(NamedFunctionExpression, fun, fun) - endFunctionName(void, () + endFunctionName(void, (, true) beginFormalParameters((, MemberKind.Local) endFormalParameters(0, (, ), MemberKind.Local) handleNoInitializers() diff --git a/pkg/front_end/parser_testcases/patterns/const_patterns.dart.intertwined.expect b/pkg/front_end/parser_testcases/patterns/const_patterns.dart.intertwined.expect index 8d07e7ec722..0da519e4805 100644 --- a/pkg/front_end/parser_testcases/patterns/const_patterns.dart.intertwined.expect +++ b/pkg/front_end/parser_testcases/patterns/const_patterns.dart.intertwined.expect @@ -1559,7 +1559,7 @@ parseUnit(import) listener: handleIdentifier(fun, localFunctionDeclaration) reportRecoverableError(fun, NamedFunctionExpression) listener: handleRecoverableError(NamedFunctionExpression, fun, fun) - listener: endFunctionName(void, () + listener: endFunctionName(void, (, true) parseFormalParametersRequiredOpt(fun, MemberKind.Local) parseFormalParametersRest((, MemberKind.Local) listener: beginFormalParameters((, MemberKind.Local) diff --git a/pkg/front_end/parser_testcases/record/record_type_01.dart.expect b/pkg/front_end/parser_testcases/record/record_type_01.dart.expect index 91a015000d6..b5b40170f80 100644 --- a/pkg/front_end/parser_testcases/record/record_type_01.dart.expect +++ b/pkg/front_end/parser_testcases/record/record_type_01.dart.expect @@ -444,7 +444,7 @@ beginCompilationUnit(void) handleType(int, null) beginFunctionName(async) handleIdentifier(async, localFunctionDeclaration) - endFunctionName(int, () + endFunctionName(int, (, false) beginFormalParameters((, MemberKind.Local) beginMetadataStar(int) endMetadataStar(0) @@ -502,7 +502,7 @@ beginCompilationUnit(void) endRecordType((, null, 2, false) beginFunctionName(async) handleIdentifier(async, localFunctionDeclaration) - endFunctionName((, () + endFunctionName((, (, false) beginFormalParameters((, MemberKind.Local) beginMetadataStar(int) endMetadataStar(0) @@ -560,7 +560,7 @@ beginCompilationUnit(void) endRecordType((, null, 2, false) beginFunctionName(async) handleIdentifier(async, localFunctionDeclaration) - endFunctionName((, () + endFunctionName((, (, false) beginFormalParameters((, MemberKind.Local) beginMetadataStar(int) endMetadataStar(0) diff --git a/pkg/front_end/parser_testcases/record/record_type_01.dart.intertwined.expect b/pkg/front_end/parser_testcases/record/record_type_01.dart.intertwined.expect index fd46786aff6..459c1bce2bb 100644 --- a/pkg/front_end/parser_testcases/record/record_type_01.dart.intertwined.expect +++ b/pkg/front_end/parser_testcases/record/record_type_01.dart.intertwined.expect @@ -886,7 +886,7 @@ parseUnit(void) ensureIdentifier(int, localFunctionDeclaration) inPlainSync() listener: handleIdentifier(async, localFunctionDeclaration) - listener: endFunctionName(int, () + listener: endFunctionName(int, (, false) parseFormalParametersRequiredOpt(async, MemberKind.Local) parseFormalParametersRest((, MemberKind.Local) listener: beginFormalParameters((, MemberKind.Local) @@ -999,7 +999,7 @@ parseUnit(void) ensureIdentifier(), localFunctionDeclaration) inPlainSync() listener: handleIdentifier(async, localFunctionDeclaration) - listener: endFunctionName((, () + listener: endFunctionName((, (, false) parseFormalParametersRequiredOpt(async, MemberKind.Local) parseFormalParametersRest((, MemberKind.Local) listener: beginFormalParameters((, MemberKind.Local) @@ -1112,7 +1112,7 @@ parseUnit(void) ensureIdentifier(), localFunctionDeclaration) inPlainSync() listener: handleIdentifier(async, localFunctionDeclaration) - listener: endFunctionName((, () + listener: endFunctionName((, (, false) parseFormalParametersRequiredOpt(async, MemberKind.Local) parseFormalParametersRest((, MemberKind.Local) listener: beginFormalParameters((, MemberKind.Local) diff --git a/pkg/front_end/test/coverage_suite_expected.dart b/pkg/front_end/test/coverage_suite_expected.dart index da6075f1f13..6f0f885e200 100644 --- a/pkg/front_end/test/coverage_suite_expected.dart +++ b/pkg/front_end/test/coverage_suite_expected.dart @@ -175,7 +175,7 @@ const Map _expect = { ), // 100.0%. "package:front_end/src/base/local_scope.dart": ( - hitCount: 60, + hitCount: 59, missCount: 0, ), // 100.0%. @@ -655,7 +655,7 @@ const Map _expect = { ), // 100.0%. "package:front_end/src/kernel/body_builder.dart": ( - hitCount: 7247, + hitCount: 7243, missCount: 0, ), // 100.0%. @@ -1001,7 +1001,7 @@ const Map _expect = { ), // 100.0%. "package:front_end/src/source/type_parameter_scope_builder.dart": ( - hitCount: 1720, + hitCount: 1760, missCount: 0, ), // 100.0%. @@ -1036,7 +1036,7 @@ const Map _expect = { ), // 100.0%. "package:front_end/src/type_inference/inference_visitor.dart": ( - hitCount: 8291, + hitCount: 8312, missCount: 0, ), // 100.0%. diff --git a/pkg/front_end/test/parser_test_listener.dart b/pkg/front_end/test/parser_test_listener.dart index bc8e248ff2d..fbbb29473a4 100644 --- a/pkg/front_end/test/parser_test_listener.dart +++ b/pkg/front_end/test/parser_test_listener.dart @@ -1151,11 +1151,13 @@ class ParserTestListener implements Listener { } @override - void endFunctionName(Token beginToken, Token token) { + void endFunctionName( + Token beginToken, Token token, bool isFunctionExpression) { indent--; seen(beginToken); seen(token); - doPrint('endFunctionName(' '$beginToken, ' '$token)'); + doPrint( + 'endFunctionName(' '$beginToken, ' '$token, ' '$isFunctionExpression)'); } @override diff --git a/pkg/front_end/test/spell_checking_list_code.txt b/pkg/front_end/test/spell_checking_list_code.txt index 590c4b1d3ae..30957b092d0 100644 --- a/pkg/front_end/test/spell_checking_list_code.txt +++ b/pkg/front_end/test/spell_checking_list_code.txt @@ -1308,6 +1308,7 @@ pm pn pointed pointwise +pollute polluted polymorphism pool diff --git a/pkg/front_end/testcases/general/duplicate_local_function.dart b/pkg/front_end/testcases/general/duplicate_local_function.dart new file mode 100644 index 00000000000..d409e893153 --- /dev/null +++ b/pkg/front_end/testcases/general/duplicate_local_function.dart @@ -0,0 +1,8 @@ +// Copyright (c) 2025, 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. + +void test() { + void local() {} + void local() {} +} \ No newline at end of file diff --git a/pkg/front_end/testcases/general/duplicate_local_function.dart.strong.expect b/pkg/front_end/testcases/general/duplicate_local_function.dart.strong.expect new file mode 100644 index 00000000000..ded6f82c6e5 --- /dev/null +++ b/pkg/front_end/testcases/general/duplicate_local_function.dart.strong.expect @@ -0,0 +1,22 @@ +library; +// +// Problems in library: +// +// pkg/front_end/testcases/general/duplicate_local_function.dart:7:8: Error: 'local' is already declared in this scope. +// void local() {} +// ^^^^^ +// pkg/front_end/testcases/general/duplicate_local_function.dart:6:8: Context: Previous declaration of 'local'. +// void local() {} +// ^^^^^ +// +import self as self; + +static method test() → void { + function local() → void {} + { + invalid-expression "pkg/front_end/testcases/general/duplicate_local_function.dart:7:8: Error: 'local' is already declared in this scope. + void local() {} + ^^^^^"; + function local() → void {} + } +} diff --git a/pkg/front_end/testcases/general/duplicate_local_function.dart.strong.modular.expect b/pkg/front_end/testcases/general/duplicate_local_function.dart.strong.modular.expect new file mode 100644 index 00000000000..ded6f82c6e5 --- /dev/null +++ b/pkg/front_end/testcases/general/duplicate_local_function.dart.strong.modular.expect @@ -0,0 +1,22 @@ +library; +// +// Problems in library: +// +// pkg/front_end/testcases/general/duplicate_local_function.dart:7:8: Error: 'local' is already declared in this scope. +// void local() {} +// ^^^^^ +// pkg/front_end/testcases/general/duplicate_local_function.dart:6:8: Context: Previous declaration of 'local'. +// void local() {} +// ^^^^^ +// +import self as self; + +static method test() → void { + function local() → void {} + { + invalid-expression "pkg/front_end/testcases/general/duplicate_local_function.dart:7:8: Error: 'local' is already declared in this scope. + void local() {} + ^^^^^"; + function local() → void {} + } +} diff --git a/pkg/front_end/testcases/general/duplicate_local_function.dart.strong.outline.expect b/pkg/front_end/testcases/general/duplicate_local_function.dart.strong.outline.expect new file mode 100644 index 00000000000..643a05ad8a2 --- /dev/null +++ b/pkg/front_end/testcases/general/duplicate_local_function.dart.strong.outline.expect @@ -0,0 +1,5 @@ +library; +import self as self; + +static method test() → void + ; diff --git a/pkg/front_end/testcases/general/duplicate_local_function.dart.strong.transformed.expect b/pkg/front_end/testcases/general/duplicate_local_function.dart.strong.transformed.expect new file mode 100644 index 00000000000..ded6f82c6e5 --- /dev/null +++ b/pkg/front_end/testcases/general/duplicate_local_function.dart.strong.transformed.expect @@ -0,0 +1,22 @@ +library; +// +// Problems in library: +// +// pkg/front_end/testcases/general/duplicate_local_function.dart:7:8: Error: 'local' is already declared in this scope. +// void local() {} +// ^^^^^ +// pkg/front_end/testcases/general/duplicate_local_function.dart:6:8: Context: Previous declaration of 'local'. +// void local() {} +// ^^^^^ +// +import self as self; + +static method test() → void { + function local() → void {} + { + invalid-expression "pkg/front_end/testcases/general/duplicate_local_function.dart:7:8: Error: 'local' is already declared in this scope. + void local() {} + ^^^^^"; + function local() → void {} + } +} diff --git a/pkg/front_end/testcases/general/duplicate_local_function.dart.textual_outline.expect b/pkg/front_end/testcases/general/duplicate_local_function.dart.textual_outline.expect new file mode 100644 index 00000000000..7da700fd123 --- /dev/null +++ b/pkg/front_end/testcases/general/duplicate_local_function.dart.textual_outline.expect @@ -0,0 +1 @@ +void test() {} diff --git a/pkg/front_end/testcases/general/duplicate_local_function.dart.textual_outline_modelled.expect b/pkg/front_end/testcases/general/duplicate_local_function.dart.textual_outline_modelled.expect new file mode 100644 index 00000000000..7da700fd123 --- /dev/null +++ b/pkg/front_end/testcases/general/duplicate_local_function.dart.textual_outline_modelled.expect @@ -0,0 +1 @@ +void test() {} diff --git a/pkg/front_end/testcases/general/named_function_scope.dart.strong.expect b/pkg/front_end/testcases/general/named_function_scope.dart.strong.expect index 338b117fd23..b92fa5d6730 100644 --- a/pkg/front_end/testcases/general/named_function_scope.dart.strong.expect +++ b/pkg/front_end/testcases/general/named_function_scope.dart.strong.expect @@ -42,13 +42,6 @@ library; // var x = T() {}; // ^ // -// pkg/front_end/testcases/general/named_function_scope.dart:52:13: Error: 'T' is already declared in this scope. -// var x = T() {}; -// ^ -// pkg/front_end/testcases/general/named_function_scope.dart:52:15: Context: Previous declaration of 'T'. -// var x = T() {}; -// ^ -// // pkg/front_end/testcases/general/named_function_scope.dart:55:5: Error: Local variable 'T' can't be referenced before it is declared. // T t; // ^ @@ -155,12 +148,7 @@ static method test() → dynamic { } { invalid-type x = block { - { - invalid-expression "pkg/front_end/testcases/general/named_function_scope.dart:52:13: Error: 'T' is already declared in this scope. - var x = T() {}; - ^"; - function T() → Null {} - } + function T() → Null {} } =>invalid-expression "pkg/front_end/testcases/general/named_function_scope.dart:52:13: Error: A function expression can't have a name. var x = T() {}; ^"; diff --git a/pkg/front_end/testcases/general/named_function_scope.dart.strong.modular.expect b/pkg/front_end/testcases/general/named_function_scope.dart.strong.modular.expect index 338b117fd23..b92fa5d6730 100644 --- a/pkg/front_end/testcases/general/named_function_scope.dart.strong.modular.expect +++ b/pkg/front_end/testcases/general/named_function_scope.dart.strong.modular.expect @@ -42,13 +42,6 @@ library; // var x = T() {}; // ^ // -// pkg/front_end/testcases/general/named_function_scope.dart:52:13: Error: 'T' is already declared in this scope. -// var x = T() {}; -// ^ -// pkg/front_end/testcases/general/named_function_scope.dart:52:15: Context: Previous declaration of 'T'. -// var x = T() {}; -// ^ -// // pkg/front_end/testcases/general/named_function_scope.dart:55:5: Error: Local variable 'T' can't be referenced before it is declared. // T t; // ^ @@ -155,12 +148,7 @@ static method test() → dynamic { } { invalid-type x = block { - { - invalid-expression "pkg/front_end/testcases/general/named_function_scope.dart:52:13: Error: 'T' is already declared in this scope. - var x = T() {}; - ^"; - function T() → Null {} - } + function T() → Null {} } =>invalid-expression "pkg/front_end/testcases/general/named_function_scope.dart:52:13: Error: A function expression can't have a name. var x = T() {}; ^"; diff --git a/pkg/front_end/testcases/general/named_function_scope.dart.strong.transformed.expect b/pkg/front_end/testcases/general/named_function_scope.dart.strong.transformed.expect index 338b117fd23..b92fa5d6730 100644 --- a/pkg/front_end/testcases/general/named_function_scope.dart.strong.transformed.expect +++ b/pkg/front_end/testcases/general/named_function_scope.dart.strong.transformed.expect @@ -42,13 +42,6 @@ library; // var x = T() {}; // ^ // -// pkg/front_end/testcases/general/named_function_scope.dart:52:13: Error: 'T' is already declared in this scope. -// var x = T() {}; -// ^ -// pkg/front_end/testcases/general/named_function_scope.dart:52:15: Context: Previous declaration of 'T'. -// var x = T() {}; -// ^ -// // pkg/front_end/testcases/general/named_function_scope.dart:55:5: Error: Local variable 'T' can't be referenced before it is declared. // T t; // ^ @@ -155,12 +148,7 @@ static method test() → dynamic { } { invalid-type x = block { - { - invalid-expression "pkg/front_end/testcases/general/named_function_scope.dart:52:13: Error: 'T' is already declared in this scope. - var x = T() {}; - ^"; - function T() → Null {} - } + function T() → Null {} } =>invalid-expression "pkg/front_end/testcases/general/named_function_scope.dart:52:13: Error: A function expression can't have a name. var x = T() {}; ^"; diff --git a/pkg/front_end/testcases/general/recursive_named_function_expression.dart b/pkg/front_end/testcases/general/recursive_named_function_expression.dart new file mode 100644 index 00000000000..7f52f75d0af --- /dev/null +++ b/pkg/front_end/testcases/general/recursive_named_function_expression.dart @@ -0,0 +1,9 @@ +// Copyright (c) 2025, 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. + +void test() { + var f = void foo() { + foo(); + }; +} \ No newline at end of file diff --git a/pkg/front_end/testcases/general/recursive_named_function_expression.dart.strong.expect b/pkg/front_end/testcases/general/recursive_named_function_expression.dart.strong.expect new file mode 100644 index 00000000000..0ab42b1efe3 --- /dev/null +++ b/pkg/front_end/testcases/general/recursive_named_function_expression.dart.strong.expect @@ -0,0 +1,19 @@ +library; +// +// Problems in library: +// +// pkg/front_end/testcases/general/recursive_named_function_expression.dart:6:16: Error: A function expression can't have a name. +// var f = void foo() { +// ^^^ +// +import self as self; + +static method test() → void { + invalid-type f = block { + function foo() → void { + foo(){() → void}; + } + } =>invalid-expression "pkg/front_end/testcases/general/recursive_named_function_expression.dart:6:11: Error: A function expression can't have a name. + var f = void foo() { + ^"; +} diff --git a/pkg/front_end/testcases/general/recursive_named_function_expression.dart.strong.modular.expect b/pkg/front_end/testcases/general/recursive_named_function_expression.dart.strong.modular.expect new file mode 100644 index 00000000000..0ab42b1efe3 --- /dev/null +++ b/pkg/front_end/testcases/general/recursive_named_function_expression.dart.strong.modular.expect @@ -0,0 +1,19 @@ +library; +// +// Problems in library: +// +// pkg/front_end/testcases/general/recursive_named_function_expression.dart:6:16: Error: A function expression can't have a name. +// var f = void foo() { +// ^^^ +// +import self as self; + +static method test() → void { + invalid-type f = block { + function foo() → void { + foo(){() → void}; + } + } =>invalid-expression "pkg/front_end/testcases/general/recursive_named_function_expression.dart:6:11: Error: A function expression can't have a name. + var f = void foo() { + ^"; +} diff --git a/pkg/front_end/testcases/general/recursive_named_function_expression.dart.strong.outline.expect b/pkg/front_end/testcases/general/recursive_named_function_expression.dart.strong.outline.expect new file mode 100644 index 00000000000..643a05ad8a2 --- /dev/null +++ b/pkg/front_end/testcases/general/recursive_named_function_expression.dart.strong.outline.expect @@ -0,0 +1,5 @@ +library; +import self as self; + +static method test() → void + ; diff --git a/pkg/front_end/testcases/general/recursive_named_function_expression.dart.strong.transformed.expect b/pkg/front_end/testcases/general/recursive_named_function_expression.dart.strong.transformed.expect new file mode 100644 index 00000000000..0ab42b1efe3 --- /dev/null +++ b/pkg/front_end/testcases/general/recursive_named_function_expression.dart.strong.transformed.expect @@ -0,0 +1,19 @@ +library; +// +// Problems in library: +// +// pkg/front_end/testcases/general/recursive_named_function_expression.dart:6:16: Error: A function expression can't have a name. +// var f = void foo() { +// ^^^ +// +import self as self; + +static method test() → void { + invalid-type f = block { + function foo() → void { + foo(){() → void}; + } + } =>invalid-expression "pkg/front_end/testcases/general/recursive_named_function_expression.dart:6:11: Error: A function expression can't have a name. + var f = void foo() { + ^"; +} diff --git a/pkg/front_end/testcases/general/recursive_named_function_expression.dart.textual_outline.expect b/pkg/front_end/testcases/general/recursive_named_function_expression.dart.textual_outline.expect new file mode 100644 index 00000000000..7da700fd123 --- /dev/null +++ b/pkg/front_end/testcases/general/recursive_named_function_expression.dart.textual_outline.expect @@ -0,0 +1 @@ +void test() {} diff --git a/pkg/front_end/testcases/general/recursive_named_function_expression.dart.textual_outline_modelled.expect b/pkg/front_end/testcases/general/recursive_named_function_expression.dart.textual_outline_modelled.expect new file mode 100644 index 00000000000..7da700fd123 --- /dev/null +++ b/pkg/front_end/testcases/general/recursive_named_function_expression.dart.textual_outline_modelled.expect @@ -0,0 +1 @@ +void test() {}