Elements. Migrate FlutterConvertToStatefulWidget.

Change-Id: Iaacb9e2ccc908c64528ae7e6ee48c7094076b7d4
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/389264
Commit-Queue: Konstantin Shcheglov <scheglov@google.com>
Reviewed-by: Samuel Rawlins <srawlins@google.com>
Reviewed-by: Phil Quitslund <pquitslund@google.com>
diff --git a/pkg/analysis_server/analyzer_use_new_elements.txt b/pkg/analysis_server/analyzer_use_new_elements.txt
index ceb17fb..17676a0 100644
--- a/pkg/analysis_server/analyzer_use_new_elements.txt
+++ b/pkg/analysis_server/analyzer_use_new_elements.txt
@@ -317,6 +317,7 @@
 lib/src/services/correction/dart/extend_class_for_mixin.dart
 lib/src/services/correction/dart/extract_local_variable.dart
 lib/src/services/correction/dart/flutter_convert_to_children.dart
+lib/src/services/correction/dart/flutter_convert_to_stateful_widget.dart
 lib/src/services/correction/dart/flutter_move_down.dart
 lib/src/services/correction/dart/flutter_move_up.dart
 lib/src/services/correction/dart/flutter_remove_widget.dart
diff --git a/pkg/analysis_server/lib/src/services/correction/dart/flutter_convert_to_stateful_widget.dart b/pkg/analysis_server/lib/src/services/correction/dart/flutter_convert_to_stateful_widget.dart
index c8f7459..a7b0682 100644
--- a/pkg/analysis_server/lib/src/services/correction/dart/flutter_convert_to_stateful_widget.dart
+++ b/pkg/analysis_server/lib/src/services/correction/dart/flutter_convert_to_stateful_widget.dart
@@ -8,7 +8,7 @@
 import 'package:analysis_server_plugin/edit/dart/correction_producer.dart';
 import 'package:analyzer/dart/ast/ast.dart';
 import 'package:analyzer/dart/ast/visitor.dart';
-import 'package:analyzer/dart/element/element.dart';
+import 'package:analyzer/dart/element/element2.dart';
 import 'package:analyzer/source/source_range.dart';
 import 'package:analyzer/src/dart/ast/extensions.dart';
 import 'package:analyzer_plugin/protocol/protocol_common.dart' hide Element;
@@ -43,7 +43,7 @@
     }
 
     // Must be a StatelessWidget subclass.
