Version 2.17.0-9.0.dev

Merge commit '6e30e5a7f62188986b0658b9fc8d5f2e85554670' into 'dev'
diff --git a/pkg/analysis_server/lib/src/services/correction/dart/extract_local_variable.dart b/pkg/analysis_server/lib/src/services/correction/dart/extract_local_variable.dart
new file mode 100644
index 0000000..bdb9fc7
--- /dev/null
+++ b/pkg/analysis_server/lib/src/services/correction/dart/extract_local_variable.dart
@@ -0,0 +1,209 @@
+// Copyright (c) 2022, 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:analysis_server/src/services/correction/dart/abstract_producer.dart';
+import 'package:analysis_server/src/services/correction/fix.dart';
+import 'package:analysis_server/src/services/correction/util.dart';
+import 'package:analyzer/dart/ast/ast.dart';
+import 'package:analyzer/dart/ast/token.dart';
+import 'package:analyzer/dart/ast/visitor.dart';
+import 'package:analyzer/dart/element/element.dart';
+import 'package:analyzer/source/source_range.dart';
+import 'package:analyzer_plugin/utilities/change_builder/change_builder_core.dart';
+import 'package:analyzer_plugin/utilities/fixes/fixes.dart';
+import 'package:analyzer_plugin/utilities/range_factory.dart';
+
+class ExtractLocalVariable extends CorrectionProducer {
+  @override
+  FixKind get fixKind => DartFixKind.EXTRACT_LOCAL_VARIABLE;
+
+  @override
+  Future<void> compute(ChangeBuilder builder) async {
+    final node = this.node;
+    if (node is! SimpleIdentifier) {
+      return;
+    }
+
+    var parent = node.parent;
+
+    if (parent is MethodInvocation && parent.methodName == node) {
+      await _rewrite(
+        builder: builder,
+        target: parent.target,
+      );
+    }
+
+    if (parent is PrefixedIdentifier && parent.identifier == node) {
+      await _rewrite(
+        builder: builder,
+        target: parent.prefix,
+      );
+    }
+
+    if (parent is PropertyAccess && parent.propertyName == node) {
+      await _rewrite(
+        builder: builder,
+        target: parent.target,
+      );
+    }
+  }
+
+  Future<void> _rewrite({
+    required ChangeBuilder builder,
+    required Expression? target,
+  }) async {
+    if (target is PrefixedIdentifier) {
+      await _rewriteProperty(
+        builder: builder,
+        target: target,
+        targetProperty: target.staticElement,
+      );
+    }
+
+    if (target is PropertyAccess) {
+      await _rewriteProperty(
+        builder: builder,
+        target: target,
+        targetProperty: target.propertyName.staticElement,
+      );
+    }
+
+    if (target is SimpleIdentifier) {
+      await _rewriteProperty(
+        builder: builder,
+        target: target,
+        targetProperty: target.staticElement,
+      );
+    }
+  }
+
+  Future<void> _rewriteProperty({
+    required ChangeBuilder builder,
+    required Expression target,
+    required Element? targetProperty,
+  }) async {
+    if (targetProperty is PropertyAccessorElement &&
+        targetProperty.isGetter &&
+        typeSystem.isPotentiallyNullable(targetProperty.returnType)) {
+      AstNode? enclosingNode = target;
+      while (true) {
+        if (enclosingNode == null || enclosingNode is FunctionBody) {
+          return;
+        }
+        if (enclosingNode is IfStatement) {
+          var condition = enclosingNode.condition;
+          if (condition is BinaryExpression &&
+              condition.rightOperand is NullLiteral &&
+              condition.operator.type == TokenType.BANG_EQ) {
+            var encoder = _ExpressionEncoder();
+            var leftCode = encoder.encode(condition.leftOperand);
+            var targetCode = encoder.encode(target);
+            if (leftCode == targetCode) {
+              var occurrences = <SourceRange>[];
+              enclosingNode.accept(
+                _OccurrencesVisitor(encoder, occurrences, leftCode),
+              );
+
+              var ifOffset = enclosingNode.offset;
+              var ifLineOffset = utils.getLineContentStart(ifOffset);
+              var prefix = utils.getLinePrefix(ifOffset);
+
+              var initializerCode = utils.getNodeText(target);
+              if (target is SimpleIdentifier) {
+                initializerCode = 'this.$initializerCode';
+              }
+
+              await builder.addDartFileEdit(file, (builder) {
+                var propertyName = targetProperty.name;
+                builder.addInsertion(ifLineOffset, (builder) {
+                  builder.write(prefix);
+                  builder.writeln('final $propertyName = $initializerCode;');
+                });
+                for (var occurrence in occurrences) {
+                  builder.addSimpleReplacement(occurrence, propertyName);
+                }
+              });
+              return;
+            }
+          }
+          break;
+        }
+        enclosingNode = enclosingNode.parent;
+      }
+    }
+  }
+
+  /// Return an instance of this class. Used as a tear-off in `FixProcessor`.
+  static ExtractLocalVariable newInstance() => ExtractLocalVariable();
+}
+
+class _ExpressionEncoder {
+  final Map<Element, int> _elementIds = {};
+
+  String encode(Expression node) {
+    var tokens = TokenUtils.getNodeTokens(node);
+
+    var tokenToElementMap = Map<Token, Element>.identity();
+    node.accept(
+      _FunctionAstVisitor(
+        simpleIdentifier: (node) {
+          var element = node.staticElement;
+          if (element != null) {
+            tokenToElementMap[node.token] = element;
+          }
+        },
+      ),
+    );
+
+    var tokensWithId = tokens.map((token) {
+      var tokenString = token.lexeme;
+      var element = tokenToElementMap[token];
+      if (element != null) {
+        var elementId = _elementIds.putIfAbsent(
+          element,
+          () => _elementIds.length,
+        );
+        tokenString += '#$elementId';
+      }
+      return tokenString;
+    });
+
+    const separator = '\uFFFF';
+    return tokensWithId.join(separator) + separator;
+  }
+}
+
+/// [RecursiveAstVisitor] that delegates visit methods to functions.
+class _FunctionAstVisitor extends RecursiveAstVisitor<void> {
+  final void Function(SimpleIdentifier)? simpleIdentifier;
+
+  _FunctionAstVisitor({
+    this.simpleIdentifier,
+  });
+
+  @override
+  void visitSimpleIdentifier(SimpleIdentifier node) {
+    if (simpleIdentifier != null) {
+      simpleIdentifier!(node);
+    }
+    super.visitSimpleIdentifier(node);
+  }
+}
+
+class _OccurrencesVisitor extends GeneralizingAstVisitor<void> {
+  final _ExpressionEncoder encoder;
+  final List<SourceRange> occurrences;
+  final String searchCode;
+
+  _OccurrencesVisitor(this.encoder, this.occurrences, this.searchCode);
+
+  @override
+  void visitExpression(Expression node) {
+    var nodeCode = encoder.encode(node);
+    if (nodeCode == searchCode) {
+      occurrences.add(range.node(node));
+    }
+    super.visitExpression(node);
+  }
+}
diff --git a/pkg/analysis_server/lib/src/services/correction/fix.dart b/pkg/analysis_server/lib/src/services/correction/fix.dart
index 666ea13..d1778fb 100644
--- a/pkg/analysis_server/lib/src/services/correction/fix.dart
+++ b/pkg/analysis_server/lib/src/services/correction/fix.dart
@@ -635,6 +635,11 @@
     DartFixKindPriority.DEFAULT,
     "Extend the class '{0}'",
   );
