Skip to content

Commit f58ddd3

Browse files
kallentuCommit Queue
authored andcommitted
[analyzer] Dot shorthands: Store DotShorthandsConstructorInvocation in summaries.
Updates the summary reading and writing to handle constructor invocations. Once this was updated, it uncovered some errors in the constant evaluation so I added some logic to the potential-constant checker to handle `DotShorthandsConstructorInvocation`s. Tests cover this new behavior. `equality_ctor_test` and `constructor_future_or_test` language tests passing. Added unit tests. Bug: #59835 Change-Id: Ia1926d5efc3bbf0207c07c43baaa4d43351c32ff Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/424340 Reviewed-by: Konstantin Shcheglov <[email protected]> Commit-Queue: Kallen Tu <[email protected]>
1 parent 06f5db3 commit f58ddd3

File tree

10 files changed

+289
-2
lines changed

10 files changed

+289
-2
lines changed

pkg/analyzer/lib/src/dart/analysis/driver.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,7 @@ import 'package:meta/meta.dart';
100100
// TODO(scheglov): Clean up the list of implicitly analyzed files.
101101
class AnalysisDriver {
102102
/// The version of data format, should be incremented on every format change.
103-
static const int DATA_VERSION = 455;
103+
static const int DATA_VERSION = 456;
104104

105105
/// The number of exception contexts allowed to write. Once this field is
106106
/// zero, we stop writing any new exception contexts in this process.

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

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,13 @@ class _Collector {
7676
return _identifier(node.propertyName);
7777
}
7878

79+
if (node is DotShorthandConstructorInvocation) {
80+
if (!node.isConst) {
81+
nodes.add(node);
82+
}
83+
return;
84+
}
85+
7986
if (node is StringInterpolation) {
8087
for (var component in node.elements) {
8188
if (component is InterpolationExpression) {

pkg/analyzer/lib/src/summary2/ast_binary_reader.dart

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,8 @@ class AstBinaryReader {
6363
return _readDeclaredIdentifier();
6464
case Tag.DefaultFormalParameter:
6565
return _readDefaultFormalParameter();
66+
case Tag.DotShorthandConstructorInvocation:
67+
return _readDotShorthandConstructorInvocation();
6668
case Tag.DotShorthandPropertyAccess:
6769
return _readDotShorthandPropertyAccess();
6870
case Tag.DottedName:
@@ -462,6 +464,23 @@ class AstBinaryReader {
462464
return node;
463465
}
464466

467+
DotShorthandConstructorInvocation _readDotShorthandConstructorInvocation() {
468+
var flags = _readByte();
469+
var constructorName = readNode() as SimpleIdentifierImpl;
470+
var argumentList = readNode() as ArgumentListImpl;
471+
472+
var node = DotShorthandConstructorInvocationImpl(
473+
constKeyword: AstBinaryFlags.isConst(flags) ? Tokens.const_() : null,
474+
period: Tokens.period(),
475+
constructorName: constructorName,
476+
typeArguments: null,
477+
argumentList: argumentList,
478+
);
479+
_readExpressionResolution(node);
480+
_resolveNamedExpressions(node.constructorName.element, node.argumentList);
481+
return node;
482+
}
483+
465484
DotShorthandPropertyAccess _readDotShorthandPropertyAccess() {
466485
var propertyName = readNode() as SimpleIdentifierImpl;
467486
var node = DotShorthandPropertyAccessImpl(

pkg/analyzer/lib/src/summary2/ast_binary_tag.dart

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,8 +40,9 @@ class Tag {
4040
static const int ConstructorReference = 101;
4141
static const int DeclaredIdentifier = 90;
4242
static const int DefaultFormalParameter = 8;
43-
static const int DottedName = 47;
43+
static const int DotShorthandConstructorInvocation = 114;
4444
static const int DotShorthandPropertyAccess = 113;
45+
static const int DottedName = 47;
4546
static const int DoubleLiteral = 9;
4647
static const int ExtensionOverride = 87;
4748
static const int FieldFormalParameter = 16;

pkg/analyzer/lib/src/summary2/ast_binary_writer.dart

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -224,6 +224,19 @@ class AstBinaryWriter extends ThrowingAstVisitor<void> {
224224
_writeOptionalNode(defaultValue);
225225
}
226226

227+
@override
228+
void visitDotShorthandConstructorInvocation(
229+
DotShorthandConstructorInvocation node,
230+
) {
231+
_writeByte(Tag.DotShorthandConstructorInvocation);
232+
_writeByte(
233+
AstBinaryFlags.encode(isConst: node.constKeyword?.type == Keyword.CONST),
234+
);
235+
_writeNode(node.constructorName);
236+
_writeNode(node.argumentList);
237+
_storeExpression(node);
238+
}
239+
227240
@override
228241
void visitDotShorthandPropertyAccess(DotShorthandPropertyAccess node) {
229242
_writeByte(Tag.DotShorthandPropertyAccess);

pkg/analyzer/lib/src/summary2/informative_data.dart

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2174,6 +2174,28 @@ abstract class _OffsetsAstVisitor extends RecursiveAstVisitor<void> {
21742174
node.name?.accept(this);
21752175
}
21762176

2177+
@override
2178+
void visitDotShorthandConstructorInvocation(
2179+
DotShorthandConstructorInvocation node,
2180+
) {
2181+
_tokenOrNull(node.constKeyword);
2182+
_tokenOrNull(node.period);
2183+
node.constructorName.accept(this);
2184+
node.argumentList.accept(this);
2185+
}
2186+
2187+
/// When we read from bytes, [DotShorthandInvocation]s are not rewritten to
2188+
/// [DotShorthandConstructorInvocation]s when they're resolved to be
2189+
/// constructor invocations. However, since the tokens happen to be the same
2190+
/// between the two in this case, we have the same offsets.
2191+
@override
2192+
void visitDotShorthandInvocation(DotShorthandInvocation node) {
2193+
_tokenOrNull(node.period);
2194+
node.memberName.accept(this);
2195+
node.typeArguments?.accept(this);
2196+
node.argumentList.accept(this);
2197+
}
2198+
21772199
@override
21782200
void visitDotShorthandPropertyAccess(DotShorthandPropertyAccess node) {
21792201
_tokenOrNull(node.period);

pkg/analyzer/lib/src/test_utilities/find_node.dart

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -363,6 +363,12 @@ class FindNode {
363363
return _node(search, (n) => n is DoStatement);
364364
}
365365

366+
DotShorthandConstructorInvocation dotShorthandConstructorInvocation(
367+
String search,
368+
) {
369+
return _node(search, (n) => n is DotShorthandConstructorInvocation);
370+
}
371+
366372
DotShorthandInvocation dotShorthandInvocation(String search) {
367373
return _node(search, (n) => n is DotShorthandInvocation);
368374
}

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

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -500,6 +500,55 @@ class B {}
500500
''', () => findNode.constructorReference('B.new'));
501501
}
502502

503+
test_dotShorthandConstructorInvocation_const() async {
504+
await _assertConst(r'''
505+
class A {
506+
const A();
507+
}
508+
509+
const A x = .new();
510+
''', () => findNode.dotShorthandConstructorInvocation('.new()'));
511+
}
512+
513+
test_dotShorthandConstructorInvocation_nonConst() async {
514+
await _assertNotConst(
515+
r'''
516+
class A {
517+
const A();
518+
}
519+
520+
A x = .new();
521+
''',
522+
() => _xInitializer(),
523+
() => [findNode.dotShorthandConstructorInvocation('.new()')],
524+
);
525+
}
526+
527+
test_dotShorthandPropertyAccess_const() async {
528+
await _assertConst(r'''
529+
class A {
530+
static const A a = A();
531+
const A();
532+
}
533+
534+
const A x = .a;
535+
''', () => findNode.dotShorthandPropertyAccess('.a'));
536+
}
537+
538+
test_dotShorthandPropertyAccess_nonConst() async {
539+
await _assertNotConst(
540+
r'''
541+
class A {
542+
static A a = A();
543+
}
544+
545+
A x = .a;
546+
''',
547+
() => _xInitializer(),
548+
() => [findNode.simple('a;')],
549+
);
550+
}
551+
503552
test_functionReference_explicitTypeArguments() async {
504553
await _assertConst('''
505554
class A {

pkg/analyzer/test/src/dart/resolution/dot_shorthand_constructor_invocation_test.dart

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,41 @@ DotShorthandConstructorInvocation
8686
''');
8787
}
8888

89+
test_const_assert() async {
90+
await assertNoErrorsInCode(r'''
91+
class C {
92+
final int x;
93+
const C.named(this.x);
94+
}
95+
96+
class CAssert {
97+
const CAssert.regular(C ctor)
98+
: assert(ctor == const .named(1));
99+
}
100+
''');
101+
102+
var node = findNode.singleDotShorthandConstructorInvocation;
103+
assertResolvedNodeText(node, r'''
104+
DotShorthandConstructorInvocation
105+
const: const
106+
period: .
107+
constructorName: SimpleIdentifier
108+
token: named
109+
element: <testLibraryFragment>::@class::C::@constructor::named#element
110+
staticType: null
111+
argumentList: ArgumentList
112+
leftParenthesis: (
113+
arguments
114+
IntegerLiteral
115+
literal: 1
116+
correspondingParameter: <testLibraryFragment>::@class::C::@constructor::named::@parameter::x#element
117+
staticType: int
118+
rightParenthesis: )
119+
correspondingParameter: dart:core::<fragment>::@class::Object::@method::==::@parameter::other#element
120+
staticType: C
121+
''');
122+
}
123+
89124
test_const_inConstantContext() async {
90125
await assertNoErrorsInCode(r'''
91126
class C {

pkg/analyzer/test/src/summary/elements/const_test.dart

Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -588,6 +588,141 @@ library
588588
''');
589589
}
590590

591+
test_const_dotShorthand_constructor_explicit() async {
592+
var library = await buildLibrary(r'''
593+
class A {
594+
const A();
595+
}
596+
597+
const A a = const .new();
598+
''');
599+
checkElementText(library, r'''
600+
library
601+
reference: <testLibrary>
602+
fragments
603+
<testLibraryFragment>
604+
element: <testLibrary>
605+
classes
606+
class A @6
607+
reference: <testLibraryFragment>::@class::A
608+
element: <testLibrary>::@class::A
609+
constructors
610+
const new
611+
reference: <testLibraryFragment>::@class::A::@constructor::new
612+
element: <testLibraryFragment>::@class::A::@constructor::new#element
613+
typeName: A
614+
typeNameOffset: 18
615+
topLevelVariables
616+
hasInitializer a @34
617+
reference: <testLibraryFragment>::@topLevelVariable::a
618+
element: <testLibrary>::@topLevelVariable::a
619+
initializer: expression_0
620+
DotShorthandConstructorInvocation
621+
const: const @38
622+
period: . @44
623+
constructorName: SimpleIdentifier
624+
token: new @45
625+
element: <testLibraryFragment>::@class::A::@constructor::new#element
626+
staticType: null
627+
argumentList: ArgumentList
628+
leftParenthesis: ( @48
629+
rightParenthesis: ) @49
630+
staticType: A
631+
getter2: <testLibraryFragment>::@getter::a
632+
getters
633+
synthetic get a
634+
reference: <testLibraryFragment>::@getter::a
635+
element: <testLibraryFragment>::@getter::a#element
636+
classes
637+
class A
638+
reference: <testLibrary>::@class::A
639+
firstFragment: <testLibraryFragment>::@class::A
640+
constructors
641+
const new
642+
firstFragment: <testLibraryFragment>::@class::A::@constructor::new
643+
topLevelVariables
644+
const hasInitializer a
645+
reference: <testLibrary>::@topLevelVariable::a
646+
firstFragment: <testLibraryFragment>::@topLevelVariable::a
647+
type: A
648+
constantInitializer
649+
fragment: <testLibraryFragment>::@topLevelVariable::a
650+
expression: expression_0
651+
getter: <testLibraryFragment>::@getter::a#element
652+
getters
653+
synthetic static get a
654+
firstFragment: <testLibraryFragment>::@getter::a
655+
returnType: A
656+
''');
657+
}
658+
659+
test_const_dotShorthand_constructor_implicit() async {
660+
var library = await buildLibrary(r'''
661+
class A {
662+
const A();
663+
}
664+
665+
const A a = .new();
666+
''');
667+
checkElementText(library, r'''
668+
library
669+
reference: <testLibrary>
670+
fragments
671+
<testLibraryFragment>
672+
element: <testLibrary>
673+
classes
674+
class A @6
675+
reference: <testLibraryFragment>::@class::A
676+
element: <testLibrary>::@class::A
677+
constructors
678+
const new
679+
reference: <testLibraryFragment>::@class::A::@constructor::new
680+
element: <testLibraryFragment>::@class::A::@constructor::new#element
681+
typeName: A
682+
typeNameOffset: 18
683+
topLevelVariables
684+
hasInitializer a @34
685+
reference: <testLibraryFragment>::@topLevelVariable::a
686+
element: <testLibrary>::@topLevelVariable::a
687+
initializer: expression_0
688+
DotShorthandConstructorInvocation
689+
period: . @38
690+
constructorName: SimpleIdentifier
691+
token: new @39
692+
element: <testLibraryFragment>::@class::A::@constructor::new#element
693+
staticType: null
694+
argumentList: ArgumentList
695+
leftParenthesis: ( @42
696+
rightParenthesis: ) @43
697+
staticType: A
698+
getter2: <testLibraryFragment>::@getter::a
699+
getters
700+
synthetic get a
701+
reference: <testLibraryFragment>::@getter::a
702+
element: <testLibraryFragment>::@getter::a#element
703+
classes
704+
class A
705+
reference: <testLibrary>::@class::A
706+
firstFragment: <testLibraryFragment>::@class::A
707+
constructors
708+
const new
709+
firstFragment: <testLibraryFragment>::@class::A::@constructor::new
710+
topLevelVariables
711+
const hasInitializer a
712+
reference: <testLibrary>::@topLevelVariable::a
713+
firstFragment: <testLibraryFragment>::@topLevelVariable::a
714+
type: A
715+
constantInitializer
716+
fragment: <testLibraryFragment>::@topLevelVariable::a
717+
expression: expression_0
718+
getter: <testLibraryFragment>::@getter::a#element
719+
getters
720+
synthetic static get a
721+
firstFragment: <testLibraryFragment>::@getter::a
722+
returnType: A
723+
''');
724+
}
725+
591726
test_const_dotShorthand_property() async {
592727
var library = await buildLibrary(r'''
593728
class A {

0 commit comments

Comments
 (0)