diff --git a/pkg/analyzer/lib/src/dart/resolver/resolution_visitor.dart b/pkg/analyzer/lib/src/dart/resolver/resolution_visitor.dart index c4ab641eb0d..4f6054b7d84 100644 --- a/pkg/analyzer/lib/src/dart/resolver/resolution_visitor.dart +++ b/pkg/analyzer/lib/src/dart/resolver/resolution_visitor.dart @@ -696,6 +696,16 @@ class ResolutionVisitor extends RecursiveAstVisitor { }); } + @override + void visitForEachPartsWithDeclaration( + covariant ForEachPartsWithDeclarationImpl node, + ) { + node.iterable.accept(this); + node.loopVariable.accept(this); + var fragment = node.loopVariable.declaredFragment!; + _define(fragment.element); + } + @override void visitForEachPartsWithPattern( covariant ForEachPartsWithPatternImpl node, @@ -717,9 +727,9 @@ class ResolutionVisitor extends RecursiveAstVisitor { } @override - void visitForPartsWithDeclarations(ForPartsWithDeclarations node) { + void visitForStatement(covariant ForStatementImpl node) { _withNameScope(() { - super.visitForPartsWithDeclarations(node); + super.visitForStatement(node); }); } diff --git a/pkg/analyzer/lib/src/generated/error_verifier.dart b/pkg/analyzer/lib/src/generated/error_verifier.dart index 21edb844cb5..9425c27842f 100644 --- a/pkg/analyzer/lib/src/generated/error_verifier.dart +++ b/pkg/analyzer/lib/src/generated/error_verifier.dart @@ -420,7 +420,7 @@ class ErrorVerifier extends RecursiveAstVisitor @override void visitBlock(covariant BlockImpl node) { - _withHiddenElements(node.statements, () { + _withHiddenElementsForStatements(node.statements, () { _duplicateDefinitionVerifier.checkStatements(node.statements); super.visitBlock(node); }); @@ -984,6 +984,13 @@ class ErrorVerifier extends RecursiveAstVisitor super.visitForEachPartsWithIdentifier(node); } + @override + void visitForElement(covariant ForElementImpl node) { + _withHiddenElementsForForParts(node.forLoopParts, () { + super.visitForElement(node); + }); + } + @override void visitFormalParameterList(covariant FormalParameterListImpl node) { _duplicateDefinitionVerifier.checkParameters(node); @@ -1000,6 +1007,13 @@ class ErrorVerifier extends RecursiveAstVisitor super.visitForPartsWithDeclarations(node); } + @override + void visitForStatement(covariant ForStatementImpl node) { + _withHiddenElementsForForParts(node.forLoopParts, () { + super.visitForStatement(node); + }); + } + @override void visitFunctionDeclaration(covariant FunctionDeclarationImpl node) { var fragment = node.declaredFragment!; @@ -1724,7 +1738,7 @@ class ErrorVerifier extends RecursiveAstVisitor @override void visitSwitchCase(covariant SwitchCaseImpl node) { - _withHiddenElements(node.statements, () { + _withHiddenElementsForStatements(node.statements, () { _duplicateDefinitionVerifier.checkStatements(node.statements); super.visitSwitchCase(node); }); @@ -1732,7 +1746,7 @@ class ErrorVerifier extends RecursiveAstVisitor @override void visitSwitchDefault(covariant SwitchDefaultImpl node) { - _withHiddenElements(node.statements, () { + _withHiddenElementsForStatements(node.statements, () { _duplicateDefinitionVerifier.checkStatements(node.statements); super.visitSwitchDefault(node); }); @@ -1746,7 +1760,7 @@ class ErrorVerifier extends RecursiveAstVisitor @override void visitSwitchPatternCase(covariant SwitchPatternCaseImpl node) { - _withHiddenElements(node.statements, () { + _withHiddenElementsForStatements(node.statements, () { _duplicateDefinitionVerifier.checkStatements(node.statements); super.visitSwitchPatternCase(node); }); @@ -6908,28 +6922,59 @@ class ErrorVerifier extends RecursiveAstVisitor } } - void _withHiddenElements(List statements, void Function() f) { - _hiddenElements = HiddenElements(_hiddenElements, statements); + void _withHiddenElements(HiddenElements hiddenElements, void Function() f) { + var outerElements = _hiddenElements; + _hiddenElements = hiddenElements; try { f(); } finally { - _hiddenElements = _hiddenElements!.outerElements; + _hiddenElements = outerElements; } } + void _withHiddenElementsForForParts( + ForLoopParts forLoopParts, + void Function() f, + ) { + if (forLoopParts is ForPartsWithDeclarations) { + _withHiddenElements( + HiddenElements.forElements( + _hiddenElements, + forLoopParts.variables.variables.map( + (variable) => variable.declaredFragment!.element, + ), + ), + f, + ); + } else { + f(); + } + } + + void _withHiddenElementsForStatements( + List statements, + void Function() f, + ) { + _withHiddenElements( + HiddenElements.forElements( + _hiddenElements, + BlockScope.elementsInStatements(statements), + ), + f, + ); + } + void _withHiddenElementsGuardedPattern( GuardedPatternImpl guardedPattern, void Function() f, ) { - _hiddenElements = HiddenElements.forGuardedPattern( - _hiddenElements, - guardedPattern, + _withHiddenElements( + HiddenElements.forElements( + _hiddenElements, + guardedPattern.variables.values, + ), + f, ); - try { - f(); - } finally { - _hiddenElements = _hiddenElements!.outerElements; - } } /// Executes [f] with [state] as the current [ThisContext]. @@ -6973,20 +7018,9 @@ class HiddenElements { final Set _elements = {}; /// Initialize a newly created set of hidden elements to include all of the - /// elements defined in the set of [outerElements] and all of the elements - /// declared in the given [statements]. - HiddenElements(this.outerElements, List statements) { - _initializeElements(statements); - } - - /// Initialize a newly created set of hidden elements to include all of the - /// elements defined in the set of [outerElements] and all of the elements - /// declared in the given [guardedPattern]. - HiddenElements.forGuardedPattern( - this.outerElements, - GuardedPatternImpl guardedPattern, - ) { - _elements.addAll(guardedPattern.variables.values); + /// elements defined in [outerElements] and the given [elements]. + HiddenElements.forElements(this.outerElements, Iterable elements) { + _elements.addAll(elements); } /// Return `true` if this set of elements contains the given [element]. @@ -7004,12 +7038,6 @@ class HiddenElements { void declare(Element element) { _elements.remove(element); } - - /// Initialize the list of elements that are not yet declared to be all of the - /// elements declared somewhere in the given [statements]. - void _initializeElements(List statements) { - _elements.addAll(BlockScope.elementsInStatements(statements)); - } } /// Information to pass from from the defining unit to augmentations. diff --git a/pkg/analyzer/lib/src/generated/resolver.dart b/pkg/analyzer/lib/src/generated/resolver.dart index fc97dbea5a8..87bc44bc610 100644 --- a/pkg/analyzer/lib/src/generated/resolver.dart +++ b/pkg/analyzer/lib/src/generated/resolver.dart @@ -5190,6 +5190,7 @@ class ScopeResolverVisitor extends UnifyingAstVisitor { try { nameScope = LocalScope(nameScope); node.nameScope = nameScope; + _predeclareForPartsVariables(node.forLoopParts); node.forLoopParts.accept(this); node.body.accept(this); } finally { @@ -5225,6 +5226,7 @@ class ScopeResolverVisitor extends UnifyingAstVisitor { nameScope = LocalScope(nameScope); _implicitLabelScope = _implicitLabelScope.nest(node); node.nameScope = nameScope; + _predeclareForPartsVariables(node.forLoopParts); node.forLoopParts.accept(this); _visitStatementInScope(node.body); } finally { @@ -5771,6 +5773,20 @@ class ScopeResolverVisitor extends UnifyingAstVisitor { } } + /// Predeclare `for`-parts variables so lexical lookup during traversal of + /// `forLoopParts` (initializer, condition, updaters) binds to loop-local + /// elements. + /// + /// This is only about binding. Reads that occur before the declaration point + /// are still reported later by error verification. + void _predeclareForPartsVariables(ForLoopParts forLoopParts) { + if (forLoopParts is ForPartsWithDeclarations) { + for (var variable in forLoopParts.variables.variables) { + _define(variable.declaredFragment!.element); + } + } + } + /// Visits a documentation comment with a [DocumentationCommentScope] that encloses the /// current [nameScope]. void _visitDocumentationComment(CommentImpl? node) { diff --git a/pkg/analyzer/test/src/dart/resolution/for_element_test.dart b/pkg/analyzer/test/src/dart/resolution/for_element_test.dart index 2d5359e111d..dd6ff9e9071 100644 --- a/pkg/analyzer/test/src/dart/resolution/for_element_test.dart +++ b/pkg/analyzer/test/src/dart/resolution/for_element_test.dart @@ -1034,7 +1034,6 @@ SimpleIdentifier '''); } - @failingTest test_scope_variables_initializer_uses_outer_sameName() async { await assertErrorsInCode( r''' diff --git a/pkg/analyzer/test/src/dart/resolution/for_statement_test.dart b/pkg/analyzer/test/src/dart/resolution/for_statement_test.dart index 8ff6f6e2035..7e4d6f34b1c 100644 --- a/pkg/analyzer/test/src/dart/resolution/for_statement_test.dart +++ b/pkg/analyzer/test/src/dart/resolution/for_statement_test.dart @@ -2415,7 +2415,6 @@ ForStatement '''); } - @failingTest test_scope_variables_initializer_uses_outer_sameName() async { await assertErrorsInCode( r''' diff --git a/pkg/analyzer/test/src/diagnostics/referenced_before_declaration_test.dart b/pkg/analyzer/test/src/diagnostics/referenced_before_declaration_test.dart index aa1eee2d1b7..6be7d7afcc7 100644 --- a/pkg/analyzer/test/src/diagnostics/referenced_before_declaration_test.dart +++ b/pkg/analyzer/test/src/diagnostics/referenced_before_declaration_test.dart @@ -67,6 +67,44 @@ main() { '''); } + test_forElement_forPartsWithDeclarations_initializer() async { + await assertErrorsInCode( + r''' +void f() { + [for (var x = x;;) x]; +} +''', + [ + error( + diag.referencedBeforeDeclaration, + 27, + 1, + contextMessages: [message(testFile, 23, 1)], + ), + ], + ); + } + + test_forStatement_forPartsWithDeclarations_initializer() async { + await assertErrorsInCode( + r''' +void f() { + for (var x = x;;) { + x; + } +} +''', + [ + error( + diag.referencedBeforeDeclaration, + 26, + 1, + contextMessages: [message(testFile, 22, 1)], + ), + ], + ); + } + test_hideInBlock_comment() async { await assertErrorsInCode( r''' diff --git a/pkg/analyzer/test/src/diagnostics/undefined_identifier_test.dart b/pkg/analyzer/test/src/diagnostics/undefined_identifier_test.dart index c7f2040e1a9..af3f5dcb746 100644 --- a/pkg/analyzer/test/src/diagnostics/undefined_identifier_test.dart +++ b/pkg/analyzer/test/src/diagnostics/undefined_identifier_test.dart @@ -278,19 +278,6 @@ f() { ); } - test_forStatement_ForPartsWithDeclarations_initializer() async { - await assertErrorsInCode( - ''' -void f() { - for (var x = x;;) { - x; - } -} -''', - [error(diag.undefinedIdentifier, 26, 1)], - ); - } - test_forStatement_inBody() async { await assertNoErrorsInCode(''' f() { diff --git a/tests/language/loop/for_test.dart b/tests/language/loop/for_test.dart index 4f555b212ef..7eeee41ff0a 100644 --- a/tests/language/loop/for_test.dart +++ b/tests/language/loop/for_test.dart @@ -88,5 +88,6 @@ void testShadowLocal() { List x = [1, 2, 3]; for (var x = x; ;) break; // ^ + // [analyzer] COMPILE_TIME_ERROR.REFERENCED_BEFORE_DECLARATION // [cfe] Local variable 'x' can't be referenced before it is declared. }