Elements. Migrate AddMissingParameterNamed.

Adds a couple tweaks to improve the lint reporting.

Change-Id: I6e85e323cc2f34f1236d46046b033c9bc72999c7
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/387130
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
Commit-Queue: Konstantin Shcheglov <scheglov@google.com>
diff --git a/pkg/analysis_server/analyzer_use_new_elements.txt b/pkg/analysis_server/analyzer_use_new_elements.txt
index 6e4a7fc..eea633f 100644
--- a/pkg/analysis_server/analyzer_use_new_elements.txt
+++ b/pkg/analysis_server/analyzer_use_new_elements.txt
@@ -11,3 +11,8 @@
 lib/src/services/correction/dart/add_explicit_call.dart
 lib/src/services/correction/dart/add_explicit_cast.dart
 lib/src/services/correction/dart/add_missing_enum_like_case_clauses.dart
+lib/src/services/correction/dart/add_field_formal_parameters.dart
+lib/src/services/correction/dart/add_key_to_constructors.dart
+lib/src/services/correction/dart/add_late.dart
+lib/src/services/correction/dart/add_leading_newline_to_string.dart
+lib/src/services/correction/dart/add_missing_parameter_named.dart
diff --git a/pkg/analysis_server/lib/src/services/correction/dart/add_missing_parameter_named.dart b/pkg/analysis_server/lib/src/services/correction/dart/add_missing_parameter_named.dart
index 32bda74..842769c 100644
--- a/pkg/analysis_server/lib/src/services/correction/dart/add_missing_parameter_named.dart
+++ b/pkg/analysis_server/lib/src/services/correction/dart/add_missing_parameter_named.dart
@@ -54,7 +54,7 @@
     }
 
     // We cannot add named parameters when there are positional positional.
-    if (context.optionalPositional.isNotEmpty) {
+    if (context.optionalPositional2.isNotEmpty) {
       return;
     }
 
@@ -71,11 +71,13 @@
       }
     }
 
