diff --git a/pkg/front_end/lib/src/testing/features.dart b/pkg/front_end/lib/src/testing/features.dart index 9b7d4242d87..7c6e5b8f795 100644 --- a/pkg/front_end/lib/src/testing/features.dart +++ b/pkg/front_end/lib/src/testing/features.dart @@ -7,6 +7,7 @@ import 'id_testing.dart'; /// Utility class for annotated testing representing a set of features. class Features { Map _features = {}; + Set _unsorted = new Set(); /// Mark the feature [key] as existing. If [value] is provided, the feature /// [key] is set to have this value. @@ -22,6 +23,12 @@ class Features { } } + /// Marks list values of [key] as unsorted. This prevents the [getText] + /// representation from automatically sorting the values. + void markAsUnsorted(String key) { + _unsorted.add(key); + } + /// Returns `true` if feature [key] exists. bool containsKey(String key) { return _features.containsKey(key); @@ -63,7 +70,11 @@ class Features { } sb.write(name); if (value is List) { - value = '[${(value..sort()).join(',')}]'; + if (_unsorted.contains(name)) { + value = '[${value.join(',')}]'; + } else { + value = '[${(value..sort()).join(',')}]'; + } } if (value != '') { sb.write('='); diff --git a/pkg/front_end/lib/src/testing/id_testing_helper.dart b/pkg/front_end/lib/src/testing/id_testing_helper.dart index 85accb695fe..6b955620ffd 100644 --- a/pkg/front_end/lib/src/testing/id_testing_helper.dart +++ b/pkg/front_end/lib/src/testing/id_testing_helper.dart @@ -3,14 +3,19 @@ // BSD-style license that can be found in the LICENSE file. import 'package:front_end/src/api_prototype/compiler_options.dart' - show CompilerOptions; + show CompilerOptions, DiagnosticMessage; import 'package:front_end/src/api_prototype/experimental_flags.dart' show ExperimentalFlag; -import 'package:front_end/src/testing/id_extractor.dart' show DataExtractor; +import 'package:front_end/src/api_prototype/terminal_color_support.dart' + show printDiagnosticMessage; +import 'package:front_end/src/fasta/messages.dart' show FormattedMessage; +import 'package:front_end/src/fasta/severity.dart' show Severity; import 'package:kernel/ast.dart'; import '../kernel_generator_impl.dart' show CompilerResult; import 'compiler_common.dart' show compileScript, toTestUri; -import 'id.dart' show ActualData, ClassId, Id, IdValue, MemberId, NodeId; +import 'id.dart' + show ActualData, ClassId, Id, IdKind, IdValue, MemberId, NodeId; +import 'id_extractor.dart' show DataExtractor; import 'id_testing.dart' show CompiledData, @@ -74,6 +79,18 @@ abstract class DataComputer { Map> actualMap, {bool verbose}) {} + /// Returns `true` if this data computer supports tests with compile-time + /// errors. + /// + /// Unsuccessful compilation might leave the compiler in an inconsistent + /// state, so this testing feature is opt-in. + bool get supportsErrors => false; + + /// Returns data corresponding to [error]. + T computeErrorData( + CompilerResult compiler, Id id, List errors) => + null; + /// Returns the [DataInterpreter] used to check the actual data with the /// expected data. DataInterpreter get dataValidator; @@ -201,16 +218,54 @@ Future runTestForConfig( testData.expectedMaps[config.marker]; Iterable globalIds = memberAnnotations.globalData.keys; CompilerOptions options = new CompilerOptions(); + List errors = []; + options.onDiagnostic = (DiagnosticMessage message) { + if (message is FormattedMessage && message.severity == Severity.error) { + errors.add(message); + } + printDiagnosticMessage(message, print); + }; options.debugDump = printCode; options.experimentalFlags.addAll(config.experimentalFlags); CompilerResult compilerResult = await compileScript( testData.memorySourceFiles, options: options, retainDataForTesting: true); + Component component = compilerResult.component; Map>> actualMaps = >>{}; Map> globalData = >{}; + Map> actualMapForUri(Uri uri) { + return actualMaps.putIfAbsent(uri, () => >{}); + } + + if (errors.isNotEmpty) { + if (!dataComputer.supportsErrors) { + onFailure("Compilation with compile-time errors not supported for this " + "testing setup."); + } + + Map>> errorMap = {}; + for (FormattedMessage error in errors) { + Map> map = + errorMap.putIfAbsent(error.uri, () => {}); + List list = map.putIfAbsent(error.charOffset, () => []); + list.add(error); + } + + errorMap.forEach((Uri uri, Map> map) { + map.forEach((int offset, List list) { + NodeId id = new NodeId(offset, IdKind.error); + T data = dataComputer.computeErrorData(compilerResult, id, list); + if (data != null) { + Map> actualMap = actualMapForUri(uri); + actualMap[id] = new ActualData(id, data, uri, offset, list); + } + }); + }); + } + Map> actualMapFor(TreeNode node) { Uri uri = node is Library ? node.fileUri : node.location.file; return actualMaps.putIfAbsent(uri, () => >{}); diff --git a/pkg/front_end/test/extensions/extensions_test.dart b/pkg/front_end/test/extensions/extensions_test.dart index 9f5f8ba9880..2a4bbb272ac 100644 --- a/pkg/front_end/test/extensions/extensions_test.dart +++ b/pkg/front_end/test/extensions/extensions_test.dart @@ -141,12 +141,14 @@ class ExtensionsDataExtractor extends CfeDataExtractor { for (FormalParameterBuilder parameter in memberBuilder.formals) { features.addElement(Tags.builderParameters, parameter.name); } + features.markAsUnsorted(Tags.builderParameters); } if (memberBuilder.typeVariables != null) { for (TypeVariableBuilder typeVariable in memberBuilder.typeVariables) { features.addElement(Tags.builderTypeParameters, typeVariableBuilderToText(typeVariable)); } + features.markAsUnsorted(Tags.builderTypeParameters); } } features[Tags.memberName] = getMemberName(member); @@ -155,10 +157,12 @@ class ExtensionsDataExtractor extends CfeDataExtractor { in member.function.positionalParameters) { features.addElement(Tags.memberParameters, parameter.name); } + features.markAsUnsorted(Tags.memberParameters); for (TypeParameter typeParameter in member.function.typeParameters) { features.addElement( Tags.memberTypeParameters, typeParameterToText(typeParameter)); } + features.markAsUnsorted(Tags.memberTypeParameters); } return features; } diff --git a/pkg/front_end/test/id_testing/data/errors.dart b/pkg/front_end/test/id_testing/data/errors.dart new file mode 100644 index 00000000000..e58d6c4cb59 --- /dev/null +++ b/pkg/front_end/test/id_testing/data/errors.dart @@ -0,0 +1,11 @@ +// 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. + +/*cfe.library: file=main.dart*/ + +/*cfe.member: main:main*/ +main() { + // ignore: undefined_function + /*error: Method not found: 'foo'.*/ foo(); +} diff --git a/pkg/front_end/test/id_testing/id_testing_test.dart b/pkg/front_end/test/id_testing/id_testing_test.dart index a73b0d639b5..bad3095b233 100644 --- a/pkg/front_end/test/id_testing/id_testing_test.dart +++ b/pkg/front_end/test/id_testing/id_testing_test.dart @@ -3,6 +3,7 @@ // BSD-style license that can be found in the LICENSE file. import 'dart:io' show Directory, Platform; +import 'package:front_end/src/fasta/messages.dart' show FormattedMessage; import 'package:front_end/src/testing/id.dart' show ActualData, Id; import 'package:front_end/src/testing/id_testing.dart' show DataInterpreter, StringDataInterpreter, runTests; @@ -60,6 +61,14 @@ class IdTestingDataComputer extends DataComputer { .computeForLibrary(library); } + @override + bool get supportsErrors => true; + + String computeErrorData( + CompilerResult compiler, Id id, List errors) { + return errors.map((m) => m.message).join(','); + } + @override DataInterpreter get dataValidator => const StringDataInterpreter(); } diff --git a/tests/compiler/dart2js/equivalence/id_equivalence_helper.dart b/tests/compiler/dart2js/equivalence/id_equivalence_helper.dart index 5e66b1aeb1e..14991150aec 100644 --- a/tests/compiler/dart2js/equivalence/id_equivalence_helper.dart +++ b/tests/compiler/dart2js/equivalence/id_equivalence_helper.dart @@ -158,7 +158,7 @@ Future> computeData(Uri entryPoint, if (skipFailedCompilations) return null; Expect.isTrue( dataComputer.supportsErrors, - "Compilation with compile-time error not supported for this " + "Compilation with compile-time errors not supported for this " "testing setup."); } if (printCode) { @@ -175,10 +175,6 @@ Future> computeData(Uri entryPoint, return actualMaps.putIfAbsent(uri, () => >{}); } - dynamic closedWorld = testFrontend - ? compiler.resolutionWorldBuilder.closedWorldForTesting - : compiler.backendClosedWorldForTesting; - ElementEnvironment elementEnvironment = closedWorld?.elementEnvironment; Map>> errors = {}; for (CollectedMessage error in diagnosticCollector.errors) { Map> map = @@ -200,9 +196,13 @@ Future> computeData(Uri entryPoint, if (!result.isSuccess) { return new Dart2jsCompiledData( - compiler, elementEnvironment, entryPoint, actualMaps, globalData); + compiler, null, entryPoint, actualMaps, globalData); } + dynamic closedWorld = testFrontend + ? compiler.resolutionWorldBuilder.closedWorldForTesting + : compiler.backendClosedWorldForTesting; + ElementEnvironment elementEnvironment = closedWorld?.elementEnvironment; CommonElements commonElements = closedWorld.commonElements; Map> actualMapFor(Entity entity) { diff --git a/tests/compiler/dart2js/equivalence/id_testing_test.dart b/tests/compiler/dart2js/equivalence/id_testing_test.dart index b91dbb6797b..c40ff6f9179 100644 --- a/tests/compiler/dart2js/equivalence/id_testing_test.dart +++ b/tests/compiler/dart2js/equivalence/id_testing_test.dart @@ -64,6 +64,15 @@ class IdTestingDataComputer extends DataComputer { .computeForLibrary(node); } + @override + bool get supportsErrors => true; + + @override + String computeErrorData( + Compiler compiler, Id id, List errors) { + return errors.map((c) => c.message.message).join(','); + } + @override bool get testFrontend => true;