Version 2.15.0-164.0.dev
Merge commit 'bb4e445b6e43937fa42c9ca75a24a51203c46dfe' into 'dev'
diff --git a/pkg/analysis_server/lib/src/services/correction/dart/remove_unnecessary_cast.dart b/pkg/analysis_server/lib/src/services/correction/dart/remove_unnecessary_cast.dart
index 4763735..850bba2 100644
--- a/pkg/analysis_server/lib/src/services/correction/dart/remove_unnecessary_cast.dart
+++ b/pkg/analysis_server/lib/src/services/correction/dart/remove_unnecessary_cast.dart
@@ -11,7 +11,7 @@
class RemoveUnnecessaryCast extends CorrectionProducer {
@override
- bool get canBeAppliedInBulk => false;
+ bool get canBeAppliedInBulk => true;
@override
bool get canBeAppliedToFile => true;
diff --git a/pkg/analysis_server/test/src/services/correction/fix/create_missing_overrides_test.dart b/pkg/analysis_server/test/src/services/correction/fix/create_missing_overrides_test.dart
index 694ecfc..638a199 100644
--- a/pkg/analysis_server/test/src/services/correction/fix/create_missing_overrides_test.dart
+++ b/pkg/analysis_server/test/src/services/correction/fix/create_missing_overrides_test.dart
@@ -168,6 +168,29 @@
''');
}
+ Future<void> test_functionTypedParameter_dynamic() async {
+ await resolveTestCode('''
+abstract class A {
+ void m(bool test(e));
+}
+
+class B extends A {
+}
+''');
+ await assertHasFix('''
+abstract class A {
+ void m(bool test(e));
+}
+
+class B extends A {
+ @override
+ void m(bool Function(dynamic e) test) {
+ // TODO: implement m
+ }
+}
+''');
+ }
+
Future<void> test_functionTypedParameter_nullable() async {
await resolveTestCode('''
abstract class A {
diff --git a/pkg/analysis_server/test/src/services/correction/fix/remove_unnecessary_cast_test.dart b/pkg/analysis_server/test/src/services/correction/fix/remove_unnecessary_cast_test.dart
index c0805e5..24cb208 100644
--- a/pkg/analysis_server/test/src/services/correction/fix/remove_unnecessary_cast_test.dart
+++ b/pkg/analysis_server/test/src/services/correction/fix/remove_unnecessary_cast_test.dart
@@ -11,12 +11,35 @@
void main() {
defineReflectiveSuite(() {
+ defineReflectiveTests(RemoveUnnecessaryCastBulkTest);
defineReflectiveTests(RemoveUnnecessaryCastMultiTest);
defineReflectiveTests(RemoveUnnecessaryCastTest);
});
}
@reflectiveTest
+class RemoveUnnecessaryCastBulkTest extends BulkFixProcessorTest {
+ Future<void> test_assignment() async {
+ await resolveTestCode('''
+void f(Object p) {
+ if (p is String) {
+ var v = (p as String) as String;
+ print(v);
+ }
+}
+''');
+ await assertHasFix('''
+void f(Object p) {
+ if (p is String) {
+ var v = p;
+ print(v);
+ }
+}
+''');
+ }
+}
+
+@reflectiveTest
class RemoveUnnecessaryCastMultiTest extends FixProcessorTest {
@override
FixKind get kind => DartFixKind.REMOVE_UNNECESSARY_CAST_MULTI;
@@ -25,7 +48,7 @@
await resolveTestCode('''
void f(Object p, Object q) {
if (p is String) {
- String v = ((p as String));
+ var v = (p as String) as String;
print(v);
}
if (q is int) {
@@ -37,7 +60,7 @@
await assertHasFixAllFix(HintCode.UNNECESSARY_CAST, '''
void f(Object p, Object q) {
if (p is String) {
- String v = p;
+ var v = p;
print(v);
}
if (q is int) {
diff --git a/pkg/analyzer/lib/src/dart/analysis/file_state.dart b/pkg/analyzer/lib/src/dart/analysis/file_state.dart
index 620c43d..410a7ce 100644
--- a/pkg/analyzer/lib/src/dart/analysis/file_state.dart
+++ b/pkg/analyzer/lib/src/dart/analysis/file_state.dart
@@ -772,10 +772,19 @@
void collectAffected(String path, Set<FileState> affected) {
final knownFiles = this.knownFiles.toList();
+ final fileToReferences = <FileState, List<FileState>>{};
+ for (var file in knownFiles) {
+ for (var referenced in file.directReferencedFiles) {
+ var references = fileToReferences[referenced] ??= [];
+ references.add(file);
+ }
+ }
+
collectAffected(FileState file) {
if (affected.add(file)) {
- for (var other in knownFiles) {
- if (other.directReferencedFiles.contains(file)) {
+ var references = fileToReferences[file];
+ if (references != null) {
+ for (var other in references) {
collectAffected(other);
}
}
diff --git a/pkg/analyzer/tool/messages/error_code_info.dart b/pkg/analyzer/tool/messages/error_code_info.dart
index 322759b..56a4947 100644
--- a/pkg/analyzer/tool/messages/error_code_info.dart
+++ b/pkg/analyzer/tool/messages/error_code_info.dart
@@ -15,6 +15,84 @@
return result;
}
+/// Data tables mapping between CFE errors and their corresponding automatically
+/// generated analyzer errors.
+class CfeToAnalyzerErrorCodeTables {
+ /// List of CFE errors for which analyzer errors should be automatically
+ /// generated, organized by their `index` property.
+ final List<ErrorCodeInfo?> indexToInfo = [];
+
+ /// Map whose values are the CFE errors for which analyzer errors should be
+ /// automatically generated, and whose keys are the corresponding analyzer
+ /// error name. (Names are simple identifiers; they are not prefixed by the
+ /// class name `ParserErrorCode`)
+ final Map<String, ErrorCodeInfo> analyzerCodeToInfo = {};
+
+ /// Map whose values are the CFE errors for which analyzer errors should be
+ /// automatically generated, and whose keys are the front end error name.
+ final Map<String, ErrorCodeInfo> frontEndCodeToInfo = {};
+
+ /// Map whose keys are the CFE errors for which analyzer errors should be
+ /// automatically generated, and whose values are the corresponding analyzer
+ /// error name. (Names are simple identifiers; they are not prefixed by the
+ /// class name `ParserErrorCode`)
+ final Map<ErrorCodeInfo, String> infoToAnalyzerCode = {};
+
+ /// Map whose keys are the CFE errors for which analyzer errors should be
+ /// automatically generated, and whose values are the front end error name.
+ final Map<ErrorCodeInfo, String> infoToFrontEndCode = {};
+
+ CfeToAnalyzerErrorCodeTables(Map<String, ErrorCodeInfo> messages) {
+ for (var entry in messages.entries) {
+ var errorCodeInfo = entry.value;
+ var index = errorCodeInfo.index;
+ if (index == null || errorCodeInfo.analyzerCode.length != 1) {
+ continue;
+ }
+ var frontEndCode = entry.key;
+ if (index < 1) {
+ throw '''
+$frontEndCode specifies index $index but indices must be 1 or greater.
+For more information run:
+pkg/front_end/tool/fasta generate-messages
+''';
+ }
+ if (indexToInfo.length <= index) {
+ indexToInfo.length = index + 1;
+ }
+ var previousEntryForIndex = indexToInfo[index];
+ if (previousEntryForIndex != null) {
+ throw 'Index $index used by both '
+ '${infoToFrontEndCode[previousEntryForIndex]} and $frontEndCode';
+ }
+ indexToInfo[index] = errorCodeInfo;
+ frontEndCodeToInfo[frontEndCode] = errorCodeInfo;
+ infoToFrontEndCode[errorCodeInfo] = frontEndCode;
+ var analyzerCodeLong = errorCodeInfo.analyzerCode.single;
+ var expectedPrefix = 'ParserErrorCode.';
+ if (!analyzerCodeLong.startsWith(expectedPrefix)) {
+ throw 'Expected all analyzer error codes to be prefixed with '
+ '${json.encode(expectedPrefix)}. Found '
+ '${json.encode(analyzerCodeLong)}.';
+ }
+ var analyzerCode = analyzerCodeLong.substring(expectedPrefix.length);
+ infoToAnalyzerCode[errorCodeInfo] = analyzerCode;
+ var previousEntryForAnalyzerCode = analyzerCodeToInfo[analyzerCode];
+ if (previousEntryForAnalyzerCode != null) {
+ throw 'Analyzer code $analyzerCode used by both '
+ '${infoToFrontEndCode[previousEntryForAnalyzerCode]} and '
+ '$frontEndCode';
+ }
+ analyzerCodeToInfo[analyzerCode] = errorCodeInfo;
+ }
+ for (int i = 1; i < indexToInfo.length; i++) {
+ if (indexToInfo[i] == null) {
+ throw 'Indices are not consecutive; no error code has index $i.';
+ }
+ }
+ }
+}
+
/// In-memory representation of error code information obtained from a
/// `messages.yaml` file.
class ErrorCodeInfo {
diff --git a/pkg/analyzer/tool/messages/generate.dart b/pkg/analyzer/tool/messages/generate.dart
index d9b973c..3aa0e3e 100644
--- a/pkg/analyzer/tool/messages/generate.dart
+++ b/pkg/analyzer/tool/messages/generate.dart
@@ -35,18 +35,6 @@
..printSummary();
}
-const invalidAnalyzerCode = """
-Error: Expected the text in the 'analyzerCode:' field to contain
- the name of the class containing the error
- and the name of the error separated by a `.`
- (e.g. ParserErrorCode.EQUALITY_CANNOT_BE_EQUALITY_OPERAND).
-""";
-
-const shouldRunFastaGenerateMessagesFirst = """
-Error: After modifying messages.yaml, run this first:
- pkg/front_end/tool/fasta generate-messages
-""";
-
/// A list of all targets generated by this code generator.
final List<GeneratedContent> allTargets = <GeneratedContent>[
GeneratedFile('lib/src/dart/error/syntactic_errors.g.dart',
@@ -62,32 +50,12 @@
final String analyzerPkgPath =
normalize(join(pkg_root.packageRoot, 'analyzer'));
-/// Return an entry containing 2 strings,
-/// the name of the class containing the error and the name of the error,
-/// or throw an exception if 'analyzerCode:' field is invalid.
-List<String> nameForEntry(ErrorCodeInfo entry) {
- final analyzerCode = entry.analyzerCode;
- if (analyzerCode.length == 1) {
- var code = analyzerCode.single;
- if (!code.startsWith('ParserErrorCode.')) {
- throw invalidAnalyzerCode;
- }
- List<String> name = code.split('.');
- if (name.length != 2 || name[1].isEmpty) {
- throw invalidAnalyzerCode;
- }
- return name;
- }
- throw invalidAnalyzerCode;
-}
-
class _SyntacticErrorGenerator {
final Map<String, ErrorCodeInfo> messages;
+ final CfeToAnalyzerErrorCodeTables tables;
final String errorConverterSource;
final String syntacticErrorsSource;
final String parserSource;
- final translatedEntries = <ErrorCodeInfo>[];
- final translatedFastaErrorCodes = <String>{};
final out = StringBuffer('''
//
// THIS FILE IS GENERATED. DO NOT EDIT.
@@ -121,15 +89,14 @@
}
_SyntacticErrorGenerator._(this.messages, this.errorConverterSource,
- this.syntacticErrorsSource, this.parserSource);
+ this.syntacticErrorsSource, this.parserSource)
+ : tables = CfeToAnalyzerErrorCodeTables(messages);
void checkForManualChanges() {
// Check for ParserErrorCodes that could be removed from
// error_converter.dart now that those ParserErrorCodes are auto generated.
int converterCount = 0;
- for (ErrorCodeInfo entry in translatedEntries) {
- final name = nameForEntry(entry);
- final errorCode = name[1];
+ for (var errorCode in tables.infoToAnalyzerCode.values) {
if (errorConverterSource.contains('"$errorCode"')) {
if (converterCount == 0) {
print('');
@@ -147,9 +114,7 @@
// Check that the public ParserErrorCodes have been updated
// to reference the generated codes.
int publicCount = 0;
- for (ErrorCodeInfo entry in translatedEntries) {
- final name = nameForEntry(entry);
- final errorCode = name[1];
+ for (var errorCode in tables.infoToAnalyzerCode.values) {
if (!syntacticErrorsSource.contains(' _$errorCode')) {
if (publicCount == 0) {
print('');
@@ -171,22 +136,10 @@
}
void generateErrorCodes() {
- final sortedErrorCodes = <String>[];
- final entryMap = <String, ErrorCodeInfo>{};
- for (var entry in translatedEntries) {
- final name = nameForEntry(entry);
- final errorCode = name[1];
- sortedErrorCodes.add(errorCode);
- if (entryMap[errorCode] == null) {
- entryMap[errorCode] = entry;
- } else {
- throw 'Error: Duplicate error code $errorCode';
- }
- }
- sortedErrorCodes.sort();
- for (var errorCode in sortedErrorCodes) {
+ final entryMap = tables.analyzerCodeToInfo;
+ for (var errorCode in entryMap.keys.toList()..sort()) {
final entry = entryMap[errorCode]!;
- final className = nameForEntry(entry)[0];
+ final className = 'ParserErrorCode';
out.writeln();
out.writeln('const $className _$errorCode =');
out.writeln(entry.toAnalyzerCode(className, errorCode));
@@ -194,37 +147,15 @@
}
void generateFastaAnalyzerErrorCodeList() {
- final sorted = List<ErrorCodeInfo?>.filled(translatedEntries.length, null);
- for (var entry in translatedEntries) {
- var index = entry.index;
- if (index is int && index >= 1 && index <= sorted.length) {
- if (sorted[index - 1] == null) {
- sorted[index - 1] = entry;
- continue;
- }
- }
- throw shouldRunFastaGenerateMessagesFirst;
- }
- out.writeln('final fastaAnalyzerErrorCodes = <ErrorCode?>[null,');
- for (var entry in sorted) {
- List<String> name = nameForEntry(entry!);
- out.writeln('_${name[1]},');
+ out.writeln('final fastaAnalyzerErrorCodes = <ErrorCode?>[');
+ for (var entry in tables.indexToInfo) {
+ var name = tables.infoToAnalyzerCode[entry];
+ out.writeln('${name == null ? 'null' : '_$name'},');
}
out.writeln('];');
}
void generateFormatCode() {
- messages.forEach((name, entry) {
- if (entry.index != null) {
- // TODO(paulberry): handle multiple analyzer codes
- if (entry.analyzerCode.length == 1) {
- translatedFastaErrorCodes.add(name);
- translatedEntries.add(entry);
- } else {
- throw invalidAnalyzerCode;
- }
- }
- });
generateFastaAnalyzerErrorCodeList();
generateErrorCodes();
}
@@ -246,13 +177,13 @@
}
// Remove entries that have already been translated
- for (ErrorCodeInfo entry in translatedEntries) {
+ for (ErrorCodeInfo entry in tables.infoToAnalyzerCode.keys) {
messageToName.remove(messageFromEntryTemplate(entry));
}
// Print the # of autogenerated ParserErrorCodes.
- print('${translatedEntries.length} of ${messageToName.length}'
- ' ParserErrorCodes generated.');
+ print('${tables.infoToAnalyzerCode.length} of '
+ '${messageToName.length} ParserErrorCodes generated.');
// List the ParserErrorCodes that could easily be auto generated
// but have not been already.
@@ -298,7 +229,7 @@
}
}
if (fastaErrorCode != null &&
- !translatedFastaErrorCodes.contains(fastaErrorCode)) {
+ tables.frontEndCodeToInfo[fastaErrorCode] == null) {
untranslatedFastaErrorCodes.add(fastaErrorCode);
}
}
diff --git a/pkg/analyzer_plugin/lib/src/utilities/change_builder/change_builder_dart.dart b/pkg/analyzer_plugin/lib/src/utilities/change_builder/change_builder_dart.dart
index bc399fb..9b0c895 100644
--- a/pkg/analyzer_plugin/lib/src/utilities/change_builder/change_builder_dart.dart
+++ b/pkg/analyzer_plugin/lib/src/utilities/change_builder/change_builder_dart.dart
@@ -516,12 +516,14 @@
ExecutableElement? methodBeingCopied,
String? nameGroupName,
DartType? type,
- String? typeGroupName}) {
+ String? typeGroupName,
+ bool isRequiredType = false}) {
bool writeType() {
if (typeGroupName != null) {
late bool hasType;
addLinkedEdit(typeGroupName, (DartLinkedEditBuilder builder) {
- hasType = _writeType(type, methodBeingCopied: methodBeingCopied);
+ hasType = _writeType(type,
+ methodBeingCopied: methodBeingCopied, required: isRequiredType);
builder.addSuperTypesAsSuggestions(type);
});
return hasType;
@@ -596,7 +598,7 @@
@override
void writeParameters(Iterable<ParameterElement> parameters,
- {ExecutableElement? methodBeingCopied}) {
+ {ExecutableElement? methodBeingCopied, bool requiredTypes = false}) {
var parameterNames = <String>{};
for (var i = 0; i < parameters.length; i++) {
var name = parameters.elementAt(i).name;
@@ -639,7 +641,8 @@
methodBeingCopied: methodBeingCopied,
nameGroupName: parameter.isNamed ? null : '${groupPrefix}PARAM$i',
type: parameter.type,
- typeGroupName: '${groupPrefix}TYPE$i');
+ typeGroupName: '${groupPrefix}TYPE$i',
+ isRequiredType: requiredTypes);
// default value
var defaultCode = parameter.defaultValueCode;
if (defaultCode != null) {
@@ -1233,7 +1236,8 @@
write('Function');
writeTypeParameters(type.typeFormals,
methodBeingCopied: methodBeingCopied);
- writeParameters(type.parameters, methodBeingCopied: methodBeingCopied);
+ writeParameters(type.parameters,
+ methodBeingCopied: methodBeingCopied, requiredTypes: true);
if (type.nullabilitySuffix == NullabilitySuffix.question) {
write('?');
}
diff --git a/pkg/analyzer_plugin/lib/utilities/change_builder/change_builder_dart.dart b/pkg/analyzer_plugin/lib/utilities/change_builder/change_builder_dart.dart
index 0707033..6bd6c78 100644
--- a/pkg/analyzer_plugin/lib/utilities/change_builder/change_builder_dart.dart
+++ b/pkg/analyzer_plugin/lib/utilities/change_builder/change_builder_dart.dart
@@ -201,13 +201,16 @@
///
/// If [isRequiredNamed] is `true` then either the keyword `required` or the
/// annotation `@required` will be included in the parameter declaration.
+ ///
+ /// If [isRequiredType] is `true` then the type is always written.
void writeParameter(String name,
{bool isCovariant,
bool isRequiredNamed,
ExecutableElement? methodBeingCopied,
String? nameGroupName,
DartType? type,
- String? typeGroupName});
+ String? typeGroupName,
+ bool isRequiredType});
/// Write the code for a parameter that would match the given [argument]. The
/// name of the parameter will be generated based on the type of the argument,
@@ -223,8 +226,10 @@
/// If a [methodBeingCopied] is provided, then type parameters defined by that
/// method are assumed to be part of what is being written and hence valid
/// types.
+ ///
+ /// If [requiredTypes] is `true`, then the types are always written.
void writeParameters(Iterable<ParameterElement> parameters,
- {ExecutableElement? methodBeingCopied});
+ {ExecutableElement? methodBeingCopied, bool requiredTypes});
/// Write the code for a list of parameters that would match the given list of
/// [arguments]. The surrounding parentheses are *not* written.
diff --git a/pkg/analyzer_plugin/test/src/utilities/change_builder/change_builder_dart_test.dart b/pkg/analyzer_plugin/test/src/utilities/change_builder/change_builder_dart_test.dart
index c58eee8..047e7a1 100644
--- a/pkg/analyzer_plugin/test/src/utilities/change_builder/change_builder_dart_test.dart
+++ b/pkg/analyzer_plugin/test/src/utilities/change_builder/change_builder_dart_test.dart
@@ -1106,6 +1106,25 @@
expect(edit.replacement, equalsIgnoringWhitespace('(int i, String s)'));
}
+ Future<void> test_writeParameters_requiredTypes() async {
+ var path = convertPath('/home/test/lib/test.dart');
+ var content = 'void f(e) {}';
+ addSource(path, content);
+ var unit = (await resolveFile(path)).unit;
+ var f = unit.declarations[0] as FunctionDeclaration;
+ var parameters = f.functionExpression.parameters;
+ var elements = parameters?.parameters.map((p) => p.declaredElement!);
+
+ var builder = newBuilder();
+ await builder.addDartFileEdit(path, (builder) {
+ builder.addInsertion(content.length - 1, (builder) {
+ builder.writeParameters(elements!, requiredTypes: true);
+ });
+ });
+ var edit = getEdit(builder);
+ expect(edit.replacement, equals('(dynamic e)'));
+ }
+
Future<void> test_writeParametersMatchingArguments_named() async {
var path = convertPath('/home/test/lib/test.dart');
var content = '''
diff --git a/pkg/dev_compiler/test/expression_compiler/expression_compiler_e2e_amd_legacy_test.dart b/pkg/dev_compiler/test/expression_compiler/expression_compiler_e2e_amd_legacy_test.dart
new file mode 100644
index 0000000..e124753
--- /dev/null
+++ b/pkg/dev_compiler/test/expression_compiler/expression_compiler_e2e_amd_legacy_test.dart
@@ -0,0 +1,28 @@
+// Copyright (c) 2021, 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 'package:dev_compiler/dev_compiler.dart' show ModuleFormat;
+import 'package:test/test.dart';
+import 'expression_compiler_e2e_shared.dart';
+import 'expression_compiler_e2e_suite.dart';
+
+void main() async {
+ var driver = await TestDriver.init();
+
+ group('(Legacy code)', () {
+ tearDownAll(() async {
+ await driver.finish();
+ });
+
+ group('(AMD module system)', () {
+ var setup = SetupCompilerOptions(
+ soundNullSafety: false,
+ legacyCode: true,
+ moduleFormat: ModuleFormat.amd);
+ runAgnosticSharedTests(setup, driver);
+ });
+ });
+}
diff --git a/pkg/dev_compiler/test/expression_compiler/expression_compiler_e2e_amd_sound_test.dart b/pkg/dev_compiler/test/expression_compiler/expression_compiler_e2e_amd_sound_test.dart
index 9b56991..24e6238 100644
--- a/pkg/dev_compiler/test/expression_compiler/expression_compiler_e2e_amd_sound_test.dart
+++ b/pkg/dev_compiler/test/expression_compiler/expression_compiler_e2e_amd_sound_test.dart
@@ -4,8 +4,6 @@
// @dart = 2.9
-library dev_compiler.test.expression_compiler;
-
import 'package:dev_compiler/dev_compiler.dart' show ModuleFormat;
import 'package:test/test.dart';
import 'expression_compiler_e2e_shared.dart';
@@ -21,8 +19,11 @@
group('(AMD module system)', () {
var setup = SetupCompilerOptions(
- soundNullSafety: true, moduleFormat: ModuleFormat.amd);
- runSharedTests(setup, driver);
+ soundNullSafety: true,
+ legacyCode: false,
+ moduleFormat: ModuleFormat.amd);
+ runAgnosticSharedTests(setup, driver);
+ runNullSafeSharedTests(setup, driver);
});
});
}
diff --git a/pkg/dev_compiler/test/expression_compiler/expression_compiler_e2e_amd_unsound_test.dart b/pkg/dev_compiler/test/expression_compiler/expression_compiler_e2e_amd_unsound_test.dart
index 92a7c96..e7a1804 100644
--- a/pkg/dev_compiler/test/expression_compiler/expression_compiler_e2e_amd_unsound_test.dart
+++ b/pkg/dev_compiler/test/expression_compiler/expression_compiler_e2e_amd_unsound_test.dart
@@ -4,8 +4,6 @@
// @dart = 2.9
-library dev_compiler.test.expression_compiler;
-
import 'package:dev_compiler/dev_compiler.dart' show ModuleFormat;
import 'package:test/test.dart';
import 'expression_compiler_e2e_shared.dart';
@@ -21,8 +19,11 @@
group('(AMD module system)', () {
var setup = SetupCompilerOptions(
- soundNullSafety: false, moduleFormat: ModuleFormat.amd);
- runSharedTests(setup, driver);
+ soundNullSafety: false,
+ legacyCode: false,
+ moduleFormat: ModuleFormat.amd);
+ runAgnosticSharedTests(setup, driver);
+ runNullSafeSharedTests(setup, driver);
});
});
}
diff --git a/pkg/dev_compiler/test/expression_compiler/expression_compiler_e2e_ddc_legacy_test.dart b/pkg/dev_compiler/test/expression_compiler/expression_compiler_e2e_ddc_legacy_test.dart
new file mode 100644
index 0000000..81952a8
--- /dev/null
+++ b/pkg/dev_compiler/test/expression_compiler/expression_compiler_e2e_ddc_legacy_test.dart
@@ -0,0 +1,28 @@
+// Copyright (c) 2021, 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 'package:dev_compiler/dev_compiler.dart' show ModuleFormat;
+import 'package:test/test.dart';
+import 'expression_compiler_e2e_shared.dart';
+import 'expression_compiler_e2e_suite.dart';
+
+void main() async {
+ var driver = await TestDriver.init();
+
+ group('(Legacy code)', () {
+ tearDownAll(() async {
+ await driver.finish();
+ });
+
+ group('(DDC module system)', () {
+ var setup = SetupCompilerOptions(
+ soundNullSafety: false,
+ legacyCode: true,
+ moduleFormat: ModuleFormat.ddc);
+ runAgnosticSharedTests(setup, driver);
+ });
+ });
+}
diff --git a/pkg/dev_compiler/test/expression_compiler/expression_compiler_e2e_ddc_sound_test.dart b/pkg/dev_compiler/test/expression_compiler/expression_compiler_e2e_ddc_sound_test.dart
index 682327e..6d40892 100644
--- a/pkg/dev_compiler/test/expression_compiler/expression_compiler_e2e_ddc_sound_test.dart
+++ b/pkg/dev_compiler/test/expression_compiler/expression_compiler_e2e_ddc_sound_test.dart
@@ -4,8 +4,6 @@
// @dart = 2.9
-library dev_compiler.test.expression_compiler;
-
import 'package:dev_compiler/dev_compiler.dart' show ModuleFormat;
import 'package:test/test.dart';
import 'expression_compiler_e2e_shared.dart';
@@ -21,8 +19,11 @@
group('(DDC module system)', () {
var setup = SetupCompilerOptions(
- soundNullSafety: true, moduleFormat: ModuleFormat.ddc);
- runSharedTests(setup, driver);
+ soundNullSafety: true,
+ legacyCode: false,
+ moduleFormat: ModuleFormat.ddc);
+ runAgnosticSharedTests(setup, driver);
+ runNullSafeSharedTests(setup, driver);
});
});
}
diff --git a/pkg/dev_compiler/test/expression_compiler/expression_compiler_e2e_ddc_unsound_test.dart b/pkg/dev_compiler/test/expression_compiler/expression_compiler_e2e_ddc_unsound_test.dart
index 21bc460..aa231e2 100644
--- a/pkg/dev_compiler/test/expression_compiler/expression_compiler_e2e_ddc_unsound_test.dart
+++ b/pkg/dev_compiler/test/expression_compiler/expression_compiler_e2e_ddc_unsound_test.dart
@@ -4,8 +4,6 @@
// @dart = 2.9
-library dev_compiler.test.expression_compiler;
-
import 'package:dev_compiler/dev_compiler.dart' show ModuleFormat;
import 'package:test/test.dart';
import 'expression_compiler_e2e_shared.dart';
@@ -21,8 +19,11 @@
group('(DDC module system)', () {
var setup = SetupCompilerOptions(
- soundNullSafety: false, moduleFormat: ModuleFormat.ddc);
- runSharedTests(setup, driver);
+ soundNullSafety: false,
+ legacyCode: false,
+ moduleFormat: ModuleFormat.ddc);
+ runAgnosticSharedTests(setup, driver);
+ runNullSafeSharedTests(setup, driver);
});
});
}
diff --git a/pkg/dev_compiler/test/expression_compiler/expression_compiler_e2e_shared.dart b/pkg/dev_compiler/test/expression_compiler/expression_compiler_e2e_shared.dart
index be6d857..c121403 100644
--- a/pkg/dev_compiler/test/expression_compiler/expression_compiler_e2e_shared.dart
+++ b/pkg/dev_compiler/test/expression_compiler/expression_compiler_e2e_shared.dart
@@ -4,8 +4,6 @@
// @dart = 2.9
-library dev_compiler.test.expression_compiler;
-
import 'package:test/test.dart';
import 'expression_compiler_e2e_suite.dart';
@@ -30,6 +28,12 @@
var nop;
}
+ C.named(this.field): _field = 42;
+
+ C.redirecting(int x) : this(x, 99);
+
+ factory C.factory() => C(42, 0);
+
int methodFieldAccess(int x) {
// Breakpoint: methodBP
var inScope = 1;
@@ -57,7 +61,300 @@
}
''';
-void runSharedTests(SetupCompilerOptions setup, TestDriver driver) {
+/// Shared tests that require a language version greater than 2.12.
+///
+/// Tests that exercise language features introduced with 2.12 or after are
+/// valid here.
+// TODO(nshahan) Merge with [runAgnosticSharedTests] after we no longer need to
+// test support for evaluation in legacy (pre-null safety) code.
+void runNullSafeSharedTests(SetupCompilerOptions setup, TestDriver driver) {
+ group('Correct null safety mode used', () {
+ var source = '''
+ const soundNullSafety = !(<Null>[] is List<int>);
+ main() {
+ // Breakpoint: bp
+ print('hello world');
+ }
+ ''';
+
+ setUpAll(() async {
+ await driver.initSource(setup, source);
+ });
+
+ tearDownAll(() async {
+ await driver.cleanupTest();
+ });
+
+ test('in original source compilation', () async {
+ await driver.check(
+ breakpointId: 'bp',
+ expression: 'soundNullSafety',
+ expectedResult: setup.soundNullSafety.toString());
+ });
+
+ test('in expression compilation', () async {
+ await driver.check(
+ breakpointId: 'bp',
+ expression: '!(<Null>[] is List<int>)',
+ expectedResult: setup.soundNullSafety.toString());
+ });
+ });
+
+ group('Expression compiler tests in method:', () {
+ var source = simpleClassSource;
+
+ setUpAll(() async {
+ await driver.initSource(setup, source);
+ });
+
+ tearDownAll(() async {
+ await driver.cleanupTest();
+ });
+
+ test('tear off default constructor', () async {
+ await driver.check(
+ breakpointId: 'methodBP',
+ expression: 'C.new.runtimeType.toString()',
+ expectedResult: '(int, int) => C');
+ });
+
+ test('call default constructor tear off', () async {
+ await driver.check(
+ breakpointId: 'methodBP',
+ expression: '(C.new)(0, 0)',
+ expectedResult: 'test.C.new {Symbol(_unusedField): 4, '
+ 'Symbol(C.field): 0, Symbol(_field): 0}');
+ });
+
+ test('tear off named constructor', () async {
+ await driver.check(
+ breakpointId: 'methodBP',
+ expression: 'C.named.runtimeType.toString()',
+ expectedResult: '(int) => C');
+ });
+
+ test('call named constructor tear off', () async {
+ await driver.check(
+ breakpointId: 'methodBP',
+ expression: '(C.named)(0)',
+ expectedResult: 'test.C.named {Symbol(_unusedField): 4, '
+ 'Symbol(C.field): 0, Symbol(_field): 42}');
+ });
+
+ test('tear off redirecting constructor', () async {
+ await driver.check(
+ breakpointId: 'methodBP',
+ expression: 'C.redirecting.runtimeType.toString()',
+ expectedResult: '(int) => C');
+ });
+
+ test('call redirecting constructor tear off', () async {
+ await driver.check(
+ breakpointId: 'methodBP',
+ expression: '(C.redirecting)(0)',
+ expectedResult: 'test.C.redirecting { Symbol(_unusedField): 4, '
+ 'Symbol(C.field): 0, Symbol(_field): 99}');
+ });
+
+ test('tear off factory constructor', () async {
+ await driver.check(
+ breakpointId: 'methodBP',
+ expression: 'C.factory.runtimeType.toString()',
+ expectedResult: '() => C');
+ });
+
+ test('call factory constructor tear off', () async {
+ await driver.check(
+ breakpointId: 'methodBP',
+ expression: '(C.factory)()',
+ expectedResult: 'test.C.new { Symbol(_unusedField): 4, '
+ 'Symbol(C.field): 42, Symbol(_field): 0}');
+ });
+ });
+
+ group('Expression compiler tests in global function:', () {
+ var source = simpleClassSource;
+
+ setUpAll(() async {
+ await driver.initSource(setup, source);
+ });
+
+ tearDownAll(() async {
+ await driver.cleanupTest();
+ });
+
+ test('tear off default constructor', () async {
+ await driver.check(
+ breakpointId: 'globalFunctionBP',
+ expression: 'C.new.runtimeType.toString()',
+ expectedResult: '(int, int) => C');
+ });
+
+ test('call default constructor tear off', () async {
+ await driver.check(
+ breakpointId: 'globalFunctionBP',
+ expression: '(C.new)(0, 0)',
+ expectedResult: 'test.C.new {Symbol(_unusedField): 4, '
+ 'Symbol(C.field): 0, Symbol(_field): 0}');
+ });
+
+ test('tear off named constructor', () async {
+ await driver.check(
+ breakpointId: 'globalFunctionBP',
+ expression: 'C.named.runtimeType.toString()',
+ expectedResult: '(int) => C');
+ });
+
+ test('call named constructor tear off', () async {
+ await driver.check(
+ breakpointId: 'globalFunctionBP',
+ expression: '(C.named)(0)',
+ expectedResult: 'test.C.named {Symbol(_unusedField): 4, '
+ 'Symbol(C.field): 0, Symbol(_field): 42}');
+ });
+
+ test('tear off redirecting constructor', () async {
+ await driver.check(
+ breakpointId: 'globalFunctionBP',
+ expression: 'C.redirecting.runtimeType.toString()',
+ expectedResult: '(int) => C');
+ });
+
+ test('call redirecting constructor tear off', () async {
+ await driver.check(
+ breakpointId: 'globalFunctionBP',
+ expression: '(C.redirecting)(0)',
+ expectedResult: 'test.C.redirecting { Symbol(_unusedField): 4, '
+ 'Symbol(C.field): 0, Symbol(_field): 99}');
+ });
+
+ test('tear off factory constructor', () async {
+ await driver.check(
+ breakpointId: 'globalFunctionBP',
+ expression: 'C.factory.runtimeType.toString()',
+ expectedResult: '() => C');
+ });
+
+ test('call factory constructor tear off', () async {
+ await driver.check(
+ breakpointId: 'globalFunctionBP',
+ expression: '(C.factory)()',
+ expectedResult: 'test.C.new { Symbol(_unusedField): 4, '
+ 'Symbol(C.field): 42, Symbol(_field): 0}');
+ });
+ });
+
+ group('Expression compiler tests in constructor:', () {
+ var source = simpleClassSource;
+
+ setUpAll(() async {
+ await driver.initSource(setup, source);
+ });
+
+ tearDownAll(() async {
+ await driver.cleanupTest();
+ });
+
+ test('tear off default constructor', () async {
+ await driver.check(
+ breakpointId: 'constructorBP',
+ expression: 'C.new.runtimeType.toString()',
+ expectedResult: '(int, int) => C');
+ });
+
+ test('call default constructor tear off', () async {
+ await driver.check(
+ breakpointId: 'constructorBP',
+ expression: '(C.new)(0, 0)',
+ expectedResult: 'test.C.new {Symbol(_unusedField): 4, '
+ 'Symbol(C.field): 0, Symbol(_field): 0}');
+ });
+
+ test('tear off named constructor', () async {
+ await driver.check(
+ breakpointId: 'constructorBP',
+ expression: 'C.named.runtimeType.toString()',
+ expectedResult: '(int) => C');
+ });
+
+ test('call named constructor tear off', () async {
+ await driver.check(
+ breakpointId: 'constructorBP',
+ expression: '(C.named)(0)',
+ expectedResult: 'test.C.named {Symbol(_unusedField): 4, '
+ 'Symbol(C.field): 0, Symbol(_field): 42}');
+ });
+
+ test('tear off redirecting constructor', () async {
+ await driver.check(
+ breakpointId: 'constructorBP',
+ expression: 'C.redirecting.runtimeType.toString()',
+ expectedResult: '(int) => C');
+ });
+
+ test('call redirecting constructor tear off', () async {
+ await driver.check(
+ breakpointId: 'constructorBP',
+ expression: '(C.redirecting)(0)',
+ expectedResult: 'test.C.redirecting { Symbol(_unusedField): 4, '
+ 'Symbol(C.field): 0, Symbol(_field): 99}');
+ });
+
+ test('tear off factory constructor', () async {
+ await driver.check(
+ breakpointId: 'constructorBP',
+ expression: 'C.factory.runtimeType.toString()',
+ expectedResult: '() => C');
+ });
+
+ test('call factory constructor tear off', () async {
+ await driver.check(
+ breakpointId: 'constructorBP',
+ expression: '(C.factory)()',
+ expectedResult: 'test.C.new { Symbol(_unusedField): 4, '
+ 'Symbol(C.field): 42, Symbol(_field): 0}');
+ });
+ });
+}
+
+/// Shared tests that are valid in legacy (before 2.12) and are agnostic to
+/// changes in modern versions of Dart.
+///
+/// Tests that exercise language features introduced strictly before 2.12 are
+/// valid here.
+void runAgnosticSharedTests(SetupCompilerOptions setup, TestDriver driver) {
+ group('Correct null safety mode used', () {
+ var source = '''
+ const soundNullSafety = !(<Null>[] is List<int>);
+ main() {
+ // Breakpoint: bp
+ print('hello world');
+ }
+ ''';
+
+ setUpAll(() async {
+ await driver.initSource(setup, source);
+ });
+
+ tearDownAll(() async {
+ await driver.cleanupTest();
+ });
+
+ test('in original source compilation', () async {
+ await driver.check(
+ breakpointId: 'bp',
+ expression: 'soundNullSafety',
+ expectedResult: setup.soundNullSafety.toString());
+ });
+
+ test('in expression compilation', () async {
+ await driver.check(
+ breakpointId: 'bp',
+ expression: '!(<Null>[] is List<int>)',
+ expectedResult: setup.soundNullSafety.toString());
+ });
+ });
+
group('Expression compiler scope collection tests', () {
var source = simpleClassSource;
diff --git a/pkg/dev_compiler/test/expression_compiler/expression_compiler_e2e_suite.dart b/pkg/dev_compiler/test/expression_compiler/expression_compiler_e2e_suite.dart
index 422caec..8e8f1bf 100644
--- a/pkg/dev_compiler/test/expression_compiler/expression_compiler_e2e_suite.dart
+++ b/pkg/dev_compiler/test/expression_compiler/expression_compiler_e2e_suite.dart
@@ -4,8 +4,6 @@
// @dart = 2.9
-library dev_compiler.test.expression_compiler;
-
import 'dart:async';
import 'dart:convert';
import 'dart:io' show Directory, File, Platform;
@@ -59,10 +57,8 @@
p.join(sdkRoot.toFilePath(), 'ddc_outline_sound.dill');
static final librariesSpecificationUri =
p.join(p.dirname(p.dirname(getSdkPath())), 'libraries.json');
- static final String dartUnsoundComment = '// @dart = 2.9';
- static final String dartSoundComment = '//';
- final String dartLangComment;
+ final bool legacyCode;
final List<String> errors = [];
final List<String> diagnosticMessages = [];
final ModuleFormat moduleFormat;
@@ -84,10 +80,10 @@
}
SetupCompilerOptions(
- {this.soundNullSafety = true, this.moduleFormat = ModuleFormat.amd})
- : options = _getOptions(soundNullSafety),
- dartLangComment =
- soundNullSafety ? dartSoundComment : dartUnsoundComment {
+ {this.soundNullSafety = true,
+ this.legacyCode = false,
+ this.moduleFormat = ModuleFormat.amd})
+ : options = _getOptions(soundNullSafety) {
options.onDiagnostic = (fe.DiagnosticMessage m) {
diagnosticMessages.addAll(m.plainTextFormatted);
if (m.severity == fe.Severity.error) {
@@ -280,8 +276,8 @@
throw StateError('Unable to find SDK summary at path: $summaryPath.');
}
- // Prepend Dart nullability comment.
- source = '${setup.dartLangComment}\n\n$source';
+ // Prepend legacy Dart version comment.
+ if (setup.legacyCode) source = '// @dart = 2.11\n\n$source';
this.setup = setup;
this.source = source;
testDir = chromeDir.createTempSync('ddc_eval_test');
@@ -524,7 +520,7 @@
expect(
result,
const TypeMatcher<TestCompilationResult>()
- .having((_) => '$value', 'result', _matches(expectedResult)));
+ .having((_) => value, 'result', _matches(expectedResult)));
}
/// Generate simple string representation of a RemoteObject that closely
diff --git a/tools/VERSION b/tools/VERSION
index a6d064a..cc57400 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
MAJOR 2
MINOR 15
PATCH 0
-PRERELEASE 163
+PRERELEASE 164
PRERELEASE_PATCH 0
\ No newline at end of file