Skip to content

Commit ba4c69a

Browse files
authored
Format mixin application classes. (#1310)
Format mixin application classes. This mostly reuses the existing createType() code with a couple of tweaks since mixin application classes don't have bodies and have the "= Superclass" part before the "with" clause. You'll notice that the modifiers test case here takes a couple of seconds to run. That's because the new back end's line splitter doesn't do any memoization or divide and conquering, so it's brute-forcing the whole compilation unit to figure out which of the mixin applications need splitting. I don't think it's intolerable yet as far as how long it takes the tests to run, but this means it's probably a good time for me to start looking into optimization soon.
1 parent d55da0b commit ba4c69a

File tree

5 files changed

+210
-27
lines changed

5 files changed

+210
-27
lines changed

lib/src/front_end/ast_node_visitor.dart

Lines changed: 28 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -217,19 +217,38 @@ class AstNodeVisitor extends ThrowingAstVisitor<void>
217217
],
218218
node.classKeyword,
219219
node.name,
220-
node.typeParameters,
221-
node.extendsClause,
222-
node.withClause,
223-
node.implementsClause,
224-
node.nativeClause,
225-
node.leftBracket,
226-
node.members,
227-
node.rightBracket);
220+
typeParameters: node.typeParameters,
221+
extendsClause: node.extendsClause,
222+
withClause: node.withClause,
223+
implementsClause: node.implementsClause,
224+
nativeClause: node.nativeClause,
225+
body: (
226+
leftBracket: node.leftBracket,
227+
members: node.members,
228+
rightBracket: node.rightBracket
229+
));
228230
}
229231

230232
@override
231233
void visitClassTypeAlias(ClassTypeAlias node) {
232-
throw UnimplementedError();
234+
createType(
235+
node.metadata,
236+
[
237+
node.abstractKeyword,
238+
node.baseKeyword,
239+
node.interfaceKeyword,
240+
node.finalKeyword,
241+
node.sealedKeyword,
242+
node.mixinKeyword,
243+
],
244+
node.typedefKeyword,
245+
node.name,
246+
equals: node.equals,
247+
superclass: node.superclass,
248+
typeParameters: node.typeParameters,
249+
withClause: node.withClause,
250+
implementsClause: node.implementsClause,
251+
semicolon: node.semicolon);
233252
}
234253

235254
@override

lib/src/front_end/piece_factory.dart

Lines changed: 38 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -408,29 +408,46 @@ mixin PieceFactory implements CommentWriter {
408408
pieces.give(builder.build());
409409
}
410410

