Skip to content

Commit 4431536

Browse files
kallentuCommit Queue
authored andcommitted
[analyzer] Dot shorthands: Report extra deprecation and @literal warnings.
Update the BestPracticesVerifier to report warnings on deprecated member usage and calling a `@literal` constructor without const. Added unit tests and updated the AST. Bug: #59835 Change-Id: I15aff72c00e9bead386f83c3a6f1054ca8324dc9 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/434881 Reviewed-by: Samuel Rawlins <[email protected]> Reviewed-by: Paul Berry <[email protected]> Commit-Queue: Kallen Tu <[email protected]>
1 parent ce32e04 commit 4431536

File tree

5 files changed

+201
-1
lines changed

5 files changed

+201
-1
lines changed

pkg/analyzer/lib/src/dart/ast/ast.dart

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6843,7 +6843,7 @@ final class DotShorthandConstructorInvocationImpl
68436843
DotShorthandConstructorInvocation {
68446844
@generated
68456845
@override
6846-
final Token? constKeyword;
6846+
Token? constKeyword;
68476847

68486848
@generated
68496849
@override
@@ -6875,6 +6875,26 @@ final class DotShorthandConstructorInvocationImpl
68756875
return period;
68766876
}
68776877

6878+
@override
6879+
bool get canBeConst {
6880+
var element = constructorName.element;
6881+
if (element is! ConstructorElementMixin2) return false;
6882+
if (!element.isConst) return false;
6883+
6884+
// Ensure that dependencies (e.g. default parameter values) are computed.
6885+
element.baseElement.computeConstantDependencies();
6886+
6887+
// Verify that the evaluation of the constructor would not produce an
6888+
// exception.
6889+
var oldKeyword = constKeyword;
6890+
try {
6891+
constKeyword = KeywordToken(Keyword.CONST, offset);
6892+
return !hasConstantVerifierError;
6893+
} finally {
6894+
constKeyword = oldKeyword;
6895+
}
6896+
}
6897+
68786898
@generated
68796899
@override
68806900
SimpleIdentifierImpl get constructorName => _constructorName;

pkg/analyzer/lib/src/error/best_practices_verifier.dart

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -325,6 +325,15 @@ class BestPracticesVerifier extends RecursiveAstVisitor<void> {
325325
}
326326
}
327327

328+
@override
329+
void visitDotShorthandConstructorInvocation(
330+
DotShorthandConstructorInvocation node,
331+
) {
332+
_deprecatedVerifier.dotShorthandConstructorInvocation(node);
333+
_checkForLiteralConstructorUseInDotShorthand(node);
334+
super.visitDotShorthandConstructorInvocation(node);
335+
}
336+
328337
@override
329338
void visitEnumDeclaration(EnumDeclaration node) {
330339
_deprecatedVerifier.pushInDeprecatedValue(
@@ -1158,6 +1167,24 @@ class BestPracticesVerifier extends RecursiveAstVisitor<void> {
11581167
}
11591168
}
11601169

1170+
/// Report a warning if the dot shorthand constructor is marked with [literal]
1171+
/// and is not const.
1172+
///
1173+
/// See [WarningCode.NON_CONST_CALL_TO_LITERAL_CONSTRUCTOR].
1174+
void _checkForLiteralConstructorUseInDotShorthand(
1175+
DotShorthandConstructorInvocation node,
1176+
) {
1177+
var constructor = node.constructorName.element;
1178+
if (constructor is! ConstructorElement) return;
1179+
if (!node.isConst && constructor.metadata.hasLiteral && node.canBeConst) {
1180+
_diagnosticReporter.atNode(
1181+
node,
1182+
WarningCode.NON_CONST_CALL_TO_LITERAL_CONSTRUCTOR,
1183+
arguments: [constructor.displayName],
1184+
);
1185+
}
1186+
}
1187+
11611188
/// Check that the imported library does not define a loadLibrary function.
11621189
/// The import has already been determined to be deferred when this is called.
11631190
///

pkg/analyzer/lib/src/error/deprecated_member_use_verifier.dart

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,12 @@ abstract class BaseDeprecatedMemberUseVerifier {
3636
_checkForDeprecated(node.element, node);
3737
}
3838

39+
void dotShorthandConstructorInvocation(
40+
DotShorthandConstructorInvocation node,
41+
) {
42+
_invocationArguments(node.constructorName.element, node.argumentList);
43+
}
44+
3945
void exportDirective(ExportDirective node) {
4046
_checkForDeprecated(node.libraryExport?.exportedLibrary2, node);
4147
}

pkg/analyzer/test/src/diagnostics/deprecated_member_use_test.dart

Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -430,6 +430,125 @@ int g(Object s) =>
430430
);
431431
}
432432

