[analysis_server] Dot shorthands: Update prefer_const_constructors lint.

Now that `DotShorthandsConstructorInvocation`'s `canBeConst` is updated, we can also update the `prefer_const_constructors` lint.

We skip the lint when we have the `@literal` metadata on the constructor since it's already reporting a warning here: https://dart-review.googlesource.com/c/sdk/+/434881. Similar to instance creation expressions, we also skip reporting a lint if we're instantiating the `Object` class.

Otherwise, if the constructor we're calling with the shorthand is const and we can const-ify it, we report this lint.

Added unit tests.

Fixes: https://github.com/dart-lang/sdk/issues/60911
Bug: https://github.com/dart-lang/sdk/issues/60893
Change-Id: Ib4bcba629f60e0dbff110080918adfacfb1ba5d2
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/434981
Commit-Queue: Kallen Tu <kallentu@google.com>
Reviewed-by: Samuel Rawlins <srawlins@google.com>
diff --git a/pkg/linter/lib/src/rules/prefer_const_constructors.dart b/pkg/linter/lib/src/rules/prefer_const_constructors.dart
index 054a79f..7f2e099 100644
--- a/pkg/linter/lib/src/rules/prefer_const_constructors.dart
+++ b/pkg/linter/lib/src/rules/prefer_const_constructors.dart
@@ -24,6 +24,7 @@
   @override
   void registerNodeProcessors(NodeLintRegistry registry, RuleContext context) {
     var visitor = _Visitor(this);
+    registry.addDotShorthandConstructorInvocation(this, visitor);
     registry.addInstanceCreationExpression(this, visitor);
   }
 }
@@ -34,6 +35,30 @@
   _Visitor(this.rule);
 
   @override
+  void visitDotShorthandConstructorInvocation(
+    DotShorthandConstructorInvocation node,
+  ) {
+    if (node.isConst) return;
+
+    var element = node.constructorName.element;
+    if (element is! ConstructorElement) return;
+    if (!element.isConst) return;
+
+    // Handled by an analyzer warning.
+    if (element.metadata.hasLiteral) return;
+
+    var enclosingElement = element.enclosingElement;
+    if (enclosingElement is ClassElement && enclosingElement.isDartCoreObject) {
+      // Skip lint for `new Object()`, because it can be used for ID creation.
+      return;
+    }
+
+    if (node.canBeConst) {
+      rule.reportAtNode(node);
+    }
+  }
+
+  @override
   void visitInstanceCreationExpression(InstanceCreationExpression node) {
     if (node.isConst) return;
     if (node.constructorName.type.isDeferred) return;
diff --git a/pkg/linter/test/rules/prefer_const_constructors_test.dart b/pkg/linter/test/rules/prefer_const_constructors_test.dart
index eafe884..4f0c654 100644
--- a/pkg/linter/test/rules/prefer_const_constructors_test.dart
+++ b/pkg/linter/test/rules/prefer_const_constructors_test.dart
@@ -74,6 +74,18 @@
     );
   }
 
+  test_canBeConst_dotShorthand() async {
+    await assertDiagnostics(
+      r'''
+class A {
+  const A();
+}
+A a = .new();
+''',
+      [lint(31, 6)],
+    );
+  }
+
   test_canBeConst_explicitTypeArgument_dynamic() async {
     await assertDiagnostics(
       r'''
@@ -275,6 +287,15 @@
 ''');
   }
 
+  test_cannotBeConst_notConstConstructor_dotShorthand() async {
+    await assertNoDiagnostics(r'''
+class A {
+  A();
+}
+A a = .new();
+''');
+  }
+
   test_cannotBeConst_stringLiteralArgument_withInterpolation() async {
     await assertNoDiagnostics(r'''
 class A {
@@ -363,6 +384,28 @@
     );
   }
 
+  test_extraPositionalArgument_dotShorthands() async {
+    await assertDiagnostics(
+      r'''
+import 'package:meta/meta.dart';
+
+class K {
+  @literal
+  const K();
+}
+
+K k() {
+  K kk = .new();
+  return kk;
+}
+''',
+      [
+        // No lint
+        error(WarningCode.NON_CONST_CALL_TO_LITERAL_CONSTRUCTOR, 88, 6),
+      ],
+    );
+  }
+
   test_isConst_intLiteralArgument() async {
     await assertNoDiagnostics(r'''
 class A {
@@ -388,4 +431,10 @@
 var x = Object();
 ''');
   }
+
+  test_objectConstructorCall_dotShorthand() async {
+    await assertNoDiagnostics(r'''
+Object x = .new();
+''');
+  }
 }