[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: https://github.com/dart-lang/sdk/issues/59835 Change-Id: I15aff72c00e9bead386f83c3a6f1054ca8324dc9 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/434881 Reviewed-by: Samuel Rawlins <srawlins@google.com> Reviewed-by: Paul Berry <paulberry@google.com> Commit-Queue: Kallen Tu <kallentu@google.com>
This commit is contained in:
@@ -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;
|
||||
|
||||
@@ -325,6 +325,15 @@ class BestPracticesVerifier extends RecursiveAstVisitor<void> {
|
||||
}
|
||||
}
|
||||
|
||||
@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<void> {
|
||||
}
|
||||
}
|
||||
|
||||
/// 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.
|
||||
///
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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'''
|
||||
|
||||
Reference in New Issue
Block a user