Skip to content

Commit efda436

Browse files
authored
Format extension types. (#1387)
Format extension types. Nothing particularly interesting here, but it did flush out a little bug in PieceFactory.createParameter() where a comment before the metadata annotation would get moved to after the parameter.
1 parent 8d7d86e commit efda436

File tree

5 files changed

+277
-21
lines changed

5 files changed

+277
-21
lines changed

lib/src/front_end/ast_node_visitor.dart

Lines changed: 46 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -255,8 +255,8 @@ class AstNodeVisitor extends ThrowingAstVisitor<Piece> with PieceFactory {
255255
node.sealedKeyword,
256256
node.macroKeyword,
257257
node.mixinKeyword,
258+
node.classKeyword,
258259
],
259-
node.classKeyword,
260260
node.name,
261261
typeParameters: node.typeParameters,
262262
extendsClause: node.extendsClause,
@@ -281,8 +281,8 @@ class AstNodeVisitor extends ThrowingAstVisitor<Piece> with PieceFactory {
281281
node.finalKeyword,
282282
node.sealedKeyword,
283283
node.mixinKeyword,
284+
node.typedefKeyword,
284285
],
285-
node.typedefKeyword,
286286
node.name,
287287
equals: node.equals,
288288
superclass: node.superclass,
@@ -607,7 +607,7 @@ class AstNodeVisitor extends ThrowingAstVisitor<Piece> with PieceFactory {
607607

608608
@override
609609
Piece visitExtensionDeclaration(ExtensionDeclaration node) {
610-
return createType(node.metadata, const [], node.extensionKeyword, node.name,
610+
return createType(node.metadata, [node.extensionKeyword], node.name,
611611
typeParameters: node.typeParameters,
612612
onType: (node.onKeyword, node.extendedType),
613613
body: (
@@ -617,6 +617,26 @@ class AstNodeVisitor extends ThrowingAstVisitor<Piece> with PieceFactory {
617617
));
618618
}
619619

620+
@override
621+
Piece visitExtensionTypeDeclaration(ExtensionTypeDeclaration node) {
622+
return createType(
623+
node.metadata,
624+
[
625+
node.extensionKeyword,
626+
node.typeKeyword,
627+
if (node.constKeyword case var keyword?) keyword
628+
],
629+
node.name,
630+
typeParameters: node.typeParameters,
631+
representation: node.representation,
632+
implementsClause: node.implementsClause,
633+
body: (
634+
leftBracket: node.leftBracket,
635+
members: node.members,
636+
rightBracket: node.rightBracket
637+
));
638+
}
639+
620640
@override
621641
Piece visitFieldDeclaration(FieldDeclaration node) {
622642
return buildPiece((b) {
@@ -1226,7 +1246,7 @@ class AstNodeVisitor extends ThrowingAstVisitor<Piece> with PieceFactory {
12261246
@override
12271247
Piece visitMixinDeclaration(MixinDeclaration node) {
12281248
return createType(
1229-
node.metadata, [node.baseKeyword], node.mixinKeyword, node.name,
1249+
node.metadata, [node.baseKeyword, node.mixinKeyword], node.name,
12301250
typeParameters: node.typeParameters,
12311251
onClause: node.onClause,
12321252
implementsClause: node.implementsClause,
@@ -1531,6 +1551,28 @@ class AstNodeVisitor extends ThrowingAstVisitor<Piece> with PieceFactory {
15311551
throw UnimplementedError();
15321552
}
15331553

1554+
@override
1555+
Piece visitRepresentationConstructorName(RepresentationConstructorName node) {
1556+
return buildPiece((b) {
1557+
b.token(node.period);
1558+
b.token(node.name);
1559+
});
1560+
}
1561+
1562+
@override
1563+
Piece visitRepresentationDeclaration(RepresentationDeclaration node) {
1564+
return buildPiece((b) {
1565+
b.visit(node.constructorName);
1566+
1567+
var builder = DelimitedListBuilder(this);
1568+
builder.leftBracket(node.leftParenthesis);
1569+
builder.add(createParameter(
1570+
metadata: node.fieldMetadata, node.fieldType, node.fieldName));
1571+
builder.rightBracket(node.rightParenthesis);
1572+
b.add(builder.build());
1573+
});
1574+
}
1575+
15341576
@override
15351577
Piece visitRethrowExpression(RethrowExpression node) {
15361578
return tokenPiece(node.rethrowKeyword);

lib/src/front_end/piece_factory.dart

Lines changed: 31 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -353,7 +353,7 @@ mixin PieceFactory {
353353
Piece createFormalParameter(
354354
NormalFormalParameter node, TypeAnnotation? type, Token? name,
355355
{Token? mutableKeyword, Token? fieldKeyword, Token? period}) {
356-
return _createParameter(
356+
return createParameter(
357357
metadata: node.metadata,
358358
modifiers: [
359359
node.requiredKeyword,
@@ -752,7 +752,7 @@ mixin PieceFactory {
752752

753753
/// Creates an [AdjacentPiece] for a given record type field.
754754
Piece createRecordTypeField(RecordTypeAnnotationField node) {
755-
return _createParameter(metadata: node.metadata, node.type, node.name);
755+
return createParameter(metadata: node.metadata, node.type, node.name);
756756
}
757757

758758
/// Creates a [ListPiece] for a record literal or pattern.
@@ -802,23 +802,28 @@ mixin PieceFactory {
802802
);
803803
}
804804

805-
/// Creates a class, enum, extension, mixin, or mixin application class
806-
/// declaration.
805+
/// Creates a class, enum, extension, extension type, mixin, or mixin
806+
/// application class declaration.
807+
///
808+
/// The [keywords] list is the ordered list of modifiers and keywords at the
809+
/// beginning of the declaration.
807810
///
808811
/// For all but a mixin application class, [body] should a record containing
809812
/// the bracket delimiters and the list of member declarations for the type's
810-
/// body.
811-
///
812-
/// For mixin application classes, [body] is `null` and instead [equals],
813-
/// [superclass], and [semicolon] are provided.
813+
/// body. For mixin application classes, [body] is `null` and instead
814+
/// [equals], [superclass], and [semicolon] are provided.
814815
///
815816
/// If the type is an extension, then [onType] is a record containing the
816817
/// `on` keyword and the on type.
817-
Piece createType(NodeList<Annotation> metadata, List<Token?> modifiers,
818-
Token keyword, Token? name,
818+
///
819+
/// If the type is an extension type, then [representation] is the primary
820+
/// constructor for it.
821+
Piece createType(
822+
NodeList<Annotation> metadata, List<Token?> keywords, Token? name,
819823
{TypeParameterList? typeParameters,
820824
Token? equals,
821825
NamedType? superclass,
826+
RepresentationDeclaration? representation,
822827
ExtendsClause? extendsClause,
823828
OnClause? onClause,
824829
WithClause? withClause,
@@ -831,8 +836,13 @@ mixin PieceFactory {
831836
metadataBuilder.metadata(metadata);
832837

833838
var header = buildPiece((b) {
834-
modifiers.forEach(b.modifier);
835-
b.token(keyword);
839+
var space = false;
840+
for (var keyword in keywords) {
841+
if (space) b.space();
842+
b.token(keyword);
843+
if (keyword != null) space = true;
844+
}
845+
836846
b.token(name, spaceBefore: true);
837847

838848
if (typeParameters != null) {
@@ -847,6 +857,11 @@ mixin PieceFactory {
847857
b.space();
848858
b.visit(superclass!);
849859
}
860+
861+
// Extension types have a representation type.
862+
if (representation != null) {
863+
b.visit(representation);
864+
}
850865
});
851866

852867
var clauses = <ClausePiece>[];
@@ -989,11 +1004,14 @@ mixin PieceFactory {
9891004
/// Creates a piece for a parameter-like constructor: Either a simple formal
9901005
/// parameter or a record type field, which is syntactically similar to a
9911006
/// parameter.
992-
Piece _createParameter(TypeAnnotation? type, Token? name,
1007+
Piece createParameter(TypeAnnotation? type, Token? name,
9931008
{List<Annotation> metadata = const [],
9941009
List<Token?> modifiers = const [],
9951010
Token? fieldKeyword,
9961011
Token? period}) {
1012+
var builder = AdjacentBuilder(this);
1013+
builder.metadata(metadata, inline: true);
1014+
9971015
Piece? typePiece;
9981016
if (type != null) {
9991017
typePiece = buildPiece((b) {
@@ -1022,9 +1040,6 @@ mixin PieceFactory {
10221040
});
10231041
}
10241042

1025-
var builder = AdjacentBuilder(this);
1026-
builder.metadata(metadata, inline: true);
1027-
10281043
if (typePiece != null && namePiece != null) {
10291044
// We have both a type and name, allow splitting between them.
10301045
builder.add(VariablePiece(typePiece, [namePiece], hasType: true));

test/declaration/extension_type.unit

Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
40 columns |
2+
>>> Empty body.
3+
extension type A(int a) {
4+
}
5+
<<<
6+
extension type A(int a) {}
7+
>>> Const and named constructor.
8+
extension type const A . name ( int a ) {}
9+
<<<
10+
extension type const A.name(int a) {}
11+
>>> Type parameters.
12+
extension type A<T extends int, R>(int a) {}
13+
<<<
14+
extension type A<T extends int, R>(
15+
int a,
16+
) {}
17+
>>> Indentation in body.
18+
extension type A(int a) {
19+
inc(int x) => ++x;
20+
foo(int x) {
21+
if (x == 0) {
22+
return true;
23+
}}}
24+
<<<
25+
extension type A(int a) {
26+
inc(int x) => ++x;
27+
foo(int x) {
28+
if (x == 0) {
29+
return true;
30+
}
31+
}
32+
}
33+
>>> Don't split clauses if they fit.
34+
extension type E(T i)
35+
implements I, J {}
36+
<<<
37+
extension type E(T i) implements I, J {}
38+
>>> Split in representation parameter list.
39+
extension type LongExtensionType(LongTypeName a) {}
40+
<<<
41+
extension type LongExtensionType(
42+
LongTypeName a,
43+
) {}
44+
>>> Split in representation with implements clause.
45+
extension type LongExtensionType(LongTypeName a) implements Something {
46+
method() {;}
47+
}
48+
<<<
49+
extension type LongExtensionType(
50+
LongTypeName a,
51+
) implements Something {
52+
method() {
53+
;
54+
}
55+
}
56+
>>> Simple body.
57+
extension type A(int a) { int meaningOfLife() => 42; }
58+
<<<
59+
extension type A(int a) {
60+
int meaningOfLife() => 42;
61+
}
62+
>>> Discard extra newlines.
63+
extension
64+
65+
type
66+
67+
const
68+
69+
A
70+
71+
<
72+
73+
T
74+
75+
>
76+
77+
.
78+
79+
name
80+
81+
(
82+
83+
int
84+
85+
a
86+
87+
)
88+
89+
{
90+
91+
}
92+
<<<
93+
extension type const A<T>.name(int a) {}
94+
>>> All kinds of members (except instance fields).
95+
extension type const A<T>.name(int a) {
96+
static const int c = 1;
97+
static final int f = 1;
98+
static late final int l;
99+
static var v;
100+
static int get g => c;
101+
static set g(int i) {}
102+
static int m<X>(X x) => c;
103+
const A(int a) : this.a = a;
104+
const A.r(int a) : this(a);
105+
const factory A.rf(int a) = A;
106+
factory A.f(int a) => A(a);
107+
int get pr => 0;
108+
set pr(int x) {}
109+
int me(int x) => x;
110+
int operator+(int x) => x;
111+
}
112+
<<<
113+
extension type const A<T>.name(int a) {
114+
static const int c = 1;
115+
static final int f = 1;
116+
static late final int l;
117+
static var v;
118+
static int get g => c;
119+
static set g(int i) {}
120+
static int m<X>(X x) => c;
121+
const A(int a) : this.a = a;
122+
const A.r(int a) : this(a);
123+
const factory A.rf(int a) = A;
124+
factory A.f(int a) => A(a);
125+
int get pr => 0;
126+
set pr(int x) {}
127+
int me(int x) => x;
128+
int operator +(int x) => x;
129+
}
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
40 columns |
2+
>>> Inline block comments everywhere.
3+
/*a*/ extension /*b*/ type /*c*/ A
4+
/*d*/ ( /*e*/ @ /*f*/ override /*g*/ int /*h*/ a /*i*/ ) /*j*/
5+
implements /*k*/ I1 /*l*/ , /*m*/ I2 /*n*/ { /*o*/ } /*p*/
6+
<<<
7+
/*a*/
8+
extension /*b*/ type /*c*/ A
9+
/*d*/ ( /*e*/
10+
@ /*f*/ override /*g*/
11+
int /*h*/ a /*i*/,
12+
) /*j*/
13+
implements /*k*/
14+
I1 /*l*/, /*m*/
15+
I2 /*n*/ {/*o*/} /*p*/
16+
>>> Line comments everywhere.
17+
// 0
18+
@patch // a
19+
extension // b
20+
type // c
21+
const // d
22+
A // e
23+
< // f
24+
T // g
25+
> // h
26+
. // i
27+
name // j
28+
( // k
29+
@ // l
30+
required // m
31+
int // n
32+
a // o
33+
) // p
34+
implements // q
35+
I // r
36+
{ // s
37+
} // t
38+
<<<
39+
// 0
40+
@patch // a
41+
extension // b
42+
type // c
43+
const // d
44+
A // e
45+
<
46+
// f
47+
T // g
48+
> // h
49+
. // i
50+
name // j
51+
( // k
52+
@ // l
53+
required // m
54+
int // n
55+
a, // o
56+
) // p
57+
implements // q
58+
I // r
59+
{
60+
// s
61+
} // t

0 commit comments

Comments
 (0)