+  static const EXTRACT_LOCAL_VARIABLE = FixKind(
+    'dart.fix.extractLocalVariable',
+    DartFixKindPriority.DEFAULT,
+    'Extract local variable',
+  );
   static const IGNORE_ERROR_LINE = FixKind(
     'dart.fix.ignore.line',
     DartFixKindPriority.IGNORE,
diff --git a/pkg/analysis_server/lib/src/services/correction/fix_internal.dart b/pkg/analysis_server/lib/src/services/correction/fix_internal.dart
index 34e4921..0a0a125 100644
--- a/pkg/analysis_server/lib/src/services/correction/fix_internal.dart
+++ b/pkg/analysis_server/lib/src/services/correction/fix_internal.dart
@@ -80,6 +80,7 @@
 import 'package:analysis_server/src/services/correction/dart/create_setter.dart';
 import 'package:analysis_server/src/services/correction/dart/data_driven.dart';
 import 'package:analysis_server/src/services/correction/dart/extend_class_for_mixin.dart';
+import 'package:analysis_server/src/services/correction/dart/extract_local_variable.dart';
 import 'package:analysis_server/src/services/correction/dart/flutter_remove_widget.dart';
 import 'package:analysis_server/src/services/correction/dart/ignore_diagnostic.dart';
 import 'package:analysis_server/src/services/correction/dart/import_library.dart';
@@ -990,6 +991,7 @@
     ],
     CompileTimeErrorCode.UNCHECKED_METHOD_INVOCATION_OF_NULLABLE_VALUE: [
       AddNullCheck.newInstance,
+      ExtractLocalVariable.newInstance,
       ReplaceWithNullAware.single,
     ],
     CompileTimeErrorCode.UNCHECKED_OPERATOR_INVOCATION_OF_NULLABLE_VALUE: [
@@ -997,6 +999,7 @@
     ],
     CompileTimeErrorCode.UNCHECKED_PROPERTY_ACCESS_OF_NULLABLE_VALUE: [
       AddNullCheck.newInstance,
+      ExtractLocalVariable.newInstance,
       ReplaceWithNullAware.single,
     ],
     CompileTimeErrorCode.UNCHECKED_USE_OF_NULLABLE_VALUE_AS_CONDITION: [
diff --git a/pkg/analysis_server/test/src/services/correction/fix/extract_local_variable_test.dart b/pkg/analysis_server/test/src/services/correction/fix/extract_local_variable_test.dart
new file mode 100644
index 0000000..3fdb4db
--- /dev/null
+++ b/pkg/analysis_server/test/src/services/correction/fix/extract_local_variable_test.dart
@@ -0,0 +1,183 @@
+// Copyright (c) 2022, 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:analysis_server/src/services/correction/fix.dart';
+import 'package:analyzer_plugin/utilities/fixes/fixes.dart';
+import 'package:test_reflective_loader/test_reflective_loader.dart';
+
+import 'fix_processor.dart';
+
+void main() {
+  defineReflectiveSuite(() {
+    defineReflectiveTests(ExtractLocalVariableTest);
+  });
+}
+
+@reflectiveTest
+class ExtractLocalVariableTest extends FixProcessorTest {
+  @override
+  FixKind get kind => DartFixKind.EXTRACT_LOCAL_VARIABLE;
+
+  Future<void> test_ifCondition_notBangEq() async {
+    await resolveTestCode('''
+abstract class A {
+  int? get foo;
+
+  void bar() {
+    if (foo == 0) {
+      foo.isEven;
+    }
+  }
+}
+''');
+    await assertNoFix();
+  }
+
+  Future<void> test_ifCondition_notBinaryExpression() async {
+    await resolveTestCode('''
+abstract class A {
+  int? get foo;
+
+  void bar() {
+    if (!(1 == 0)) {
+      foo.isEven;
+    }
+  }
+}
+''');
+    await assertNoFix();
+  }
+
+  Future<void> test_ifCondition_notNull() async {
+    await resolveTestCode('''
+abstract class A {
+  int? get foo;
+
+  void bar() {
+    if (foo != 0) {
+      foo.isEven;
+    }
+  }
+}
+''');
+    await assertNoFix();
+  }
+
+  Future<void> test_noEnclosingIf() async {
+    await resolveTestCode('''
+abstract class A {
+  int? get foo;
+
+  void bar() {
+    foo.isEven;
+  }
+}
+''');
+    await assertNoFix();
+  }
+
+  Future<void> test_prefixedIdentifier() async {
+    await resolveTestCode('''
+abstract class A {
+  int? get foo;
+
+  void bar() {
+    if (foo != null) {
+      foo.isEven;
+    }
+  }
+}
+''');
+    await assertHasFix('''
+abstract class A {
+  int? get foo;
+
+  void bar() {
+    final foo = this.foo;
+    if (foo != null) {
+      foo.isEven;
+    }
+  }
+}
+''');
+  }
+
+  Future<void> test_prefixedIdentifier_methodInvocation() async {
+    await resolveTestCode('''
+abstract class A {
+  int? get foo;
+}
+void f(A a) {
+  if (a.foo != null) {
+    a.foo.abs();
+  }
+}
+''');
+    await assertHasFix('''
+abstract class A {
+  int? get foo;
+}
+void f(A a) {
+  final foo = a.foo;
+  if (foo != null) {
+    foo.abs();
+  }
+}
+''');
+  }
+
+  Future<void> test_prefixedIdentifier_propertyAccess() async {
+    await resolveTestCode('''
+abstract class A {
+  int? get foo;
+}
+void f(A a) {
+  if (a.foo != null) {
+    a.foo.isEven;
+  }
+}
+''');
+    await assertHasFix('''
+abstract class A {
+  int? get foo;
+}
+void f(A a) {
+  final foo = a.foo;
+  if (foo != null) {
+    foo.isEven;
+  }
+}
+''');
+  }
+
+  Future<void> test_propertyAccess_methodInvocation() async {
+    await resolveTestCode('''
+abstract class A {
+  int? get foo;
+}
+abstract class B {
+  A get a;
+}
+void f(B b) {
+  if (b.a.foo != null) {
+    b.a.foo.abs();
+  }
+}
+''');
+    await assertHasFix('''
+abstract class A {
+  int? get foo;
+}
+abstract class B {
+  A get a;
+}
+void f(B b) {
+  final foo = b.a.foo;
+  if (foo != null) {
+    foo.abs();
+  }
+}
+''');
+  }
+}
diff --git a/pkg/analysis_server/test/src/services/correction/fix/test_all.dart b/pkg/analysis_server/test/src/services/correction/fix/test_all.dart
index 3174635..d202946 100644
--- a/pkg/analysis_server/test/src/services/correction/fix/test_all.dart
+++ b/pkg/analysis_server/test/src/services/correction/fix/test_all.dart
@@ -96,6 +96,7 @@
 import 'create_setter_test.dart' as create_setter;
 import 'data_driven/test_all.dart' as data_driven;
 import 'extend_class_for_mixin_test.dart' as extend_class_for_mixin;
+import 'extract_local_variable_test.dart' as extract_local_variable;
 import 'fix_in_file_test.dart' as fix_in_file;
 import 'fix_processor_map_test.dart' as fix_processor_map;
 import 'fix_test.dart' as fix;
@@ -296,6 +297,7 @@
     data_driven.main();
     extend_class_for_mixin.main();
     fix.main();
+    extract_local_variable.main();
     fix_in_file.main();
     fix_processor_map.main();
     ignore_error.main();
diff --git a/pkg/analyzer/lib/src/dart/element/scope.dart b/pkg/analyzer/lib/src/dart/element/scope.dart
index 4df2190..7287b59 100644
--- a/pkg/analyzer/lib/src/dart/element/scope.dart
+++ b/pkg/analyzer/lib/src/dart/element/scope.dart
@@ -83,7 +83,8 @@
     List<ParameterElement> elements,
   ) : super(parent) {
     for (var parameter in elements) {
-      if (parameter is! FieldFormalParameterElement) {
+      if (parameter is! FieldFormalParameterElement &&
+          parameter is! SuperFormalParameterElement) {
         _addGetter(parameter);
       }
     }
diff --git a/pkg/analyzer/test/src/dart/resolution/super_formal_parameter_test.dart b/pkg/analyzer/test/src/dart/resolution/super_formal_parameter_test.dart
index 83f2777..42db1a5 100644
--- a/pkg/analyzer/test/src/dart/resolution/super_formal_parameter_test.dart
+++ b/pkg/analyzer/test/src/dart/resolution/super_formal_parameter_test.dart
@@ -130,4 +130,42 @@
       findElement.unnamedConstructor('B').superFormalParameter('a'),
     );
   }
+
+  test_scoping_inBody() async {
+    await assertNoErrorsInCode(r'''
+class A {
+  final int a;
+  A(this.a);
+}
+
+class B extends A {
+  B(super.a) {
+    a; // ref
+  }
+}
+''');
+
+    assertElement(
+      findNode.simple('a; // ref'),
+      findElement.getter('a', of: 'A'),
+    );
+  }
+
+  test_scoping_inInitializer() async {
+    await assertNoErrorsInCode(r'''
+class A {
+  A(int a);
+}
+
+class B extends A {
+  var f;
+  B(super.a) : f = ((){ a; });
+}
+''');
+
+    assertElement(
+      findNode.simple('a; }'),
+      findElement.unnamedConstructor('B').superFormalParameter('a'),
+    );
+  }
 }
diff --git a/tools/VERSION b/tools/VERSION
index eafe4ae..a83e30d 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
 MAJOR 2
 MINOR 17
 PATCH 0
-PRERELEASE 8
+PRERELEASE 9
 PRERELEASE_PATCH 0
\ No newline at end of file