Skip to content

Commit

Permalink
Version 3.3.0-10.0.dev
Browse files Browse the repository at this point in the history
Merge ac7059a into dev
  • Loading branch information
Dart CI committed Oct 11, 2023
2 parents f8130d5 + ac7059a commit 4ea301f
Show file tree
Hide file tree
Showing 3 changed files with 87 additions and 13 deletions.
48 changes: 36 additions & 12 deletions pkg/linter/lib/src/rules/unreachable_from_main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,13 @@ const _desc = 'Unreachable top-level members in executable libraries.';

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

@override
void visitConstructorDeclaration(ConstructorDeclaration node) {
// If a constructor does not have an explicit super-initializer (or
// redirection?) then it has an implicit super-initializer to the
// super-type's unnamed constructor.
// If a constructor in a class declaration does not have an explicit
// super-initializer (or redirection?) then it has an implicit
// super-initializer to the super-type's unnamed constructor.
var hasSuperInitializer =
node.initializers.any((e) => e is SuperConstructorInvocation);
if (!hasSuperInitializer) {
Expand All @@ -264,6 +276,13 @@ class _ReferenceVisitor extends RecursiveAstVisitor {
super.visitConstructorName(node);
}

@override
void visitExtensionTypeDeclaration(ExtensionTypeDeclaration node) {
_addNamedTypes(node.implementsClause?.interfaces);

super.visitExtensionTypeDeclaration(node);
}

@override
void visitNamedType(NamedType node) {
var element = node.element;
Expand All @@ -274,14 +293,18 @@ class _ReferenceVisitor extends RecursiveAstVisitor {
var nodeIsInTypeArgument =
node.thisOrAncestorOfType<TypeArgumentList>() != null;

// Any reference to a typedef is reachable, since structural typing is used
// to match against objects.
//
// The return type of an external variable declaration is reachable, since
// the external implementation can instantiate it.
if (node.type?.alias != null ||
nodeIsInTypeArgument ||
node.isInExternalVariableTypeOrFunctionReturnType) {
if (
// Any reference to a typedef marks it as reachable, since structural
// typing is used to match against objects.
node.type?.alias != null ||
// Any reference to an extension type marks it as reachable, since
// casting can be used to instantiate the type.
node.type?.element is ExtensionTypeElement ||
nodeIsInTypeArgument ||
// A reference to any type in an external variable declaration marks
// that type as reachable, since the external implementation can
// instantiate it.
node.isInExternalVariableTypeOrFunctionReturnType) {
_addDeclaration(element);
}

Expand Down Expand Up @@ -378,7 +401,8 @@ class _ReferenceVisitor extends RecursiveAstVisitor {
return;
}
if (enclosingElement is InterfaceElement ||
enclosingElement is ExtensionElement) {
enclosingElement is ExtensionElement ||
enclosingElement is ExtensionTypeElement) {
var declarationElement = element.declaration;
var declaration = declarationMap[declarationElement];
if (declaration != null) {
Expand Down
50 changes: 50 additions & 0 deletions pkg/linter/test/rules/unreachable_from_main_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -705,6 +705,20 @@ class C {
]);
}

test_constructor_named_unreachable_inExtensionType() async {
await assertDiagnostics(r'''
void main() {
E(7);
}
extension type E(int it) {
E.named(this.it);
}
''', [
lint(56, 5),
]);
}

test_constructor_named_unreachable_otherHasRedirectedConstructor() async {
await assertDiagnostics(r'''
void main() {
Expand Down Expand Up @@ -985,6 +999,27 @@ extension IntExtension on int {}
]);
}

test_extensionType_reachable_referencedInTypeAnnotation_asExpression() async {
await assertNoDiagnostics(r'''
void main() {
1 as E;
}
extension type E(int it) {}
''');
}

test_extensionType_reachable_referencedInTypeAnnotation_castPattern() async {
await assertNoDiagnostics(r'''
void main() {
var r = (1, );
var (s as E, ) = r;
}
extension type E(int it) {}
''');
}

test_mixin_reachable_implemented() async {
await assertNoDiagnostics(r'''
void main() {
Expand Down Expand Up @@ -1070,6 +1105,21 @@ extension E on int {
''');
}

test_staticFieldOnExtension_unreachable() async {
await assertDiagnostics(r'''
void main() {
E(1).m();
}
extension E on int {
static int f = 1;
void m() {}
}
''', [
lint(63, 1),
]);
}

test_staticFieldOnMixin_reachable() async {
await assertNoDiagnostics(r'''
void main() {
Expand Down
2 changes: 1 addition & 1 deletion tools/VERSION
Original file line number Diff line number Diff line change
Expand Up @@ -27,5 +27,5 @@ CHANNEL dev
MAJOR 3
MINOR 3
PATCH 0
PRERELEASE 9
PRERELEASE 10
PRERELEASE_PATCH 0

0 comments on commit 4ea301f

Please sign in to comment.