Split empty catch blocks with finally clauses.
Fix #1029
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 387b186..a5c022f 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,6 +5,7 @@
* Always split cascades with multiple sections (#1006).
* Don't indent cascades farther than their receiver method chains.
* Optimize line splitting cascades (#811).
+* Split empty catch blocks with finally clauses (#1029).
# 2.0.1
diff --git a/lib/src/argument_list_visitor.dart b/lib/src/argument_list_visitor.dart
index a22864f..429d5bc 100644
--- a/lib/src/argument_list_visitor.dart
+++ b/lib/src/argument_list_visitor.dart
@@ -316,7 +316,7 @@
final int _trailingBlocks;
/// The rule used to split the bodies of all block arguments.
- Rule? get blockRule => _blockRule;
+ Rule get blockRule => _blockRule!;
Rule? _blockRule;
/// The most recent chunk that split before an argument.
@@ -446,7 +446,7 @@
rule.disableSplitOnInnerRules();
// Tell it to use the rule we've already created.
- visitor.beforeBlock(argumentBlock, blockRule!, previousSplit);
+ visitor.beforeBlock(argumentBlock, blockRule, previousSplit);
} else if (_allArguments.length > 1) {
// Edge case: Only bump the nesting if there are multiple arguments. This
// lets us avoid spurious indentation in cases like:
diff --git a/lib/src/nesting_level.dart b/lib/src/nesting_level.dart
index acbdfe3..3e2c5f8 100644
--- a/lib/src/nesting_level.dart
+++ b/lib/src/nesting_level.dart
@@ -25,7 +25,7 @@
/// The nesting level surrounding this one, or `null` if this is represents
/// top level code in a block.
NestingLevel? get parent => _parent;
- NestingLevel? _parent;
+ final NestingLevel? _parent;
/// The number of characters that this nesting level is indented relative to
/// the containing level.
@@ -42,7 +42,9 @@
bool get isNested => _parent != null;
- NestingLevel() : indent = 0;
+ NestingLevel()
+ : _parent = null,
+ indent = 0;
NestingLevel._(this._parent, this.indent);
diff --git a/lib/src/source_visitor.dart b/lib/src/source_visitor.dart
index 9b0517d..c97f0b5 100644
--- a/lib/src/source_visitor.dart
+++ b/lib/src/source_visitor.dart
@@ -478,19 +478,7 @@
// else is always split.
if (_isEmptyCollection(node.statements, node.rightBracket)) {
token(node.leftBracket);
-
- // Force a split when used as the then body of an if with an else:
- //
- // if (condition) {
- // } else ...
- if (node.parent is IfStatement) {
- var ifStatement = node.parent as IfStatement;
- if (ifStatement.elseStatement != null &&
- ifStatement.thenStatement == node) {
- newline();
- }
- }
-
+ if (_splitEmptyBlock(node)) newline();
token(node.rightBracket);
return;
}
@@ -2034,7 +2022,6 @@
token(node.rightParenthesis);
builder.unnest();
- @override
void visitClause(Statement clause) {
if (clause is Block || clause is IfStatement) {
space();
@@ -3417,6 +3404,31 @@
bool _isEmptyCollection(Iterable<AstNode> nodes, Token rightBracket) =>
nodes.isEmpty && rightBracket.precedingComments == null;
+ /// Whether [node] should be forced to split even if completely empty.
+ ///
+ /// Most empty blocks format as `{}` but in a couple of cases where there is
+ /// a subsequent block, we split the previous one.
+ bool _splitEmptyBlock(Block node) {
+ // Force a split when used as the then body of an if with an else:
+ //
+ // if (condition) {
+ // } else ...
+ if (node.parent is IfStatement) {
+ var ifStatement = node.parent as IfStatement;
+ return ifStatement.elseStatement != null &&
+ ifStatement.thenStatement == node;
+ }
+
+ // Force a split in an empty catch if there is a finally:
+ if (node.parent is CatchClause && node.parent!.parent is TryStatement) {
+ var tryStatement = node.parent!.parent as TryStatement;
+ return tryStatement.finallyBlock != null &&
+ tryStatement.catchClauses.any((clause) => clause.body == node);
+ }
+
+ return false;
+ }
+
/// If [node] is a spread of a non-empty collection literal, then this
/// returns the token for the opening bracket of the collection, as in:
///
diff --git a/test/regression/1000/1029.unit b/test/regression/1000/1029.unit
new file mode 100644
index 0000000..8dda931
--- /dev/null
+++ b/test/regression/1000/1029.unit
@@ -0,0 +1,17 @@
+>>>
+void main() {
+ try {
+ doSomething();
+ } on Exception catch (e) {} finally {
+ cleanupSomething();
+ }
+}
+<<<
+void main() {
+ try {
+ doSomething();
+ } on Exception catch (e) {
+ } finally {
+ cleanupSomething();
+ }
+}
\ No newline at end of file
diff --git a/test/splitting/statements.stmt b/test/splitting/statements.stmt
index 13e9aea..dde279c 100644
--- a/test/splitting/statements.stmt
+++ b/test/splitting/statements.stmt
@@ -70,4 +70,33 @@
}
<<<
if (condition) {
-} else {}
\ No newline at end of file
+} else {}
+>>> split empty catch if there is a finally
+try {;} catch (err) {} finally {;}
+<<<
+try {
+ ;
+} catch (err) {
+} finally {
+ ;
+}
+>>> split empty on if there is a finally
+try {;} on Exception {} finally {;}
+<<<
+try {
+ ;
+} on Exception {
+} finally {
+ ;
+}
+>>> split all empty catches if there is a finally
+try {;} catch (err1) {} catch (err2) {} catch (err3) {} finally {;}
+<<<
+try {
+ ;
+} catch (err1) {
+} catch (err2) {
+} catch (err3) {
+} finally {
+ ;
+}
\ No newline at end of file