411-
/// Creates a class, enum, extension, etc. declaration with a body containing
412-
/// members.
413-
void createType(
414-
NodeList<Annotation> metadata,
415-
List<Token?> modifiers,
416-
Token keyword,
417-
Token name,
418-
TypeParameterList? typeParameters,
411+
/// Creates a class, enum, extension, mixin, or mixin application class
412+
/// declaration.
413+
///
414+
/// For all but a mixin application class, [body] should a record containing
415+
/// the bracket delimiters and the list of member declarations for the type's
416+
/// body.
417+
///
418+
/// For mixin application classes, [body] is `null` and instead [equals],
419+
/// [superclass], and [semicolon] are provided.
420+
void createType(NodeList<Annotation> metadata, List<Token?> modifiers,
421+
Token keyword, Token name,
422+
{TypeParameterList? typeParameters,
423+
Token? equals,
424+
NamedType? superclass,
419425
ExtendsClause? extendsClause,
420426
WithClause? withClause,
421427
ImplementsClause? implementsClause,
422428
NativeClause? nativeClause,
423-
Token leftBracket,
424-
List<AstNode> members,
425-
Token rightBracket) {
429+
({Token leftBracket, List<AstNode> members, Token rightBracket})? body,
430+
Token? semicolon}) {
426431
if (metadata.isNotEmpty) throw UnimplementedError('Type metadata.');
427-
if (members.isNotEmpty) throw UnimplementedError('Type members.');
432+
if (body != null && body.members.isNotEmpty) {
433+
throw UnimplementedError('Type members.');
434+
}
428435

429436
modifiers.forEach(modifier);
430437
token(keyword);
431438
space();
432439
token(name);
433440
visit(typeParameters);
441+
442+
// Mixin application classes have ` = Superclass` after the declaration
443+
// name.
444+
if (equals != null) {
445+
space();
446+
token(equals);
447+
space();
448+
visit(superclass);
449+
}
450+
434451
var header = pieces.split();
435452

436453
var clauses = <ClausePiece>[];
@@ -470,10 +487,16 @@ mixin PieceFactory implements CommentWriter {
470487

471488
visit(nativeClause);
472489
space();
473-
createBody(leftBracket, members, rightBracket);
474-
var body = pieces.take();
475490

476-
pieces.give(TypePiece(header, clausesPiece, body));
491+
if (body != null) {
492+
createBody(body.leftBracket, body.members, body.rightBracket);
493+
} else {
494+
token(semicolon);
495+
}
496+
var bodyPiece = pieces.take();
497+
498+
pieces.give(
499+
TypePiece(header, clausesPiece, bodyPiece, hasBody: body != null));
477500
}
478501

479502
/// Creates a [ListPiece] for a type argument or type parameter list.

lib/src/piece/type.dart

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,13 +18,18 @@ class TypePiece extends Piece {
1818
/// The `native` clause, if any, and the type body.
1919
final Piece _body;
2020

21-
TypePiece(this._header, this._clauses, this._body);
21+
/// Whether the type has a `{ ... }` body or just a `;` at the end (for a
22+
/// mixin application class).
23+
final bool _hasBody;
24+
25+
TypePiece(this._header, this._clauses, this._body, {required bool hasBody})
26+
: _hasBody = hasBody;
2227

2328
@override
2429
void format(CodeWriter writer, State state) {
2530
writer.format(_header);
2631
if (_clauses case var clauses?) writer.format(clauses);
27-
writer.space();
32+
if (_hasBody) writer.space();
2833
writer.format(_body);
2934
}
3035

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
40 columns |
2+
>>> Inline.
3+
class SomeClass = Base with Mixin ;
4+
<<<
5+
class SomeClass = Base with Mixin;
6+
>>> Split at `with`.
7+
class SomeClass = Base with VeryLongMixin;
8+
<<<
9+
class SomeClass = Base
10+
with VeryLongMixin;
11+
>>> Split at `with` but not between mixins.
12+
class SomeClass = Base with Mixin, AnotherOne;
13+
<<<
14+
class SomeClass = Base
15+
with Mixin, AnotherOne;
16+
>>> Split at `with` and mixins.
17+
class SomeClass = Base with Mixin, Another, Third, FourthOne;
18+
<<<
19+
class SomeClass = Base
20+
with
21+
Mixin,
22+
Another,
23+
Third,
24+
FourthOne;
25+
>>> Unsplit generic mixin.
26+
class C = Object with M<int>;
27+
<<<
28+
class C = Object with M<int>;
29+
>>> Split before `with` on generic mixin.
30+
class C = Object with Mixin<SomeLongClass>;
31+
<<<
32+
class C = Object
33+
with Mixin<SomeLongClass>;
34+
>>> Split in generic mixin.
35+
class C = Object with Mixin<VeryLongType, AnotherLongType>;
36+
<<<
37+
class C = Object
38+
with
39+
Mixin<
40+
VeryLongType,
41+
AnotherLongType
42+
>;
43+
>>> Split within `with` but not `implements`.
44+
class C = Object with Mixin, Another, Third, Fourth, Fifth implements Interface;
45+
<<<
46+
class C = Object
47+
with
48+
Mixin,
49+
Another,
50+
Third,
51+
Fourth,
52+
Fifth
53+
implements Interface;
54+
>>> Split within `implements` but not `with`.
55+
class C = Object with Mixin implements Interface, Another, Third;
56+
<<<
57+
class C = Object
58+
with Mixin
59+
implements
60+
Interface,
61+
Another,
62+
Third;
63+
>>> Split within both `with` and `implements`.
64+
class C = Object with Mixin, Another, Third, Fourth, Fifth implements Interface, Another, Third;
65+
<<<
66+
class C = Object
67+
with
68+
Mixin,
69+
Another,
70+
Third,
71+
Fourth,
72+
Fifth
73+
implements
74+
Interface,
75+
Another,
76+
Third;
77+
>>> Modifiers.
78+
class C1 = Object with Mixin;
79+
base class C2 = Object with Mixin;
80+
interface class C3 = Object with Mixin;
81+
final class C4 = Object with Mixin;
82+
sealed class C5 = Object with Mixin;
83+
abstract class C6 = Object with Mixin;
84+
abstract base class C7 = Object with Mixin;
85+
abstract interface class C8 = Object with Mixin;
86+
abstract final class C9 = Object with Mixin;
87+
mixin class C10 = Object with Mixin;
88+
base mixin class C11 = Object with Mixin;
89+
abstract mixin class C12 = Object with Mixin;
90+
abstract base mixin class C13 = Object with Mixin;
91+
<<<
92+
class C1 = Object with Mixin;
93+
base class C2 = Object with Mixin;
94+
interface class C3 = Object with Mixin;
95+
final class C4 = Object with Mixin;
96+
sealed class C5 = Object with Mixin;
97+
abstract class C6 = Object with Mixin;
98+
abstract base class C7 = Object
99+
with Mixin;
100+
abstract interface class C8 = Object
101+
with Mixin;
102+
abstract final class C9 = Object
103+
with Mixin;
104+
mixin class C10 = Object with Mixin;
105+
base mixin class C11 = Object
106+
with Mixin;
107+
abstract mixin class C12 = Object
108+
with Mixin;
109+
abstract base mixin class C13 = Object
110+
with Mixin;

test/top_level/mixed.unit

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,4 +19,30 @@ part 'a.dart';
1919
<<<
2020
library foo;
2121

22-
part 'a.dart';
22+
part 'a.dart';
23+
>>> Collapse extra newlines between declarations.
24+
25+
26+
27+
class A {}
28+
29+
30+
31+
class B = Base with Mixin;
32+
33+
34+
35+
var c = 1;
36+
37+
38+
d() {}
39+
40+
41+
<<<
42+
class A {}
43+
44+
class B = Base with Mixin;
45+
46+
var c = 1;
47+
48+
d() {}

0 commit comments

Comments
 (0)