Skip to content

Commit d0d0952

Browse files
srawlinscommit-bot@chromium.org
authored andcommitted
analyzer: Report unused public members of private and unnamed extensions
Fixes #41592 Change-Id: I95b79724bfcfc5d48b8f6cca61c6a3a681de3662 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/144353 Commit-Queue: Samuel Rawlins <[email protected]> Reviewed-by: Brian Wilkerson <[email protected]>
1 parent 1279bbf commit d0d0952

File tree

6 files changed

+68
-11
lines changed

6 files changed

+68
-11
lines changed

pkg/analyzer/lib/src/error/unused_local_elements_verifier.dart

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -323,13 +323,18 @@ class UnusedLocalElementsVerifier extends RecursiveAstVisitor {
323323
return _usedElements.elements.contains(element);
324324
}
325325

326-
bool _isUsedMember(Element element) {
326+
bool _isUsedMember(ExecutableElement element) {
327+
var enclosingElement = element.enclosingElement;
327328
if (element.isPublic) {
328-
if (_isPrivateClassOrExtension(element.enclosingElement) &&
329-
element is ExecutableElement &&
329+
if (enclosingElement is ClassElement &&
330+
enclosingElement.isPrivate &&
330331
element.isStatic) {
331-
// Public static members of private classes, mixins, and extensions are
332-
// inaccessible from outside the library in which they are declared.
332+
// Public static members of private classes and mixins are inaccessible
333+
// from outside the library in which they are declared.
334+
} else if (enclosingElement is ExtensionElement &&
335+
enclosingElement.isPrivate) {
336+
// Public members of private extensions are inaccessible from outside
337+
// the library in which they are declared.
333338
} else {
334339
return true;
335340
}

pkg/analyzer/test/src/dart/resolution/extension_method_test.dart

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1414,7 +1414,7 @@ f() => E.a;
14141414

14151415
test_thisAccessOnDynamic() async {
14161416
await assertNoErrorsInCode('''
1417-
extension on dynamic {
1417+
extension E on dynamic {
14181418
int get d => 3;
14191419
14201420
void testDynamic() {
@@ -1427,7 +1427,7 @@ extension on dynamic {
14271427

14281428
test_thisAccessOnFunction() async {
14291429
await assertNoErrorsInCode('''
1430-
extension on Function {
1430+
extension E on Function {
14311431
int get f => 4;
14321432
14331433
void testFunction() {

pkg/analyzer/test/src/diagnostics/super_in_extension_test.dart

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ extension <T> on T {
3333
}
3434
}
3535
''', [
36+
error(HintCode.UNUSED_ELEMENT, 23, 1),
3637
error(CompileTimeErrorCode.SUPER_IN_EXTENSION, 33, 5),
3738
]);
3839
}

pkg/analyzer/test/src/diagnostics/unused_element_test.dart

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -715,6 +715,17 @@ main() {
715715
''');
716716
}
717717

718+
test_method_isUsed_privateExtension() async {
719+
await assertNoErrorsInCode(r'''
720+
extension _A on String {
721+
void m() {}
722+
}
723+
void main() {
724+
"hello".m();
725+
}
726+
''');
727+
}
728+
718729
test_method_isUsed_staticInvocation() async {
719730
await assertNoErrorsInCode(r'''
720731
class A {
@@ -726,6 +737,17 @@ main() {
726737
''');
727738
}
728739

740+
test_method_isUsed_unnamedExtension() async {
741+
await assertNoErrorsInCode(r'''
742+
extension on String {
743+
void m() {}
744+
}
745+
void main() {
746+
"hello".m();
747+
}
748+
''');
749+
}
750+
729751
test_method_notUsed_hasSameNameAsUsed() async {
730752
await assertErrorsInCode(r'''
731753
class A {
@@ -750,6 +772,16 @@ class A {
750772
]);
751773
}
752774

775+
test_method_notUsed_privateExtension() async {
776+
await assertErrorsInCode(r'''
777+
extension _A on String {
778+
void m() {}
779+
}
780+
''', [
781+
error(HintCode.UNUSED_ELEMENT, 32, 1),
782+
]);
783+
}
784+
753785
test_method_notUsed_referenceFromItself() async {
754786
await assertErrorsInCode(r'''
755787
class A {
@@ -785,6 +817,16 @@ int g() => 7;
785817
]);
786818
}
787819

820+
test_method_notUsed_unnamedExtension() async {
821+
await assertErrorsInCode(r'''
822+
extension on String {
823+
void m() {}
824+
}
825+
''', [
826+
error(HintCode.UNUSED_ELEMENT, 29, 1),
827+
]);
828+
}
829+
788830
test_publicStaticMethod_privateClass_isUsed() async {
789831
await assertNoErrorsInCode(r'''
790832
class _A {

pkg/analyzer/test/src/diagnostics/use_of_void_result_test.dart

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -568,6 +568,7 @@ extension on void {
568568
}
569569
}
570570
''', [
571+
error(HintCode.UNUSED_ELEMENT, 22, 8),
571572
error(StaticWarningCode.USE_OF_VOID_RESULT, 96, 4),
572573
]);
573574
}

pkg/analyzer/test/src/lint/linter/resolve_name_in_scope_test.dart

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -551,7 +551,9 @@ extension on A {
551551
this.foo();
552552
}
553553
}
554-
''');
554+
''', [
555+
error(HintCode.UNUSED_ELEMENT, 53, 3),
556+
]);
555557
_checkMethodNone();
556558
}
557559

@@ -566,7 +568,9 @@ extension on A {
566568
this.foo();
567569
}
568570
}
569-
''');
571+
''', [
572+
error(HintCode.UNUSED_ELEMENT, 53, 3),
573+
]);
570574
_checkMethodRequested(findElement.parameter('foo'));
571575
}
572576

@@ -583,7 +587,9 @@ extension on A {
583587
}
584588
585589
var foo = 0;
586-
''');
590+
''', [
591+
error(HintCode.UNUSED_ELEMENT, 53, 3),
592+
]);
587593
_checkMethodRequested(findElement.topGet('foo'));
588594
}
589595

@@ -598,7 +604,9 @@ extension on A {
598604
this.foo();
599605
}
600606
}
601-
''');
607+
''', [
608+
error(HintCode.UNUSED_ELEMENT, 53, 3),
609+
]);
602610
_checkMethodRequested(findElement.method('foo'));
603611
}
604612

0 commit comments

Comments
 (0)