Skip to content

Commit f12f19b

Browse files
DanTupCommit Queue
authored and
Commit Queue
committed
[analysis_server] Exclude some potential side effects from Inline Values
When the property experiment is enabled, we may evaluate getters in inline values. For some types, the chance of these having side effects are much higher (for example accessing `length`, `last`, `first` on `Iterable`s or `Stream`s. This suppresses inline values on these types (both the variables themselves to avoid any `toString()`s, and any getters). Fixes #60402 Change-Id: Ie9b524a679df5e39856ecd900d94f2fb41b779bf Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/420703 Commit-Queue: Brian Wilkerson <[email protected]> Reviewed-by: Brian Wilkerson <[email protected]> Reviewed-by: Phil Quitslund <[email protected]>
1 parent 82559e2 commit f12f19b

File tree

2 files changed

+114
-1
lines changed

2 files changed

+114
-1
lines changed

pkg/analysis_server/lib/src/lsp/handlers/handler_inline_value.dart

+40-1
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import 'package:analysis_server/src/services/correction/dart/convert_null_check_
1414
import 'package:analyzer/dart/ast/ast.dart';
1515
import 'package:analyzer/dart/ast/visitor.dart';
1616
import 'package:analyzer/dart/element/element2.dart';
17+
import 'package:analyzer/dart/element/type.dart';
1718
import 'package:analyzer/source/line_info.dart';
1819
import 'package:analyzer/src/dart/element/extensions.dart';
1920

@@ -210,9 +211,36 @@ class _InlineValueCollector {
210211
);
211212
}
212213

214+
/// Returns whether [element] is something that should never be eagerly
215+
/// evaluated because of potential side-effects (such as `iterable.length`).
216+
bool _isExcludedElement(Element2 element) {
217+
return switch (element) {
218+
VariableElement2() => _isExcludedType(element.type),
219+
GetterElement() => _isExcludedType(element.returnType),
220+
_ => false,
221+
};
222+
}
223+
224+
/// Returns whether [type] is something that should never be eagerly
225+
/// evaluated because of potential side-effects (such as `iterable.length`).
226+
bool _isExcludedType(DartType? type) {
227+
if (type == null) {
228+
return false;
229+
}
230+
return type.isDartCoreIterable ||
231+
type.isDartAsyncFuture ||
232+
type.isDartAsyncFutureOr ||
233+
type.isDartAsyncStream;
234+
}
235+
213236
/// Records an inline value [value] for [element] if it is within range and is
214237
/// the latest one in the source for that element.
215238
void _record(InlineValue value, Element2 element) {
239+
// Don't create values for any elements that are excluded types.
240+
if (_isExcludedElement(element)) {
241+
return;
242+
}
243+
216244
var range = _getRange(value);
217245

218246
// We only want to show each variable once, so keep only the one furthest
@@ -266,6 +294,11 @@ class _InlineValueVisitor extends GeneralizingAstVisitor<void> {
266294
@override
267295
void visitPrefixedIdentifier(PrefixedIdentifier node) {
268296
if (experimentalInlineValuesProperties) {
297+
// Don't create values for excluded types or access of their properties.
298+
if (collector._isExcludedType(node.prefix.staticType)) {
299+
return;
300+
}
301+
269302
var parent = node.parent;
270303

271304
// Never produce values for the left side of a property access.
@@ -286,7 +319,13 @@ class _InlineValueVisitor extends GeneralizingAstVisitor<void> {
286319

287320
@override
288321
void visitPropertyAccess(PropertyAccess node) {
289-
if (experimentalInlineValuesProperties && node.target is Identifier) {
322+
var target = node.target;
323+
if (experimentalInlineValuesProperties && target is Identifier) {
324+
// Don't create values for excluded types or access of their properties.
325+
if (collector._isExcludedType(target.staticType)) {
326+
return;
327+
}
328+
290329
collector.recordExpression(
291330
node.canonicalElement,
292331
node.offset,

pkg/analysis_server/test/lsp/inline_value_test.dart

+74
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,26 @@ class InlineValueTest extends AbstractLspAnalysisServerTest {
2525
/// client configuration passed during initialization.
2626
bool experimentalInlineValuesProperties = false;
2727

28+
Future<void> test_iterables() async {
29+
experimentalInlineValuesProperties = true;
30+
31+
// There are no marked ranges, because none of these should produce values.
32+
code = TestCode.parse(r'''
33+
import 'dart:async';
34+
35+
void f(
36+
Iterable<int> p1,
37+
Future<int> p2,
38+
FutureOr<int> p3,
39+
Stream<int> p4,
40+
) {
41+
^
42+
}
43+
''');
44+
45+
await verify_values(code);
46+
}
47+
2848
Future<void> test_parameter_declaration() async {
2949
code = TestCode.parse(r'''
3050
void f(int /*[0*/aaa/*0]*/, int /*[1*/bbb/*1]*/) {
@@ -36,6 +56,21 @@ void f(int /*[0*/aaa/*0]*/, int /*[1*/bbb/*1]*/) {
3656
await verify_values(code, ofType: InlineValueVariableLookup);
3757
}
3858

59+
/// Lists are included, iterables are not.
60+
Future<void> test_parameter_iterables() async {
61+
experimentalInlineValuesProperties = true;
62+
63+
code = TestCode.parse(r'''
64+
void f(List list1, List<int> /*[0*/list2/*0]*/, Iterable iterable1, Iterable iterable2) {
65+
print(/*[1*/list1/*1]*/);
66+
print(iterable1);
67+
^
68+
}
69+
''');
70+
71+
await verify_values(code, ofType: InlineValueVariableLookup);
72+
}
73+
3974
Future<void> test_parameter_read() async {
4075
code = TestCode.parse(r'''
4176
void f(int aaa, int bbb) {
@@ -156,6 +191,27 @@ void f() {
156191
await verify_values(code, ofType: InlineValueEvaluatableExpression);
157192
}
158193

194+
/// Lists are included, iterables are not.
195+
Future<void> test_property_iterables() async {
196+
experimentalInlineValuesProperties = true;
197+
198+
code = TestCode.parse(r'''
199+
void f(List<int> /*[0*/list/*0]*/, Iterable<int> iterable) {
200+
print(/*[1*/list.length/*1]*/);
201+
print(iterable.length);
202+
^
203+
}
204+
''');
205+
206+
await verify_values(
207+
code,
208+
ofTypes: {
209+
0: InlineValueVariableLookup,
210+
1: InlineValueEvaluatableExpression,
211+
},
212+
);
213+
}
214+
159215
Future<void> test_property_method() async {
160216
experimentalInlineValuesProperties = true;
161217

@@ -300,6 +356,24 @@ void f() {
300356
await verify_values(code, ofType: InlineValueVariableLookup);
301357
}
302358

359+
/// Lists are included, iterables are not.
360+
Future<void> test_variable_iterables() async {
361+
experimentalInlineValuesProperties = true;
362+
363+
code = TestCode.parse(r'''
364+
void f() {
365+
var list = [1,];
366+
var iterable = list as Iterable<int>;
367+
368+
print(/*[0*/list/*0]*/);
369+
print(iterable);
370+
^
371+
}
372+
''');
373+
374+
await verify_values(code, ofType: InlineValueVariableLookup);
375+
}
376+
303377
Future<void> test_variable_propertyAccess() async {
304378
code = TestCode.parse(r'''
305379
void f(int /*[0*/aaa/*0]*/) {

0 commit comments

Comments
 (0)