From 0bb629e708af2253bd2112fe569e84dfd556b135 Mon Sep 17 00:00:00 2001 From: Kallen Tu Date: Wed, 18 Jun 2025 08:25:38 -0700 Subject: [PATCH] [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 Reviewed-by: Samuel Rawlins --- .../src/rules/prefer_const_constructors.dart | 25 ++++++++++ .../rules/prefer_const_constructors_test.dart | 49 +++++++++++++++++++ 2 files changed, 74 insertions(+) diff --git a/pkg/linter/lib/src/rules/prefer_const_constructors.dart b/pkg/linter/lib/src/rules/prefer_const_constructors.dart index 054a79f4b44..7f2e099e432 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 @@ 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 { _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; diff --git a/pkg/linter/test/rules/prefer_const_constructors_test.dart b/pkg/linter/test/rules/prefer_const_constructors_test.dart index eafe8843ad2..4f0c654cde0 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 @@ 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(); '''); } }