-    if (context.named.isNotEmpty) {
-      var prevNode = await context.getParameterNode(context.named.last);
+    if (context.named2.isNotEmpty) {
+      var prevNode =
+          await context.getParameterNode2(context.named2.last.firstFragment!);
       await addParameter(prevNode?.end, ', ', '');
-    } else if (context.required.isNotEmpty) {
-      var prevNode = await context.getParameterNode(context.required.last);
+    } else if (context.required2.isNotEmpty) {
+      var prevNode = await context
+          .getParameterNode2(context.required2.last.firstFragment!);
       await addParameter(prevNode?.end, ', {', '}');
     } else {
       var parameterList = await context.getParameterList();
diff --git a/pkg/analysis_server/lib/src/services/correction/executable_parameters.dart b/pkg/analysis_server/lib/src/services/correction/executable_parameters.dart
index 824b3eb..c81d271 100644
--- a/pkg/analysis_server/lib/src/services/correction/executable_parameters.dart
+++ b/pkg/analysis_server/lib/src/services/correction/executable_parameters.dart
@@ -4,6 +4,7 @@
 
 import 'package:analyzer/dart/ast/ast.dart';
 import 'package:analyzer/dart/element/element.dart';
+import 'package:analyzer/dart/element/element2.dart';
 import 'package:analyzer/src/dart/analysis/session_helper.dart';
 
 /// [ExecutableElement], its parameters, and operations on them.
@@ -15,14 +16,21 @@
   final List<ParameterElement> optionalPositional = [];
   final List<ParameterElement> named = [];
 
+  final List<FormalParameterElement> required2 = [];
+  final List<FormalParameterElement> optionalPositional2 = [];
+  final List<FormalParameterElement> named2 = [];
+
   ExecutableParameters._(this.sessionHelper, this.executable) {
     for (var parameter in executable.parameters) {
       if (parameter.isRequiredPositional) {
         required.add(parameter);
+        required2.add(parameter.element);
       } else if (parameter.isOptionalPositional) {
         optionalPositional.add(parameter);
+        optionalPositional2.add(parameter.element);
       } else if (parameter.isNamed) {
         named.add(parameter);
+        named2.add(parameter.element);
       }
     }
   }
@@ -64,6 +72,20 @@
     return null;
   }
 
+  /// Return the [FormalParameter] of the [fragment] in [FormalParameterList],
+  /// or `null` if it can't be found.
+  Future<FormalParameter?> getParameterNode2(
+      FormalParameterFragment fragment) async {
+    var result = await sessionHelper.getElementDeclaration2(fragment);
+    var declaration = result?.node;
+    for (var node = declaration; node != null; node = node.parent) {
+      if (node is FormalParameter && node.parent is FormalParameterList) {
+        return node;
+      }
+    }
+    return null;
+  }
+
   static ExecutableParameters? forInvocation(
       AnalysisSessionHelper sessionHelper, AstNode? invocation) {
     Element? element;
diff --git a/pkg/analyzer/lib/dart/element/element.dart b/pkg/analyzer/lib/dart/element/element.dart
index 292da89..d9037bc 100644
--- a/pkg/analyzer/lib/dart/element/element.dart
+++ b/pkg/analyzer/lib/dart/element/element.dart
@@ -39,6 +39,7 @@
 import 'package:analyzer/dart/analysis/features.dart';
 import 'package:analyzer/dart/analysis/session.dart';
 import 'package:analyzer/dart/constant/value.dart';
+import 'package:analyzer/dart/element/element2.dart';
 import 'package:analyzer/dart/element/nullability_suffix.dart';
 import 'package:analyzer/dart/element/scope.dart';
 import 'package:analyzer/dart/element/type.dart';
@@ -2203,6 +2204,9 @@
   /// The code of the default value, or `null` if no default value.
   String? get defaultValueCode;
 
+  @experimental
+  FormalParameterElement get element;
+
   /// Whether the parameter has a default value.
   bool get hasDefaultValue;
 
diff --git a/pkg/analyzer/lib/dart/element/element2.dart b/pkg/analyzer/lib/dart/element/element2.dart
index 53b8578..54af4ce 100644
--- a/pkg/analyzer/lib/dart/element/element2.dart
+++ b/pkg/analyzer/lib/dart/element/element2.dart
@@ -593,6 +593,9 @@
   /// Returns `null` if no default value.
   String? get defaultValueCode;
 
+  @override
+  FormalParameterFragment? get firstFragment;
+
   /// The formal parameters defined by this formal parameter.
   ///
   /// A parameter will only define other parameters if it is a function typed
diff --git a/pkg/analyzer/lib/src/dart/element/member.dart b/pkg/analyzer/lib/src/dart/element/member.dart
index 46df70b..1fea4a8 100644
--- a/pkg/analyzer/lib/src/dart/element/member.dart
+++ b/pkg/analyzer/lib/src/dart/element/member.dart
@@ -1023,6 +1023,10 @@
   @override
   String? get defaultValueCode => declaration.defaultValueCode;
 
+  @override
+  // TODO(scheglov): we lose types
+  FormalParameterElement get element => declaration.element;
+
   @Deprecated('Use enclosingElement3 instead')
   @override
   Element? get enclosingElement => declaration.enclosingElement;
diff --git a/pkg/linter/lib/src/rules/analyzer_use_new_elements.dart b/pkg/linter/lib/src/rules/analyzer_use_new_elements.dart
index 5cef7b8..397629e 100644
--- a/pkg/linter/lib/src/rules/analyzer_use_new_elements.dart
+++ b/pkg/linter/lib/src/rules/analyzer_use_new_elements.dart
@@ -6,13 +6,33 @@
 import 'package:analyzer/dart/ast/visitor.dart';
 import 'package:analyzer/dart/element/element.dart';
 import 'package:analyzer/dart/element/element2.dart';
+import 'package:analyzer/dart/element/type.dart';
 import 'package:analyzer/file_system/file_system.dart';
 import 'package:analyzer/src/dart/element/element.dart'; // ignore: implementation_imports
+import 'package:analyzer/src/dart/element/type_visitor.dart'; // ignore: implementation_imports
 
 import '../analyzer.dart';
 
 const _desc = r'Use new element model in opted-in files.';
 
+bool _isOldModelElement(Element2? element) {
+  var firstFragment = element?.firstFragment;
+  if (firstFragment != null) {
+    var libraryFragment = firstFragment.libraryFragment;
+    var uriStr = libraryFragment.source.uri.toString();
+    return const {
+      'package:analyzer/dart/element/element.dart',
+    }.contains(uriStr);
+  }
+  return false;
+}
+
+bool _isOldModelType(DartType? type) {
+  var visitor = _TypeVisitor();
+  type?.accept(visitor);
+  return visitor.result;
+}
+
 /// The lint must be enabled for a Pub package.
 ///
 /// The lint rule reads once the file `analyzer_use_new_elements.txt`
@@ -51,8 +71,9 @@
     }
 
     var visitor = _Visitor(this);
-    registry.addSimpleIdentifier(this, visitor);
+    registry.addMethodInvocation(this, visitor);
     registry.addNamedType(this, visitor);
+    registry.addSimpleIdentifier(this, visitor);
   }
 
   bool _isEnabledForFile(LinterContext context) {
@@ -123,37 +144,47 @@
   }
 }
 
