Begin running the shared "ID tests" for constants on the analyzer.

This exposed an analyzer bug--see #37608.

Change-Id: I96af785ce81ebb7b142b4f71cb43942b2b15f879
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/110123
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
Commit-Queue: Paul Berry <paulberry@google.com>
This commit is contained in:
Paul Berry 2019-07-23 22:08:12 +00:00 committed by commit-bot@chromium.org
parent 2ada1edb59
commit 6c43390cd8
7 changed files with 263 additions and 62 deletions

View file

@ -0,0 +1,94 @@
// 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';
import 'package:analyzer/dart/ast/ast.dart';
import 'package:analyzer/dart/constant/value.dart';
import 'package:analyzer/dart/element/element.dart';
import 'package:analyzer/dart/element/type.dart';
import 'package:analyzer/src/util/ast_data_extractor.dart';
import 'package:front_end/src/testing/id.dart' show ActualData, Id;
import 'package:front_end/src/testing/id_testing.dart';
import '../util/id_testing_helper.dart';
main(List<String> args) async {
Directory dataDir = new Directory.fromUri(
Platform.script.resolve('../../../front_end/test/constants/data'));
await runTests(dataDir,
args: args,
supportedMarkers: sharedMarkers,
createUriForFileName: createUriForFileName,
onFailure: onFailure,
runTest: runTestFor(
const ConstantsDataComputer(), [analyzerConstantUpdate2018Config]));
}
class ConstantsDataComputer extends DataComputer<String> {
const ConstantsDataComputer();
@override
DataInterpreter<String> get dataValidator => const StringDataInterpreter();
@override
void computeUnitData(
CompilationUnit unit, Map<Id, ActualData<String>> actualMap) {
ConstantsDataExtractor(unit.declaredElement.source.uri, actualMap)
.run(unit);
}
}
class ConstantsDataExtractor extends AstDataExtractor<String> {
ConstantsDataExtractor(Uri uri, Map<Id, ActualData<String>> actualMap)
: super(uri, actualMap);
@override
String computeNodeValue(Id id, AstNode node) {
if (node is Identifier) {
var element = node.staticElement;
if (element is PropertyAccessorElement && element.isSynthetic) {
var variable = element.variable;
if (!variable.isSynthetic && variable.isConst) {
var value = variable.constantValue;
if (value != null) return _stringify(value);
}
}
}
return null;
}
String _stringify(DartObject value) {
var type = value.type;
if (type is InterfaceType) {
if (type.isDartCoreNull) return 'Null()';
if (type.isDartCoreBool) return 'Bool(${value.toBoolValue()})';
if (type.isDartCoreString) return 'String(${value.toStringValue()})';
if (type.isDartCoreInt) return 'Int(${value.toIntValue()})';
if (type.isDartCoreDouble) return 'Double(${value.toDoubleValue()})';
if (type.isDartCoreSymbol) return 'Symbol(${value.toSymbolValue()})';
if (type.isDartCoreSet) {
var elements = value.toSetValue().map(_stringify).join(',');
return 'Set<${type.typeArguments[0]}>($elements)';
}
if (type.isDartCoreList) {
var elements = value.toListValue().map(_stringify).join(',');
return 'List<${type.typeArguments[0]}>($elements)';
}
if (type.isDartCoreMap) {
var typeArguments = type.typeArguments.join(',');
var elements = value.toMapValue().entries.map((entry) {
var key = _stringify(entry.key);
var value = _stringify(entry.value);
return '$key:$value';
}).join(',');
return 'Map<$typeArguments>($elements)';
}
} else if (type is FunctionType) {
var element = value.toFunctionValue();
return 'Function(${element.name})';
}
throw UnimplementedError('_stringify for type $type');
}
}

View file

