Encode unconditional control flow information in nullability graph.
Change-Id: I598e05b61d6639d72ae6f84ab48f4e4051c87014
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/102302
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
diff --git a/pkg/analysis_server/lib/src/nullability/constraint_gatherer.dart b/pkg/analysis_server/lib/src/nullability/constraint_gatherer.dart
index c30ef23..c96c5b9 100644
--- a/pkg/analysis_server/lib/src/nullability/constraint_gatherer.dart
+++ b/pkg/analysis_server/lib/src/nullability/constraint_gatherer.dart
@@ -146,7 +146,7 @@
if (!_inConditionalControlFlow &&
_conditionInfo.trueDemonstratesNonNullIntent != null) {
_conditionInfo.trueDemonstratesNonNullIntent
- ?.recordNonNullIntent(_constraints, _guards);
+ ?.recordNonNullIntent(_constraints, _guards, _graph);
}
}
node.message?.accept(this);
diff --git a/pkg/analysis_server/lib/src/nullability/nullability_graph.dart b/pkg/analysis_server/lib/src/nullability/nullability_graph.dart
index 311db8a..c48537b 100644
--- a/pkg/analysis_server/lib/src/nullability/nullability_graph.dart
+++ b/pkg/analysis_server/lib/src/nullability/nullability_graph.dart
@@ -18,15 +18,32 @@
/// key node will have to be nullable, or null checks will have to be added).
final _upstream = Map<NullabilityNode, List<NullabilityNode>>.identity();
+ /// Map from a nullability node to those nodes that are "upstream" from it
+ /// via unconditional control flow (meaning that if a node in the value is
+ /// nullable, then there exists code that is unguarded by an "if" statement
+ /// that indicates that the corresponding key node will have to be nullable,
+ /// or null checks will have to be added).
+ final _unconditionalUpstream =
+ Map<NullabilityNode, List<NullabilityNode>>.identity();
+
/// Records that [sourceNode] is immediately upstream from [destinationNode].
- void connect(NullabilityNode sourceNode, NullabilityNode destinationNode) {
+ void connect(NullabilityNode sourceNode, NullabilityNode destinationNode,
+ {bool unconditional: false}) {
(_downstream[sourceNode] ??= []).add(destinationNode);
(_upstream[destinationNode] ??= []).add(sourceNode);
+ if (unconditional) {
+ (_unconditionalUpstream[destinationNode] ??= []).add(sourceNode);
+ }
}
void debugDump() {
for (var entry in _downstream.entries) {
- print('${entry.key} -> ${entry.value.join(', ')}');
+ print('${entry.key} -> ${entry.value.map((value) {
+ var suffix = getUnconditionalUpstreamNodes(value).contains(entry.key)
+ ? ' (unconditional)'
+ : '';
+ return '$value$suffix';
+ }).join(', ')}');
}
}
@@ -38,6 +55,14 @@
Iterable<NullabilityNode> getDownstreamNodes(NullabilityNode node) =>
_downstream[node] ?? const [];
+ /// Iterates through all nodes that are "upstream" of [node] due to
+ /// unconditional control flow.
+ ///
+ /// There is no guarantee of uniqueness of the iterated nodes.
+ Iterable<NullabilityNode> getUnconditionalUpstreamNodes(
+ NullabilityNode node) =>
+ _unconditionalUpstream[node] ?? const [];
+
/// Iterates through all nodes that are "upstream" of [node] (i.e. if
/// any of the iterated nodes are nullable, then [node] will either have to be
/// nullable, or null checks will have to be added).
diff --git a/pkg/analysis_server/lib/src/nullability/nullability_node.dart b/pkg/analysis_server/lib/src/nullability/nullability_node.dart
index b5c809d..f332806 100644
--- a/pkg/analysis_server/lib/src/nullability/nullability_node.dart
+++ b/pkg/analysis_server/lib/src/nullability/nullability_node.dart
@@ -126,9 +126,10 @@
}
}
- void recordNonNullIntent(
- Constraints constraints, List<NullabilityNode> guards) {
+ void recordNonNullIntent(Constraints constraints,
+ List<NullabilityNode> guards, NullabilityGraph graph) {
_recordConstraints(constraints, guards, const [], nonNullIntent);
+ graph.connect(this, NullabilityNode.never, unconditional: true);
}
String toString() {
@@ -186,7 +187,8 @@
NullabilityGraph graph,
bool inConditionalControlFlow) {
var additionalConditions = <ConstraintVariable>[];
- graph.connect(sourceNode, destinationNode);
+ graph.connect(sourceNode, destinationNode,
+ unconditional: !inConditionalControlFlow);
if (sourceNode._nullable != null) {
additionalConditions.add(sourceNode._nullable);
var destinationNonNullIntent = destinationNode.nonNullIntent;