Skip to content

Commit 0d12749

Browse files
stereotype441Commit Queue
authored andcommitted
[analyzer] Change ResolverVisitor to always visit subexpressions using analyzeExpression.
Previously, when `ResolverVisitor` needed to provide a context to a subexpression, it would always use `analyzeExpression` to visit it, but when no context was needed, it would sometimes visit the subexpression by calling `ExpressionImpl.accept`. This made the `ResolverVisitor` more difficult to reason about, since there wasn't a common method through which all subexpression visits were dispatched. With change, any time the `ResolverVisitor` visits an expression in a context that's inside a method body or initializer, it visits it using `analyzeExpression`. To make sure I haven't missed anything, and to prevent future regressions, I've added an assertion to `_InferenceLogWriterImpl.enterExpression` that verifies that all expressions visited by the `ResolverVisitor` are visited via `analyzeExpression` when inside a top level declaration. This change paves the way for allowing the analyzer to use the shared implementation of null-shorting introduced in https://dart-review.googlesource.com/c/sdk/+/399480. Change-Id: I56eb102ae0034898f5171ac4cc3e90676bce5db7 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/399781 Reviewed-by: Brian Wilkerson <[email protected]> Commit-Queue: Paul Berry <[email protected]>
1 parent c878108 commit 0d12749

File tree

9 files changed

+165
-25
lines changed

9 files changed

+165
-25
lines changed

pkg/analyzer/analyzer_use_new_elements.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,7 @@ lib/src/dart/resolver/this_lookup.dart
9292
lib/src/dart/resolver/type_property_resolver.dart
9393
lib/src/dart/resolver/typed_literal_resolver.dart
9494
lib/src/dart/resolver/variable_declaration_resolver.dart
95+
lib/src/dart/resolver/yield_statement_resolver.dart
9596
lib/src/diagnostic/diagnostic_factory.dart
9697
lib/src/error/best_practices_verifier.dart
9798
lib/src/error/correct_override.dart

pkg/analyzer/lib/src/dart/resolver/binary_expression_resolver.dart

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -306,8 +306,12 @@ class BinaryExpressionResolver {
306306
}
307307

308308
void _resolveUnsupportedOperator(BinaryExpressionImpl node) {
309-
node.leftOperand.accept(_resolver);
310-
node.rightOperand.accept(_resolver);
309+
_resolver.analyzeExpression(
310+
node.leftOperand, _resolver.operations.unknownType);
311+
_resolver.popRewrite();
312+
_resolver.analyzeExpression(
313+
node.rightOperand, _resolver.operations.unknownType);
314+
_resolver.popRewrite();
311315
node.recordStaticType(InvalidTypeImpl.instance, resolver: _resolver);
312316
}
313317

pkg/analyzer/lib/src/dart/resolver/flow_analysis_visitor.dart

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import 'package:analyzer/src/dart/element/element.dart';
2323
import 'package:analyzer/src/dart/element/type.dart';
2424
import 'package:analyzer/src/dart/element/type_schema.dart';
2525
import 'package:analyzer/src/dart/element/type_system.dart' show TypeSystemImpl;
26+
import 'package:analyzer/src/generated/inference_log.dart';
2627
import 'package:analyzer/src/generated/variable_type_provider.dart';
2728

