Merge pull request #1002 from dart-lang/fix-crashes
Fix a couple of crashes I found when testing the migration on a corpus.
diff --git a/lib/src/call_chain_visitor.dart b/lib/src/call_chain_visitor.dart
index 5af43f7..4151cb7 100644
--- a/lib/src/call_chain_visitor.dart
+++ b/lib/src/call_chain_visitor.dart
@@ -558,7 +558,7 @@
}
// Postfix expressions.
- if (node is IndexExpression) {
+ if (node is IndexExpression && node.target != null) {
return _unwrapPostfix(node, node.target!, calls);
}
diff --git a/lib/src/chunk.dart b/lib/src/chunk.dart
index 02dbfb6..90d5636 100644
--- a/lib/src/chunk.dart
+++ b/lib/src/chunk.dart
@@ -87,8 +87,8 @@
/// someFunctionName(argument, argument,
/// argument, anotherFunction(argument,
/// argument));
- NestingLevel get nesting => _nesting;
- late NestingLevel _nesting;
+ NestingLevel? get nesting => _nesting;
+ NestingLevel? _nesting;
/// If this chunk marks the beginning of a block, this contains the child
/// chunks and other data about that nested block.
@@ -178,6 +178,12 @@
/// Creates a new chunk starting with [_text].
Chunk(this._text);
+ /// Creates a dummy chunk.
+ ///
+ /// This is returned in some places by [ChunkBuilder] when there is no useful
+ /// chunk to yield and it will not end up being used by the caller anyway.
+ Chunk.dummy() : _text = '(dummy)';
+
/// Discard the split for the chunk and put it back into the state where more
/// text can be appended.
void allowText() {
@@ -232,7 +238,14 @@
var argument = block.argument;
if (argument == null) return false;
- return argument.rule!.isSplit(getValue(argument.rule!), argument);
+ var rule = argument.rule;
+
+ // There may be no rule if the block occurs inside a string interpolation.
+ // In that case, it's not clear if anything will look particularly nice, but
+ // expression nesting is probably marginally better.
+ if (rule == null) return true;
+
+ return rule.isSplit(getValue(rule), argument);
}
// Mark whether this chunk can divide the range of chunks.
diff --git a/lib/src/chunk_builder.dart b/lib/src/chunk_builder.dart
index a17c89a..c2ab67b 100644
--- a/lib/src/chunk_builder.dart
+++ b/lib/src/chunk_builder.dart
@@ -181,7 +181,7 @@
/// If [nest] is `false`, ignores any current expression nesting. Otherwise,
/// uses the current nesting level. If unsplit, it expands to a space if
/// [space] is `true`.
- Chunk? split({bool? flushLeft, bool? isDouble, bool? nest, bool? space}) {
+ Chunk split({bool? flushLeft, bool? isDouble, bool? nest, bool? space}) {
space ??= false;
// If we are not allowed to split at all, don't. Returning null for the
@@ -189,7 +189,7 @@
// discarded because no chunk references it.
if (_preventSplitNesting > 0) {
if (space) _pendingWhitespace = Whitespace.space;
- return null;
+ return Chunk.dummy();
}
return _writeSplit(_rules.last,
@@ -847,15 +847,14 @@
/// Ends the current chunk (if any) with the given split information.
///
/// Returns the chunk.
- Chunk? _writeSplit(Rule rule,
+ Chunk _writeSplit(Rule rule,
{bool? flushLeft, bool? isDouble, bool? nest, bool? space}) {
nest ??= true;
space ??= false;
if (_chunks.isEmpty) {
if (flushLeft != null) _firstFlushLeft = flushLeft;
-
- return null;
+ return Chunk.dummy();
}
_chunks.last.applySplit(
@@ -884,7 +883,7 @@
var chunk = _chunks[i];
if (!chunk.rule!.isHardened) return false;
- if (chunk.nesting.isNested) return false;
+ if (chunk.nesting!.isNested) return false;
if (chunk.isBlock) return false;
return true;
diff --git a/lib/src/debug.dart b/lib/src/debug.dart
index 5934afd..f796af3 100644
--- a/lib/src/debug.dart
+++ b/lib/src/debug.dart
@@ -146,7 +146,7 @@
writeIf(chunk.indent != null && chunk.indent != 0,
() => 'indent ${chunk.indent}');
- writeIf(chunk.nesting.indent != 0, () => 'nest ${chunk.nesting}');
+ writeIf(chunk.nesting?.indent != 0, () => 'nest ${chunk.nesting}');
writeIf(chunk.flushLeft, () => 'flush');
diff --git a/lib/src/line_splitting/solve_state.dart b/lib/src/line_splitting/solve_state.dart
index 708353e..6701c7a 100644
--- a/lib/src/line_splitting/solve_state.dart
+++ b/lib/src/line_splitting/solve_state.dart
@@ -279,8 +279,8 @@
for (var i = 0; i < _splitter.chunks.length - 1; i++) {
var chunk = _splitter.chunks[i];
if (chunk.rule!.isSplit(getValue(chunk.rule!), chunk)) {
- usedNestingLevels.add(chunk.nesting);
- chunk.nesting.clearTotalUsedIndent();
+ usedNestingLevels.add(chunk.nesting!);
+ chunk.nesting!.clearTotalUsedIndent();
}
}
@@ -298,7 +298,7 @@
indent = _splitter.blockIndentation + chunk.indent!;
// And any expression nesting.
- indent += chunk.nesting.totalUsedIndent;
+ indent += chunk.nesting!.totalUsedIndent;
if (chunk.indentBlock(getValue)) indent += Indent.expression;
}
@@ -385,9 +385,10 @@
// But there are a couple of squirrely cases where it's hard to prevent
// by construction. Instead, this outlaws it by penalizing it very
// heavily if it happens to get this far.
+ var totalIndent = chunk.nesting!.totalUsedIndent;
if (previousNesting != null &&
- chunk.nesting.totalUsedIndent != 0 &&
- chunk.nesting.totalUsedIndent == previousNesting.totalUsedIndent &&
+ totalIndent != 0 &&
+ totalIndent == previousNesting.totalUsedIndent &&
!identical(chunk.nesting, previousNesting)) {
_overflowChars += 10000;
}
diff --git a/lib/src/source_visitor.dart b/lib/src/source_visitor.dart
index 50986a4..6a2ccd4 100644
--- a/lib/src/source_visitor.dart
+++ b/lib/src/source_visitor.dart
@@ -2879,7 +2879,7 @@
builder.nestExpression();
token(leftBracket);
- rule.beforeArgument(zeroSplit()!);
+ rule.beforeArgument(zeroSplit());
for (var node in nodes) {
visit(node);
@@ -2887,7 +2887,7 @@
// Write the trailing comma.
if (node != nodes.last) {
token(node.endToken.next);
- rule.beforeArgument(split()!);
+ rule.beforeArgument(split());
}
}
@@ -3493,13 +3493,13 @@
void _visitCombinator(Token keyword, Iterable<AstNode> nodes) {
// Allow splitting before the keyword.
var rule = builder.rule as CombinatorRule;
- rule.addCombinator(split()!);
+ rule.addCombinator(split());
builder.nestExpression();
token(keyword);
- rule.addName(split()!);
- visitCommaSeparatedNodes(nodes, between: () => rule.addName(split()!));
+ rule.addName(split());
+ visitCommaSeparatedNodes(nodes, between: () => rule.addName(split()));
builder.unnest();
}
@@ -3665,12 +3665,12 @@
/// Writes a single space split owned by the current rule.
///
/// Returns the chunk the split was applied to.
- Chunk? split() => builder.split(space: true);
+ Chunk split() => builder.split(space: true);
/// Writes a zero-space split owned by the current rule.
///
/// Returns the chunk the split was applied to.
- Chunk? zeroSplit() => builder.split();
+ Chunk zeroSplit() => builder.split();
/// Writes a single space split with its own rule.
Rule soloSplit([int? cost]) {
diff --git a/test/regression/other/null_safety.unit b/test/regression/other/null_safety.unit
new file mode 100644
index 0000000..5dc376d
--- /dev/null
+++ b/test/regression/other/null_safety.unit
@@ -0,0 +1,20 @@
+>>> issues found when migrating to null safety
+void main() => print('first: ${first<int>([1, 2, 3])}');
+<<<
+void main() => print('first: ${first<int>([1, 2, 3])}');
+>>>
+var x = '${jsonEncode([
+ 1,
+])}';
+<<<
+var x = '${jsonEncode([
+ 1,
+ ])}';
+>>>
+main() {
+ patch..[entityId].removeWhere();
+}
+<<<
+main() {
+ patch..[entityId].removeWhere();
+}
\ No newline at end of file
diff --git a/test/splitting/invocations.stmt b/test/splitting/invocations.stmt
index 17948be..6650f93 100644
--- a/test/splitting/invocations.stmt
+++ b/test/splitting/invocations.stmt
@@ -159,6 +159,20 @@
.method()
..x = 1
..y = 2;
+>>> cascade index
+object..[index]..method()..[index]=value;
+<<<
+object
+ ..[index]
+ ..method()
+ ..[index] = value;
+>>> null-aware cascade index
+object?..[index]..method()..[index]=value;
+<<<
+object
+ ?..[index]
+ ..method()
+ ..[index] = value;
>>> conditional invocation
object?.method().method()?.method().method();
<<<