| // Copyright (c) 2019, 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 'dart:io' show Directory, Platform; | 
 |  | 
 | import 'package:_fe_analyzer_shared/src/testing/id.dart' show ActualData, Id; | 
 | import 'package:_fe_analyzer_shared/src/testing/id_testing.dart' | 
 |     show DataInterpreter, runTests; | 
 | import 'package:front_end/src/source/source_loader.dart'; | 
 | import 'package:front_end/src/source/source_member_builder.dart'; | 
 | import 'package:front_end/src/testing/id_testing_helper.dart'; | 
 | import 'package:front_end/src/testing/id_testing_utils.dart'; | 
 | import 'package:front_end/src/type_inference/type_inference_engine.dart'; | 
 | import 'package:kernel/ast.dart'; | 
 |  | 
 | Future<void> main(List<String> args) async { | 
 |   Directory dataDir = new Directory.fromUri( | 
 |     Platform.script.resolve( | 
 |       '../../../_fe_analyzer_shared/test/flow_analysis/reachability/data', | 
 |     ), | 
 |   ); | 
 |   await runTests<Set<_ReachabilityAssertion>>( | 
 |     dataDir, | 
 |     args: args, | 
 |     createUriForFileName: createUriForFileName, | 
 |     onFailure: onFailure, | 
 |     runTest: runTestFor(const ReachabilityDataComputer(), [defaultCfeConfig]), | 
 |   ); | 
 | } | 
 |  | 
 | class ReachabilityDataComputer | 
 |     extends CfeDataComputer<Set<_ReachabilityAssertion>> { | 
 |   const ReachabilityDataComputer(); | 
 |  | 
 |   @override | 
 |   DataInterpreter<Set<_ReachabilityAssertion>> get dataValidator => | 
 |       const _ReachabilityDataInterpreter(); | 
 |  | 
 |   /// Function that computes a data mapping for [member]. | 
 |   /// | 
 |   /// Fills [actualMap] with the data. | 
 |   @override | 
 |   void computeMemberData( | 
 |     CfeTestResultData testResultData, | 
 |     Member member, | 
 |     Map<Id, ActualData<Set<_ReachabilityAssertion>>> actualMap, { | 
 |     bool? verbose, | 
 |   }) { | 
 |     SourceMemberBuilder memberBuilder = | 
 |         lookupMemberBuilder(testResultData.compilerResult, member) | 
 |             as SourceMemberBuilder; | 
 |     member.accept( | 
 |       new ReachabilityDataExtractor( | 
 |         testResultData.compilerResult, | 
 |         actualMap, | 
 |         memberBuilder.dataForTesting!.inferenceData.flowAnalysisResult, | 
 |       ), | 
 |     ); | 
 |   } | 
 |  | 
 |   /// Errors are supported for testing erroneous code. The reported errors are | 
 |   /// not tested. | 
 |   @override | 
 |   bool get supportsErrors => true; | 
 | } | 
 |  | 
 | class ReachabilityDataExtractor | 
 |     extends CfeDataExtractor<Set<_ReachabilityAssertion>> { | 
 |   final SourceLoaderDataForTesting _sourceLoaderDataForTesting; | 
 |   final FlowAnalysisResult _flowResult; | 
 |  | 
 |   ReachabilityDataExtractor( | 
 |     InternalCompilerResult compilerResult, | 
 |     Map<Id, ActualData<Set<_ReachabilityAssertion>>> actualMap, | 
 |     this._flowResult, | 
 |   ) : _sourceLoaderDataForTesting = | 
 |           compilerResult.kernelTargetForTesting!.loader.dataForTesting!, | 
 |       super(compilerResult, actualMap); | 
 |  | 
 |   @override | 
 |   Set<_ReachabilityAssertion>? computeMemberValue(Id id, Member member) { | 
 |     Set<_ReachabilityAssertion> result = {}; | 
 |     if (member.function != null) { | 
 |       TreeNode alias = _sourceLoaderDataForTesting.toOriginal( | 
 |         member.function!.body!, | 
 |       ); | 
 |       if (_flowResult.functionBodiesThatDontComplete.contains(alias)) { | 
 |         result.add(_ReachabilityAssertion.doesNotComplete); | 
 |       } | 
 |     } | 
 |     return result.isEmpty ? null : result; | 
 |   } | 
 |  | 
 |   @override | 
 |   Set<_ReachabilityAssertion>? computeNodeValue(Id id, TreeNode node) { | 
 |     Set<_ReachabilityAssertion> result = {}; | 
 |     TreeNode alias = _sourceLoaderDataForTesting.toOriginal(node); | 
 |     if (node is Expression && node.parent is ExpressionStatement) { | 
 |       // The reachability of an expression statement and the statement it | 
 |       // contains should always be the same.  We check this with an assert | 
 |       // statement, and only annotate the expression statement, to reduce the | 
 |       // amount of redundancy in the test files. | 
 |       assert( | 
 |         _flowResult.unreachableNodes.contains(alias) == | 
 |             _flowResult.unreachableNodes.contains( | 
 |               _sourceLoaderDataForTesting.toOriginal(node.parent!), | 
 |             ), | 
 |       ); | 
 |     } else if (_flowResult.unreachableNodes.contains(alias)) { | 
 |       result.add(_ReachabilityAssertion.unreachable); | 
 |     } | 
 |     if (node is FunctionDeclaration) { | 
 |       Statement? body = node.function.body; | 
 |       if (body != null && | 
 |           _flowResult.functionBodiesThatDontComplete.contains( | 
 |             _sourceLoaderDataForTesting.toOriginal(body), | 
 |           )) { | 
 |         result.add(_ReachabilityAssertion.doesNotComplete); | 
 |       } | 
 |     } | 
 |     return result.isEmpty ? null : result; | 
 |   } | 
 | } | 
 |  | 
 | enum _ReachabilityAssertion { doesNotComplete, unreachable } | 
 |  | 
 | class _ReachabilityDataInterpreter | 
 |     implements DataInterpreter<Set<_ReachabilityAssertion>> { | 
 |   const _ReachabilityDataInterpreter(); | 
 |  | 
 |   @override | 
 |   String getText( | 
 |     Set<_ReachabilityAssertion> actualData, [ | 
 |     String? indentation, | 
 |   ]) => _sortedRepresentation(_toStrings(actualData)); | 
 |  | 
 |   @override | 
 |   String? isAsExpected( | 
 |     Set<_ReachabilityAssertion> actualData, | 
 |     String? expectedData, | 
 |   ) { | 
 |     var actualStrings = _toStrings(actualData); | 
 |     var actualSorted = _sortedRepresentation(actualStrings); | 
 |     var expectedSorted = _sortedRepresentation(expectedData?.split(',')); | 
 |     if (actualSorted == expectedSorted) { | 
 |       return null; | 
 |     } else { | 
 |       return 'Expected $expectedData, got $actualSorted'; | 
 |     } | 
 |   } | 
 |  | 
 |   @override | 
 |   bool isEmpty(Set<_ReachabilityAssertion> actualData) => actualData.isEmpty; | 
 |  | 
 |   String _sortedRepresentation(Iterable<String>? values) { | 
 |     var list = values == null || values.isEmpty ? ['none'] : values.toList(); | 
 |     list.sort(); | 
 |     return list.join(','); | 
 |   } | 
 |  | 
 |   List<String> _toStrings(Set<_ReachabilityAssertion> actualData) => actualData | 
 |       .map((flowAssertion) => flowAssertion.toString().split('.')[1]) | 
 |       .toList(); | 
 | } |