@ -2,14 +2,12 @@
// 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:analyzer/dart/analysis/results.dart';
import 'package:analyzer/dart/analysis/features.dart';
import 'package:analyzer/dart/ast/ast.dart';
import 'package:analyzer/dart/element/element.dart';
import 'package:analyzer/dart/element/type_system.dart';
import 'package:analyzer/src/dart/analysis/experiments.dart';
import 'package:analyzer/src/dart/element/type.dart';
import 'package:analyzer/src/dart/resolver/flow_analysis_visitor.dart';
import 'package:analyzer/src/generated/engine.dart';
import 'package:analyzer/src/util/ast_data_extractor.dart';
import 'package:front_end/src/testing/id.dart' show ActualData, Id;
import 'package:front_end/src/testing/id_testing.dart' show DataInterpreter;
@ -17,7 +15,6 @@ import 'package:test/test.dart';
import 'package:test_reflective_loader/test_reflective_loader.dart';
import '../../../util/id_testing_helper.dart';
import 'driver_resolution.dart';
main() {
defineReflectiveSuite(() {
@ -26,32 +23,23 @@ main() {
});
}
class FlowTestBase extends DriverResolutionTest {
class FlowTestBase {
FlowAnalysisResult flowResult;
/// Resolve the given [code] and track nullability in the unit.
Future<void> trackCode(String code) async {
if (await checkTests(
code, _computeResult, const _FlowAnalysisDataComputer())) {
code,
const _FlowAnalysisDataComputer(),
FeatureSet.forTesting(
sdkVersion: '2.2.2', additionalFeatures: [Feature.non_nullable]))) {
fail('Failure(s)');
}
}
Future<ResolvedUnitResult> _computeResult(String code) async {
addTestFile(code);
await resolveTestFile();
var unit = result.unit;
flowResult = FlowAnalysisResult.getFromNode(unit);
return result;
}
}
@reflectiveTest
class NullableFlowTest extends FlowTestBase {
@override
AnalysisOptionsImpl get analysisOptions =>
AnalysisOptionsImpl()..enabledExperiments = [EnableString.non_nullable];
test_assign_toNonNull() async {
await trackCode(r'''
void f(int? x) {
@ -336,10 +324,6 @@ void f(int? x) {
@reflectiveTest
class ReachableFlowTest extends FlowTestBase {
@override
AnalysisOptionsImpl get analysisOptions =>
AnalysisOptionsImpl()..enabledExperiments = [EnableString.non_nullable];
test_conditional_false() async {
await trackCode(r'''
void f() {

View file

@ -2,13 +2,11 @@
// 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:analyzer/dart/analysis/results.dart';
import 'package:analyzer/dart/analysis/features.dart';
import 'package:analyzer/dart/ast/ast.dart';
import 'package:analyzer/dart/element/element.dart';
import 'package:analyzer/dart/element/type.dart';
import 'package:analyzer/src/dart/analysis/experiments.dart';
import 'package:analyzer/src/dart/element/type.dart';
import 'package:analyzer/src/generated/engine.dart';
import 'package:analyzer/src/util/ast_data_extractor.dart';
import 'package:front_end/src/testing/id.dart' show ActualData, Id;
import 'package:front_end/src/testing/id_testing.dart' show DataInterpreter;
@ -16,7 +14,6 @@ import 'package:test/test.dart';
import 'package:test_reflective_loader/test_reflective_loader.dart';
import '../../../util/id_testing_helper.dart';
import 'driver_resolution.dart';
main() {
defineReflectiveSuite(() {
@ -25,14 +22,13 @@ main() {
}
@reflectiveTest
class TypePromotionTest extends DriverResolutionTest {
@override
AnalysisOptionsImpl get analysisOptions =>
AnalysisOptionsImpl()..enabledExperiments = [EnableString.non_nullable];
class TypePromotionTest {
Future<void> resolveCode(String code) async {
if (await checkTests(
code, _computeResult, const _TypePromotionDataComputer())) {
code,
const _TypePromotionDataComputer(),
FeatureSet.forTesting(
sdkVersion: '2.2.2', additionalFeatures: [Feature.non_nullable]))) {
fail('Failure(s)');
}
}
@ -730,12 +726,6 @@ void f(bool b, Object x) {
}
''');
}
Future<ResolvedUnitResult> _computeResult(String code) async {
addTestFile(code);
await resolveTestFile();
return result;
}
}
class _TypePromotionDataComputer extends DataComputer<DartType> {

View file

@ -5,38 +5,149 @@
// TODO(paulberry,johnniwinther): Use the code for extraction of test data from
// annotated code from CFE.
import 'package:analyzer/dart/analysis/results.dart';
import 'package:analyzer/dart/analysis/features.dart';
import 'package:analyzer/dart/analysis/utilities.dart';
import 'package:analyzer/dart/ast/ast.dart' hide Annotation;
import 'package:analyzer/file_system/file_system.dart';
import 'package:analyzer/file_system/memory_file_system.dart';
import 'package:analyzer/src/dart/analysis/byte_store.dart';
import 'package:analyzer/src/dart/analysis/driver.dart';
import 'package:analyzer/src/dart/analysis/file_state.dart';
import 'package:analyzer/src/dart/analysis/performance_logger.dart';
import 'package:analyzer/src/generated/engine.dart';
import 'package:analyzer/src/generated/source.dart';
import 'package:analyzer/src/source/package_map_resolver.dart';
import 'package:analyzer/src/test_utilities/mock_sdk.dart';
import 'package:front_end/src/testing/annotated_code_helper.dart';
import 'package:front_end/src/testing/id.dart'
show ActualData, Id, IdValue, MemberId, NodeId;
import 'package:front_end/src/testing/id_testing.dart';
/// Test configuration used for testing CFE with constant evaluation.
final TestConfig analyzerConstantUpdate2018Config = TestConfig(
analyzerMarker, 'analyzer with constant-update-2018',
featureSet: FeatureSet.forTesting(
sdkVersion: '2.2.2',
additionalFeatures: [Feature.constant_update_2018]));
/// A fake absolute directory used as the root of a memory-file system in ID
/// tests.
Uri _defaultDir = Uri.parse('file:///a/b/c/');
Future<bool> checkTests<T>(
String rawCode,
Future<ResolvedUnitResult> resultComputer(String rawCode),
DataComputer<T> dataComputer) async {
String rawCode, DataComputer<T> dataComputer, FeatureSet featureSet) async {
AnnotatedCode code =
new AnnotatedCode.fromText(rawCode, commentStart, commentEnd);
var result = await resultComputer(code.sourceCode);
var uri = result.libraryElement.source.uri;
var marker = 'normal';
String testFileName = 'test.dart';
var testFileUri = _toTestUri(testFileName);
var memorySourceFiles = {testFileName: code.sourceCode};
var marker = 'analyzer';
Map<String, MemberAnnotations<IdValue>> expectedMaps = {
marker: new MemberAnnotations<IdValue>(),
};
computeExpectedMap(uri, code, expectedMaps, onFailure: onFailure);
MemberAnnotations<IdValue> annotations = expectedMaps[marker];
Map<Id, ActualData<T>> actualMap = {};
dataComputer.computeUnitData(result.unit, actualMap);
Map<Uri, AnnotatedCode> codeMap = {uri: code};
var compiledData =
AnalyzerCompiledData<T>(codeMap, uri, {uri: actualMap}, {});
return await checkCode(marker, uri, codeMap, annotations, compiledData,
dataComputer.dataValidator,
onFailure: onFailure);
computeExpectedMap(testFileUri, code, expectedMaps, onFailure: onFailure);
Map<Uri, AnnotatedCode> codeMap = {testFileUri: code};
var libFileNames = <String>[];
var testData = TestData(testFileUri, testFileUri, memorySourceFiles, codeMap,
expectedMaps, libFileNames);
var config =
TestConfig(marker, 'provisional test config', featureSet: featureSet);
return runTestForConfig<T>(testData, dataComputer, config);
}
/// Creates the testing URI used for [fileName] in annotated tests.
Uri createUriForFileName(String fileName, {bool isLib}) => _toTestUri(fileName);
void onFailure(String message) {
throw StateError(message);
}
/// Runs [dataComputer] on [testData] for all [testedConfigs].
///
/// Returns `true` if an error was encountered.
Future<bool> runTest<T>(TestData testData, DataComputer<T> dataComputer,
List<TestConfig> testedConfigs,
{bool testAfterFailures,
bool forUserLibrariesOnly: true,
Iterable<Id> globalIds: const <Id>[],
void onFailure(String message)}) async {
bool hasFailures = false;
for (TestConfig config in testedConfigs) {
if (await runTestForConfig(testData, dataComputer, config,
fatalErrors: !testAfterFailures, onFailure: onFailure)) {
hasFailures = true;
}
}
return hasFailures;
}
/// Creates a test runner for [dataComputer] on [testedConfigs].
RunTestFunction runTestFor<T>(
DataComputer<T> dataComputer, List<TestConfig> testedConfigs) {
return (TestData testData,
{bool testAfterFailures, bool verbose, bool printCode}) {
return runTest(testData, dataComputer, testedConfigs,
testAfterFailures: testAfterFailures, onFailure: onFailure);
};
}
/// Runs [dataComputer] on [testData] for [config].
///
/// Returns `true` if an error was encountered.
Future<bool> runTestForConfig<T>(
TestData testData, DataComputer<T> dataComputer, TestConfig config,
{bool fatalErrors, void onFailure(String message)}) async {
MemberAnnotations<IdValue> memberAnnotations =
testData.expectedMaps[config.marker];
var resourceProvider = new MemoryResourceProvider();
for (var entry in testData.memorySourceFiles.entries) {
resourceProvider.newFile(
resourceProvider.convertPath(_toTestUri(entry.key).path), entry.value);
}
var sdk = new MockSdk(resourceProvider: resourceProvider);
var logBuffer = new StringBuffer();
var logger = new PerformanceLog(logBuffer);
var scheduler = new AnalysisDriverScheduler(logger);
// TODO(paulberry): Do we need a non-empty package map for any of these tests?
var packageMap = <String, List<Folder>>{};
var byteStore = new MemoryByteStore();
var analysisOptions = AnalysisOptionsImpl()
..contextFeatures = config.featureSet;
var driver = new AnalysisDriver(
scheduler,
logger,
resourceProvider,
byteStore,
new FileContentOverlay(),
null,
new SourceFactory([
new DartUriResolver(sdk),
new PackageMapUriResolver(resourceProvider, packageMap),
new ResourceUriResolver(resourceProvider)
], null, resourceProvider),
analysisOptions);
scheduler.start();
var result = await driver
.getResult(resourceProvider.convertPath(testData.entryPoint.path));
Map<Uri, Map<Id, ActualData<T>>> actualMaps = <Uri, Map<Id, ActualData<T>>>{};
Map<Id, ActualData<T>> globalData = <Id, ActualData<T>>{};
Map<Id, ActualData<T>> actualMapFor(Uri uri) {
return actualMaps.putIfAbsent(uri, () => <Id, ActualData<T>>{});
}
dataComputer.computeUnitData(result.unit, actualMapFor(testData.entryPoint));
var compiledData = AnalyzerCompiledData<T>(
testData.code, testData.entryPoint, actualMaps, globalData);
return checkCode(config.name, testData.testFileUri, testData.code,
memberAnnotations, compiledData, dataComputer.dataValidator,
fatalErrors: fatalErrors, onFailure: onFailure);
}
/// Convert relative file paths into an absolute Uri as expected by the test
/// helpers.
Uri _toTestUri(String relativePath) => _defaultDir.resolve(relativePath);
class AnalyzerCompiledData<T> extends CompiledData<T> {
// TODO(johnniwinther,paulberry): Maybe this should have access to the
// [ResolvedUnitResult] instead.
@ -80,10 +191,6 @@ class AnalyzerCompiledData<T> extends CompiledData<T> {
}
}
void onFailure(String message) {
throw StateError(message);
}
abstract class DataComputer<T> {
const DataComputer();
@ -95,3 +202,12 @@ abstract class DataComputer<T> {
/// for the data origin.
void computeUnitData(CompilationUnit unit, Map<Id, ActualData<T>> actualMap);
}
class TestConfig {
final String marker;
final String name;
final FeatureSet featureSet;
TestConfig(this.marker, this.name, {FeatureSet featureSet})
: featureSet = featureSet ?? FeatureSet.fromEnableFlags([]);
}

View file

@ -59,7 +59,13 @@ void buildTestsIn(
return;
}
if (testAllFile == null) {
fail('Missing "test_all.dart" in $relativePath');
if (relativePath != 'id_tests') {
fail('Missing "test_all.dart" in $relativePath');
} else {
// The tests in the id_tests folder don't have a test_all.dart file
// because they don't use the package:test framework.
return;
}
}
ParsedUnitResult result = session.getParsedUnit(testAllFile.path);
if (result.state != ResultState.VALID) {

View file

@ -19,6 +19,7 @@ main() {
print(/*Double(0.5)*/ double0);
print(
/*cfe.Symbol(foo)*/
/*analyzer.Symbol(foo)*/
/*dart2js.Instance(Symbol,{_name:String(foo))*/
symbol0);
}

View file

@ -15,6 +15,16 @@ const Map<String, int> Function(String, int) instantiation1 =
main() {
print(/*Function(method1)*/ function0);
print(/*Instantiation(method1<int>)*/ instantiation0);
print(/*Instantiation(method2<String,int>)*/ instantiation1);
// TODO(paulberry): analyzer should record instantiation information. See
// dartbug.com/37608.
print(
/*cfe.Instantiation(method1<int>)*/
/*dart2js.Instantiation(method1<int>)*/
/*analyzer.Function(method1)*/
instantiation0);
print(
/*cfe.Instantiation(method2<String,int>)*/
/*dart2js.Instantiation(method2<String,int>)*/
/*analyzer.Function(method2)*/
instantiation1);
}