Skip to content

Commit c12926e

Browse files
kallentuCommit Queue
authored and
Commit Queue
committed
[analyzer] Dot shorthands: Const constructor invocations.
Added `isConst` and `constKeyword` to `DotShorthandConstructorInvocation`. `DotShorthandInvocation`s are rewritten to `DotShorthandConstructorInvocation` immediately once we parse the `const` keyword. If we don't resolve to a constructor, we'll produce the `CONST_WITH_UNDEFINED_CONSTRUCTOR` error. Summary changes in a different CL that follows this one: https://dart-review.googlesource.com/c/sdk/+/424340 co19 tests passing and unit tests added. Bug: #59835 Change-Id: Ic12cd98cf08009187b8bf32afb62aa026cb7c8c7 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/424240 Reviewed-by: Paul Berry <[email protected]> Reviewed-by: Brian Wilkerson <[email protected]> Commit-Queue: Kallen Tu <[email protected]>
1 parent 5d4d797 commit c12926e

11 files changed

+249
-27
lines changed

pkg/analyzer/api.txt

+2
Original file line numberDiff line numberDiff line change
@@ -721,7 +721,9 @@ package:analyzer/dart/ast/ast.dart:
721721
semicolon (getter: Token)
722722
whileKeyword (getter: Token)
723723
DotShorthandConstructorInvocation (class extends InvocationExpression implements ConstructorReferenceNode, experimental):
724+
constKeyword (getter: Token?)
724725
constructorName (getter: SimpleIdentifier)
726+
isConst (getter: bool)
725727
period (getter: Token)
726728
DotShorthandInvocation (class extends InvocationExpression, experimental):
727729
memberName (getter: SimpleIdentifier)

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

