Skip to content

Commit 214f289

Browse files
authored
Format try statements. (#1332)
* Format try statements. * Add try comment tests just in case. * Use token's API to add spaces. * Remove unnecessary override after rebase. * Put the exception into a delimited list and everything else into an adjacentpiece. * Remove unnecessary space.
1 parent 8b1f24a commit 214f289

File tree

6 files changed

+383
-4
lines changed

6 files changed

+383
-4
lines changed

lib/src/front_end/ast_node_visitor.dart

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -206,12 +206,12 @@ class AstNodeVisitor extends ThrowingAstVisitor<void>
206206

207207
@override
208208
void visitCatchClause(CatchClause node) {
209-
throw UnimplementedError();
209+
assert(false, 'This node is handled by visitTryStatement().');
210210
}
211211

212212
@override
213213
void visitCatchClauseParameter(CatchClauseParameter node) {
214-
throw UnimplementedError();
214+
token(node.name);
215215
}
216216

217217
@override
@@ -1390,7 +1390,7 @@ class AstNodeVisitor extends ThrowingAstVisitor<void>
13901390

13911391
@override
13921392
void visitTryStatement(TryStatement node) {
1393-
throw UnimplementedError();
1393+
createTry(node);
13941394
}
13951395

13961396
@override

lib/src/front_end/piece_factory.dart

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import '../piece/infix.dart';
1515
import '../piece/list.dart';
1616
import '../piece/piece.dart';
1717
import '../piece/postfix.dart';
18+
import '../piece/try.dart';
1819
import '../piece/type.dart';
1920
import 'ast_node_visitor.dart';
2021
import 'comment_writer.dart';
@@ -235,6 +236,92 @@ mixin PieceFactory implements CommentWriter {
235236
pieces.give(FunctionPiece(returnTypePiece, parametersPiece));
236237
}
237238

239+
/// Creates a [TryPiece] for try statement.
240+
void createTry(TryStatement tryStatement) {
241+
var piece = TryPiece();
242+
243+
token(tryStatement.tryKeyword);
244+
var tryHeader = pieces.split();
245+
createBlock(tryStatement.body);
246+
var tryBlock = pieces.split();
247+
piece.add(tryHeader, tryBlock);
248+
249+
for (var i = 0; i < tryStatement.catchClauses.length; i++) {
250+
var catchClause = tryStatement.catchClauses[i];
251+
252+
Piece? onPiece;
253+
if (catchClause.onKeyword case var onKeyword?) {
254+
token(onKeyword, after: space);
255+
visit(catchClause.exceptionType);
256+
onPiece = pieces.split();
257+
}
258+
259+
Piece? catchPiece;
260+
if (catchClause.catchKeyword case var catchKeyword?) {
261+
token(catchKeyword);
262+
var catchKeywordPiece = pieces.split();
263+
264+
var builder = DelimitedListBuilder(this);
265+
builder.leftBracket(catchClause.leftParenthesis!);
266+
if (catchClause.exceptionParameter case var exceptionParameter?) {
267+
builder.visit(exceptionParameter);
268+
}
269+
if (catchClause.stackTraceParameter case var stackTraceParameter?) {
270+
builder.visit(stackTraceParameter);
271+
}
272+
builder.rightBracket(catchClause.rightParenthesis!);
273+
274+
catchPiece = AdjacentPiece(
275+
[catchKeywordPiece, builder.build()],
276+
spaceAfter: [catchKeywordPiece],
277+
);
278+
}
279+
280+
if (onPiece != null && catchPiece != null) {
281+
pieces.give(AdjacentPiece(
282+
[onPiece, catchPiece],
283+
spaceAfter: [onPiece],
284+
));
285+
} else if (onPiece != null) {
286+
pieces.give(onPiece);
287+
} else if (catchPiece != null) {
288+
pieces.give(catchPiece);
289+
}
290+
var catchClauseHeader = pieces.split();
291+
292+
// Edge case: When there's another catch/on/finally after this one, we
293+
// want to force the block to split even if it's empty.
294+
//
295+
// ```
296+
// try {
297+
// ..
298+
// } on Foo {
299+
// } finally Bar {
300+
// body;
301+
// }
302+
// ```
303+
var forceSplit = i < tryStatement.catchClauses.length - 1 ||
304+
tryStatement.finallyBlock != null;
305+
createBlock(
306+
catchClause.body,
307+
forceSplit: forceSplit,
308+
);
309+
var catchClauseBody = pieces.split();
310+
311+
piece.add(catchClauseHeader, catchClauseBody);
312+
}
313+
314+
if (tryStatement.finallyBlock case var finallyBlock?) {
315+
token(tryStatement.finallyKeyword);
316+
var finallyHeader = pieces.split();
317+
createBlock(finallyBlock);
318+
var finallyBody = pieces.split();
319+
piece.add(finallyHeader, finallyBody);
320+
}
321+
322+
pieces.give(piece);
323+
}
324+
238325
// TODO(tall): Generalize this to work with if elements too.
239326
/// Creates a piece for a chain of if-else-if... statements.
240327
void createIf(IfStatement ifStatement) {

lib/src/piece/adjacent.dart

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,17 @@ import 'piece.dart';
88
class AdjacentPiece extends Piece {
99
final List<Piece> _pieces;
1010

11-
AdjacentPiece(this._pieces);
11+
/// The pieces that should have a space after them.
12+
final Set<Piece> _spaceAfter;
13+
14+
AdjacentPiece(this._pieces, {List<Piece> spaceAfter = const []})
15+
: _spaceAfter = spaceAfter.toSet();
1216

1317
@override
1418
void format(CodeWriter writer, State state) {
1519
for (var piece in _pieces) {
1620
writer.format(piece);
21+
if (_spaceAfter.contains(piece)) writer.space();
1722
}
1823
}
1924

lib/src/piece/try.dart

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
// Copyright (c) 2023, the Dart project authors. Please see the AUTHORS file
2+
// for details. All rights reserved. Use of this source code is governed by a
3+
// BSD-style license that can be found in the LICENSE file.
4+
import '../back_end/code_writer.dart';
5+
import 'piece.dart';
6+
7+
/// A piece for a try statement.
8+
class TryPiece extends Piece {
9+
final List<_TrySectionPiece> _sections = [];
10+
11+
void add(Piece header, Piece block) {
12+
_sections.add(_TrySectionPiece(header, block));
13+
}
14+
15+
@override
16+
void format(CodeWriter writer, State state) {
17+
for (var i = 0; i < _sections.length; i++) {
18+
var section = _sections[i];
19+
writer.format(section.header);
20+
writer.space();
21+
writer.format(section.body);
22+
23+
// Adds the space between the end of a block and the next header.
24+
if (i < _sections.length - 1) {
25+
writer.space();
26+
}
27+
}
28+
}
29+
30+
@override
31+
void forEachChild(void Function(Piece piece) callback) {
32+
for (var section in _sections) {
33+
callback(section.header);
34+
callback(section.body);
35+
}
36+
}
37+
}
38+
39+
/// A section for a try.
40+
///
41+
/// This could be a try, an on/catch, or a finally section. They are all
42+
/// formatted similarly.
43+
class _TrySectionPiece {
44+
final Piece header;
45+
final Piece body;
46+
47+
_TrySectionPiece(this.header, this.body);
48+
}

test/statement/try.stmt

Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
40 columns |
2+
>>> Try and catch exception.
3+
try {
4+
doSomething();
5+
} catch (e) {
6+
print(e);
7+
}
8+
<<<
9+
try {
10+
doSomething();
11+
} catch (e) {
12+
print(e);
13+
}
14+
>>> Try and catch exception with on clause.
15+
try{
16+
doSomething();
17+
}on Exception catch (e){
18+
print(e);
19+
}
20+
<<<
21+
try {
22+
doSomething();
23+
} on Exception catch (e) {
24+
print(e);
25+
}
26+
>>> Try and catch exception with on clause and stack trace.
27+
try{
28+
doSomething();
29+
}on Exception catch (e, s){
30+
print(e);
31+
}
32+
<<<
33+
try {
34+
doSomething();
35+
} on Exception catch (e, s) {
36+
print(e);
37+
}
38+
>>> Split empty catch if there is a finally.
39+
try {;} catch (err) {} finally {;}
40+
<<<
41+
try {
42+
;
43+
} catch (err) {
44+
} finally {
45+
;
46+
}
47+
>>> Split empty on if there is a finally.
48+
try {;} on Exception {} finally {;}
49+
<<<
50+
try {
51+
;
52+
} on Exception {
53+
} finally {
54+
;
55+
}
56+
>>> Split all empty catches if there is a finally.
57+
try {;} catch (err1) {} catch (err2) {} catch (err3) {} finally {;}
58+
<<<
59+
try {
60+
;
61+
} catch (err1) {
62+
} catch (err2) {
63+
} catch (err3) {
64+
} finally {
65+
;
66+
}
67+
>>> Split leading empty catches if there are multiple.
68+
try {;} catch (err1) {} catch (err2) {} catch (err3) {}
69+
<<<
70+
try {
71+
;
72+
} catch (err1) {
73+
} catch (err2) {
74+
} catch (err3) {}
75+
>>> Split empty catch with on clause if there is a finally.
76+
try {
77+
doSomething();
78+
} on Exception catch (e) {} finally {
79+
cleanupSomething();
80+
}
81+
<<<
82+
try {
83+
doSomething();
84+
} on Exception catch (e) {
85+
} finally {
86+
cleanupSomething();
87+
}
88+
>>> Split multiple on clauses.
89+
try {
90+
doSomething();
91+
} on FooException {} on BarException {
92+
doSomething();
93+
}
94+
<<<
95+
try {
96+
doSomething();
97+
} on FooException {
98+
} on BarException {
99+
doSomething();
100+
}
101+
>>> Don't split try.
102+
try {
103+
doSomething();
104+
} on FooException {} on BarException {
105+
doSomething();
106+
}
107+
<<<
108+
try {
109+
doSomething();
110+
} on FooException {
111+
} on BarException {
112+
doSomething();
113+
}

0 commit comments

Comments
 (0)