-    var widgetClassElement = widgetClass.declaredElement!;
+    var widgetClassElement = widgetClass.declaredFragment!.element;
     var superType = widgetClassElement.supertype;
     if (superType == null || !superType.isExactlyStatelessWidgetType) {
       return;
@@ -70,21 +70,22 @@
 
     // Prepare nodes to move.
     var nodesToMove = <ClassMember>{};
-    var elementsToMove = <Element>{};
+    var elementsToMove = <Element2>{};
     for (var member in widgetClass.members) {
       if (member is FieldDeclaration && !member.isStatic) {
         for (var fieldNode in member.fields.variables) {
-          var fieldElement = fieldNode.declaredElement as FieldElement;
+          var fieldFragment = fieldNode.declaredFragment as FieldFragment;
+          var fieldElement = fieldFragment.element;
           if (!fieldsAssignedInConstructors.contains(fieldElement)) {
             nodesToMove.add(member);
             elementsToMove.add(fieldElement);
 
-            var getter = fieldElement.getter;
+            var getter = fieldElement.getter2;
             if (getter != null) {
               elementsToMove.add(getter);
             }
 
-            var setter = fieldElement.setter;
+            var setter = fieldElement.setter2;
             if (setter != null) {
               elementsToMove.add(setter);
             }
@@ -92,7 +93,7 @@
         }
       } else if (member is MethodDeclaration && !member.isStatic) {
         nodesToMove.add(member);
-        elementsToMove.add(member.declaredElement!);
+        elementsToMove.add(member.declaredFragment!.element);
       }
     }
 
@@ -112,15 +113,15 @@
     }
 
     var statefulWidgetClass =
-        await sessionHelper.getFlutterClass('StatefulWidget');
-    var stateClass = await sessionHelper.getFlutterClass('State');
+        await sessionHelper.getFlutterClass2('StatefulWidget');
+    var stateClass = await sessionHelper.getFlutterClass2('State');
     if (statefulWidgetClass == null || stateClass == null) {
       return;
     }
 
     await builder.addDartFileEdit(file, (builder) {
       builder.addReplacement(range.node(superclass), (builder) {
-        builder.writeReference(statefulWidgetClass);
+        builder.writeReference2(statefulWidgetClass);
       });
 
       var replaceOffset = 0;
@@ -148,7 +149,7 @@
               }
               builder.writeln('  @override');
               builder.write('  ');
-              builder.writeReference(stateClass);
+              builder.writeReference2(stateClass);
               builder.write('<${widgetClass.name.lexeme}$typeParams>');
               builder.writeln(' createState() => $stateName$typeParams();');
               if (hasEmptyLineAfterCreateState) {
@@ -205,7 +206,7 @@
         builder.writeln();
 
         builder.write('class $stateName$typeParams extends ');
-        builder.writeReference(stateClass);
+        builder.writeReference2(stateClass);
 
         // Write just param names (and not bounds, metadata and docs).
         builder.write('<${widgetClass.name.lexeme}');
@@ -262,13 +263,13 @@
 }
 
 class _FieldFinder extends RecursiveAstVisitor<void> {
-  Set<FieldElement> fieldsAssignedInConstructors = {};
+  Set<FieldElement2> fieldsAssignedInConstructors = {};
 
   @override
   void visitFieldFormalParameter(FieldFormalParameter node) {
-    var element = node.declaredElement;
-    if (element is FieldFormalParameterElement) {
-      var field = element.field;
+    var element = node.declaredFragment?.element;
+    if (element is FieldFormalParameterElement2) {
+      var field = element.field2;
       if (field != null) {
         fieldsAssignedInConstructors.add(field);
       }
@@ -280,16 +281,16 @@
   @override
   void visitSimpleIdentifier(SimpleIdentifier node) {
     if (node.parent is ConstructorFieldInitializer) {
-      var element = node.staticElement;
-      if (element is FieldElement) {
+      var element = node.element;
+      if (element is FieldElement2) {
         fieldsAssignedInConstructors.add(element);
       }
     }
     if (node.inSetterContext()) {
-      var element = node.writeOrReadElement;
-      if (element is PropertyAccessorElement) {
-        var field = element.variable2;
-        if (field is FieldElement) {
+      var element = node.writeOrReadElement2;
+      if (element is SetterElement) {
+        var field = element.variable3;
+        if (field is FieldElement2) {
           fieldsAssignedInConstructors.add(field);
         }
       }
@@ -298,9 +299,9 @@
 }
 
 class _ReplacementEditBuilder extends RecursiveAstVisitor<void> {
-  final ClassElement widgetClassElement;
+  final ClassElement2 widgetClassElement;
 
-  final Set<Element> elementsToMove;
+  final Set<Element2> elementsToMove;
 
   final SourceRange linesRange;
 
@@ -314,9 +315,9 @@
     if (node.inDeclarationContext()) {
       return;
     }
-    var element = node.staticElement;
-    if (element is ExecutableElement &&
-        element.enclosingElement3 == widgetClassElement &&
+    var element = node.element;
+    if (element is ExecutableElement2 &&
+        element.enclosingElement2 == widgetClassElement &&
         !elementsToMove.contains(element)) {
       var offset = node.offset - linesRange.offset;
       var qualifier =
diff --git a/pkg/analyzer_plugin/lib/src/utilities/change_builder/change_builder_dart.dart b/pkg/analyzer_plugin/lib/src/utilities/change_builder/change_builder_dart.dart
index 16dd85f..08ab499 100644
--- a/pkg/analyzer_plugin/lib/src/utilities/change_builder/change_builder_dart.dart
+++ b/pkg/analyzer_plugin/lib/src/utilities/change_builder/change_builder_dart.dart
@@ -948,6 +948,14 @@
   }
 
   @override
+  void writeReference2(Element2 element) {
+    if (element.enclosingElement2 is LibraryElement2) {
+      _writeLibraryReference2(element);
+    }
+    write(element.displayName);
+  }
+
+  @override
   void writeSetterDeclaration(String name,
       {void Function()? bodyWriter,
       bool isStatic = false,
@@ -1502,6 +1510,42 @@
     }
   }
 
+  /// Writes the import prefix to reference the [element], if needed.
+  ///
+  /// The prefix is not needed if the [element] is defined in the target
+  /// library, or there is already an import without prefix that exports the
+  /// [element]. If there are no existing import that exports the [element], a
+  /// library that exports the [element] is scheduled for import, possibly with
+  /// a prefix.
+  void _writeLibraryReference2(Element2 element) {
+    // If the element is defined in the library, then no prefix needed.
+    if (dartFileEditBuilder._isDefinedLocally2(element)) {
+      return;
+    }
+
+    // TODO(scheglov): We should use "methodBeingCopied" to verify that
+    // we really are just copying this type parameter.
+    if (element is TypeParameterElement2) {
+      return;
+    }
+
+    var import = dartFileEditBuilder._getImportElement2(element);
+    if (import == null) {
+      var library = element.library2?.firstFragment.source.uri;
+      if (library != null) {
+        import = dartFileEditBuilder._importLibrary(library);
+      }
+    }
+    if (import == null) {
+      return;
+    }
+    import.ensureShown(element.name!);
+    var prefix = import.prefix;
+    if (prefix.isNotEmpty) {
+      write('$prefix.');
+    }
+  }
+
   void _writeSuperMemberInvocation(ExecutableElement element, String memberName,
       List<ParameterElement> parameters) {
     var isOperator = element.isOperator;
@@ -1921,6 +1965,10 @@
   /// them visible in the generated code.
   final Map<Element, _LibraryImport> _elementLibrariesToImport = {};
 
+  /// A mapping of [Element2]s to pending imports that will be added to make
+  /// them visible in the generated code.
+  final Map<Element2, _LibraryImport> _elementLibrariesToImport2 = {};
+
   /// Initializes a newly created builder to build a source file edit within the
   /// change being built by the given [changeBuilder].
   ///
@@ -2001,6 +2049,9 @@
     for (var entry in _elementLibrariesToImport.entries) {
       copy._elementLibrariesToImport[entry.key] = entry.value;
     }
+    for (var entry in _elementLibrariesToImport2.entries) {
+      copy._elementLibrariesToImport2[entry.key] = entry.value;
+    }
     return copy;
   }
 
@@ -2696,6 +2747,40 @@
     return (libraryChangeBuilder ?? this)._elementLibrariesToImport[element];
   }
 
+  /// Returns information about the library used to import the given [element]
+  /// into the target library, or `null` if the element was not imported, such
+  /// as when the element is declared in the same library.
+  ///
+  /// The result may be an existing import, or one that is pending.
+  _LibraryImport? _getImportElement2(Element2 element) {
+    for (var import
+        in resolvedUnit.libraryElement2.firstFragment.libraryImports2) {
+      var definedNames = import.namespace.definedNames2;
+      if (definedNames.containsValue(element)) {
+        var importedLibrary = import.importedLibrary2;
+        if (importedLibrary != null) {
+          return _LibraryImport(
+            uriText: importedLibrary.firstFragment.source.uri.toString(),
+            isExplicitlyImported: true,
+            shownNames: [
+              for (var combinator in import.combinators)
+                if (combinator is ShowElementCombinator)
+                  combinator.shownNames.toList(),
+            ],
+            hiddenNames: [
+              for (var combinator in import.combinators)
+                if (combinator is HideElementCombinator)
+                  combinator.hiddenNames.toList(),
+            ],
+            prefix: import.prefix2?.name ?? '',
+          );
+        }
+      }
+    }
+
+    return (libraryChangeBuilder ?? this)._elementLibrariesToImport2[element];
+  }
+
   List<LibraryImportElement> _getImportsForUri(Uri uri) {
     return [
       for (var import
@@ -2829,6 +2914,11 @@
     return element.library == resolvedUnit.libraryElement;
   }
 
+  /// Returns whether the [element] is defined in the target library.
+  bool _isDefinedLocally2(Element2 element) {
+    return element.library2 == resolvedUnit.libraryElement2;
+  }
+
   /// Removes any pending imports (for [Element]s) that are no longer necessary
   /// because the newly-added [newImport] for [newLibrary] also provides those
   /// [Element]s.
diff --git a/pkg/analyzer_plugin/lib/utilities/change_builder/change_builder_dart.dart b/pkg/analyzer_plugin/lib/utilities/change_builder/change_builder_dart.dart
index 4c64883..8fc424d 100644
--- a/pkg/analyzer_plugin/lib/utilities/change_builder/change_builder_dart.dart
+++ b/pkg/analyzer_plugin/lib/utilities/change_builder/change_builder_dart.dart
@@ -333,6 +333,13 @@
   /// the current library, imports will be updated.
   void writeReference(Element element);
 
+  /// Writes the code that references the [element].
+  ///
+  /// If the [element] is a top-level element that has not been imported into
+  /// the current library, imports will be updated.
+  @experimental
+  void writeReference2(Element2 element);
+
   /// Writes the code for a declaration of a setter with the given [name].
   ///
   /// If a [bodyWriter] is provided, it will be invoked to write the body of the