| // Copyright (c) 2024, the Dart project authors. Please see the AUTHORS file |
| // for details. All rights reserved. Use of this source code is governed by a |
| // BSD-style license that can be found in the LICENSE file. |
| |
| import 'package:_fe_analyzer_shared/src/type_inference/shared_inference_log.dart'; |
| import 'package:analyzer/dart/ast/ast.dart'; |
| import 'package:analyzer/dart/element/type.dart'; |
| import 'package:analyzer/src/generated/resolver.dart'; |
| |
| final bool _assertionsEnabled = () { |
| bool enabled = false; |
| assert(enabled = true); |
| return enabled; |
| }(); |
| |
| /// The [InferenceLogWriter] currently being used by the analyzer, if inference |
| /// logging is active, otherwise `null`. |
| InferenceLogWriter? _inferenceLogWriter; |
| |
| /// Expando storing a value `true` for each expression that has been passed to |
| /// [_InferenceLogWriterImpl.enterExpression]. |
| /// |
| /// This is used by [_InferenceLogWriterImpl.assertExpressionWasRecorded] to |
| /// verify that [_InferenceLogWriterImpl.enterExpression] was called when it |
| /// should be. |
| final _recordedExpressions = Expando<bool>(); |
| |
| /// Returns the [InferenceLogWriter] currently being used by the analyzer, if |
| /// inference logging is active, otherwise `null`. |
| InferenceLogWriter? get inferenceLogWriter => _inferenceLogWriter; |
| |
| /// Starts up inference logging if appropriate. |
| /// |
| /// Inference logging will be started up if either of the following conditions |
| /// are met: |
| /// - The [dump] parameter is `true` (in which case the log will immediately |
| /// start dumping events to standard output) |
| /// - Assertions are enabled. |
| void conditionallyStartInferenceLogging({bool dump = false}) { |
| assert(_inferenceLogWriter == null); |
| if (_assertionsEnabled || dump) { |
| var inferenceLogWriter = _inferenceLogWriter = _InferenceLogWriterImpl(); |
| if (dump) { |
| inferenceLogWriter.dump(); |
| } |
| } |
| } |
| |
| /// Stops inference logging if it's been started. |
| void stopInferenceLogging() { |
| _inferenceLogWriter = null; |
| } |
| |
| /// The [SharedInferenceLogWriter] interface, augmented with analyzer-specific |
| /// functionality. |
| abstract interface class InferenceLogWriter |
| implements SharedInferenceLogWriter<DartType> { |
| /// Checks that [enterExpression] was properly called for [expression]. |
| /// |
| /// This is called from [ResolverVisitor.dispatchExpression], to verify that |
| /// each expression's visit method property calls [enterExpression]. |
| void assertExpressionWasRecorded(Expression expression); |
| } |
| |
| /// The [SharedInferenceLogWriterImpl] implementation, augmented with |
| /// analyzer-specific functionality. |
| final class _InferenceLogWriterImpl |
| extends SharedInferenceLogWriterImpl<DartType> |
| implements InferenceLogWriter { |
| @override |
| void assertExpressionWasRecorded(Object expression) { |
| if (_recordedExpressions[expression] ?? false) return; |
| fail('failed to record ${describe(expression)}'); |
| } |
| |
| @override |
| void enterExpression(covariant Expression node, DartType contextType) { |
| checkCall( |
| method: 'enterExpression', |
| arguments: [node, contextType], |
| expectedNode: traceableAncestor(node)); |
| super.enterExpression(node, contextType); |
| _recordedExpressions[node] = true; |
| } |
| |
| @override |
| void enterExtensionOverride( |
| covariant ExtensionOverride node, DartType contextType) { |
| checkCall( |
| method: 'enterExtensionOverride', |
| arguments: [node, contextType], |
| expectedNode: traceableAncestor(node)); |
| super.enterExtensionOverride(node, contextType); |
| _recordedExpressions[node] = true; |
| } |
| |
| @override |
| void enterLValue(covariant Expression node) { |
| checkCall( |
| method: 'enterLValue', |
| arguments: [node], |
| expectedNode: traceableAncestor(node)); |
| super.enterLValue(node); |
| } |
| |
| /// Returns the nearest ancestor of [node] for which a call to `enter...` |
| /// should have been made. |
| /// |
| /// This is used to verify proper nesting of `enter...` method calls. |
| AstNode? traceableAncestor(covariant AstNode node) { |
| for (var parent = node.parent;; parent = parent.parent) { |
| switch (parent) { |
| case null: |
| case Expression(): |
| return parent; |
| default: |
| break; |
| } |
| } |
| } |
| } |