Skip to content

Commit 104f455

Browse files
pqCommit Queue
authored and
Commit Queue
committed
[element model] migrate use_key_in_widget_constructors
Bug: https://github.com/dart-lang/linter/issues/5099 Change-Id: I2355eda7d2516137f4bb31b12dc5403e3c003295 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/390502 Auto-Submit: Phil Quitslund <[email protected]> Reviewed-by: Brian Wilkerson <[email protected]> Commit-Queue: Phil Quitslund <[email protected]>
1 parent 7093fba commit 104f455

File tree

3 files changed

+86
-24
lines changed

3 files changed

+86
-24
lines changed

pkg/linter/lib/src/rules/use_key_in_widget_constructors.dart

Lines changed: 18 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
import 'package:analyzer/dart/ast/ast.dart';
66
import 'package:analyzer/dart/ast/visitor.dart';
7-
import 'package:analyzer/dart/element/element.dart';
7+
import 'package:analyzer/dart/element/element2.dart';
88
import 'package:analyzer/dart/element/type.dart';
99

1010
import '../analyzer.dart';
@@ -39,11 +39,11 @@ class _Visitor extends SimpleAstVisitor<void> {
3939

4040
@override
4141
void visitClassDeclaration(ClassDeclaration node) {
42-
var classElement = node.declaredElement;
42+
var classElement = node.declaredFragment?.element;
4343
if (classElement != null &&
4444
classElement.isPublic &&
45-
hasWidgetAsAscendant(classElement) &&
46-
classElement.allConstructors.where((e) => !e.isSynthetic).isEmpty) {
45+
classElement.extendsWidget &&
46+
classElement.constructors2.where((e) => !e.isSynthetic).isEmpty) {
4747
rule.reportLintForToken(node.name);
4848
}
4949
super.visitClassDeclaration(node);
@@ -53,26 +53,26 @@ class _Visitor extends SimpleAstVisitor<void> {
5353
void visitConstructorDeclaration(ConstructorDeclaration node) {
5454
if (node.isAugmentation) return;
5555

56-
var constructorElement = node.declaredElement;
56+
var constructorElement = node.declaredFragment?.element;
5757
if (constructorElement == null) {
5858
return;
5959
}
60-
var classElement = constructorElement.enclosingElement3;
60+
var classElement = constructorElement.enclosingElement2;
6161
if (constructorElement.isPublic &&
6262
!constructorElement.isFactory &&
6363
classElement.isPublic &&
64-
classElement is ClassElement &&
65-
hasWidgetAsAscendant(classElement) &&
66-
!isExactWidget(classElement) &&
64+
classElement is ClassElement2 &&
65+
!classElement.isExactlyWidget &&
66+
classElement.extendsWidget &&
6767
!_hasKeySuperParameterInitializerArg(node) &&
6868
!node.initializers.any((initializer) {
6969
if (initializer is SuperConstructorInvocation) {
70-
var staticElement = initializer.staticElement;
70+
var staticElement = initializer.element;
7171
return staticElement != null &&
7272
(!_defineKeyParameter(staticElement) ||
7373
_defineKeyArgument(initializer.argumentList));
7474
} else if (initializer is RedirectingConstructorInvocation) {
75-
var staticElement = initializer.staticElement;
75+
var staticElement = initializer.element;
7676
return staticElement != null &&
7777
(!_defineKeyParameter(staticElement) ||
7878
_defineKeyArgument(initializer.argumentList));
@@ -86,15 +86,16 @@ class _Visitor extends SimpleAstVisitor<void> {
8686
}
8787

8888
bool _defineKeyArgument(ArgumentList argumentList) => argumentList.arguments
89-
.any((a) => a.staticParameterElement?.name == 'key');
89+
.any((a) => a.correspondingParameter?.name3 == 'key');
9090

91-
bool _defineKeyParameter(ConstructorElement element) =>
92-
element.parameters.any((e) => e.name == 'key' && _isKeyType(e.type));
91+
bool _defineKeyParameter(ConstructorElement2 element) =>
92+
element.formalParameters
93+
.any((e) => e.name3 == 'key' && _isKeyType(e.type));
9394

9495
bool _hasKeySuperParameterInitializerArg(ConstructorDeclaration node) {
95-
for (var parameter in node.parameters.parameters) {
96-
var element = parameter.declaredElement;
97-
if (element is SuperFormalParameterElement && element.name == 'key') {
96+
for (var parameter in node.parameters.parameterFragments) {
97+
var element = parameter?.element;
98+
if (element is SuperFormalParameterElement2 && element.name3 == 'key') {
9899
return true;
99100
}
100101
}

pkg/linter/lib/src/util/flutter_utils.dart

Lines changed: 50 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,13 @@ import 'package:analyzer/dart/element/type.dart';
1010
import '../extensions.dart';
1111
import '../util/dart_type_utilities.dart';
1212

13+
const _nameBuildContext = 'BuildContext';
14+
const _nameContainer = 'Container';
15+
const _nameSizedBox = 'SizedBox';
16+
const _nameState = 'State';
17+
const _nameStatefulWidget = 'StatefulWidget';
18+
const _nameWidget = 'Widget';
19+
1320
var _collectionInterfaces = <InterfaceTypeDefinition>[
1421
InterfaceTypeDefinition('List', 'dart.core'),
1522
InterfaceTypeDefinition('Map', 'dart.core'),
@@ -20,6 +27,10 @@ var _collectionInterfaces = <InterfaceTypeDefinition>[
2027

2128
_Flutter _flutterInstance = _Flutter('flutter', 'package:flutter');
2229

30+
final Uri _uriFramework = Uri.parse(
31+
'package:flutter/src/widgets/framework.dart',
32+
);
33+
2334
_Flutter get _flutter => _flutterInstance;
2435

2536
bool hasWidgetAsAscendant(ClassElement element) =>
@@ -64,13 +75,6 @@ bool isWidgetType(DartType? type) => _flutter.isWidgetType(type);
6475
///
6576
/// See pkg/analysis_server/lib/src/utilities/flutter.dart.
6677
class _Flutter {
67-
static const _nameBuildContext = 'BuildContext';
68-
static const _nameContainer = 'Container';
69-
static const _nameSizedBox = 'SizedBox';
70-
static const _nameState = 'State';
71-
static const _nameStatefulWidget = 'StatefulWidget';
72-
static const _nameWidget = 'Widget';
73-
7478
final String packageName;
7579
final String widgetsUri;
7680

@@ -122,6 +126,9 @@ class _Flutter {
122126
bool isExactWidget(ClassElement element) =>
123127
isExactly(element, _nameWidget, _uriFramework);
124128

129+
bool isExactWidget2(ClassElement2 element) =>
130+
isExactly2(element, _nameWidget, _uriFramework);
131+
125132
bool isExactWidgetTypeContainer(DartType? type) =>
126133
type is InterfaceType &&
127134
isExactly(type.element, _nameContainer, _uriContainer);
@@ -165,3 +172,39 @@ class _Flutter {
165172
bool isWidgetType(DartType? type) =>
166173
type is InterfaceType && isWidget(type.element3);
167174
}
175+
176+
// TODO(pq): based on similar extension in server. (Move and reuse.)
177+
extension InterfaceElementExtension2 on InterfaceElement2? {
178+
bool get extendsWidget => _hasWidgetAsAscendant(this, {});
179+
180+
bool get isExactlyWidget => _isExactly(_nameWidget, _uriFramework);
181+
182+
/// Whether this is the Flutter class `Widget`, or a subtype.
183+
bool get isWidget {
184+
var self = this;
185+
if (self is! ClassElement2) return false;
186+
187+
if (isExactlyWidget) return true;
188+
189+
return self.allSupertypes
190+
.any((type) => type.element3._isExactly(_nameWidget, _uriFramework));
191+
}
192+
193+
/// Whether this is the exact [type] defined in the file with the given [uri].
194+
bool _isExactly(String type, Uri uri) {
195+
var self = this;
196+
return self is ClassElement2 &&
197+
self.name3 == type &&
198+
self.firstFragment.libraryFragment.source.uri == uri;
199+
}
200+
201+
static bool _hasWidgetAsAscendant(
202+
InterfaceElement2? element, Set<InterfaceElement2> alreadySeen) {
203+
if (element == null) return false;
204+
if (element.isExactlyWidget) return true;
205+
206+
if (!alreadySeen.add(element)) return false;
207+
208+
return _hasWidgetAsAscendant(element.supertype?.element3, alreadySeen);
209+
}
210+
}

pkg/linter/test/rules/use_key_in_widget_constructors_test.dart

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,24 @@ class MyWidget extends StatelessWidget {
127127
''');
128128
}
129129

130+
test_implementsWidget() async {
131+
await assertNoDiagnostics(r'''
132+
import 'package:flutter/widgets.dart';
133+
134+
abstract class AbstractWidget implements Widget {}
135+
''');
136+
}
137+
138+
test_key_keyPassedToSuper() async {
139+
await assertNoDiagnostics(r'''
140+
import 'package:flutter/widgets.dart';
141+
142+
abstract class MyWidget extends StatelessWidget {
143+
MyWidget({Key? key}) : super(key: key ?? Key(''));
144+
}
145+
''');
146+
}
147+
130148
test_keyUse_inAugmentedConstructor() async {
131149
newFile('$testPackageLibPath/a.dart', r'''
132150
part of 'test.dart';

0 commit comments

Comments
 (0)