Skip to content

Commit 998a421

Browse files
stereotype441commit-bot@chromium.org
authored andcommitted
Migration: expose fix reasons through the instrumentation interface.
Change-Id: I0f8ba9aa58d098b431fad338d46a2f14d87e43b4 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/117686 Reviewed-by: Brian Wilkerson <[email protected]> Commit-Queue: Paul Berry <[email protected]>
1 parent a5683df commit 998a421

7 files changed

+101
-7
lines changed

pkg/nnbd_migration/lib/instrumentation.dart

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import 'package:analyzer/dart/ast/ast.dart';
66
import 'package:analyzer/dart/element/element.dart';
77
import 'package:analyzer/dart/element/type.dart';
88
import 'package:analyzer/src/generated/source.dart';
9+
import 'package:nnbd_migration/nnbd_migration.dart';
910
import 'package:nnbd_migration/nullability_state.dart';
1011

1112
/// Information exposed to the migration client about the set of nullability
@@ -42,7 +43,7 @@ abstract class DecoratedTypeInfo {
4243
/// A graph edge represents a dependency relationship between two types being
4344
/// migrated, suggesting that if one type (the source) is made nullable, it may
4445
/// be desirable to make the other type (the destination) nullable as well.
45-
abstract class EdgeInfo {
46+
abstract class EdgeInfo implements FixReasonInfo {
4647
/// Information about the graph node that this edge "points to".
4748
NullabilityNodeInfo get destinationNode;
4849

@@ -96,6 +97,10 @@ abstract class EdgeOriginInfo {
9697
Source get source;
9798
}
9899

100+
/// Interface used by the migration engine to expose information to its client
101+
/// about a reason for a modification to the source file.
102+
abstract class FixReasonInfo {}
103+
99104
/// Interface used by the migration engine to expose information to its client
100105
/// about the decisions made during migration, and how those decisions relate to
101106
/// the input source code.
@@ -112,6 +117,9 @@ abstract class NullabilityMigrationInstrumentation {
112117
/// of the element.
113118
void externalDecoratedType(Element element, DecoratedTypeInfo decoratedType);
114119

120+
/// Called whenever a fix is decided upon, to report the reasons for the fix.
121+
void fix(SingleNullabilityFix fix, Iterable<FixReasonInfo> reasons);
122+
115123
/// Called whenever the migration engine creates a graph edge between
116124
/// nullability nodes, to report information about the edge that was created,
117125
/// and why it was created.
@@ -162,7 +170,7 @@ abstract class NullabilityMigrationInstrumentation {
162170

163171
/// Information exposed to the migration client about a single node in the
164172
/// nullability graph.
165-
abstract class NullabilityNodeInfo {
173+
abstract class NullabilityNodeInfo implements FixReasonInfo {
166174
/// Indicates whether the node is immutable. The only immutable nodes in the
167175
/// nullability graph are the nodes `never` and `always` that are used as the
168176
/// starting points for nullability propagation.

pkg/nnbd_migration/lib/src/conditional_discard.dart

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
// for details. All rights reserved. Use of this source code is governed by a
33
// BSD-style license that can be found in the LICENSE file.
44

5+
import 'package:nnbd_migration/instrumentation.dart';
56
import 'package:nnbd_migration/src/nullability_node.dart';
67

78
/// Container for information gathered during nullability migration about a
@@ -48,4 +49,10 @@ class ConditionalDiscard {
4849
/// Indicates whether the code path that results from the condition evaluating
4950
/// to `true` is reachable after migration.
5051
bool get keepTrue => trueGuard == null || trueGuard.isNullable;
52+
53+
@override
54+
Iterable<FixReasonInfo> get reasons sync* {
55+
if (!keepTrue) yield falseGuard;
56+
if (!keepFalse) yield trueGuard;
57+
}
5158
}

pkg/nnbd_migration/lib/src/expression_checks.dart

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import 'package:analyzer/dart/ast/ast.dart';
66
import 'package:analyzer/src/generated/source.dart';
77
import 'package:analyzer_plugin/protocol/protocol_common.dart';
8+
import 'package:nnbd_migration/instrumentation.dart';
89
import 'package:nnbd_migration/nnbd_migration.dart';
910
import 'package:nnbd_migration/src/edge_origin.dart';
1011
import 'package:nnbd_migration/src/nullability_node.dart';
@@ -65,6 +66,13 @@ class ExpressionChecks extends PotentialModification {
6566
// reified to contain only non-null ints.
6667
return isEmpty ? [] : [SourceEdit(offset, 0, '!')];
6768
}
69+
70+
@override
71+
Iterable<FixReasonInfo> get reasons sync* {
72+
for (var edge in edges) {
73+
if (!edge.isSatisfied) yield edge;
74+
}
75+
}
6876
}
6977

7078
/// [EdgeOrigin] object associated with [ExpressionChecks]. This is a separate

pkg/nnbd_migration/lib/src/nullability_migration_impl.dart

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ class NullabilityMigrationImpl implements NullabilityMigration {
5555
// it, we can't report on every unsatisfied edge. We need to figure out a
5656
// way to report unsatisfied edges that isn't too overwhelming.
5757
if (_variables != null) {
58-
broadcast(_variables, listener);
58+
broadcast(_variables, listener, _instrumentation);
5959
}
6060
}
6161

@@ -77,7 +77,9 @@ class NullabilityMigrationImpl implements NullabilityMigration {
7777

7878
@visibleForTesting
7979
static void broadcast(
80-
Variables variables, NullabilityMigrationListener listener) {
80+
Variables variables,
81+
NullabilityMigrationListener listener,
82+
NullabilityMigrationInstrumentation instrumentation) {
8183
for (var entry in variables.getPotentialModifications().entries) {
8284
var source = entry.key;
8385
final lineInfo = LineInfo.fromContent(source.contents.data);
@@ -89,6 +91,7 @@ class NullabilityMigrationImpl implements NullabilityMigration {
8991
var fix =
9092
_SingleNullabilityFix(source, potentialModification, lineInfo);
9193
listener.addFix(fix);
94+
instrumentation?.fix(fix, potentialModification.reasons);
9295
for (var edit in modifications) {
9396
listener.addEdit(fix, edit);
9497
}

pkg/nnbd_migration/lib/src/potential_modification.dart

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import 'package:analyzer/dart/ast/ast.dart';
66
import 'package:analyzer/dart/element/type.dart';
77
import 'package:analyzer_plugin/protocol/protocol_common.dart' show SourceEdit;
8+
import 'package:nnbd_migration/instrumentation.dart';
89
import 'package:nnbd_migration/nnbd_migration.dart';
910
import 'package:nnbd_migration/src/conditional_discard.dart';
1011
import 'package:nnbd_migration/src/nullability_node.dart';
@@ -87,6 +88,9 @@ class ConditionalModification extends PotentialModification {
8788
}
8889
return result;
8990
}
91+
92+
@override
93+
Iterable<FixReasonInfo> get reasons => discard.reasons;
9094
}
9195

9296
/// Records information about the possible addition of an import to the source
@@ -125,6 +129,13 @@ class PotentiallyAddImport extends PotentialModification {
125129
Iterable<SourceEdit> get modifications =>
126130
isEmpty ? const [] : [SourceEdit(_offset, 0, "import '$importPath';\n")];
127131

132+
@override
133+
Iterable<FixReasonInfo> get reasons sync* {
134+
for (var usage in _usages) {
135+
if (!usage.isEmpty) yield* usage.reasons;
136+
}
137+
}
138+
128139
void addUsage(PotentialModification usage) {
129140
_usages.add(usage);
130141
}
@@ -149,6 +160,9 @@ class PotentiallyAddQuestionSuffix extends PotentialModification {
149160
@override
150161
Iterable<SourceEdit> get modifications =>
151162
isEmpty ? [] : [SourceEdit(_offset, 0, '?')];
163+
164+
@override
165+
Iterable<FixReasonInfo> get reasons => [node];
152166
}
153167

154168
/// Records information about the possible addition of a `@required` annotation
@@ -184,6 +198,9 @@ class PotentiallyAddRequired extends PotentialModification {
184198
@override
185199
Iterable<SourceEdit> get modifications =>
186200
isEmpty ? const [] : [SourceEdit(_offset, 0, '@required ')];
201+
202+
@override
203+
Iterable<FixReasonInfo> get reasons => [_node];
187204
}
188205

189206
/// Interface used by data structures representing potential modifications to
@@ -197,6 +214,9 @@ abstract class PotentialModification {
197214
/// Gets the individual migrations that need to be done, considering the
198215
/// solution to the constraint equations.
199216
Iterable<SourceEdit> get modifications;
217+
218+
/// Gets the reasons for this potential modification.
219+
Iterable<FixReasonInfo> get reasons;
200220
}
201221

202222
/// Helper object used by [ConditionalModification] to keep track of AST nodes

pkg/nnbd_migration/test/instrumentation_test.dart

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,11 @@ class _InstrumentationClient implements NullabilityMigrationInstrumentation {
4040
test.externalDecoratedType[element] = decoratedType;
4141
}
4242

43+
@override
44+
void fix(SingleNullabilityFix fix, Iterable<FixReasonInfo> reasons) {
45+
test.fixes[fix] = reasons.toList();
46+
}
47+
4348
@override
4449
void graphEdge(EdgeInfo edge, EdgeOriginInfo originInfo) {
4550
expect(test.edgeOrigin, isNot(contains(edge)));
@@ -93,6 +98,8 @@ class _InstrumentationTest extends AbstractContextTest {
9398

9499
final List<EdgeInfo> edges = [];
95100

101+
Map<SingleNullabilityFix, List<FixReasonInfo>> fixes = {};
102+
96103
final Map<AstNode, DecoratedTypeInfo> implicitReturnType = {};
97104

98105
final Map<AstNode, DecoratedTypeInfo> implicitType = {};
@@ -148,6 +155,47 @@ main() {
148155
'void Function(Object)');
149156
}
150157

158+
test_fix_reason_edge() async {
159+
await analyze('''
160+
void f(int x) {
161+
print(x.isEven);
162+
}
163+
void g(int y, bool b) {
164+
if (b) {
165+
f(y);
166+
}
167+
}
168+
main() {
169+
g(null, false);
170+
}
171+
''');
172+
var yUsage = findNode.simple('y);');
173+
var entry =
174+
fixes.entries.where((e) => e.key.location.offset == yUsage.end).single;
175+
var reasons = entry.value;
176+
expect(reasons, hasLength(1));
177+
var edge = reasons[0] as EdgeInfo;
178+
expect(edge.sourceNode,
179+
same(explicitTypeNullability[findNode.typeAnnotation('int y')]));
180+
expect(edge.destinationNode,
181+
same(explicitTypeNullability[findNode.typeAnnotation('int x')]));
182+
expect(edge.isSatisfied, false);
183+
expect(edgeOrigin[edge].node, same(yUsage));
184+
}
185+
186+
test_fix_reason_node() async {
187+
await analyze('''
188+
int x = null;
189+
''');
190+
var entries = fixes.entries.toList();
191+
expect(entries, hasLength(1));
192+
var intAnnotation = findNode.typeAnnotation('int');
193+
expect(entries.single.key.location.offset, intAnnotation.end);
194+
var reasons = entries.single.value;
195+
expect(reasons, hasLength(1));
196+
expect(reasons.single, same(explicitTypeNullability[intAnnotation]));
197+
}
198+
151199
test_graphEdge() async {
152200
await analyze('''
153201
int f(int x) => x;

pkg/nnbd_migration/test/nullability_migration_impl_test.dart

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ class NullabilityMigrationImplTest {
3939

4040
when(innerModification.isEmpty).thenReturn(false);
4141

42-
NullabilityMigrationImpl.broadcast(variables, listener);
42+
NullabilityMigrationImpl.broadcast(variables, listener, null);
4343

4444
final fix = verify(listener.addFix(captureAny)).captured.single
4545
as SingleNullabilityFix;
@@ -68,7 +68,7 @@ class NullabilityMigrationImplTest {
6868

6969
when(potentialModification.modifications).thenReturn([]);
7070

71-
NullabilityMigrationImpl.broadcast(variables, listener);
71+
NullabilityMigrationImpl.broadcast(variables, listener, null);
7272

7373
verifyNever(listener.addFix(any));
7474
verifyNever(listener.reportException(any, any, any, any));
@@ -80,7 +80,7 @@ class NullabilityMigrationImplTest {
8080
final source = SourceMock('');
8181
when(variables.getPotentialModifications()).thenReturn({source: []});
8282

83-
NullabilityMigrationImpl.broadcast(variables, listener);
83+
NullabilityMigrationImpl.broadcast(variables, listener, null);
8484

8585
verifyNever(listener.addFix(any));
8686
verifyNever(listener.reportException(any, any, any, any));

0 commit comments

Comments
 (0)