Version 2.15.0-1.0.dev

Merge commit 'b266aeebc3ffa25d9a7774513cb99eb206e39766' into 'dev'
diff --git a/pkg/analyzer/lib/src/dart/analysis/library_analyzer.dart b/pkg/analyzer/lib/src/dart/analysis/library_analyzer.dart
index c8caff3..a568624 100644
--- a/pkg/analyzer/lib/src/dart/analysis/library_analyzer.dart
+++ b/pkg/analyzer/lib/src/dart/analysis/library_analyzer.dart
@@ -682,7 +682,7 @@
       ),
     );
 
-    unit.accept(VariableResolverVisitor(
+    unit.accept(ScopeResolverVisitor(
         _libraryElement, source, _typeProvider, errorListener,
         nameScope: _libraryElement.scope));
 
diff --git a/pkg/analyzer/lib/src/dart/micro/library_analyzer.dart b/pkg/analyzer/lib/src/dart/micro/library_analyzer.dart
index 930340a..a723a79 100644
--- a/pkg/analyzer/lib/src/dart/micro/library_analyzer.dart
+++ b/pkg/analyzer/lib/src/dart/micro/library_analyzer.dart
@@ -693,7 +693,7 @@
       ),
     );
 
-    unit.accept(VariableResolverVisitor(
+    unit.accept(ScopeResolverVisitor(
         _libraryElement, source, _typeProvider, errorListener,
         nameScope: _libraryElement.scope));
 
diff --git a/pkg/analyzer/lib/src/dart/resolver/lexical_lookup.dart b/pkg/analyzer/lib/src/dart/resolver/lexical_lookup.dart
index 542fd5e..f0735d6 100644
--- a/pkg/analyzer/lib/src/dart/resolver/lexical_lookup.dart
+++ b/pkg/analyzer/lib/src/dart/resolver/lexical_lookup.dart
@@ -2,94 +2,55 @@
 // 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/dart/ast/ast.dart';
 import 'package:analyzer/dart/element/element.dart';
+import 'package:analyzer/dart/element/scope.dart';
 import 'package:analyzer/src/dart/element/extensions.dart';
-import 'package:analyzer/src/generated/resolver.dart';
 
+/// Class containing static methods for performing lexical resolution of
+/// identifiers.
 class LexicalLookup {
-  final ResolverVisitor _resolver;
+  /// Do not construct
+  LexicalLookup._() {
+    assert(false, 'Do not construct instances of LexicalLookup');
+  }
 
-  LexicalLookup(this._resolver);
+  /// Interprets the result of a scope lookup, assuming we are trying to look up
+  /// a getter.  If a matching element is found, a [LexicalLookupResult] is
+  /// returned.  Otherwise `null` is returned.
+  static LexicalLookupResult? resolveGetter(ScopeLookupResult scopeResult) {
+    var scopeGetter = scopeResult.getter;
+    var scopeSetter = scopeResult.setter;
+    if (scopeGetter != null || scopeSetter != null) {
+      if (scopeGetter != null) {
+        return LexicalLookupResult(requested: scopeGetter);
+      }
+      if (scopeSetter != null && !scopeSetter.isInstanceMember) {
+        return LexicalLookupResult(recovery: scopeSetter);
+      }
+    }
 
-  LexicalLookupResult perform({
-    required SimpleIdentifier node,
-    required bool setter,
-  }) {
-    var id = node.name;
-    var scopeResult = _resolver.nameScope.lookup(id);
+    return null;
+  }
+
+  /// Interprets the result of a scope lookup, assuming we are trying to look up
+  /// a setter.  If a matching element is found, a [LexicalLookupResult] is
+  /// returned.  Otherwise `null` is returned.
+  static LexicalLookupResult? resolveSetter(ScopeLookupResult scopeResult) {
     var scopeGetter = scopeResult.getter;
     var scopeSetter = scopeResult.setter;
     if (scopeGetter != null || scopeSetter != null) {
       if (scopeGetter is VariableElement) {
         return LexicalLookupResult(requested: scopeGetter);
       }
-      if (setter) {
-        if (scopeSetter != null) {
-          return LexicalLookupResult(
-            requested: _resolver.toLegacyElement(scopeSetter),
-          );
-        }
-        if (scopeGetter != null && !scopeGetter.isInstanceMember) {
-          return LexicalLookupResult(
-            recovery: _resolver.toLegacyElement(scopeGetter),
-          );
-        }
-      } else {
-        if (scopeGetter != null) {
-          return LexicalLookupResult(
-            requested: _resolver.toLegacyElement(scopeGetter),
-          );
-        }
-        if (scopeSetter != null && !scopeSetter.isInstanceMember) {
-          return LexicalLookupResult(
-            recovery: _resolver.toLegacyElement(scopeSetter),
-          );
-        }
+      if (scopeSetter != null) {
+        return LexicalLookupResult(requested: scopeSetter);
+      }
+      if (scopeGetter != null && !scopeGetter.isInstanceMember) {
+        return LexicalLookupResult(recovery: scopeGetter);
       }
     }
 
-    var thisType = _resolver.thisType;
-    if (thisType == null) {
-      var recoveryElement = setter ? scopeGetter : scopeGetter;
-      return LexicalLookupResult(
-        recovery: _resolver.toLegacyElement(recoveryElement),
-      );
-    }
-
-    var propertyResult = _resolver.typePropertyResolver.resolve(
-      receiver: null,
-      receiverType: thisType,
-      name: id,
-      propertyErrorEntity: node,
-      nameErrorEntity: node,
-    );
-
-    if (setter) {
-      var setterElement = propertyResult.setter;
-      if (setterElement != null) {
-        return LexicalLookupResult(
-          requested: _resolver.toLegacyElement(setterElement),
-        );
-      } else {
-        var recoveryElement = scopeGetter ?? propertyResult.getter;
-        return LexicalLookupResult(
-          recovery: _resolver.toLegacyElement(recoveryElement),
-        );
-      }
-    } else {
-      var getterElement = propertyResult.getter;
-      if (getterElement != null) {
-        return LexicalLookupResult(
-          requested: _resolver.toLegacyElement(getterElement),
-        );
-      } else {
-        var recoveryElement = scopeSetter ?? propertyResult.setter;
-        return LexicalLookupResult(
-          recovery: _resolver.toLegacyElement(recoveryElement),
-        );
-      }
-    }
+    return null;
   }
 }
 
diff --git a/pkg/analyzer/lib/src/dart/resolver/property_element_resolver.dart b/pkg/analyzer/lib/src/dart/resolver/property_element_resolver.dart
index 10c9f96..31a3d2c 100644
--- a/pkg/analyzer/lib/src/dart/resolver/property_element_resolver.dart
+++ b/pkg/analyzer/lib/src/dart/resolver/property_element_resolver.dart
@@ -12,6 +12,7 @@
 import 'package:analyzer/src/dart/element/type.dart';
 import 'package:analyzer/src/dart/element/type_system.dart';
 import 'package:analyzer/src/dart/resolver/extension_member_resolver.dart';
+import 'package:analyzer/src/dart/resolver/lexical_lookup.dart';
 import 'package:analyzer/src/dart/resolver/resolution_result.dart';
 import 'package:analyzer/src/error/assignment_verifier.dart';
 import 'package:analyzer/src/error/codes.dart';
@@ -198,8 +199,10 @@
     Element? readElementRequested;
     Element? readElementRecovery;
     if (hasRead) {
-      var readLookup = _resolver.lexicalLookup(node: node, setter: false);
-      readElementRequested = readLookup.requested;
+      var readLookup =
+          LexicalLookup.resolveGetter(_resolver.nameScope.lookup(node.name)) ??
+              _resolver.thisLookupGetter(node);
+      readElementRequested = _resolver.toLegacyElement(readLookup?.requested);
       if (readElementRequested is PropertyAccessorElement &&
           !readElementRequested.isStatic) {
         _resolver.flowAnalysis?.flow?.thisOrSuperPropertyGet(node, node.name,
@@ -211,9 +214,11 @@
     Element? writeElementRequested;
     Element? writeElementRecovery;
     if (hasWrite) {
-      var writeLookup = _resolver.lexicalLookup(node: node, setter: true);
-      writeElementRequested = writeLookup.requested;
-      writeElementRecovery = writeLookup.recovery;
+      var writeLookup =
+          LexicalLookup.resolveSetter(_resolver.nameScope.lookup(node.name)) ??
+              _resolver.thisLookupSetter(node);
+      writeElementRequested = _resolver.toLegacyElement(writeLookup?.requested);
+      writeElementRecovery = _resolver.toLegacyElement(writeLookup?.recovery);
 
       AssignmentVerifier(_resolver.definingLibrary, _errorReporter).verify(
         node: node,
diff --git a/pkg/analyzer/lib/src/dart/resolver/this_lookup.dart b/pkg/analyzer/lib/src/dart/resolver/this_lookup.dart
new file mode 100644
index 0000000..d274eb8
--- /dev/null
+++ b/pkg/analyzer/lib/src/dart/resolver/this_lookup.dart
@@ -0,0 +1,70 @@
+// Copyright (c) 2021, 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.
+
+import 'package:analyzer/dart/ast/ast.dart';
+import 'package:analyzer/src/dart/resolver/lexical_lookup.dart';
+import 'package:analyzer/src/generated/resolver.dart';
+
+/// Class containing static methods for resolving identifiers as implicit
+/// property gets/sets on the type of `this`.
+class ThisLookup {
+  /// Do not construct
+  ThisLookup._() {
+    assert(false, 'Do not construct instances of LexicalLookup');
+  }
+
+  /// Attempts to resolve an identifier with name [id] via implicit `this.`,
+  /// assuming we are trying to look up a getter.  If a matching element is
+  /// found, a [LexicalLookupResult] is returned.  Otherwise `null` is returned.
+  static LexicalLookupResult? lookupGetter(
+      ResolverVisitor resolver, SimpleIdentifier node) {
+    var id = node.name;
+    var thisType = resolver.thisType;
+    if (thisType == null) {
+      return null;
+    }
+
+    var propertyResult = resolver.typePropertyResolver.resolve(
+      receiver: null,
+      receiverType: thisType,
+      name: id,
+      propertyErrorEntity: node,
+      nameErrorEntity: node,
+    );
+
+    var getterElement = propertyResult.getter;
+    if (getterElement != null) {
+      return LexicalLookupResult(requested: getterElement);
+    } else {
+      return LexicalLookupResult(recovery: propertyResult.setter);
+    }
+  }
+
+  /// Attempts to resolve an identifier with name [id] via implicit `this.`,
+  /// assuming we are trying to look up a setter.  If a matching element is
+  /// found, a [LexicalLookupResult] is returned.  Otherwise `null` is returned.
+  static LexicalLookupResult? lookupSetter(
+      ResolverVisitor resolver, SimpleIdentifier node) {
+    var id = node.name;
+    var thisType = resolver.thisType;
+    if (thisType == null) {
+      return null;
+    }
+
+    var propertyResult = resolver.typePropertyResolver.resolve(
+      receiver: null,
+      receiverType: thisType,
+      name: id,
+      propertyErrorEntity: node,
+      nameErrorEntity: node,
+    );
+
+    var setterElement = propertyResult.setter;
+    if (setterElement != null) {
+      return LexicalLookupResult(requested: setterElement);
+    } else {
+      return LexicalLookupResult(recovery: propertyResult.getter);
+    }
+  }
+}
diff --git a/pkg/analyzer/lib/src/generated/element_resolver.dart b/pkg/analyzer/lib/src/generated/element_resolver.dart
index 2a378e11..e19c9e5 100644
--- a/pkg/analyzer/lib/src/generated/element_resolver.dart
+++ b/pkg/analyzer/lib/src/generated/element_resolver.dart
@@ -115,11 +115,6 @@
   TypeProviderImpl get _typeProvider => _resolver.typeProvider;
 
   @override
-  void visitBreakStatement(covariant BreakStatementImpl node) {
-    node.target = _lookupBreakOrContinueTarget(node, node.label, false);
-  }
-
-  @override
   void visitClassDeclaration(ClassDeclaration node) {
     _resolveAnnotations(node.metadata);
   }
@@ -276,11 +271,6 @@
   }
 
   @override
-  void visitContinueStatement(covariant ContinueStatementImpl node) {
-    node.target = _lookupBreakOrContinueTarget(node, node.label, true);
-  }
-
-  @override
   void visitDeclaredIdentifier(DeclaredIdentifier node) {
     _resolveAnnotations(node.metadata);
   }
@@ -537,46 +527,6 @@
     _resolveAnnotations(node.metadata);
   }
 
-  /// Return the target of a break or continue statement, and update the static
-  /// element of its label (if any). The [parentNode] is the AST node of the
-  /// break or continue statement. The [labelNode] is the label contained in
-  /// that statement (if any). The flag [isContinue] is `true` if the node being
-  /// visited is a continue statement.
-  AstNode? _lookupBreakOrContinueTarget(
-      AstNode parentNode, SimpleIdentifierImpl? labelNode, bool isContinue) {
-    if (labelNode == null) {
-      return _resolver.implicitLabelScope.getTarget(isContinue);
-    } else {
-      var labelScope = _resolver.labelScope;
-      if (labelScope == null) {
-        // There are no labels in scope, so by definition the label is
-        // undefined.
-        _errorReporter.reportErrorForNode(
-            CompileTimeErrorCode.LABEL_UNDEFINED, labelNode, [labelNode.name]);
-        return null;
-      }
-      var definingScope = labelScope.lookup(labelNode.name);
-      if (definingScope == null) {
-        // No definition of the given label name could be found in any
-        // enclosing scope.
-        _errorReporter.reportErrorForNode(
-            CompileTimeErrorCode.LABEL_UNDEFINED, labelNode, [labelNode.name]);
-        return null;
-      }
-      // The target has been found.
-      labelNode.staticElement = definingScope.element;
-      ExecutableElement? labelContainer =
-          definingScope.element.thisOrAncestorOfType();
-      if (!identical(labelContainer, _resolver.enclosingFunction)) {
-        _errorReporter.reportErrorForNode(
-            CompileTimeErrorCode.LABEL_IN_OUTER_SCOPE,
-            labelNode,
-            [labelNode.name]);
-      }
-      return definingScope.node;
-    }
-  }
-
   /// Given an [argumentList] and the [executableElement] that will be invoked
   /// using those argument, compute the list of parameters that correspond to
   /// the list of arguments. An error will be reported if any of the arguments
diff --git a/pkg/analyzer/lib/src/generated/resolver.dart b/pkg/analyzer/lib/src/generated/resolver.dart
index 158edb0..bd08b70 100644
--- a/pkg/analyzer/lib/src/generated/resolver.dart
+++ b/pkg/analyzer/lib/src/generated/resolver.dart
@@ -48,6 +48,7 @@
 import 'package:analyzer/src/dart/resolver/property_element_resolver.dart';
 import 'package:analyzer/src/dart/resolver/scope.dart';
 import 'package:analyzer/src/dart/resolver/simple_identifier_resolver.dart';
+import 'package:analyzer/src/dart/resolver/this_lookup.dart';
 import 'package:analyzer/src/dart/resolver/type_property_resolver.dart';
 import 'package:analyzer/src/dart/resolver/typed_literal_resolver.dart';
 import 'package:analyzer/src/dart/resolver/variable_declaration_resolver.dart';
@@ -160,6 +161,18 @@
 /// Instances of the class `ResolverVisitor` are used to resolve the nodes
 /// within a single compilation unit.
 class ResolverVisitor extends ScopedVisitor with ErrorDetectionHelpers {
+  /// The class containing the AST nodes being visited,
+  /// or `null` if we are not in the scope of a class.
+  ClassElement? enclosingClass;
+
+  /// The element representing the extension containing the AST nodes being
+  /// visited, or `null` if we are not in the scope of an extension.
+  ExtensionElement? enclosingExtension;
+
+  /// The element representing the function containing the current node, or
+  /// `null` if the current node is not contained in a function.
+  ExecutableElement? _enclosingFunction;
+
   /// The manager for the inheritance mappings.
   final InheritanceManager3 inheritance;
 
@@ -378,6 +391,12 @@
         FunctionReferenceResolver(this, _isNonNullableByDefault);
   }
 
+  /// Return the element representing the function containing the current node,
+  /// or `null` if the current node is not contained in a function.
+  ///
+  /// @return the element representing the function containing the current node
+  ExecutableElement? get enclosingFunction => _enclosingFunction;
+
   bool get isConstructorTearoffsEnabled =>
       _featureSet.isEnabled(Feature.constructor_tearoffs);
 
@@ -580,16 +599,6 @@
     return null;
   }
 
-  /// Return the result of lexical lookup for the [node], not `null`.
-  ///
-  /// Implements `16.35 Lexical Lookup` from the language specification.
-  LexicalLookupResult lexicalLookup({
-    required SimpleIdentifier node,
-    required bool setter,
-  }) {
-    return LexicalLookup(this).perform(node: node, setter: setter);
-  }
-
   /// If we reached a null-shorting termination, and the [node] has null
   /// shorting, make the type of the [node] nullable.
   void nullShortingTermination(ExpressionImpl node,
@@ -847,6 +856,18 @@
     }
   }
 
+  /// Returns the result of an implicit `this.` lookup for the identifier string
+  /// [id] in a getter context, or `null` if no match was found.
+  LexicalLookupResult? thisLookupGetter(SimpleIdentifier node) {
+    return ThisLookup.lookupGetter(this, node);
+  }
+
+  /// Returns the result of an implicit `this.` lookup for the identifier string
+  /// [id] in a setter context, or `null` if no match was found.
+  LexicalLookupResult? thisLookupSetter(SimpleIdentifier node) {
+    return ThisLookup.lookupSetter(this, node);
+  }
+
   /// If in a legacy library, return the legacy view on the [element].
   /// Otherwise, return the original element.
   T toLegacyElement<T extends Element?>(T element) {
@@ -1066,6 +1087,7 @@
     // Continue the class resolution.
     //
     var outerType = enclosingClass;
+    enclosingClass = node.declaredElement;
     try {
       super.visitClassDeclaration(node);
       node.accept(elementResolver);
@@ -1171,7 +1193,13 @@
     var returnType = node.declaredElement!.type.returnType;
     InferenceContext.setType(node.body, returnType);
 
-    super.visitConstructorDeclaration(node);
+    var outerFunction = _enclosingFunction;
+    try {
+      _enclosingFunction = node.declaredElement;
+      super.visitConstructorDeclaration(node);
+    } finally {
+      _enclosingFunction = outerFunction;
+    }
 
     if (node.factoryKeyword != null) {
       var bodyContext = BodyInferenceContext.of(node.body);
@@ -1336,11 +1364,14 @@
 
   @override
   void visitExtensionDeclaration(ExtensionDeclaration node) {
+    var outerExtension = enclosingExtension;
     try {
+      enclosingExtension = node.declaredElement!;
       super.visitExtensionDeclaration(node);
       node.accept(elementResolver);
       node.accept(typeAnalyzer);
     } finally {
+      enclosingExtension = outerExtension;
       _thisType = null;
     }
   }
@@ -1405,7 +1436,13 @@
     var functionType = node.declaredElement!.type;
     InferenceContext.setType(node.functionExpression, functionType);
 
-    super.visitFunctionDeclaration(node);
+    var outerFunction = _enclosingFunction;
+    try {
+      _enclosingFunction = node.declaredElement;
+      super.visitFunctionDeclaration(node);
+    } finally {
+      _enclosingFunction = outerFunction;
+    }
 
     // TODO(scheglov) encapsulate
     var bodyContext = BodyInferenceContext.of(
@@ -1434,8 +1471,6 @@
 
   @override
   void visitFunctionExpression(covariant FunctionExpressionImpl node) {
-    // Note: we have to update _enclosingFunction because we don't make use of
-    // super.visitFunctionExpression.
     var outerFunction = _enclosingFunction;
     _enclosingFunction = node.declaredElement;
 
@@ -1639,7 +1674,13 @@
     DartType returnType = node.declaredElement!.returnType;
     InferenceContext.setType(node.body, returnType);
 
-    super.visitMethodDeclaration(node);
+    var outerFunction = _enclosingFunction;
+    try {
+      _enclosingFunction = node.declaredElement;
+      super.visitMethodDeclaration(node);
+    } finally {
+      _enclosingFunction = outerFunction;
+    }
 
     // TODO(scheglov) encapsulate
     var bodyContext = BodyInferenceContext.of(node.body);
@@ -1700,6 +1741,7 @@
     //
     var outerType = enclosingClass;
     try {
+      enclosingClass = node.declaredElement!;
       super.visitMixinDeclaration(node);
       node.accept(elementResolver);
       node.accept(typeAnalyzer);
@@ -2327,18 +2369,6 @@
   /// `null` if no labels have been defined in the current context.
   LabelScope? labelScope;
 
-  /// The class containing the AST nodes being visited,
-  /// or `null` if we are not in the scope of a class.
-  ClassElement? enclosingClass;
-
-  /// The element representing the extension containing the AST nodes being
-  /// visited, or `null` if we are not in the scope of an extension.
-  ExtensionElement? enclosingExtension;
-
-  /// The element representing the function containing the current node, or
-  /// `null` if the current node is not contained in a function.
-  ExecutableElement? _enclosingFunction;
-
   /// Initialize a newly created visitor to resolve the nodes in a compilation
   /// unit.
   ///
@@ -2363,12 +2393,6 @@
         ),
         nameScope = nameScope ?? LibraryScope(definingLibrary);
 
-  /// Return the element representing the function containing the current node,
-  /// or `null` if the current node is not contained in a function.
-  ///
-  /// @return the element representing the function containing the current node
-  ExecutableElement? get enclosingFunction => _enclosingFunction;
-
   /// Return the implicit label scope in which the current node is being
   /// resolved.
   ImplicitLabelScope get implicitLabelScope => _implicitLabelScope;
@@ -2432,10 +2456,8 @@
   @override
   void visitClassDeclaration(ClassDeclaration node) {
     Scope outerScope = nameScope;
-    var outerClass = enclosingClass;
     try {
       ClassElement element = node.declaredElement!;
-      enclosingClass = node.declaredElement;
       node.metadata.accept(this);
 
       nameScope = TypeParameterScope(
@@ -2448,7 +2470,6 @@
       nameScope = ClassScope(nameScope, element);
       visitClassMembersInScope(node);
     } finally {
-      enclosingClass = outerClass;
       nameScope = outerScope;
     }
   }
@@ -2503,8 +2524,6 @@
 
   @override
   void visitConstructorDeclaration(ConstructorDeclaration node) {
-    var outerFunction = _enclosingFunction;
-    _enclosingFunction = node.declaredElement;
     Scope outerScope = nameScope;
     try {
       ConstructorElement element = node.declaredElement!;
@@ -2533,7 +2552,6 @@
       visitConstructorDeclarationInScope(node);
     } finally {
       nameScope = outerScope;
-      _enclosingFunction = outerFunction;
     }
   }
 
@@ -2567,16 +2585,13 @@
   @override
   void visitEnumDeclaration(EnumDeclaration node) {
     Scope outerScope = nameScope;
-    var outerClass = enclosingClass;
     try {
       ClassElement element = node.declaredElement!;
-      enclosingClass = node.declaredElement;
       node.metadata.accept(this);
 
       nameScope = ClassScope(nameScope, element);
       visitEnumMembersInScope(node);
     } finally {
-      enclosingClass = outerClass;
       nameScope = outerScope;
     }
   }
@@ -2595,10 +2610,8 @@
   @override
   void visitExtensionDeclaration(ExtensionDeclaration node) {
     Scope outerScope = nameScope;
-    var outerExtension = enclosingExtension;
     try {
       ExtensionElement element = node.declaredElement!;
-      enclosingExtension = element;
       node.metadata.accept(this);
 
       nameScope = TypeParameterScope(
@@ -2611,7 +2624,6 @@
       nameScope = ExtensionScope(nameScope, element);
       visitExtensionMembersInScope(node);
     } finally {
-      enclosingExtension = outerExtension;
       nameScope = outerScope;
     }
   }
@@ -2714,9 +2726,6 @@
 
   @override
   void visitFunctionDeclaration(FunctionDeclaration node) {
-    var outerFunction = _enclosingFunction;
-    _enclosingFunction = node.declaredElement;
-
     node.metadata.accept(this);
     Scope outerScope = nameScope;
     try {
@@ -2729,7 +2738,6 @@
       visitFunctionDeclarationInScope(node);
     } finally {
       nameScope = outerScope;
-      _enclosingFunction = outerFunction;
     }
   }
 
@@ -2751,8 +2759,6 @@
       return;
     }
 
-    var outerFunction = _enclosingFunction;
-    _enclosingFunction = node.declaredElement;
     Scope outerScope = nameScope;
     try {
       ExecutableElement element = node.declaredElement!;
@@ -2763,7 +2769,6 @@
       super.visitFunctionExpression(node);
     } finally {
       nameScope = outerScope;
-      _enclosingFunction = outerFunction;
     }
   }
 
@@ -2893,9 +2898,6 @@
 
   @override
   void visitMethodDeclaration(MethodDeclaration node) {
-    var outerFunction = _enclosingFunction;
-    _enclosingFunction = node.declaredElement;
-
     node.metadata.accept(this);
     Scope outerScope = nameScope;
     try {
@@ -2908,7 +2910,6 @@
       visitMethodDeclarationInScope(node);
     } finally {
       nameScope = outerScope;
-      _enclosingFunction = outerFunction;
     }
   }
 
@@ -2928,10 +2929,8 @@
   @override
   void visitMixinDeclaration(MixinDeclaration node) {
     Scope outerScope = nameScope;
-    var outerClass = enclosingClass;
     try {
       ClassElement element = node.declaredElement!;
-      enclosingClass = element;
       node.metadata.accept(this);
 
       nameScope = TypeParameterScope(nameScope, element.typeParameters);
@@ -2942,7 +2941,6 @@
       visitMixinMembersInScope(node);
     } finally {
       nameScope = outerScope;
-      enclosingClass = outerClass;
     }
   }
 
@@ -3094,12 +3092,20 @@
   }
 }
 
-/// Instances of the class `VariableResolverVisitor` are used to resolve
-/// [SimpleIdentifier]s to local variables and formal parameters.
-class VariableResolverVisitor extends ScopedVisitor {
+/// Instances of the class `ScopeResolverVisitor` are used to resolve
+/// [SimpleIdentifier]s to declarations using scoping rules.
+///
+/// TODO(paulberry): migrate the responsibility for all scope resolution into
+/// this visitor.
+class ScopeResolverVisitor extends ScopedVisitor {
   /// The container with information about local variables.
   final LocalVariableInfo _localVariableInfo = LocalVariableInfo();
 
+  /// If the current function is contained within a closure (a local function or
+  /// function expression inside another executable declaration), the element
+  /// representing the closure; otherwise `null`.
+  ExecutableElement? _enclosingClosure;
+
   /// Initialize a newly created visitor to resolve the nodes in an AST node.
   ///
   /// [definingLibrary] is the element for the library containing the node being
@@ -3113,7 +3119,7 @@
   /// [nameScope] is the scope used to resolve identifiers in the node that will
   /// first be visited.  If `null` or unspecified, a new [LibraryScope] will be
   /// created based on [definingLibrary] and [typeProvider].
-  VariableResolverVisitor(LibraryElementImpl definingLibrary, Source source,
+  ScopeResolverVisitor(LibraryElementImpl definingLibrary, Source source,
       TypeProvider typeProvider, AnalysisErrorListener errorListener,
       {Scope? nameScope})
       : super(definingLibrary, source, typeProvider as TypeProviderImpl,
@@ -3121,26 +3127,50 @@
             nameScope: nameScope);
 
   @override
+  void visitBreakStatement(covariant BreakStatementImpl node) {
+    node.target = _lookupBreakOrContinueTarget(node, node.label, false);
+  }
+
+  @override
   void visitConstructorDeclaration(ConstructorDeclaration node) {
     (node.body as FunctionBodyImpl).localVariableInfo = _localVariableInfo;
     super.visitConstructorDeclaration(node);
   }
 
   @override
+  void visitContinueStatement(covariant ContinueStatementImpl node) {
+    node.target = _lookupBreakOrContinueTarget(node, node.label, true);
+  }
+
+  @override
   void visitExportDirective(ExportDirective node) {}
 
   @override
   void visitFunctionDeclaration(FunctionDeclaration node) {
     (node.functionExpression.body as FunctionBodyImpl).localVariableInfo =
         _localVariableInfo;
-    super.visitFunctionDeclaration(node);
+    var outerClosure = _enclosingClosure;
+    try {
+      _enclosingClosure = node.parent is FunctionDeclarationStatement
+          ? node.declaredElement
+          : null;
+      super.visitFunctionDeclaration(node);
+    } finally {
+      _enclosingClosure = outerClosure;
+    }
   }
 
   @override
   void visitFunctionExpression(FunctionExpression node) {
     if (node.parent is! FunctionDeclaration) {
       (node.body as FunctionBodyImpl).localVariableInfo = _localVariableInfo;
-      super.visitFunctionExpression(node);
+      var outerClosure = _enclosingClosure;
+      try {
+        _enclosingClosure = node.declaredElement;
+        super.visitFunctionExpression(node);
+      } finally {
+        _enclosingClosure = outerClosure;
+      }
     } else {
       super.visitFunctionExpression(node);
     }
@@ -3200,7 +3230,8 @@
       node.staticElement = element;
       if (node.inSetterContext()) {
         _localVariableInfo.potentiallyMutatedInScope.add(element);
-        if (element.enclosingElement != _enclosingFunction) {
+        if (_enclosingClosure != null &&
+            element.enclosingElement != _enclosingClosure) {
           _localVariableInfo.potentiallyMutatedInClosure.add(element);
         }
       }
@@ -3209,6 +3240,47 @@
 
   @override
   void visitTypeName(TypeName node) {}
+
+  /// Return the target of a break or continue statement, and update the static
+  /// element of its label (if any). The [parentNode] is the AST node of the
+  /// break or continue statement. The [labelNode] is the label contained in
+  /// that statement (if any). The flag [isContinue] is `true` if the node being
+  /// visited is a continue statement.
+  AstNode? _lookupBreakOrContinueTarget(
+      AstNode parentNode, SimpleIdentifierImpl? labelNode, bool isContinue) {
+    if (labelNode == null) {
+      return implicitLabelScope.getTarget(isContinue);
+    } else {
+      var labelScope = this.labelScope;
+      if (labelScope == null) {
+        // There are no labels in scope, so by definition the label is
+        // undefined.
+        errorReporter.reportErrorForNode(
+            CompileTimeErrorCode.LABEL_UNDEFINED, labelNode, [labelNode.name]);
+        return null;
+      }
+      var definingScope = labelScope.lookup(labelNode.name);
+      if (definingScope == null) {
+        // No definition of the given label name could be found in any
+        // enclosing scope.
+        errorReporter.reportErrorForNode(
+            CompileTimeErrorCode.LABEL_UNDEFINED, labelNode, [labelNode.name]);
+        return null;
+      }
+      // The target has been found.
+      labelNode.staticElement = definingScope.element;
+      ExecutableElement? labelContainer =
+          definingScope.element.thisOrAncestorOfType();
+      if (_enclosingClosure != null &&
+          !identical(labelContainer, _enclosingClosure)) {
+        errorReporter.reportErrorForNode(
+            CompileTimeErrorCode.LABEL_IN_OUTER_SCOPE,
+            labelNode,
+            [labelNode.name]);
+      }
+      return definingScope.node;
+    }
+  }
 }
 
 /// Tracker for whether a `switch` statement has `default` or is on an
diff --git a/pkg/analyzer/lib/src/summary2/ast_resolver.dart b/pkg/analyzer/lib/src/summary2/ast_resolver.dart
index 0c63950..df4c79f 100644
--- a/pkg/analyzer/lib/src/summary2/ast_resolver.dart
+++ b/pkg/analyzer/lib/src/summary2/ast_resolver.dart
@@ -31,7 +31,7 @@
     nameScope: _nameScope,
     errorListener: _errorListener,
   );
-  late final _variableResolverVisitor = VariableResolverVisitor(
+  late final _scopeResolverVisitor = ScopeResolverVisitor(
     _unitElement.library,
     _unitElement.source,
     _unitElement.library.typeProvider,
@@ -58,7 +58,7 @@
   void resolveAnnotation(AnnotationImpl node) {
     _resolverVisitor.shouldCloneAnnotations = false;
     node.accept(_resolutionVisitor);
-    node.accept(_variableResolverVisitor);
+    node.accept(_scopeResolverVisitor);
     _prepareEnclosingDeclarations();
     _flowAnalysis.topLevelDeclaration_enter(node, null);
     node.accept(_resolverVisitor);
@@ -75,7 +75,7 @@
     }
 
     visit(_resolutionVisitor);
-    visit(_variableResolverVisitor);
+    visit(_scopeResolverVisitor);
 
     _prepareEnclosingDeclarations();
     _flowAnalysis.topLevelDeclaration_enter(node, node.parameters,
@@ -94,7 +94,7 @@
       if (contextType != null) {
         InferenceContext.setType(node, contextType);
       }
-      node.accept(_variableResolverVisitor);
+      node.accept(_scopeResolverVisitor);
     }
     _prepareEnclosingDeclarations();
     _flowAnalysis.topLevelDeclaration_enter(node.parent!, null);
diff --git a/sdk/lib/core/bool.dart b/sdk/lib/core/bool.dart
index 5f4d6d1..491ce16 100644
--- a/sdk/lib/core/bool.dart
+++ b/sdk/lib/core/bool.dart
@@ -41,6 +41,11 @@
   /// must be consistent across all calls to [String.fromEnvironment],
   /// [int.fromEnvironment], `bool.fromEnvironment` and [bool.hasEnvironment]
   /// in a single program.
+  ///
+  /// This constructor is only guaranteed to work when invoked as `const`.
+  /// It may work as a non-constant invocation on some platforms which
+  /// have access to compiler options at run-time, but most ahead-of-time
+  /// compiled platforms will not have this information.
   // The .fromEnvironment() constructors are special in that we do not want
   // users to call them using "new". We prohibit that by giving them bodies
   // that throw, even though const constructors are not allowed to have bodies.
@@ -76,6 +81,11 @@
   /// must be consistent across all calls to [String.fromEnvironment],
   /// [int.fromEnvironment], [bool.fromEnvironment] and `bool.hasEnvironment`
   /// in a single program.
+  ///
+  /// This constructor is only guaranteed to work when invoked as `const`.
+  /// It may work as a non-constant invocation on some platforms which
+  /// have access to compiler options at run-time, but most ahead-of-time
+  /// compiled platforms will not have this information.
   // The .hasEnvironment() constructor is special in that we do not want
   // users to call them using "new". We prohibit that by giving them bodies
   // that throw, even though const constructors are not allowed to have bodies.
diff --git a/sdk/lib/core/int.dart b/sdk/lib/core/int.dart
index 3e5bb4b..9bf2671 100644
--- a/sdk/lib/core/int.dart
+++ b/sdk/lib/core/int.dart
@@ -37,6 +37,11 @@
   /// must be consistent across all calls to [String.fromEnvironment],
   /// `int.fromEnvironment`, [bool.fromEnvironment] and [bool.hasEnvironment]
   /// in a single program.
+  ///
+  /// This constructor is only guaranteed to work when invoked as `const`.
+  /// It may work as a non-constant invocation on some platforms which
+  /// have access to compiler options at run-time, but most ahead-of-time
+  /// compiled platforms will not have this information.
   // The .fromEnvironment() constructors are special in that we do not want
   // users to call them using "new". We prohibit that by giving them bodies
   // that throw, even though const constructors are not allowed to have bodies.
diff --git a/sdk/lib/core/string.dart b/sdk/lib/core/string.dart
index fbe6a4b..d856ac2 100644
--- a/sdk/lib/core/string.dart
+++ b/sdk/lib/core/string.dart
@@ -151,6 +151,11 @@
   /// must be consistent across all calls to `String.fromEnvironment`,
   /// [int.fromEnvironment], [bool.fromEnvironment] and [bool.hasEnvironment]
   /// in a single program.
+  ///
+  /// This constructor is only guaranteed to work when invoked as `const`.
+  /// It may work as a non-constant invocation on some platforms which
+  /// have access to compiler options at run-time, but most ahead-of-time
+  /// compiled platforms will not have this information.
   // The .fromEnvironment() constructors are special in that we do not want
   // users to call them using "new". We prohibit that by giving them bodies
   // that throw, even though const constructors are not allowed to have bodies.
diff --git a/tools/VERSION b/tools/VERSION
index 5d2c02e..369f442 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
 MAJOR 2
 MINOR 15
 PATCH 0
-PRERELEASE 0
+PRERELEASE 1
 PRERELEASE_PATCH 0
\ No newline at end of file