+26-5
Original file line numberDiff line numberDiff line change
@@ -5210,28 +5210,43 @@ final class DoStatementImpl extends StatementImpl implements DoStatement {
52105210
abstract final class DotShorthandConstructorInvocation
52115211
extends InvocationExpression
52125212
implements ConstructorReferenceNode {
5213+
/// The `const` keyword, or `null` if the expression isn't preceded by the
5214+
/// keyword `const`.
5215+
Token? get constKeyword;
5216+
52135217
/// The name of the constructor invocation.
52145218
SimpleIdentifier get constructorName;
52155219

5220+
/// Whether this dot shorthand constructor invocation will be evaluated at
5221+
/// compile-time, either because the keyword `const` was explicitly provided
5222+
/// or because no keyword was provided and this expression is in a constant
5223+
/// context.
5224+
bool get isConst;
5225+
52165226
/// The token representing the period.
52175227
Token get period;
52185228
}
52195229

52205230
final class DotShorthandConstructorInvocationImpl
52215231
extends InvocationExpressionImpl
5232+
with DotShorthandMixin
52225233
implements
52235234
DotShorthandConstructorInvocation,
52245235
RewrittenMethodInvocationImpl {
52255236
@override
52265237
final Token period;
52275238

5239+
@override
5240+
Token? constKeyword;
5241+
52285242
SimpleIdentifierImpl _constructorName;
52295243

52305244
@override
52315245
ConstructorElementImpl2? element;
52325246

52335247
/// Initializes a newly created dot shorthand constructor invocation.
52345248
DotShorthandConstructorInvocationImpl({
5249+
required this.constKeyword,
52355250
required this.period,
52365251
required SimpleIdentifierImpl constructorName,
52375252
required super.typeArguments,
@@ -5241,7 +5256,7 @@ final class DotShorthandConstructorInvocationImpl
52415256
}
52425257

52435258
@override
5244-
Token get beginToken => period;
5259+
Token get beginToken => constKeyword ?? period;
52455260

52465261
@override
52475262
SimpleIdentifierImpl get constructorName => _constructorName;
@@ -5256,12 +5271,18 @@ final class DotShorthandConstructorInvocationImpl
52565271
@override
52575272
ExpressionImpl get function => constructorName;
52585273

5274+
@override
5275+
bool get isConst {
5276+
return constKeyword?.keyword == Keyword.CONST || inConstantContext;
5277+
}
5278+
52595279
@override
52605280
Precedence get precedence => Precedence.postfix;
52615281

52625282
@override
52635283
ChildEntities get _childEntities =>
52645284
ChildEntities()
5285+
..addToken('const', constKeyword)
52655286
..addToken('period', period)
52665287
..addNode('constructorName', constructorName)
52675288
..addNode('typeArguments', typeArguments)
@@ -5273,9 +5294,9 @@ final class DotShorthandConstructorInvocationImpl
52735294

52745295
@override
52755296
void resolveExpression(ResolverVisitor resolver, TypeImpl contextType) {
5276-
throw StateError(
5277-
'DotShorthandConstructorInvocationImpl should only appear in fully'
5278-
' resolved ASTs',
5297+
resolver.visitDotShorthandConstructorInvocation(
5298+
this,
5299+
contextType: contextType,
52795300
);
52805301
}
52815302

@@ -10300,7 +10321,7 @@ abstract final class InstanceCreationExpression implements Expression {
1030010321
/// The name of the constructor to be invoked.
1030110322
ConstructorName get constructorName;
1030210323

10303-
/// Whether this creation expression is used to invoke a constant constructor,
10324+
/// Whether this creation expression will be evaluated at compile-time,
1030410325
/// either because the keyword `const` was explicitly provided or because no
1030510326
/// keyword was provided and this expression is in a constant context.
1030610327
bool get isConst;

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

+1
Original file line numberDiff line numberDiff line change
@@ -352,6 +352,7 @@ class ToSourceVisitor implements AstVisitor<void> {
352352
void visitDotShorthandConstructorInvocation(
353353
DotShorthandConstructorInvocation node,
354354
) {
355+
_visitToken(node.constKeyword, suffix: ' ');
355356
_visitToken(node.period);
356357
_visitNode(node.constructorName);
357358
_visitNode(node.typeArguments);

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

+24
Original file line numberDiff line numberDiff line change
@@ -875,6 +875,30 @@ class ConstantVisitor extends UnifyingAstVisitor<Constant> {
875875
);
876876
}
877877

878+
@override
879+
Constant visitDotShorthandConstructorInvocation(
880+
covariant DotShorthandConstructorInvocationImpl node,
881+
) {
882+
var constructor = node.constructorName.element;
883+
if (constructor is ConstructorElementMixin2) {
884+
return _evaluationEngine.evaluateAndFormatErrorsInConstructorCall(
885+
_library,
886+
node,
887+
constructor.returnType.typeArguments,
888+
node.argumentList.arguments,
889+
constructor.asElement,
890+
this,
891+
);
892+
}
893+
894+
// Couldn't resolve the constructor so we can't compute a value. No
895+
// problem - the error has already been reported.
896+
return InvalidConstant.forEntity(
897+
entity: node,
898+
errorCode: CompileTimeErrorCode.INVALID_CONSTANT,
899+
);
900+
}
901+
878902
@override
879903
Constant visitDotShorthandPropertyAccess(
880904
covariant DotShorthandPropertyAccessImpl node,

pkg/analyzer/lib/src/dart/resolver/instance_creation_expression_resolver.dart

+21
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,10 @@
33
// BSD-style license that can be found in the LICENSE file.
44

55
import 'package:analyzer/src/dart/ast/ast.dart';
6+
import 'package:analyzer/src/dart/element/element.dart';
67
import 'package:analyzer/src/dart/element/type.dart';
78
import 'package:analyzer/src/dart/resolver/invocation_inferrer.dart';
9+
import 'package:analyzer/src/error/codes.dart';
810
import 'package:analyzer/src/generated/resolver.dart';
911

1012
/// A resolver for [InstanceCreationExpression] and
@@ -71,6 +73,25 @@ class InstanceCreationExpressionResolver {
7173

7274
// TODO(kallentu): Support other context types
7375
if (dotShorthandContextType is InterfaceTypeImpl) {
76+
// This branch will be true if we're resolving an explicitly marked
77+
// const constructor invocation. It's completely unresolved, unlike a
78+
// rewritten [DotShorthandConstructorInvocation] that resulted from
79+
// resolving a [DotShorthandInvocation].
80+
if (node.element == null) {
81+
var contextElement = dotShorthandContextType.element3;
82+
if (contextElement.getNamedConstructor2(node.constructorName.name)
83+
case ConstructorElementImpl2 element?
84+
when element.isAccessibleIn2(_resolver.definingLibrary)) {
85+
node.element = element;
86+
} else {
87+
_resolver.errorReporter.atNode(
88+
node.constructorName,
89+
CompileTimeErrorCode.CONST_WITH_UNDEFINED_CONSTRUCTOR,
90+
arguments: [contextType, node.constructorName.name],
91+
);
92+
}
93+
}
94+
7495
_resolveDotShorthandConstructorInvocation(
7596
node,
7697
contextType: contextType,

pkg/analyzer/lib/src/dart/resolver/invocation_inferrer.dart

+1-2
Original file line numberDiff line numberDiff line change
@@ -151,9 +151,8 @@ class DotShorthandConstructorInvocationInferrer
151151
@override
152152
SimpleIdentifierImpl get _errorEntity => node.constructorName;
153153

154-
// TODO(kallentu): Implement const constructors.
155154
@override
156-
bool get _isConst => false;
155+
bool get _isConst => node.isConst;
157156

158157
@override
159158
bool get _needsTypeArgumentBoundsCheck => true;

pkg/analyzer/lib/src/dart/resolver/method_invocation_resolver.dart

+3-1
Original file line numberDiff line numberDiff line change
@@ -1272,8 +1272,10 @@ class MethodInvocationResolver with ScopeHelpers {
12721272
if (receiver.getNamedConstructor2(name)
12731273
case ConstructorElementImpl2 element?
12741274
when element.isAccessibleIn2(_resolver.definingLibrary)) {
1275-
nameNode.element = element;
1275+
// TODO(kallentu): Produce an error if there are type arguments for this
1276+
// constructor.
12761277
var replacement = DotShorthandConstructorInvocationImpl(
1278+
constKeyword: null,
12771279
period: node.period,
12781280
constructorName: nameNode,
12791281
typeArguments: node.typeArguments,

pkg/analyzer/lib/src/fasta/ast_builder.dart

+11-1
Original file line numberDiff line numberDiff line change
@@ -1619,7 +1619,17 @@ class AstBuilder extends StackListener {
16191619
);
16201620
}
16211621

1622-
// TODO(kallentu): Handle dot shorthands.
1622+
var dotShorthand = pop() as DotShorthandInvocationImpl;
1623+
// TODO(kallentu): Report error if there are type arguments here.
1624+
push(
1625+
DotShorthandConstructorInvocationImpl(
1626+
constKeyword: token,
1627+
period: dotShorthand.period,
1628+
constructorName: dotShorthand.memberName,
1629+
typeArguments: dotShorthand.typeArguments,
1630+
argumentList: dotShorthand.argumentList,
1631+
)..isDotShorthand = dotShorthand.isDotShorthand,
1632+
);
16231633
}
16241634

16251635
@override

pkg/analyzer/lib/src/generated/error_verifier.dart

+28-7
Original file line numberDiff line numberDiff line change
@@ -646,6 +646,20 @@ class ErrorVerifier extends RecursiveAstVisitor<void>
646646
super.visitDefaultFormalParameter(node);
647647
}
648648

649+
@override
650+
void visitDotShorthandConstructorInvocation(
651+
DotShorthandConstructorInvocation node,
652+
) {
653+
if (node.isConst) {
654+
_checkForConstWithNonConst(
655+
node,
656+
node.constructorName.element as ConstructorElement?,
657+
node.constKeyword,
658+
);
659+
}
660+
super.visitDotShorthandConstructorInvocation(node);
661+
}
662+
649663
@override
650664
void visitEnumConstantDeclaration(
651665
covariant EnumConstantDeclarationImpl node,
@@ -1081,7 +1095,11 @@ class ErrorVerifier extends RecursiveAstVisitor<void>
10811095
_constArgumentsVerifier.visitInstanceCreationExpression(node);
10821096
_checkUseVerifier.checkInstanceCreationExpression(node);
10831097
if (node.isConst) {
1084-
_checkForConstWithNonConst(node);
1098+
_checkForConstWithNonConst(
1099+
node,
1100+
node.constructorName.element,
1101+
node.keyword,
1102+
);
10851103
_checkForConstWithUndefinedConstructor(
10861104
node,
10871105
constructorName,
@@ -2788,16 +2806,19 @@ class ErrorVerifier extends RecursiveAstVisitor<void>
27882806
/// Verify that the given 'const' instance creation [expression] is not being
27892807
/// invoked on a constructor that is not 'const'.
27902808
///
2791-
/// This method assumes that the instance creation was tested to be 'const'
2792-
/// before being called.
2809+
/// This method assumes that the instance creation or dot shorthand
2810+
/// constructor invocation was tested to be 'const' before being called.
27932811
///
27942812
/// See [CompileTimeErrorCode.CONST_WITH_NON_CONST].
2795-
void _checkForConstWithNonConst(InstanceCreationExpression expression) {
2796-
var constructorElement = expression.constructorName.element;
2813+
void _checkForConstWithNonConst(
2814+
Expression expression,
2815+
ConstructorElement? constructorElement,
2816+
Token? keyword,
2817+
) {
27972818
if (constructorElement != null && !constructorElement.isConst) {
2798-
if (expression.keyword != null) {
2819+
if (keyword != null) {
27992820
errorReporter.atToken(
2800-
expression.keyword!,
2821+
keyword,
28012822
CompileTimeErrorCode.CONST_WITH_NON_CONST,
28022823
);
28032824
} else {

pkg/analyzer/lib/src/generated/resolver.dart

+24
Original file line numberDiff line numberDiff line change
@@ -2410,6 +2410,30 @@ class ResolverVisitor extends ThrowingAstVisitor<void>
24102410
inferenceLogWriter?.exitStatement(node);
24112411
}
24122412

2413+
@override
2414+
void visitDotShorthandConstructorInvocation(
2415+
covariant DotShorthandConstructorInvocationImpl node, {
2416+
TypeImpl contextType = UnknownInferredType.instance,
2417+
}) {
2418+
inferenceLogWriter?.enterExpression(node, contextType);
2419+
2420+
// If [isDotShorthand] is set, cache the context type for resolution.
2421+
if (isDotShorthand(node)) {
2422+
pushDotShorthandContext(node, SharedTypeSchemaView(contextType));
2423+
}
2424+
2425+
_instanceCreationExpressionResolver.resolveDotShorthand(
2426+
node,
2427+
contextType: contextType,
2428+
);
2429+
2430+
if (isDotShorthand(node)) {
2431+
popDotShorthandContext();
2432+
}
2433+
2434+
inferenceLogWriter?.exitExpression(node);
2435+
}
2436+
24132437
@override
24142438
void visitDotShorthandInvocation(
24152439
covariant DotShorthandInvocationImpl node, {

0 commit comments

Comments
 (0)