Skip to content

Commit d3b5aed

Browse files
authored
Handle imports with "as" and "if" clauses in either order. (#1555)
PR #1550 fixed the bug where if you had both clauses, it would output them in the *wrong* order. But it turns out that the parser today allows them in *either* order even though the language spec doesn't. Since I've seen a handful of cases like this in the wild, instead of just failing to format, simply preserve the clause order that the user wrote.
1 parent 953ecbc commit d3b5aed

File tree

4 files changed

+60
-6
lines changed

4 files changed

+60
-6
lines changed

lib/src/front_end/piece_factory.dart

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -812,8 +812,26 @@ mixin PieceFactory {
812812
pieces.visit(directive.uri);
813813
});
814814

815-
// Include the `as` clause.
815+
// Add all of the clauses and combinators.
816816
var clauses = <Piece>[];
817+
818+
// The language specifies that configurations must appear after any `as`
819+
// clause but the parser incorrectly accepts them before it and code in
820+
// the wild relies on that. Instead of failing with an "unexpected output"
821+
// error, just preserve the order of the clauses if they are out of order.
822+
// See: https://github.com/dart-lang/sdk/issues/56641
823+
var wroteConfigurations = false;
824+
if (directive.configurations.isNotEmpty &&
825+
asKeyword != null &&
826+
directive.configurations.first.ifKeyword.offset < asKeyword.offset) {
827+
for (var configuration in directive.configurations) {
828+
clauses.add(nodePiece(configuration));
829+
}
830+
831+
wroteConfigurations = true;
832+
}
833+
834+
// Include the `as` clause.
817835
if (asKeyword != null) {
818836
clauses.add(pieces.build(() {
819837
pieces.token(deferredKeyword, spaceAfter: true);
@@ -824,8 +842,10 @@ mixin PieceFactory {
824842
}
825843

826844
// Include any `if` clauses.
827-
for (var configuration in directive.configurations) {
828-
clauses.add(nodePiece(configuration));
845+
if (!wroteConfigurations) {
846+
for (var configuration in directive.configurations) {
847+
clauses.add(nodePiece(configuration));
848+
}
829849
}
830850

831851
// Include the `show` and `hide` clauses.

lib/src/short/source_visitor.dart

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1955,6 +1955,19 @@ class SourceVisitor extends ThrowingAstVisitor {
19551955
space();
19561956
visit(node.uri);
19571957

1958+
// The language specifies that configurations must appear after any `as`
1959+
// clause but the parser incorrectly accepts them before it and code in
1960+
// the wild relies on that. Instead of failing with an "unexpected output"
1961+
// error, just preserve the order of the clauses if they are out of order.
1962+
// See: https://github.com/dart-lang/sdk/issues/56641
1963+
var wroteConfigurations = false;
1964+
if (node.asKeyword case var asKeyword?
1965+
when node.configurations.isNotEmpty &&
1966+
node.configurations.first.ifKeyword.offset < asKeyword.offset) {
1967+
_visitConfigurations(node.configurations);
1968+
wroteConfigurations = true;
1969+
}
1970+
19581971
if (node.asKeyword != null) {
19591972
soloSplit();
19601973
token(node.deferredKeyword, after: space);
@@ -1963,7 +1976,10 @@ class SourceVisitor extends ThrowingAstVisitor {
19631976
visit(node.prefix);
19641977
}
19651978

1966-
_visitConfigurations(node.configurations);
1979+
if (!wroteConfigurations) {
1980+
_visitConfigurations(node.configurations);
1981+
}
1982+
19671983
_visitCombinators(node.combinators);
19681984
});
19691985
}

test/short/whitespace/directives.unit

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,4 +70,13 @@ part of 'uri.dart';
7070
import 'foo.dart' as foo if (config == 'value') 'other.dart';
7171
<<<
7272
import 'foo.dart' as foo
73-
if (config == 'value') 'other.dart';
73+
if (config == 'value') 'other.dart';
74+
>>> Configuration before prefix.
75+
### This violates the language spec, but the parser currently allows it without
76+
### reporting an error and code in the wild relies on that. So the formatter
77+
### handles it gracefully. See: https://github.com/dart-lang/sdk/issues/56641
78+
import 'foo.dart' if (config == 'value') 'other.dart' as foo;
79+
<<<
80+
import 'foo.dart'
81+
if (config == 'value') 'other.dart'
82+
as foo;

test/tall/top_level/import.unit

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,4 +73,13 @@ import 'foo.dart' as foo if (config == 'value') 'other.dart';
7373
<<<
7474
import 'foo.dart'
7575
as foo
76-
if (config == 'value') 'other.dart';
76+
if (config == 'value') 'other.dart';
77+
>>> Configuration before prefix.
78+
### This violates the language spec, but the parser currently allows it without
79+
### reporting an error and code in the wild relies on that. So the formatter
80+
### handles it gracefully. See: https://github.com/dart-lang/sdk/issues/56641
81+
import 'foo.dart' if (config == 'value') 'other.dart' as foo;
82+
<<<
83+
import 'foo.dart'
84+
if (config == 'value') 'other.dart'
85+
as foo;

0 commit comments

Comments
 (0)