Skip to content

Commit ac7059a

Browse files
srawlinsCommit Queue
authored and
Commit Queue
committed
Support extension types in unreachable_from_main
Work towards https://github.com/dart-lang/linter/issues/3625 Change-Id: I41a28795f16eb19b41f864ef512cf10936c2e11f Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/327713 Commit-Queue: Samuel Rawlins <[email protected]> Auto-Submit: Samuel Rawlins <[email protected]> Reviewed-by: Phil Quitslund <[email protected]>
1 parent 5d19972 commit ac7059a

File tree

2 files changed

+86
-12
lines changed

2 files changed

+86
-12
lines changed

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

Lines changed: 36 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,13 @@ const _desc = 'Unreachable top-level members in executable libraries.';
1717

1818
const _details = r'''
1919
Top-level members and static members in an executable library should be used
20+
'''
21+
// TODO(srawlins): Lasse
22+
// [suggests](https://github.com/dart-lang/linter/issues/3625#issuecomment-1735355630)
23+
// changing this to use "statically resolved" members, which I love. But it
24+
// will mean reporting additionally on extension instance members and extension
25+
// type instance members, so land carefully.
26+
'''
2027
directly inside this library. An executable library is a library that contains
2128
a `main` top-level function or that contains a top-level function annotated with
2229
`@pragma('vm:entry-point')`). Executable libraries are not usually imported
@@ -106,6 +113,11 @@ class _DeclarationGatherer {
106113
containerElement: null,
107114
members: declaration.members,
108115
);
116+
} else if (declaration is ExtensionTypeDeclaration) {
117+
_addMembers(
118+
containerElement: null,
119+
members: declaration.members,
120+
);
109121
} else if (declaration is MixinDeclaration) {
110122
_addMembers(
111123
containerElement: declaration.declaredElement,
@@ -241,9 +253,9 @@ class _ReferenceVisitor extends RecursiveAstVisitor {
241253

242254
@override
243255
void visitConstructorDeclaration(ConstructorDeclaration node) {
244-
// If a constructor does not have an explicit super-initializer (or
245-
// redirection?) then it has an implicit super-initializer to the
246-
// super-type's unnamed constructor.
256+
// If a constructor in a class declaration does not have an explicit
257+
// super-initializer (or redirection?) then it has an implicit
258+
// super-initializer to the super-type's unnamed constructor.
247259
var hasSuperInitializer =
248260
node.initializers.any((e) => e is SuperConstructorInvocation);
249261
if (!hasSuperInitializer) {
@@ -264,6 +276,13 @@ class _ReferenceVisitor extends RecursiveAstVisitor {
264276
super.visitConstructorName(node);
265277
}
266278

279+
@override
280+
void visitExtensionTypeDeclaration(ExtensionTypeDeclaration node) {
281+
_addNamedTypes(node.implementsClause?.interfaces);
282+
283+
super.visitExtensionTypeDeclaration(node);
284+
}
285+
267286
@override
268287
void visitNamedType(NamedType node) {
269288
var element = node.element;
@@ -274,14 +293,18 @@ class _ReferenceVisitor extends RecursiveAstVisitor {
274293
var nodeIsInTypeArgument =
275294
node.thisOrAncestorOfType<TypeArgumentList>() != null;
276295

277-
// Any reference to a typedef is reachable, since structural typing is used
278-
// to match against objects.
279-
//
280-
// The return type of an external variable declaration is reachable, since
281-
// the external implementation can instantiate it.
282-
if (node.type?.alias != null ||
283-
nodeIsInTypeArgument ||
284-
node.isInExternalVariableTypeOrFunctionReturnType) {
296+
if (
297+
// Any reference to a typedef marks it as reachable, since structural
298+
// typing is used to match against objects.
299+
node.type?.alias != null ||
300+
// Any reference to an extension type marks it as reachable, since
301+
// casting can be used to instantiate the type.
302+
node.type?.element is ExtensionTypeElement ||
303+
nodeIsInTypeArgument ||
304+
// A reference to any type in an external variable declaration marks
305+
// that type as reachable, since the external implementation can
306+
// instantiate it.
307+
node.isInExternalVariableTypeOrFunctionReturnType) {
285308
_addDeclaration(element);
286309
}
287310

@@ -378,7 +401,8 @@ class _ReferenceVisitor extends RecursiveAstVisitor {
378401
return;
379402
}
380403
if (enclosingElement is InterfaceElement ||
381-
enclosingElement is ExtensionElement) {
404+
enclosingElement is ExtensionElement ||
405+
enclosingElement is ExtensionTypeElement) {
382406
var declarationElement = element.declaration;
383407
var declaration = declarationMap[declarationElement];
384408
if (declaration != null) {

pkg/linter/test/rules/unreachable_from_main_test.dart

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -705,6 +705,20 @@ class C {
705705
]);
706706
}
707707

708+
test_constructor_named_unreachable_inExtensionType() async {
709+
await assertDiagnostics(r'''
710+
void main() {
711+
E(7);
712+
}
713+
714+
extension type E(int it) {
715+
E.named(this.it);
716+
}
717+
''', [
718+
lint(56, 5),
719+
]);
720+
}
721+
708722
test_constructor_named_unreachable_otherHasRedirectedConstructor() async {
709723
await assertDiagnostics(r'''
710724
void main() {
@@ -985,6 +999,27 @@ extension IntExtension on int {}
985999
]);
9861000
}
9871001

1002+
test_extensionType_reachable_referencedInTypeAnnotation_asExpression() async {
1003+
await assertNoDiagnostics(r'''
1004+
void main() {
1005+
1 as E;
1006+
}
1007+
1008+
extension type E(int it) {}
1009+
''');
1010+
}
1011+
1012+
test_extensionType_reachable_referencedInTypeAnnotation_castPattern() async {
1013+
await assertNoDiagnostics(r'''
1014+
void main() {
1015+
var r = (1, );
1016+
var (s as E, ) = r;
1017+
}
1018+
1019+
extension type E(int it) {}
1020+
''');
1021+
}
1022+
9881023
test_mixin_reachable_implemented() async {
9891024
await assertNoDiagnostics(r'''
9901025
void main() {
@@ -1070,6 +1105,21 @@ extension E on int {
10701105
''');
10711106
}
10721107

1108+
test_staticFieldOnExtension_unreachable() async {
1109+
await assertDiagnostics(r'''
1110+
void main() {
1111+
E(1).m();
1112+
}
1113+
1114+
extension E on int {
1115+
static int f = 1;
1116+
void m() {}
1117+
}
1118+
''', [
1119+
lint(63, 1),
1120+
]);
1121+
}
1122+
10731123
test_staticFieldOnMixin_reachable() async {
10741124
await assertNoDiagnostics(r'''
10751125
void main() {

0 commit comments

Comments
 (0)