+class _TypeVisitor extends RecursiveTypeVisitor {
+  bool result = false;
+
+  @override
+  bool visitInterfaceType(InterfaceType type) {
+    result |= _isOldModelElement(type.element3);
+    return super.visitInterfaceType(type);
+  }
+}
+
 class _Visitor extends SimpleAstVisitor {
   final LintRule rule;
 
   _Visitor(this.rule);
 
   @override
+  visitMethodInvocation(MethodInvocation node) {
+    if (_isOldModelType(node.staticType)) {
+      rule.reportLint(node.methodName);
+    }
+  }
+
+  @override
   visitNamedType(NamedType node) {
-    if (_isOldElementModel(node.element2)) {
+    if (_isOldModelElement(node.element2)) {
       rule.reportLintForToken(node.name2);
     }
   }
 
   @override
   void visitSimpleIdentifier(SimpleIdentifier node) {
-    var element = node.staticType?.element3;
-    if (_isOldElementModel(element)) {
+    if (node.parent case MethodInvocation invocation) {
+      if (invocation.methodName == node) {
+        return;
+      }
+    }
+
+    if (_isOldModelType(node.staticType)) {
       rule.reportLint(node);
     }
   }
-
-  static bool _isOldElementModel(Element2? element) {
-    var firstFragment = element?.firstFragment;
-    if (firstFragment != null) {
-      var libraryFragment = firstFragment.libraryFragment;
-      var uriStr = libraryFragment.source.uri.toString();
-      return const {
-        'package:analyzer/dart/element/element.dart',
-      }.contains(uriStr);
-    }
-    return false;
-  }
 }
 
 extension on Element2 {
diff --git a/pkg/linter/test/rules/analyzer_use_new_elements_test.dart b/pkg/linter/test/rules/analyzer_use_new_elements_test.dart
index ab7c7c9..a0ef717 100644
--- a/pkg/linter/test/rules/analyzer_use_new_elements_test.dart
+++ b/pkg/linter/test/rules/analyzer_use_new_elements_test.dart
@@ -101,6 +101,40 @@
     ]);
   }
 
+  test_methodInvocation_hasFormalParameter() async {
+    newFile('$testPackageLibPath/a.dart', r'''
+import 'package:analyzer/dart/element/element.dart';
+
+void foo([List<ClassElement>? elements]) {}
+''');
+
+    await assertNoDiagnostics(r'''
+import 'a.dart';
+
+void f() {
+  foo();
+}
+''');
+  }
+
+  test_methodInvocation_hasType() async {
+    newFile('$testPackageLibPath/a.dart', r'''
+import 'package:analyzer/dart/element/element.dart';
+
+List<ClassElement> getAllClasses() => [];
+''');
+
+    await assertDiagnostics(r'''
+import 'a.dart';
+
+void f() {
+  getAllClasses();
+}
+''', [
+      lint(31, 13),
+    ]);
+  }
+
   test_namedType() async {
     await assertDiagnostics(r'''
 import 'package:analyzer/dart/element/element.dart';
@@ -113,7 +147,7 @@
     ]);
   }
 
-  test_simpleIdentifier_propertyAccess() async {
+  test_propertyAccess() async {
     await assertDiagnostics(r'''
 import 'package:analyzer/dart/ast/ast.dart';
 
@@ -125,6 +159,24 @@
     ]);
   }
 
+  test_propertyAccess_nestedType() async {
+    newFile('$testPackageLibPath/a.dart', r'''
+import 'package:analyzer/dart/element/element.dart';
+
+List<ClassElement> get allClasses => [];
+''');
+
+    await assertDiagnostics(r'''
+import 'a.dart';
+
+void f() {
+  allClasses;
+}
+''', [
+      lint(31, 10),
+    ]);
+  }
+
   void _writeOptIns(String lines) {
     newFile('$testPackageRootPath/analyzer_use_new_elements.txt', lines);
   }