[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>
This commit is contained in:
Kallen Tu
2025-06-18 08:25:38 -07:00
committed by Commit Queue
parent b3c579d3ee
commit 0bb629e708
2 changed files with 74 additions and 0 deletions
@@ -24,6 +24,7 @@ class PreferConstConstructors extends LintRule {
@override
void registerNodeProcessors(NodeLintRegistry registry, RuleContext context) {
var visitor = _Visitor(this);
registry.addDotShorthandConstructorInvocation(this, visitor);
registry.addInstanceCreationExpression(this, visitor);
}
}
@@ -33,6 +34,30 @@ class _Visitor extends SimpleAstVisitor<void> {
_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;
@@ -74,6 +74,18 @@ var a = A({});
);
}
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 @@ var a = A();
''');
}
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 @@ K k() {
);
}
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 {
@@ -386,6 +429,12 @@ var a = const A();
test_objectConstructorCall() async {
await assertNoDiagnostics(r'''
var x = Object();
''');
}
test_objectConstructorCall_dotShorthand() async {
await assertNoDiagnostics(r'''
Object x = .new();
''');
}
}