mirror of
https://github.com/dart-lang/sdk
synced 2024-09-16 01:13:04 +00:00
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:
parent
2ada1edb59
commit
6c43390cd8
94
pkg/analyzer/test/id_tests/constant_test.dart
Normal file
94
pkg/analyzer/test/id_tests/constant_test.dart
Normal 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');
|
||||
}
|
||||
}
|
|
@ -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() {
|
||||
|
|
|
@ -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> {
|
||||
|
|
|
@ -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([]);
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -19,6 +19,7 @@ main() {
|
|||
print(/*Double(0.5)*/ double0);
|
||||
print(
|
||||
/*cfe.Symbol(foo)*/
|
||||
/*analyzer.Symbol(foo)*/
|
||||
/*dart2js.Instance(Symbol,{_name:String(foo))*/
|
||||
symbol0);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue