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);
}