analyzer: Check implicit super-constructor use in ElementUsageDetector

When a generative, non-redirecting constructor does not feature a
super-initiailizer, then the supertype's unnamed constructor is called
implicitly. If that super-constructor is marked for usage (e.g. marked
with `@Deprecated` or `@experimental`), then we should report any
implicit call as usage.

Change-Id: I2c5ae31720a6024b0ff357db686b6d85d2ccdf58
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/455040
Reviewed-by: Paul Berry <paulberry@google.com>
Commit-Queue: Samuel Rawlins <srawlins@google.com>
diff --git a/pkg/analyzer/lib/src/error/best_practices_verifier.dart b/pkg/analyzer/lib/src/error/best_practices_verifier.dart
index 3594be5..536867c 100644
--- a/pkg/analyzer/lib/src/error/best_practices_verifier.dart
+++ b/pkg/analyzer/lib/src/error/best_practices_verifier.dart
@@ -316,6 +316,9 @@
     for (var v in _elementUsageFrontierDetectors) {
       v.pushElement(element);
     }
+    for (var v in _elementUsageFrontierDetectors) {
+      v.constructorDeclaration(node);
+    }
 
     // TODO(srawlins): Use _elementUsageFrontierDetectors to detect
     // `@Deprecated` or `@experimental` parameters in a redirecting factory
diff --git a/pkg/analyzer/lib/src/error/element_usage_detector.dart b/pkg/analyzer/lib/src/error/element_usage_detector.dart
index e72bb6b..58c530d 100644
--- a/pkg/analyzer/lib/src/error/element_usage_detector.dart
+++ b/pkg/analyzer/lib/src/error/element_usage_detector.dart
@@ -113,6 +113,11 @@
       var invokeClass = invokeType.element;
       displayName = '${invokeClass.name}.${element.displayName}';
     }
+
+    // TODO(srawlins): Consider `node` being a `ConstructorDeclaration`, and use
+    // `ConstructorDeclaration.errorRange` here. This would stray from the API
+    // of passing a SyntacticEntity here.
+
     elementUsageReporter.report(
       errorEntity,
       displayName,
@@ -121,6 +126,23 @@
     );
   }
 
+  void constructorDeclaration(ConstructorDeclaration node) {
+    // Check usage of any implicit super-constructor call.
+    // There is only an implicit super-constructor if:
+    // * this is not a factory constructor,
+    // * there is no redirecting constructor invocation, and
+    // * there is no explicit super constructor invocation.
+    if (node.factoryKeyword != null) return;
+    var hasConstructorInvocation = node.initializers.any(
+      (i) =>
+          i is SuperConstructorInvocation ||
+          i is RedirectingConstructorInvocation,
+    );
+    if (hasConstructorInvocation) return;
+
+    checkUsage(node.declaredFragment!.element.superConstructor, node);
+  }
+
   void constructorName(ConstructorName node) {
     checkUsage(node.element, node);
   }
diff --git a/pkg/analyzer/test/src/diagnostics/deprecated_member_use_test.dart b/pkg/analyzer/test/src/diagnostics/deprecated_member_use_test.dart
index 796a872..61f7c30 100644
--- a/pkg/analyzer/test/src/diagnostics/deprecated_member_use_test.dart
+++ b/pkg/analyzer/test/src/diagnostics/deprecated_member_use_test.dart
@@ -1947,6 +1947,41 @@
     );
   }
 
+  test_superConstructor_factoryConstructor() async {
+    await assertNoErrorsInCode2(
+      externalCode: r'''
+class A {
+  @deprecated
+  A();
+  A.two();
+}
+''',
+      code: r'''
+class B extends A {
+  factory B() => B.two();
+  B.two() : super.two();
+}
+''',
+    );
+  }
+
+  test_superConstructor_implicitCall() async {
+    await assertErrorsInCode2(
+      externalCode: r'''
+class A {
+  @deprecated
+  A();
+}
+''',
+      code: r'''
+class B extends A {
+  B();
+}
+''',
+      [error(HintCode.deprecatedMemberUse, 51, 4)],
+    );
+  }
+
   test_superConstructor_namedConstructor() async {
     await assertErrorsInCode2(
       externalCode: r'''
@@ -1971,6 +2006,24 @@
     );
   }
 
+  test_superConstructor_redirectingConstructor() async {
+    await assertNoErrorsInCode2(
+      externalCode: r'''
+class A {
+  @deprecated
+  A();
+  A.two();
+}
+''',
+      code: r'''
+class B extends A {
+  B() : this.two();
+  B.two() : super.two();
+}
+''',
+    );
+  }
+
   test_superConstructor_unnamedConstructor() async {
     await assertErrorsInCode2(
       externalCode: r'''