2829
export 'package:_fe_analyzer_shared/src/type_inference/nullability_suffix.dart'
@@ -165,6 +166,7 @@ class FlowAnalysisHelper {
165166
/// will be visited.
166167
void bodyOrInitializer_enter(AstNode node, FormalParameterList? parameters,
167168
{void Function(AstVisitor<Object?> visitor)? visit}) {
169+
inferenceLogWriter?.enterBodyOrInitializer(node);
168170
assert(flow == null);
169171
assignedVariables = computeAssignedVariables(node, parameters,
170172
retainDataForTesting: dataForTesting != null, visit: visit);
@@ -190,6 +192,7 @@ class FlowAnalysisHelper {
190192
/// This method is called whenever the [ResolverVisitor] leaves the body or
191193
/// initializer of a top level declaration.
192194
void bodyOrInitializer_exit() {
195+
inferenceLogWriter?.exitBodyOrInitializer();
193196
// Set this.flow to null before doing any clean-up so that if an exception
194197
// is raised, the state is already updated correctly, and we don't have
195198
// cascading failures.

pkg/analyzer/lib/src/dart/resolver/for_resolver.dart

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import 'package:analyzer/src/dart/element/type_system.dart';
1414
import 'package:analyzer/src/dart/resolver/assignment_expression_resolver.dart';
1515
import 'package:analyzer/src/dart/resolver/typed_literal_resolver.dart';
1616
import 'package:analyzer/src/error/codes.dart';
17+
import 'package:analyzer/src/generated/inference_log.dart';
1718
import 'package:analyzer/src/generated/resolver.dart';
1819

1920
/// Helper for resolving [ForStatement]s and [ForElement]s.
@@ -129,6 +130,8 @@ class ForResolver {
129130
} else if (forEachParts is ForEachPartsWithIdentifier) {
130131
identifier = forEachParts.identifier;
131132
// TODO(scheglov): replace with lexical lookup
133+
inferenceLogWriter?.setExpressionVisitCodePath(
134+
identifier, ExpressionVisitCodePath.forEachIdentifier);
132135
identifier.accept(_resolver);
133136
AssignmentExpressionShared(
134137
resolver: _resolver,
@@ -199,7 +202,11 @@ class ForResolver {
199202
if (forParts is ForPartsWithDeclarations) {
200203
forParts.variables.accept(_resolver);
201204
} else if (forParts is ForPartsWithExpression) {
202-
forParts.initialization?.accept(_resolver);
205+
if (forParts.initialization case var initialization?) {
206+
_resolver.analyzeExpression(
207+
initialization, _resolver.operations.unknownType);
208+
_resolver.popRewrite();
209+
}
203210
} else if (forParts is ForPartsWithPattern) {
204211
forParts.variables.accept(_resolver);
205212
} else {
@@ -223,7 +230,10 @@ class ForResolver {
223230
visitBody();
224231

225232
_resolver.flowAnalysis.flow?.for_updaterBegin();
226-
forParts.updaters.accept(_resolver);
233+
for (var updater in forParts.updaters) {
234+
_resolver.analyzeExpression(updater, _resolver.operations.unknownType);
235+
_resolver.popRewrite();
236+
}
227237

228238
_resolver.flowAnalysis.flow?.for_end();
229239
}

pkg/analyzer/lib/src/dart/resolver/function_reference_resolver.dart

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,8 @@ class FunctionReferenceResolver {
5050
var typeArguments = node.typeArguments;
5151
if (typeArguments != null) {
5252
// Something like `List.filled<int>`.
53-
function.accept(_resolver);
53+
_resolver.analyzeExpression(function, _resolver.operations.unknownType);
54+
_resolver.popRewrite();
5455
// We can safely assume `function.constructorName.name` is non-null
5556
// because if no name had been given, the construct would have been
5657
// interpreted as a type literal (e.g. `List<int>`).
@@ -67,7 +68,8 @@ class FunctionReferenceResolver {
6768
} else {
6869
// TODO(srawlins): Handle `function` being a [SuperExpression].
6970

70-
function.accept(_resolver);
71+
_resolver.analyzeExpression(function, _resolver.operations.unknownType);
72+
function = _resolver.popRewrite()!;
7173
var functionType = function.staticType;
7274
if (functionType == null) {
7375
_resolveDisallowedExpression(node, functionType);
@@ -493,13 +495,15 @@ class FunctionReferenceResolver {
493495
CompileTimeErrorCode.DISALLOWED_TYPE_INSTANTIATION_EXPRESSION,
494496
);
495497
}
496-
function.accept(_resolver);
498+
_resolver.analyzeExpression(function, _resolver.operations.unknownType);
499+
_resolver.popRewrite();
497500
node.recordStaticType(InvalidTypeImpl.instance, resolver: _resolver);
498501
}
499502

500503
void _resolvePropertyAccessFunction(
501504
FunctionReferenceImpl node, PropertyAccessImpl function) {
502-
function.accept(_resolver);
505+
_resolver.analyzeExpression(function, _resolver.operations.unknownType);
506+
_resolver.popRewrite();
503507
var callMethod = _getCallMethod(node, function.staticType);
504508
if (callMethod is MethodElement) {
505509
_resolveAsImplicitCallReference(node, callMethod);
@@ -617,16 +621,21 @@ class FunctionReferenceResolver {
617621
_resolveConstructorReference(node);
618622
return;
619623
} else if (element is InterfaceElement) {
620-
node.function.accept(_resolver);
624+
_resolver.analyzeExpression(
625+
node.function, _resolver.operations.unknownType);
626+
_resolver.popRewrite();
621627
_resolveDirectTypeLiteral(node, prefix, element);
622628
return;
623629
} else if (element is TypeAliasElement) {
624-
prefix.accept(_resolver);
630+
_resolver.analyzeExpression(prefix, _resolver.operations.unknownType);
631+
_resolver.popRewrite();
625632
_resolveTypeAlias(node: node, element: element, typeAlias: prefix);
626633
return;
627634
}
628635
} else if (element is ExecutableElement) {
629-
node.function.accept(_resolver);
636+
_resolver.analyzeExpression(
637+
node.function, _resolver.operations.unknownType);
638+
_resolver.popRewrite();
630639
var callMethod = _getCallMethod(node, node.function.staticType);
631640
if (callMethod is MethodElement) {
632641
_resolveAsImplicitCallReference(node, callMethod);

pkg/analyzer/lib/src/dart/resolver/prefixed_identifier_resolver.dart

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,8 @@ class PrefixedIdentifierResolver {
2727
PrefixedIdentifierImpl node, {
2828
required DartType contextType,
2929
}) {
30-
node.prefix.accept(_resolver);
30+
_resolver.analyzeExpression(node.prefix, _resolver.operations.unknownType);
31+
_resolver.popRewrite();
3132

3233
var prefixElement = node.prefix.staticElement;
3334
if (prefixElement is! PrefixElement) {

pkg/analyzer/lib/src/dart/resolver/yield_statement_resolver.dart

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -183,7 +183,9 @@ class YieldStatementResolver {
183183
}
184184

185185
void _resolve_notGenerator(YieldStatement node) {
186-
node.expression.accept(_resolver);
186+
_resolver.analyzeExpression(
187+
node.expression, _resolver.operations.unknownType);
188+
_resolver.popRewrite();
187189

188190
_errorReporter.atNode(
189191
node,

pkg/analyzer/lib/src/generated/inference_log.dart

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import 'package:_fe_analyzer_shared/src/type_inference/shared_inference_log.dart
66
import 'package:analyzer/dart/ast/ast.dart';
77
import 'package:analyzer/dart/element/element.dart';
88
import 'package:analyzer/dart/element/type.dart';
9+
import 'package:analyzer/src/dart/ast/ast.dart';
910
import 'package:analyzer/src/generated/resolver.dart';
1011

1112
final bool _assertionsEnabled = () {
@@ -14,6 +15,11 @@ final bool _assertionsEnabled = () {
1415
return enabled;
1516
}();
1617

18+
/// Expando used by [_InferenceLogWriterImpl.setExpressionVisitCodePath] to
19+
/// record the code path that's being used by the [ResolverVisitor] to visit a
20+
/// subexpression.
21+
final _expressionVisitCodePaths = Expando<ExpressionVisitCodePath>();
22+
1723
/// The [InferenceLogWriter] currently being used by the analyzer, if inference
1824
/// logging is active, otherwise `null`.
1925
InferenceLogWriter? _inferenceLogWriter;
@@ -52,6 +58,18 @@ void stopInferenceLogging() {
5258
_inferenceLogWriter = null;
5359
}
5460

61+
/// Enum of all the possible code paths that can be used by the
62+
/// [ResolverVisitor] to visit expressions when inside the body or initializer
63+
/// of a top level construct.
64+
enum ExpressionVisitCodePath {
65+
/// The expression is being visited via [ResolverVisitor.analyzeExpression].
66+
analyzeExpression,
67+
68+
/// The expression is the identifier in a "for each" loop, so it is not a true
69+
/// expression, and it is being visited directly using [Expression.accept].
70+
forEachIdentifier
71+
}
72+
5573
/// The [SharedInferenceLogWriter] interface, augmented with analyzer-specific
5674
/// functionality.
5775
abstract interface class InferenceLogWriter
@@ -62,12 +80,44 @@ abstract interface class InferenceLogWriter
6280
/// This is called from [ResolverVisitor.dispatchExpression], to verify that
6381
/// each expression's visit method property calls [enterExpression].
6482
void assertExpressionWasRecorded(Expression expression);
83+
84+
/// Called when type inference enters the body of a top level function or
85+
/// method, or the initializer of a top level variable or field, or the
86+
/// initializers and body of a constructor.
87+
void enterBodyOrInitializer(AstNode node);
88+
89+
/// Called when type inference enters the body of a top level function or
90+
/// method, or the initializer of a top level variable or field, or the
91+
/// initializers and body of a constructor.
92+
void exitBodyOrInitializer();
93+
94+
/// Records [source] as the code path that the [ResolverVisitor] is about to
95+
/// use to visit the expression [node].
96+
///
97+
/// An assertion in [enterExpression] verifies that when inside a method body
98+
/// or initializer (see [enterBodyOrInitializer]), every call to
99+
/// [enterExpression] is preceded by exactly one call to
100+
/// [setExpressionVisitCodePath]. This ensures that the resolution process
101+
/// doesn't ever try to resolve a subexpression more than once. It also
102+
/// ensures that every code path that resolves subexpressions inside method
103+
/// bodies and initializers calls this method, making it easy to statically
104+
/// locate these code paths.
105+
void setExpressionVisitCodePath(
106+
Expression node, ExpressionVisitCodePath source);
65107
}
66108

67109
/// The [SharedInferenceLogWriterImpl] implementation, augmented with
68110
/// analyzer-specific functionality.
69111
final class _InferenceLogWriterImpl extends SharedInferenceLogWriterImpl<
70112
DartType, DartType, TypeParameterElement> implements InferenceLogWriter {
113+
/// Whether type inference is currently inside the body of a top level
114+
/// function or method, or the initializer of a top level variable or field,
115+
/// or the initializers and body of a constructor.
116+
///
117+
/// When this value is `true`, flow analysis is active, and expressions must
118+
/// be visited using [ResolverVisitor.analyzeExpression].
119+
bool _inBodyOrInitializer = false;
120+
71121
@override
72122
void assertExpressionWasRecorded(Object expression) {
73123
if (_recordedExpressions[expression] ?? false) return;
@@ -90,6 +140,12 @@ final class _InferenceLogWriterImpl extends SharedInferenceLogWriterImpl<
90140
super.enterAnnotation(node);
91141
}
92142

143+
@override
144+
void enterBodyOrInitializer(AstNode node) {
145+
assert(!_inBodyOrInitializer, 'Already in a body or initializer');
146+
_inBodyOrInitializer = true;
147+
}
148+
93149
@override
94150
void enterElement(covariant CollectionElement node) {
95151
checkCall(
@@ -101,6 +157,10 @@ final class _InferenceLogWriterImpl extends SharedInferenceLogWriterImpl<
101157

102158
@override
103159
void enterExpression(covariant Expression node, DartType contextType) {
160+
assert(
161+
!_inBodyOrInitializer || _expressionVisitCodePaths[node] != null,
162+
'When in a body or initializer, setExpressionVisitSource should be '
163+
'called prior to enterExpression. Not called for $node.');
104164
checkCall(
105165
method: 'enterExpression',
106166
arguments: [node, contextType],
@@ -147,6 +207,20 @@ final class _InferenceLogWriterImpl extends SharedInferenceLogWriterImpl<
147207
super.enterStatement(node);
148208
}
149209

210+
@override
211+
void exitBodyOrInitializer() {
212+
assert(_inBodyOrInitializer, 'Not in a method body or initializer');
213+
_inBodyOrInitializer = false;
214+
}
215+
216+
@override
217+
void setExpressionVisitCodePath(
218+
Expression node, ExpressionVisitCodePath source) {
219+
assert(_expressionVisitCodePaths[node] == null,
220+
'An expression visit source was already set for $node');
221+
_expressionVisitCodePaths[node] = source;
222+
}
223+
150224
/// Returns the nearest ancestor of [node] for which a call to `enter...`
151225
/// should have been made.
152226
///

0 commit comments

Comments
 (0)