4ee861b7d0
We now track the following additional information: - All local variable reads and read captures (previously we only tracked writes) - For any conditional expression (`?:`) or if-test, the variables written in the "then" branch - For any logical and expression (`&&`), the variables written in the right hand side of the expression Tracking this information will allow us to implement legacy type promotion as part of _fe_analyzer_shared, using the same API as flow anaylsis, which will in turn allow a lot of front end and analyzer code to be removed and simplified. Change-Id: I01aaf64cefb989a4450c8e6d3a8373c1ca2b6dec Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/179980 Commit-Queue: Paul Berry <paulberry@google.com> Reviewed-by: Konstantin Shcheglov <scheglov@google.com> Reviewed-by: Johnni Winther <johnniwinther@google.com>
164 lines
5.5 KiB
Dart
164 lines
5.5 KiB
Dart
// 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.
|
|
|
|
// @dart = 2.9
|
|
|
|
import 'dart:io' show Directory, Platform;
|
|
|
|
import 'package:_fe_analyzer_shared/src/flow_analysis/flow_analysis.dart';
|
|
import 'package:_fe_analyzer_shared/src/testing/id.dart'
|
|
show ActualData, Id, IdKind;
|
|
import 'package:_fe_analyzer_shared/src/testing/id_testing.dart'
|
|
show DataInterpreter, runTests;
|
|
import 'package:_fe_analyzer_shared/src/testing/id_testing.dart';
|
|
import 'package:front_end/src/fasta/builder/member_builder.dart';
|
|
import 'package:front_end/src/fasta/source/source_loader.dart';
|
|
|
|
import 'package:front_end/src/testing/id_testing_helper.dart';
|
|
import 'package:front_end/src/testing/id_testing_utils.dart';
|
|
import 'package:kernel/ast.dart' hide Variance;
|
|
|
|
main(List<String> args) async {
|
|
Directory dataDir = new Directory.fromUri(Platform.script.resolve(
|
|
'../../../_fe_analyzer_shared/test/flow_analysis/assigned_variables/'
|
|
'data'));
|
|
await runTests<_Data>(dataDir,
|
|
args: args,
|
|
createUriForFileName: createUriForFileName,
|
|
onFailure: onFailure,
|
|
runTest: runTestFor<_Data>(
|
|
const AssignedVariablesDataComputer(), [cfeNonNullableOnlyConfig]));
|
|
}
|
|
|
|
class AssignedVariablesDataComputer extends DataComputer<_Data> {
|
|
const AssignedVariablesDataComputer();
|
|
|
|
@override
|
|
DataInterpreter<_Data> get dataValidator =>
|
|
const _AssignedVariablesDataInterpreter();
|
|
|
|
/// Function that computes a data mapping for [member].
|
|
///
|
|
/// Fills [actualMap] with the data.
|
|
void computeMemberData(
|
|
TestConfig config,
|
|
InternalCompilerResult compilerResult,
|
|
Member member,
|
|
Map<Id, ActualData<_Data>> actualMap,
|
|
{bool verbose}) {
|
|
MemberBuilderImpl memberBuilder =
|
|
lookupMemberBuilder(compilerResult, member);
|
|
AssignedVariablesForTesting<TreeNode, VariableDeclaration>
|
|
assignedVariables = memberBuilder
|
|
.dataForTesting.inferenceData.flowAnalysisResult.assignedVariables;
|
|
if (assignedVariables == null) return;
|
|
member.accept(new AssignedVariablesDataExtractor(
|
|
compilerResult, actualMap, assignedVariables));
|
|
}
|
|
}
|
|
|
|
class AssignedVariablesDataExtractor extends CfeDataExtractor<_Data> {
|
|
final SourceLoaderDataForTesting _sourceLoaderDataForTesting;
|
|
final AssignedVariablesForTesting<TreeNode, VariableDeclaration>
|
|
_assignedVariables;
|
|
|
|
AssignedVariablesDataExtractor(InternalCompilerResult compilerResult,
|
|
Map<Id, ActualData<_Data>> actualMap, this._assignedVariables)
|
|
: _sourceLoaderDataForTesting =
|
|
compilerResult.kernelTargetForTesting.loader.dataForTesting,
|
|
super(compilerResult, actualMap);
|
|
|
|
@override
|
|
_Data computeMemberValue(Id id, Member member) {
|
|
return new _Data(
|
|
_convertVars(_assignedVariables.declaredAtTopLevel),
|
|
_convertVars(_assignedVariables.readAnywhere),
|
|
_convertVars(_assignedVariables.readCapturedAnywhere),
|
|
_convertVars(_assignedVariables.writtenAnywhere),
|
|
_convertVars(_assignedVariables.capturedAnywhere));
|
|
}
|
|
|
|
Set<String> _convertVars(Iterable<VariableDeclaration> x) =>
|
|
x.map((e) => e.name).toSet();
|
|
|
|
@override
|
|
_Data computeNodeValue(Id id, TreeNode node) {
|
|
switch (id.kind) {
|
|
case IdKind.iterator:
|
|
case IdKind.current:
|
|
case IdKind.moveNext:
|
|
return null;
|
|
default:
|
|
}
|
|
TreeNode alias = _sourceLoaderDataForTesting.toOriginal(node);
|
|
if (!_assignedVariables.isTracked(alias)) return null;
|
|
return new _Data(
|
|
_convertVars(_assignedVariables.declaredInNode(alias)),
|
|
_convertVars(_assignedVariables.readInNode(alias)),
|
|
_convertVars(_assignedVariables.readCapturedInNode(alias)),
|
|
_convertVars(_assignedVariables.writtenInNode(alias)),
|
|
_convertVars(_assignedVariables.capturedInNode(alias)));
|
|
}
|
|
}
|
|
|
|
class _AssignedVariablesDataInterpreter implements DataInterpreter<_Data> {
|
|
const _AssignedVariablesDataInterpreter();
|
|
|
|
@override
|
|
String getText(_Data actualData, [String indentation]) {
|
|
var parts = <String>[];
|
|
if (actualData.declared.isNotEmpty) {
|
|
parts.add('declared=${_setToString(actualData.declared)}');
|
|
}
|
|
if (actualData.read.isNotEmpty) {
|
|
parts.add('read=${_setToString(actualData.read)}');
|
|
}
|
|
if (actualData.readCaptured.isNotEmpty) {
|
|
parts.add('readCaptured=${_setToString(actualData.readCaptured)}');
|
|
}
|
|
if (actualData.assigned.isNotEmpty) {
|
|
parts.add('assigned=${_setToString(actualData.assigned)}');
|
|
}
|
|
if (actualData.captured.isNotEmpty) {
|
|
parts.add('captured=${_setToString(actualData.captured)}');
|
|
}
|
|
if (parts.isEmpty) return 'none';
|
|
return parts.join(', ');
|
|
}
|
|
|
|
@override
|
|
String isAsExpected(_Data actualData, String expectedData) {
|
|
var actualDataText = getText(actualData);
|
|
if (actualDataText == expectedData) {
|
|
return null;
|
|
} else {
|
|
return 'Expected "$expectedData", got "$actualDataText"';
|
|
}
|
|
}
|
|
|
|
@override
|
|
bool isEmpty(_Data actualData) =>
|
|
actualData.assigned.isEmpty && actualData.captured.isEmpty;
|
|
|
|
String _setToString(Set<String> values) {
|
|
List<String> sortedValues = values.toList()..sort();
|
|
return '{${sortedValues.join(', ')}}';
|
|
}
|
|
}
|
|
|
|
class _Data {
|
|
final Set<String> declared;
|
|
|
|
final Set<String> read;
|
|
|
|
final Set<String> readCaptured;
|
|
|
|
final Set<String> assigned;
|
|
|
|
final Set<String> captured;
|
|
|
|
_Data(this.declared, this.read, this.readCaptured, this.assigned,
|
|
this.captured);
|
|
}
|