433+
test_dotShorthandConstructorInvocation_deprecatedClass_deprecatedConstructor() async {
434+
newFile('$workspaceRootPath/aaa/lib/a.dart', r'''
435+
@deprecated
436+
class A {
437+
@deprecated
438+
A();
439+
}
440+
''');
441+
442+
await assertErrorsInCode(
443+
r'''
444+
import 'package:aaa/a.dart';
445+
446+
void f() {
447+
A a = .new();
448+
}
449+
''',
450+
[
451+
error(HintCode.DEPRECATED_MEMBER_USE, 43, 1),
452+
error(WarningCode.UNUSED_LOCAL_VARIABLE, 45, 1),
453+
error(HintCode.DEPRECATED_MEMBER_USE, 50, 3),
454+
],
455+
);
456+
}
457+
458+
test_dotShorthandConstructorInvocation_deprecatedClass_undeprecatedConstructor() async {
459+
newFile('$workspaceRootPath/aaa/lib/a.dart', r'''
460+
@deprecated
461+
class A {
462+
A();
463+
}
464+
''');
465+
466+
await assertErrorsInCode(
467+
r'''
468+
import 'package:aaa/a.dart';
469+
470+
void f() {
471+
A a = .new();
472+
}
473+
''',
474+
[
475+
error(HintCode.DEPRECATED_MEMBER_USE, 43, 1),
476+
error(WarningCode.UNUSED_LOCAL_VARIABLE, 45, 1),
477+
],
478+
);
479+
}
480+
481+
test_dotShorthandConstructorInvocation_deprecatedClass_undeprecatedNamedConstructor() async {
482+
newFile('$workspaceRootPath/aaa/lib/a.dart', r'''
483+
@deprecated
484+
class A {
485+
A.a();
486+
}
487+
''');
488+
489+
await assertErrorsInCode(
490+
r'''
491+
import 'package:aaa/a.dart';
492+
493+
void f() {
494+
A a = .a();
495+
}
496+
''',
497+
[
498+
error(HintCode.DEPRECATED_MEMBER_USE, 43, 1),
499+
error(WarningCode.UNUSED_LOCAL_VARIABLE, 45, 1),
500+
],
501+
);
502+
}
503+
504+
test_dotShorthandConstructorInvocation_namedConstructor() async {
505+
await assertErrorsInCode2(
506+
externalCode: r'''
507+
class A {
508+
@deprecated
509+
A.named(int i) {}
510+
}
511+
''',
512+
code: r'''
513+
f() {
514+
A a = .named(1);
515+
}
516+
''',
517+
[
518+
error(WarningCode.UNUSED_LOCAL_VARIABLE, 39, 1),
519+
error(
520+
HintCode.DEPRECATED_MEMBER_USE,
521+
44,
522+
5,
523+
messageContains: ["'A.named' is deprecated and shouldn't be used."],
524+
),
525+
],
526+
);
527+
}
528+
529+
test_dotShorthandConstructorInvocation_undeprecatedClass_deprecatedConstructor() async {
530+
newFile('$workspaceRootPath/aaa/lib/a.dart', r'''
531+
class A {
532+
@deprecated
533+
A();
534+
}
535+
''');
536+
537+
await assertErrorsInCode(
538+
r'''
539+
import 'package:aaa/a.dart';
540+
541+
void f() {
542+
A a = .new();
543+
}
544+
''',
545+
[
546+
error(WarningCode.UNUSED_LOCAL_VARIABLE, 45, 1),
547+
error(HintCode.DEPRECATED_MEMBER_USE, 50, 3),
548+
],
549+
);
550+
}
551+
433552
test_export() async {
434553
newFile('$workspaceRootPath/aaa/lib/a.dart', r'''
435554
@deprecated

pkg/analyzer/test/src/diagnostics/non_const_call_to_literal_constructor_test.dart

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,34 @@ E e = const E.zero();
6464
''');
6565
}
6666

67+
test_dotShorthand_namedConstructor() async {
68+
await assertErrorsInCode(
69+
r'''
70+
import 'package:meta/meta.dart';
71+
class A {
72+
@literal
73+
const A.named();
74+
}
75+
A a = .named();
76+
''',
77+
[error(WarningCode.NON_CONST_CALL_TO_LITERAL_CONSTRUCTOR, 81, 8)],
78+
);
79+
}
80+
81+
test_dotShorthand_unnamedConstructor() async {
82+
await assertErrorsInCode(
83+
r'''
84+
import 'package:meta/meta.dart';
85+
class A {
86+
@literal
87+
const A();
88+
}
89+
A a = .new();
90+
''',
91+
[error(WarningCode.NON_CONST_CALL_TO_LITERAL_CONSTRUCTOR, 75, 6)],
92+
);
93+
}
94+
6795
test_namedConstructor() async {
6896
await assertErrorsInCode(
6997
r'''

0 commit comments

Comments
 (0)