Skip to content

Commit c7765a8

Browse files
kallentuCommit Queue
authored andcommitted
[analyzer/linter] Dot shorthands: Update ConstantVerifier for dot shorthand constructor invocations.
Updates the `ConstantVerifier` so we're checking the dot shorthand constructor invocation is a constant constructor call and that its arguments are constant expressions. Tweaked the constant error handling a little bit to handle dot shorthands. It's always a little funky, but needed to prevent duplicate errors. Added a few unit tests from the example in the bug. Fixes: #60963 Bug: #60893 Change-Id: I77b2dff4a274241b2c98b3e6cd34c620d08435a7 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/436742 Reviewed-by: Brian Wilkerson <[email protected]> Commit-Queue: Kallen Tu <[email protected]> Reviewed-by: Chloe Stefantsova <[email protected]>
1 parent 59db156 commit c7765a8

File tree

4 files changed

+111
-38
lines changed

4 files changed

+111
-38
lines changed

pkg/analyzer/lib/src/dart/constant/constant_verifier.dart

Lines changed: 56 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -206,6 +206,24 @@ class ConstantVerifier extends RecursiveAstVisitor<void> {
206206
}
207207
}
208208

209+
@override
210+
void visitDotShorthandConstructorInvocation(
211+
DotShorthandConstructorInvocation node,
212+
) {
213+
if (node.isConst) {
214+
var constructor = node.constructorName.element;
215+
if (constructor is ConstructorElementMixin2) {
216+
_validateConstructorInvocation(
217+
node,
218+
constructor.asElement,
219+
node.argumentList,
220+
);
221+
}
222+
} else {
223+
super.visitDotShorthandConstructorInvocation(node);
224+
}
225+
}
226+
209227
@override
210228
visitEnumConstantDeclaration(covariant EnumConstantDeclarationImpl node) {
211229
super.visitEnumConstantDeclaration(node);
@@ -270,38 +288,9 @@ class ConstantVerifier extends RecursiveAstVisitor<void> {
270288
CompileTimeErrorCode.CONST_WITH_TYPE_PARAMETERS,
271289
);
272290

273-
// We need to evaluate the constant to see if any errors occur during its
274-
// evaluation.
275291
var constructor = node.constructorName.element?.asElement;
276292
if (constructor != null) {
277-
var constantVisitor = ConstantVisitor(
278-
_evaluationEngine,
279-
_currentLibrary,
280-
_diagnosticReporter,
281-
);
282-
var result = _evaluationEngine.evaluateAndFormatErrorsInConstructorCall(
283-
_currentLibrary,
284-
node,
285-
constructor.returnType.typeArguments,
286-
node.argumentList.arguments,
287-
constructor,
288-
constantVisitor,
289-
);
290-
switch (result) {
291-
case InvalidConstant():
292-
if (!result.avoidReporting) {
293-
_diagnosticReporter.atOffset(
294-
offset: result.offset,
295-
length: result.length,
296-
diagnosticCode: result.diagnosticCode,
297-
arguments: result.arguments,
298-
contextMessages: result.contextMessages,
299-
);
300-
}
301-
case DartObjectImpl():
302-
// Check for further errors in individual arguments.
303-
node.argumentList.accept(this);
304-
}
293+
_validateConstructorInvocation(node, constructor, node.argumentList);
305294
}
306295
} else {
307296
super.visitInstanceCreationExpression(node);
@@ -923,6 +912,43 @@ class ConstantVerifier extends RecursiveAstVisitor<void> {
923912
}
924913
}
925914

