diff --git a/pkg/analyzer/lib/src/dart/ast/ast.dart b/pkg/analyzer/lib/src/dart/ast/ast.dart index 3e089699deb..a80efbb0125 100644 --- a/pkg/analyzer/lib/src/dart/ast/ast.dart +++ b/pkg/analyzer/lib/src/dart/ast/ast.dart @@ -6843,7 +6843,7 @@ final class DotShorthandConstructorInvocationImpl DotShorthandConstructorInvocation { @generated @override - final Token? constKeyword; + Token? constKeyword; @generated @override @@ -6875,6 +6875,26 @@ final class DotShorthandConstructorInvocationImpl return period; } + @override + bool get canBeConst { + var element = constructorName.element; + if (element is! ConstructorElementMixin2) return false; + if (!element.isConst) return false; + + // Ensure that dependencies (e.g. default parameter values) are computed. + element.baseElement.computeConstantDependencies(); + + // Verify that the evaluation of the constructor would not produce an + // exception. + var oldKeyword = constKeyword; + try { + constKeyword = KeywordToken(Keyword.CONST, offset); + return !hasConstantVerifierError; + } finally { + constKeyword = oldKeyword; + } + } + @generated @override SimpleIdentifierImpl get constructorName => _constructorName; diff --git a/pkg/analyzer/lib/src/error/best_practices_verifier.dart b/pkg/analyzer/lib/src/error/best_practices_verifier.dart index 652d89fe09a..bcea26ffc92 100644 --- a/pkg/analyzer/lib/src/error/best_practices_verifier.dart +++ b/pkg/analyzer/lib/src/error/best_practices_verifier.dart @@ -325,6 +325,15 @@ class BestPracticesVerifier extends RecursiveAstVisitor { } } + @override + void visitDotShorthandConstructorInvocation( + DotShorthandConstructorInvocation node, + ) { + _deprecatedVerifier.dotShorthandConstructorInvocation(node); + _checkForLiteralConstructorUseInDotShorthand(node); + super.visitDotShorthandConstructorInvocation(node); + } + @override void visitEnumDeclaration(EnumDeclaration node) { _deprecatedVerifier.pushInDeprecatedValue( @@ -1158,6 +1167,24 @@ class BestPracticesVerifier extends RecursiveAstVisitor { } } + /// Report a warning if the dot shorthand constructor is marked with [literal] + /// and is not const. + /// + /// See [WarningCode.NON_CONST_CALL_TO_LITERAL_CONSTRUCTOR]. + void _checkForLiteralConstructorUseInDotShorthand( + DotShorthandConstructorInvocation node, + ) { + var constructor = node.constructorName.element; + if (constructor is! ConstructorElement) return; + if (!node.isConst && constructor.metadata.hasLiteral && node.canBeConst) { + _diagnosticReporter.atNode( + node, + WarningCode.NON_CONST_CALL_TO_LITERAL_CONSTRUCTOR, + arguments: [constructor.displayName], + ); + } + } + /// Check that the imported library does not define a loadLibrary function. /// The import has already been determined to be deferred when this is called. /// diff --git a/pkg/analyzer/lib/src/error/deprecated_member_use_verifier.dart b/pkg/analyzer/lib/src/error/deprecated_member_use_verifier.dart index 3fcf9232ac8..8a467bdf194 100644 --- a/pkg/analyzer/lib/src/error/deprecated_member_use_verifier.dart +++ b/pkg/analyzer/lib/src/error/deprecated_member_use_verifier.dart @@ -36,6 +36,12 @@ abstract class BaseDeprecatedMemberUseVerifier { _checkForDeprecated(node.element, node); } + void dotShorthandConstructorInvocation( + DotShorthandConstructorInvocation node, + ) { + _invocationArguments(node.constructorName.element, node.argumentList); + } + void exportDirective(ExportDirective node) { _checkForDeprecated(node.libraryExport?.exportedLibrary2, 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 6f74cb3f304..bbe43d51aea 100644 --- a/pkg/analyzer/test/src/diagnostics/deprecated_member_use_test.dart +++ b/pkg/analyzer/test/src/diagnostics/deprecated_member_use_test.dart @@ -430,6 +430,125 @@ int g(Object s) => ); } + test_dotShorthandConstructorInvocation_deprecatedClass_deprecatedConstructor() async { + newFile('$workspaceRootPath/aaa/lib/a.dart', r''' +@deprecated +class A { + @deprecated + A(); +} +'''); + + await assertErrorsInCode( + r''' +import 'package:aaa/a.dart'; + +void f() { + A a = .new(); +} +''', + [ + error(HintCode.DEPRECATED_MEMBER_USE, 43, 1), + error(WarningCode.UNUSED_LOCAL_VARIABLE, 45, 1), + error(HintCode.DEPRECATED_MEMBER_USE, 50, 3), + ], + ); + } + + test_dotShorthandConstructorInvocation_deprecatedClass_undeprecatedConstructor() async { + newFile('$workspaceRootPath/aaa/lib/a.dart', r''' +@deprecated +class A { + A(); +} +'''); + + await assertErrorsInCode( + r''' +import 'package:aaa/a.dart'; + +void f() { + A a = .new(); +} +''', + [ + error(HintCode.DEPRECATED_MEMBER_USE, 43, 1), + error(WarningCode.UNUSED_LOCAL_VARIABLE, 45, 1), + ], + ); + } + + test_dotShorthandConstructorInvocation_deprecatedClass_undeprecatedNamedConstructor() async { + newFile('$workspaceRootPath/aaa/lib/a.dart', r''' +@deprecated +class A { + A.a(); +} +'''); + + await assertErrorsInCode( + r''' +import 'package:aaa/a.dart'; + +void f() { + A a = .a(); +} +''', + [ + error(HintCode.DEPRECATED_MEMBER_USE, 43, 1), + error(WarningCode.UNUSED_LOCAL_VARIABLE, 45, 1), + ], + ); + } + + test_dotShorthandConstructorInvocation_namedConstructor() async { + await assertErrorsInCode2( + externalCode: r''' +class A { + @deprecated + A.named(int i) {} +} +''', + code: r''' +f() { + A a = .named(1); +} +''', + [ + error(WarningCode.UNUSED_LOCAL_VARIABLE, 39, 1), + error( + HintCode.DEPRECATED_MEMBER_USE, + 44, + 5, + messageContains: ["'A.named' is deprecated and shouldn't be used."], + ), + ], + ); + } + + test_dotShorthandConstructorInvocation_undeprecatedClass_deprecatedConstructor() async { + newFile('$workspaceRootPath/aaa/lib/a.dart', r''' +class A { + @deprecated + A(); +} +'''); + + await assertErrorsInCode( + r''' +import 'package:aaa/a.dart'; + +void f() { + A a = .new(); +} +''', + [ + error(WarningCode.UNUSED_LOCAL_VARIABLE, 45, 1), + error(HintCode.DEPRECATED_MEMBER_USE, 50, 3), + ], + ); + } + test_export() async { newFile('$workspaceRootPath/aaa/lib/a.dart', r''' @deprecated diff --git a/pkg/analyzer/test/src/diagnostics/non_const_call_to_literal_constructor_test.dart b/pkg/analyzer/test/src/diagnostics/non_const_call_to_literal_constructor_test.dart index 25a00f34a23..04757a69610 100644 --- a/pkg/analyzer/test/src/diagnostics/non_const_call_to_literal_constructor_test.dart +++ b/pkg/analyzer/test/src/diagnostics/non_const_call_to_literal_constructor_test.dart @@ -64,6 +64,34 @@ E e = const E.zero(); '''); } + test_dotShorthand_namedConstructor() async { + await assertErrorsInCode( + r''' +import 'package:meta/meta.dart'; +class A { + @literal + const A.named(); +} +A a = .named(); +''', + [error(WarningCode.NON_CONST_CALL_TO_LITERAL_CONSTRUCTOR, 81, 8)], + ); + } + + test_dotShorthand_unnamedConstructor() async { + await assertErrorsInCode( + r''' +import 'package:meta/meta.dart'; +class A { + @literal + const A(); +} +A a = .new(); +''', + [error(WarningCode.NON_CONST_CALL_TO_LITERAL_CONSTRUCTOR, 75, 6)], + ); + } + test_namedConstructor() async { await assertErrorsInCode( r'''