915+
/// Validates that the [constructor] invocation, its type arguments, and its
916+
/// arguments are constant expressions.
917+
void _validateConstructorInvocation(
918+
AstNode node,
919+
ConstructorElementMixin constructor,
920+
ArgumentList argumentList,
921+
) {
922+
var constantVisitor = ConstantVisitor(
923+
_evaluationEngine,
924+
_currentLibrary,
925+
_diagnosticReporter,
926+
);
927+
var result = _evaluationEngine.evaluateAndFormatErrorsInConstructorCall(
928+
_currentLibrary,
929+
node,
930+
constructor.returnType.typeArguments,
931+
argumentList.arguments,
932+
constructor,
933+
constantVisitor,
934+
);
935+
switch (result) {
936+
case InvalidConstant():
937+
if (!result.avoidReporting) {
938+
_diagnosticReporter.atOffset(
939+
offset: result.offset,
940+
length: result.length,
941+
diagnosticCode: result.diagnosticCode,
942+
arguments: result.arguments,
943+
contextMessages: result.contextMessages,
944+
);
945+
}
946+
case DartObjectImpl():
947+
// Check for further errors in individual arguments.
948+
argumentList.accept(this);
949+
}
950+
}
951+
926952
/// Validates that the default value associated with each of the parameters in
927953
/// [parameters] is a constant expression.
928954
void _validateDefaultValues(covariant FormalParameterListImpl? parameters) {

pkg/analyzer/lib/src/dart/constant/evaluation.dart

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3560,17 +3560,14 @@ class _InstanceCreationEvaluator {
35603560
ConstructorInvocation? invocation,
35613561
}) {
35623562
if (!constructor.isConst) {
3563+
Token? keyword;
35633564
if (node is InstanceCreationExpression) {
3564-
var newKeyword = node.keyword;
3565-
if (newKeyword != null) {
3566-
return InvalidConstant.forEntity(
3567-
entity: newKeyword,
3568-
diagnosticCode: CompileTimeErrorCode.CONST_WITH_NON_CONST,
3569-
);
3570-
}
3565+
keyword = node.keyword;
3566+
} else if (node is DotShorthandConstructorInvocation) {
3567+
keyword = node.constKeyword;
35713568
}
35723569
return InvalidConstant.forEntity(
3573-
entity: node,
3570+
entity: keyword ?? node,
35743571
diagnosticCode: CompileTimeErrorCode.CONST_WITH_NON_CONST,
35753572
);
35763573
}

pkg/analyzer/test/src/dart/constant/evaluation_test.dart

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6111,6 +6111,44 @@ bool true
61116111
);
61126112
}
61136113

6114+
test_dotShorthand_constantArgument_issue60963() async {
6115+
await assertNoErrorsInCode('''
6116+
class A {
6117+
const A();
6118+
}
6119+
extension type const B(A a) {}
6120+
6121+
const B b = .new(A());
6122+
''');
6123+
var result = _topLevelVar('b');
6124+
assertDartObjectText(result, '''
6125+
A
6126+
variable: <testLibrary>::@topLevelVariable::b
6127+
''');
6128+
}
6129+
6130+
test_dotShorthand_nonConstantArgument_issue60963() async {
6131+
await assertErrorsInCode(
6132+
'''
6133+
class A {
6134+
int cannotBeConst;
6135+
A(): cannotBeConst = 0;
6136+
}
6137+
extension type const B(A a) {}
6138+
6139+
const B b = .new(A());
6140+
''',
6141+
[
6142+
error(CompileTimeErrorCode.CONST_WITH_NON_CONST, 108, 3),
6143+
error(
6144+
CompileTimeErrorCode.CONST_INITIALIZED_WITH_NON_CONSTANT_VALUE,
6145+
108,
6146+
3,
6147+
),
6148+
],
6149+
);
6150+
}
6151+
61146152
test_field_deferred_issue48991() async {
61156153
newFile('$testPackageLibPath/a.dart', '''
61166154
class A {

pkg/linter/test/rules/prefer_const_constructors_test.dart

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -258,6 +258,18 @@ A f(List<int> l) => A(l);
258258
''');
259259
}
260260

261+
test_cannotBeConst_dotShorthands_issue60963() async {
262+
await assertNoDiagnostics(r'''
263+
class A {
264+
int cannotBeConst;
265+
A(): cannotBeConst = 0;
266+
}
267+
extension type const B(A a) {}
268+
269+
B get b => .new(A());
270+
''');
271+
}
272+
261273
test_cannotBeConst_explicitTypeArgument_typeVariable() async {
262274
await assertNoDiagnostics(r'''
263275
class A<T> {

0 commit comments

Comments
 (0)