[_fe_analyzer_shared] Remove macro tests and helpers

These are no longer used.

TEST=removed

Change-Id: Ibf5b2de9d1b550c21873b48111161366deb2ddc0
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/407980
Reviewed-by: Martin Kustermann <kustermann@google.com>
Commit-Queue: Johnni Winther <johnniwinther@google.com>
Reviewed-by: Morgan :) <davidmorgan@google.com>
This commit is contained in:
Johnni Winther
2025-02-05 03:59:31 -08:00
committed by Commit Queue
parent ca16a4271c
commit 7c22f942aa
16 changed files with 4 additions and 2379 deletions
@@ -1,458 +0,0 @@
// Copyright (c) 2024, 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 'package:_fe_analyzer_shared/src/messages/codes.dart';
import 'package:_fe_analyzer_shared/src/parser/parser.dart';
import 'package:_fe_analyzer_shared/src/scanner/scanner.dart';
import 'package:_fe_analyzer_shared/src/scanner/token.dart';
abstract class CodeOptimizer {
/// Returns names exported from the library [uriStr].
Set<String> getImportedNames(String uriStr);
List<Edit> optimize(
String code, {
required Set<String> libraryDeclarationNames,
required ScannerConfiguration scannerConfiguration,
bool throwIfHasErrors = false,
}) {
List<Edit> edits = [];
ScannerResult result = scanString(
code,
configuration: scannerConfiguration,
includeComments: true,
languageVersionChanged: (scanner, languageVersion) {
throw new UnimplementedError();
},
);
if (result.hasErrors) {
if (throwIfHasErrors) {
throw new StateError('Has scan errors');
}
return [];
}
_Listener listener = new _Listener(
getImportedNames: getImportedNames,
);
try {
new Parser(
listener,
allowPatterns: true,
).parseUnit(result.tokens);
} on _StateError {
// Recover by doing nothing.
return [];
}
if (listener.hasErrors) {
if (throwIfHasErrors) {
throw new StateError('Has parse errors');
}
return [];
}
List<_Import> imports = listener.importScope.imports;
for (_Import import in imports) {
for (_PrefixedName prefixedName in import.prefixedNames) {
String name = prefixedName.name.lexeme;
// If there is more than one import that exports the name.
if (!listener.importScope.hasUniqueImport(name)) {
import.namesWithPrefix.add(name);
continue;
}
// Might be shadowed by a library declaration.
if (libraryDeclarationNames.contains(name)) {
import.namesWithPrefix.add(name);
continue;
}
// Might be shadowed by a local declaration.
if (listener.declaredNames.contains(name)) {
import.namesWithPrefix.add(name);
continue;
}
// Might shadow super declaration.
if (listener.unqualifiedNames.contains(name)) {
import.namesWithPrefix.add(name);
continue;
}
import.namesWithoutPrefix.add(name);
int prefixOffset = prefixedName.prefix.offset;
edits.add(
new RemoveImportPrefixReferenceEdit(
offset: prefixOffset,
length: prefixedName.name.offset - prefixOffset,
),
);
}
}
for (_Import import in imports) {
if (import.namesWithPrefix.isEmpty) {
int uriEnd = import.uriToken.end;
edits.add(
new RemoveImportPrefixDeclarationEdit(
offset: uriEnd,
length: import.semicolon.offset - uriEnd,
),
);
} else if (import.namesWithoutPrefix.isNotEmpty) {
// If some names require the prefix, and some not, add a new import
// without a prefix, but hide those which require prefix.
List<String> namesToHide = import.namesWithPrefix.toList();
namesToHide.sort();
edits.add(
new ImportWithoutPrefixEdit(
offset: import.semicolon.end,
uriStr: import.uriStr,
namesToHide: namesToHide,
),
);
}
}
edits.sort((a, b) => a.offset - b.offset);
return edits;
}
}
sealed class Edit {
final int offset;
final int length;
final String replacement;
Edit({
required this.offset,
required this.length,
required this.replacement,
});
static String applyList(List<Edit> edits, String value) {
final StringBuffer buffer = new StringBuffer();
int offset = 0;
for (Edit edit in edits) {
buffer.write(value.substring(offset, edit.offset));
buffer.write(edit.replacement);
offset = edit.offset + edit.length;
}
if (offset < value.length) buffer.write(value.substring(offset));
return buffer.toString();
}
}
final class ImportWithoutPrefixEdit extends Edit {
final String uriStr;
final List<String> namesToHide;
ImportWithoutPrefixEdit({
required super.offset,
required this.uriStr,
required this.namesToHide,
}) : super(
length: 0,
replacement: '\nimport \'$uriStr\' hide ${namesToHide.join(', ')};',
);
}
final class RemoveDartCoreImportEdit extends RemoveEdit {
RemoveDartCoreImportEdit({
required super.offset,
required super.length,
});
}
sealed class RemoveEdit extends Edit {
RemoveEdit({
required super.offset,
required super.length,
}) : super(replacement: '');
}
final class RemoveImportPrefixDeclarationEdit extends RemoveEdit {
RemoveImportPrefixDeclarationEdit({
required super.offset,
required super.length,
});
}
final class RemoveImportPrefixReferenceEdit extends RemoveEdit {
RemoveImportPrefixReferenceEdit({
required super.offset,
required super.length,
});
}
class _Import {
final Token importKeyword;
final Token uriToken;
final String uriStr;
final _ImportPrefix prefix;
final Set<String> names;
final Token semicolon;
final List<_PrefixedName> prefixedNames = [];
/// Names that are used with [prefix], but can be used without it.
final Set<String> namesWithoutPrefix = {};
/// Names that are used with [prefix], and the prefix cannot be removed.
final Set<String> namesWithPrefix = {};
_Import({
required this.importKeyword,
required this.uriToken,
required this.uriStr,
required this.prefix,
required this.names,
required this.semicolon,
});
}
class _ImportPrefix {
final Token name;
_ImportPrefix({
required this.name,
});
}
class _ImportScope {
final List<_Import> imports = [];
_ImportScope();
void addPrefixedName(_PrefixedName prefixed) {
for (_Import import in imports) {
if (import.prefix.name.lexeme == prefixed.prefix.lexeme) {
import.prefixedNames.add(prefixed);
}
}
}
bool hasUniqueImport(String name) {
int importCount = 0;
for (_Import import in imports) {
if (import.names.contains(name)) {
importCount++;
}
}
return importCount == 1;
}
}
class _Listener extends Listener {
Set<String> Function(String uriStr) getImportedNames;
bool hasErrors = false;
_ImportScope importScope = new _ImportScope();
/// The names of local declarations.
final Set<String> declaredNames = {};
/// The names that are referenced without a preceding `<something>.`.
/// These can be references to super declarations.
final Set<String> unqualifiedNames = {};
final List<Object?> stack = [];
_Listener({
required this.getImportedNames,
});
@override
void beginExtensionDeclaration(
Token? augmentToken, Token extensionKeyword, Token? name) {
if (name != null) {
declaredNames.add(name.lexeme);
}
}
@override
void beginExtensionTypeDeclaration(
Token? augmentToken, Token extensionKeyword, Token name) {
declaredNames.add(name.lexeme);
}
@override
void endBinaryExpression(Token token, Token endToken) {
Token? prefixToken = token.previous;
if (prefixToken == null || prefixToken.type != TokenType.IDENTIFIER) {
return;
}
Token? nameToken = token.next;
if (nameToken == null || nameToken.type != TokenType.IDENTIFIER) {
return;
}
importScope.addPrefixedName(
new _PrefixedName(
prefix: prefixToken,
name: nameToken,
),
);
}
@override
void endImport(Token importKeyword, Token? augmentToken, Token? semicolon) {
_ImportPrefix prefix = popOrThrow();
Token? uriToken = importKeyword.next;
if (uriToken == null) {
throw new _StateError();
}
String uriStr = uriToken.lexeme;
if (uriStr.startsWith('\'') && uriStr.endsWith('\'')) {
uriStr = uriStr.substring(1, uriStr.length - 1);
} else {
throw new _StateError();
}
importScope.imports.add(
new _Import(
importKeyword: importKeyword,
uriToken: uriToken,
uriStr: uriStr,
prefix: prefix,
semicolon: semicolon!,
names: getImportedNames(uriStr),
),
);
}
@override
void endMetadata(Token beginToken, Token? periodBeforeName, Token endToken) {
if (beginToken.type != TokenType.AT) {
throw new _StateError();
}
Token? prefixToken = beginToken.next;
if (prefixToken == null || prefixToken.type != TokenType.IDENTIFIER) {
throw new _StateError();
}
Token? periodToken = prefixToken.next;
if (periodToken == null || periodToken.type != TokenType.PERIOD) {
return;
}
Token? nameToken = periodToken.next;
if (nameToken == null || nameToken.type != TokenType.IDENTIFIER) {
throw new _StateError();
}
importScope.addPrefixedName(
new _PrefixedName(
prefix: prefixToken,
name: nameToken,
),
);
}
@override
void handleIdentifier(Token token, IdentifierContext context) {
if (context.inDeclaration) {
declaredNames.add(token.lexeme);
}
if (context == IdentifierContext.importPrefixDeclaration) {
push(
new _ImportPrefix(
name: token,
),
);
}
}
@override
void handleRecoverableError(
Message message,
Token startToken,
Token endToken,
) {
hasErrors = true;
}
@override
void handleSend(Token beginToken, Token endToken) {
if (beginToken.type != TokenType.IDENTIFIER) {
return;
}
// If not qualified with another identifier, or expression, then it could
// be an invocation of a method from a superclass. So, we cannot remove
// the prefix from the import that provides this name, imported names
// shadow super names.
Token? period = beginToken.previous;
if (period == null || period.type != TokenType.PERIOD) {
unqualifiedNames.add(beginToken.lexeme);
}
}
@override
void handleType(Token beginToken, Token? questionMark) {
Token prefixToken = beginToken;
if (prefixToken.type != TokenType.IDENTIFIER) {
throw new _StateError();
}
Token? periodToken = prefixToken.next;
if (periodToken == null || periodToken.type != TokenType.PERIOD) {
return;
}
Token? nameToken = periodToken.next;
if (nameToken == null || nameToken.type != TokenType.IDENTIFIER) {
throw new _StateError();
}
importScope.addPrefixedName(
new _PrefixedName(
prefix: prefixToken,
name: nameToken,
),
);
}
T popOrThrow<T>() {
if (stack.lastOrNull case T last) {
stack.removeLast();
return last;
}
throw new _StateError();
}
void push(Object? value) {
stack.add(value);
}
}
class _PrefixedName {
final Token prefix;
final Token name;
_PrefixedName({
required this.prefix,
required this.name,
});
@override
String toString() {
return '$prefix.$name';
}
}
/// The exception that is thrown if an unexpected syntax found.
class _StateError {}
@@ -1,27 +0,0 @@
// Copyright (c) 2024, 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.
/// The prefix used to create macro library URIs.
const String macroSchemePrefix = 'dart-macro+';
/// Returns `true` if [uri] is a macro library URI.
bool isMacroLibraryUri(Uri uri) {
return uri.scheme.startsWith(macroSchemePrefix);
}
/// Creates the macro library URI corresponding to the [originLibraryUri].
Uri toMacroLibraryUri(Uri originLibraryUri) {
return Uri.parse('${macroSchemePrefix}${originLibraryUri}');
}
/// Extracts the origin library URI from [macroLibraryUri].
///
/// This assumes that [macroLibraryUri] is a macro library URI as determined
/// by [isMacroLibraryUri].
Uri toOriginLibraryUri(Uri macroLibraryUri) {
assert(isMacroLibraryUri(macroLibraryUri),
"Invalid macro library uri $macroLibraryUri");
return Uri.parse(
macroLibraryUri.toString().substring(macroSchemePrefix.length));
}
@@ -3,7 +3,6 @@
// BSD-style license that can be found in the LICENSE file.
import 'dart:io';
import '../macros/uri.dart';
import 'annotated_code_helper.dart';
import 'id.dart';
import 'id_generation.dart';
@@ -929,9 +928,6 @@ Future<void> runTests<T>(Directory dataDir,
actualData[marker] = {};
void addActualData(Uri uri, Map<Id, ActualData<T>> actualData) {
if (isMacroLibraryUri(uri)) {
uri = toOriginLibraryUri(uri);
}
assert(testData.code.containsKey(uri) || actualData.isEmpty,
"Unexpected data ${actualData} for $uri");
if (actualData.isEmpty) {
-1
View File
@@ -16,7 +16,6 @@ dependencies:
dev_dependencies:
checks: any
collection: any
macros: any
test: any
dependency_overrides:
@@ -1,94 +0,0 @@
// Copyright (c) 2022, 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 'api_test_macro.dart';
main() {}
var field;
get getter => null;
set setter(_) => null;
@ClassMacro()
class Class1 {
var field1;
Class1();
}
@ClassMacro()
abstract class Class2 extends Object {}
@ClassMacro()
class Class3 extends Class2 implements Interface1 {
var field1;
var field2;
Class3.new();
Class3.named();
factory Class3.fact() => Class3.named();
factory Class3.redirect() = Class3.named;
void method1() {}
void method2() {}
get getter1 => null;
set setter1(_) {}
get property1 => null;
set property1(_) {}
static var staticField1;
static void staticMethod1() {}
}
@ClassMacro()
class Class4 extends Class1 with Mixin1 {}
@ClassMacro()
abstract class Class5 extends Class2
with Mixin1, Mixin2
implements Interface1, Interface2 {}
@MixinMacro()
mixin Mixin1 {}
@MixinMacro()
mixin Mixin2 {
var instanceField;
static var staticField;
get instanceGetter => 42;
set instanceSetter(int value) {}
static get staticGetter => 42;
static set staticSetter(int value) {}
instanceMethod() {}
abstractMethod();
static staticMethod() {}
}
@ClassMacro()
abstract class Interface1 {}
@ClassMacro()
abstract class Interface2 {}
@FunctionMacro()
void topLevelFunction1(Class1 a, {Class1? b, required Class2? c}) {}
@FunctionMacro()
external Class2 topLevelFunction2(Class1 a, [Class2? b]);
@ExtensionTypeMacro()
extension type ExtensionType1(int i) {
ExtensionType1.constructor(this.i);
factory ExtensionType1.fact(int i) => ExtensionType1(i);
factory ExtensionType1.redirect(int i) = ExtensionType1.constructor;
static var staticField;
get instanceGetter => 42;
set instanceSetter(int value) {}
static get staticGetter => 42;
static set staticSetter(int value) {}
instanceMethod() {}
static staticMethod() {}
}
@@ -1,526 +0,0 @@
// Copyright (c) 2022, 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 'package:macros/macros.dart';
const Map<String, ClassData> expectedClassData = {
'Class1': ClassData(fieldsOf: ['field1'], constructorsOf: ['']),
'Class2': ClassData(isAbstract: true, superclass: 'Object'),
'Class3': ClassData(
superclass: 'Class2',
superSuperclass: 'Object',
interfaces: [
'Interface1'
],
// TODO(johnniwinther): Should we require a specific order?
fieldsOf: [
'field1',
'field2',
'staticField1',
],
// TODO(johnniwinther): Should we require a specific order?
methodsOf: [
'method1',
'method2',
'getter1',
'property1',
'staticMethod1',
'setter1',
'property1',
],
// TODO(johnniwinther): Should we require a specific order?
constructorsOf: [
// TODO(johnniwinther): Should we normalize no-name constructor names?
'',
'named',
'fact',
'redirect',
]),
'Class4': ClassData(superclass: 'Class1', mixins: ['Mixin1']),
'Class5': ClassData(
superclass: 'Class2',
superSuperclass: 'Object',
isAbstract: true,
mixins: ['Mixin1', 'Mixin2'],
interfaces: ['Interface1', 'Interface2']),
'Interface1': ClassData(isAbstract: true),
'Interface2': ClassData(isAbstract: true),
};
const Map<String, MixinData> expectedMixinData = {
'Mixin1': MixinData(),
'Mixin2': MixinData(
// TODO(johnniwinther): Should we require a specific order?
fieldsOf: [
'instanceField',
'staticField',
],
// TODO(johnniwinther): Should we require a specific order?
methodsOf: [
'instanceGetter',
'staticGetter',
'instanceMethod',
'abstractMethod',
'staticMethod',
'instanceSetter',
'staticSetter',
],
),
};
const Map<String, ExtensionTypeData> expectedExtensionTypeData = {
'ExtensionType1': ExtensionTypeData(
representationType: NamedTypeData(name: 'int'),
// TODO(johnniwinther): Should we require a specific order?
fieldsOf: [
'i',
'staticField',
],
// TODO(johnniwinther): Should we require a specific order?
methodsOf: [
'instanceGetter',
'staticGetter',
'instanceMethod',
'staticMethod',
'instanceSetter',
'staticSetter',
],
// TODO(johnniwinther): Should we require a specific order?
constructorsOf: [
// TODO(johnniwinther): Should we normalize no-name constructor names?
'',
'constructor',
'fact',
'redirect',
],
),
};
const Map<String, FunctionData> expectedFunctionData = {
'topLevelFunction1': FunctionData(
returnType: NamedTypeData(name: 'void'),
positionalParameters: [
ParameterData('a',
type: NamedTypeData(name: 'Class1'), isRequired: true),
],
namedParameters: [
ParameterData('b',
type: NamedTypeData(name: 'Class1', isNullable: true),
isNamed: true,
isRequired: false),
ParameterData('c',
type: NamedTypeData(name: 'Class2', isNullable: true),
isNamed: true,
isRequired: true),
]),
'topLevelFunction2': FunctionData(
isExternal: true,
returnType: NamedTypeData(name: 'Class2'),
positionalParameters: [
ParameterData('a', type: NamedTypeData(name: 'Class1'), isRequired: true),
ParameterData('b', type: NamedTypeData(name: 'Class2', isNullable: true)),
],
),
};
expect(expected, actual, property) {
if (expected != actual) {
throw 'Expected $expected, actual $actual on $property';
}
}
Future<void> throws(Future<void> Function() f, property,
{String? Function(Object)? expectedError}) async {
try {
await f();
} catch (e) {
if (expectedError != null) {
String? errorMessage = expectedError(e);
if (errorMessage != null) {
throw 'Unexpected exception on $property: $errorMessage';
}
}
return;
}
throw 'Expected throws on $property';
}
void checkTypeAnnotation(
TypeData expected, TypeAnnotation typeAnnotation, String context) {
expect(expected.isNullable, typeAnnotation.isNullable, '$context.isNullable');
expect(expected is NamedTypeData, typeAnnotation is NamedTypeAnnotation,
'$context is NamedTypeAnnotation');
if (expected is NamedTypeData && typeAnnotation is NamedTypeAnnotation) {
expect(expected.name, typeAnnotation.identifier.name, '$context.name');
// TODO(johnniwinther): Test more properties.
}
}
void checkParameterDeclaration(ParameterData expected,
FormalParameterDeclaration declaration, String context) {
expect(
expected.name, declaration.identifier.name, '$context.identifier.name');
expect(expected.isNamed, declaration.isNamed, '$context.isNamed');
expect(expected.isRequired, declaration.isRequired, '$context.isRequired');
checkTypeAnnotation(expected.type, declaration.type, '$context.type');
}
Future<void> checkClassDeclaration(ClassDeclaration declaration,
{DeclarationPhaseIntrospector? introspector}) async {
String name = declaration.identifier.name;
ClassData? expected = expectedClassData[name];
if (expected != null) {
expect(expected.isAbstract, declaration.hasAbstract, '$name.isAbstract');
expect(expected.isExternal, declaration.hasExternal, '$name.isExternal');
if (introspector != null) {
TypeDeclaration? superclass = declaration.superclass == null
? null
: await introspector
.typeDeclarationOf(declaration.superclass!.identifier);
expect(
expected.superclass, superclass?.identifier.name, '$name.superclass');
if (superclass is ClassDeclaration) {
TypeDeclaration? superSuperclass = superclass.superclass == null
? null
: await introspector
.typeDeclarationOf(superclass.superclass!.identifier);
expect(expected.superSuperclass, superSuperclass?.identifier.name,
'$name.superSuperclass');
}
List<TypeDeclaration> mixins = [
for (NamedTypeAnnotation mixin in declaration.mixins)
await introspector.typeDeclarationOf(mixin.identifier),
];
expect(expected.mixins.length, mixins.length, '$name.mixins.length');
for (int i = 0; i < mixins.length; i++) {
expect(
expected.mixins[i], mixins[i].identifier.name, '$name.mixins[$i]');
}
List<TypeDeclaration> interfaces = [
for (NamedTypeAnnotation interface in declaration.interfaces)
await introspector.typeDeclarationOf(interface.identifier),
];
expect(expected.interfaces.length, interfaces.length,
'$name.interfaces.length');
for (int i = 0; i < interfaces.length; i++) {
expect(expected.interfaces[i], interfaces[i].identifier.name,
'$name.interfaces[$i]');
}
}
if (introspector != null) {
List<FieldDeclaration> fieldsOf =
await introspector.fieldsOf(declaration);
expect(
expected.fieldsOf.length, fieldsOf.length, '$name.fieldsOf.length');
for (int i = 0; i < fieldsOf.length; i++) {
expect(expected.fieldsOf[i], fieldsOf[i].identifier.name,
'$name.fieldsOf[$i]');
}
List<MethodDeclaration> methodsOf =
await introspector.methodsOf(declaration);
expect(expected.methodsOf.length, methodsOf.length,
'$name.methodsOf.length');
for (int i = 0; i < methodsOf.length; i++) {
expect(expected.methodsOf[i], methodsOf[i].identifier.name,
'$name.methodsOf[$i]');
}
List<ConstructorDeclaration> constructorsOf =
await introspector.constructorsOf(declaration);
expect(expected.constructorsOf.length, constructorsOf.length,
'$name.constructorsOf.length');
for (int i = 0; i < constructorsOf.length; i++) {
expect(expected.constructorsOf[i], constructorsOf[i].identifier.name,
'$name.constructorsOf[$i]');
}
}
// TODO(johnniwinther): Test more properties when they are supported.
} else {
throw 'Unexpected class declaration "${name}"';
}
}
Future<void> checkMixinDeclaration(MixinDeclaration declaration,
{DeclarationPhaseIntrospector? introspector}) async {
String name = declaration.identifier.name;
MixinData? expected = expectedMixinData[name];
if (expected != null) {
expect(expected.hasBase, declaration.hasBase, '$name.hasBase');
if (introspector != null) {
List<TypeDeclaration> superClassConstraints = [
for (NamedTypeAnnotation superclassConstraint
in declaration.superclassConstraints)
await introspector.typeDeclarationOf(superclassConstraint.identifier),
];
expect(expected.superclassConstraints.length,
superClassConstraints.length, '$name.superClassConstraints.length');
for (int i = 0; i < superClassConstraints.length; i++) {
expect(
expected.superclassConstraints[i],
superClassConstraints[i].identifier.name,
'$name.superClassConstraints[$i]');
}
List<TypeDeclaration> interfaces = [
for (NamedTypeAnnotation interface in declaration.interfaces)
await introspector.typeDeclarationOf(interface.identifier),
];
expect(expected.interfaces.length, interfaces.length,
'$name.interfaces.length');
for (int i = 0; i < interfaces.length; i++) {
expect(expected.interfaces[i], interfaces[i].identifier.name,
'$name.interfaces[$i]');
}
}
if (introspector != null) {
List<FieldDeclaration> fieldsOf =
await introspector.fieldsOf(declaration);
expect(
expected.fieldsOf.length, fieldsOf.length, '$name.fieldsOf.length');
for (int i = 0; i < fieldsOf.length; i++) {
expect(expected.fieldsOf[i], fieldsOf[i].identifier.name,
'$name.fieldsOf[$i]');
}
List<MethodDeclaration> methodsOf =
await introspector.methodsOf(declaration);
expect(expected.methodsOf.length, methodsOf.length,
'$name.methodsOf.length');
for (int i = 0; i < methodsOf.length; i++) {
expect(expected.methodsOf[i], methodsOf[i].identifier.name,
'$name.methodsOf[$i]');
}
}
// TODO(johnniwinther): Test more properties when they are supported.
} else {
throw 'Unexpected mixin declaration "${name}"';
}
}
Future<void> checkExtensionTypeDeclaration(ExtensionTypeDeclaration declaration,
{DeclarationPhaseIntrospector? introspector}) async {
String name = declaration.identifier.name;
ExtensionTypeData? expected = expectedExtensionTypeData[name];
if (expected != null) {
checkTypeAnnotation(expected.representationType,
declaration.representationType, '$name.representationType');
if (introspector != null) {
List<FieldDeclaration> fieldsOf =
await introspector.fieldsOf(declaration);
expect(
expected.fieldsOf.length, fieldsOf.length, '$name.fieldsOf.length');
for (int i = 0; i < fieldsOf.length; i++) {
expect(expected.fieldsOf[i], fieldsOf[i].identifier.name,
'$name.fieldsOf[$i]');
}
List<MethodDeclaration> methodsOf =
await introspector.methodsOf(declaration);
expect(expected.methodsOf.length, methodsOf.length,
'$name.methodsOf.length');
for (int i = 0; i < methodsOf.length; i++) {
expect(expected.methodsOf[i], methodsOf[i].identifier.name,
'$name.methodsOf[$i]');
}
List<ConstructorDeclaration> constructorsOf =
await introspector.constructorsOf(declaration);
expect(expected.constructorsOf.length, constructorsOf.length,
'$name.constructorsOf.length');
for (int i = 0; i < constructorsOf.length; i++) {
expect(expected.constructorsOf[i], constructorsOf[i].identifier.name,
'$name.constructorsOf[$i]');
}
}
// TODO(johnniwinther): Test more properties when they are supported.
} else {
throw 'Unexpected class declaration "${name}"';
}
}
void checkFunctionDeclaration(FunctionDeclaration actual) {
String name = actual.identifier.name;
FunctionData? expected = expectedFunctionData[name];
if (expected != null) {
expect(expected.isExternal, actual.hasExternal, '$name.isExternal');
expect(expected.isOperator, actual.isOperator, '$name.isOperator');
expect(expected.isGetter, actual.isGetter, '$name.isGetter');
expect(expected.isSetter, actual.isSetter, '$name.isSetter');
checkTypeAnnotation(
expected.returnType, actual.returnType, '$name.returnType');
expect(
expected.positionalParameters.length,
actual.positionalParameters.length,
'$name.positionalParameters.length');
for (int i = 0; i < expected.positionalParameters.length; i++) {
checkParameterDeclaration(
expected.positionalParameters[i],
actual.positionalParameters.elementAt(i),
'$name.positionalParameters[$i]');
}
expect(expected.namedParameters.length, actual.namedParameters.length,
'$name.namedParameters.length');
for (int i = 0; i < expected.namedParameters.length; i++) {
checkParameterDeclaration(expected.namedParameters[i],
actual.namedParameters.elementAt(i), '$name.namedParameters[$i]');
}
// TODO(johnniwinther): Test more properties.
} else {
throw 'Unexpected function declaration "${name}"';
}
}
Future<void> checkIdentifierResolver(TypePhaseIntrospector introspector) async {
Uri dartCore = Uri.parse('dart:core');
Uri macroApiData = Uri.parse('package:macro_api_test/api_test_data.dart');
Future<void> check(Uri uri, String name, {bool expectThrows = false}) async {
if (expectThrows) {
await throws(() async {
// ignore: deprecated_member_use
await introspector.resolveIdentifier(uri, name);
}, '$name from $uri');
} else {
// ignore: deprecated_member_use
Identifier result = await introspector.resolveIdentifier(uri, name);
expect(name, result.name, '$name from $uri');
}
}
await check(dartCore, 'Object');
await check(dartCore, 'String');
await check(dartCore, 'override');
await check(macroApiData, 'Class1');
await check(macroApiData, 'getter');
await check(macroApiData, 'setter=');
await check(macroApiData, 'field');
await check(macroApiData, 'non-existing', expectThrows: true);
await check(macroApiData, 'getter=', expectThrows: true);
await check(macroApiData, 'setter', expectThrows: true);
await check(macroApiData, 'field=', expectThrows: true);
}
Future<void> checkTypeDeclarationResolver(
DeclarationPhaseIntrospector introspector,
Map<Identifier, String?> test) async {
Future<void> check(Identifier identifier, String name,
{bool expectThrows = false}) async {
if (expectThrows) {
await throws(() async {
await introspector.typeDeclarationOf(identifier);
}, '$name from $identifier',
expectedError: (e) => e is! MacroImplementationException
? 'Expected MacroImplementationException, got ${e.runtimeType}: '
'$e'
: null);
} else {
TypeDeclaration result = await introspector.typeDeclarationOf(identifier);
expect(name, result.identifier.name, '$name from $identifier');
}
}
for (var MapEntry(key: identifier, value: expectedName) in test.entries) {
await check(identifier, expectedName ?? identifier.name,
expectThrows: expectedName == null);
}
}
class ClassData {
final bool isAbstract;
final bool isExternal;
final String? superclass;
final String? superSuperclass;
final List<String> interfaces;
final List<String> mixins;
final List<String> fieldsOf;
final List<String> methodsOf;
final List<String> constructorsOf;
const ClassData(
{this.isAbstract = false,
this.isExternal = false,
this.superclass,
this.superSuperclass,
this.interfaces = const [],
this.mixins = const [],
this.fieldsOf = const [],
this.methodsOf = const [],
this.constructorsOf = const []});
}
class MixinData {
final bool hasBase;
final List<String> interfaces;
final List<String> superclassConstraints;
final List<String> fieldsOf;
final List<String> methodsOf;
const MixinData(
{this.hasBase = false,
this.interfaces = const [],
this.superclassConstraints = const [],
this.fieldsOf = const [],
this.methodsOf = const []});
}
class ExtensionTypeData {
final TypeData representationType;
final List<String> fieldsOf;
final List<String> methodsOf;
final List<String> constructorsOf;
const ExtensionTypeData(
{required this.representationType,
this.fieldsOf = const [],
this.methodsOf = const [],
this.constructorsOf = const []});
}
class FunctionData {
final bool isAbstract;
final bool isExternal;
final bool isOperator;
final bool isGetter;
final bool isSetter;
final TypeData returnType;
final List<ParameterData> positionalParameters;
final List<ParameterData> namedParameters;
const FunctionData(
{this.isAbstract = false,
this.isExternal = false,
this.isOperator = false,
this.isGetter = false,
this.isSetter = false,
required this.returnType,
this.positionalParameters = const [],
this.namedParameters = const []});
}
class TypeData {
final bool isNullable;
const TypeData({this.isNullable = false});
}
class NamedTypeData extends TypeData {
final String? name;
final List<TypeData>? typeArguments;
const NamedTypeData({super.isNullable, this.name, this.typeArguments});
}
class ParameterData {
final String name;
final TypeData type;
final bool isRequired;
final bool isNamed;
const ParameterData(this.name,
{required this.type, this.isNamed = false, this.isRequired = false});
}
@@ -1,126 +0,0 @@
// Copyright (c) 2022, 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.
// ignore_for_file: experiment_not_enabled
import 'dart:async';
import 'package:macros/macros.dart';
import 'api_test_expectations.dart';
macro class ClassMacro
implements ClassTypesMacro, ClassDeclarationsMacro, ClassDefinitionMacro {
const ClassMacro();
@override
FutureOr<void> buildTypesForClass(ClassDeclaration clazz,
TypeBuilder builder) async {
await checkClassDeclaration(clazz);
}
@override
FutureOr<void> buildDeclarationsForClass(ClassDeclaration clazz,
MemberDeclarationBuilder builder) async {
await checkClassDeclaration(
clazz, introspector: builder);
}
@override
FutureOr<void> buildDefinitionForClass(ClassDeclaration clazz,
TypeDefinitionBuilder builder) async {
await checkClassDeclaration(clazz, introspector: builder);
await checkIdentifierResolver(builder);
await checkTypeDeclarationResolver(builder,
{clazz.identifier: clazz.identifier.name});
}
}
macro class MixinMacro
implements MixinTypesMacro, MixinDeclarationsMacro, MixinDefinitionMacro {
const MixinMacro();
@override
FutureOr<void> buildTypesForMixin(MixinDeclaration mixin,
MixinTypeBuilder builder) async {
await checkMixinDeclaration(mixin);
}
@override
FutureOr<void> buildDeclarationsForMixin(MixinDeclaration mixin,
MemberDeclarationBuilder builder) async {
await checkMixinDeclaration(
mixin, introspector: builder);
}
@override
FutureOr<void> buildDefinitionForMixin(MixinDeclaration mixin,
TypeDefinitionBuilder builder) async {
await checkMixinDeclaration(mixin, introspector: builder);
await checkIdentifierResolver(builder);
await checkTypeDeclarationResolver(builder,
{mixin.identifier: mixin.identifier.name});
}
}
macro class ExtensionTypeMacro
implements
ExtensionTypeTypesMacro,
ExtensionTypeDeclarationsMacro,
ExtensionTypeDefinitionMacro {
const ExtensionTypeMacro();
@override
FutureOr<void> buildTypesForExtensionType(
ExtensionTypeDeclaration extensionType,
TypeBuilder builder) async {
await checkExtensionTypeDeclaration(extensionType);
}
@override
FutureOr<void> buildDeclarationsForExtensionType(
ExtensionTypeDeclaration extensionType,
MemberDeclarationBuilder builder) async {
await checkExtensionTypeDeclaration(
extensionType, introspector: builder);
}
@override
FutureOr<void> buildDefinitionForExtensionType(
ExtensionTypeDeclaration extensionType,
TypeDefinitionBuilder builder) async {
await checkExtensionTypeDeclaration(extensionType, introspector: builder);
await checkIdentifierResolver(builder);
await checkTypeDeclarationResolver(builder,
{extensionType.identifier: extensionType.identifier.name});
}
}
macro class FunctionMacro
implements
FunctionTypesMacro,
FunctionDeclarationsMacro,
FunctionDefinitionMacro {
const FunctionMacro();
@override
FutureOr<void> buildTypesForFunction(FunctionDeclaration function,
TypeBuilder builder) async {
checkFunctionDeclaration(function);
await checkIdentifierResolver(builder);
}
@override
FutureOr<void> buildDeclarationsForFunction(FunctionDeclaration function,
DeclarationBuilder builder) async {
checkFunctionDeclaration(function);
await checkIdentifierResolver(builder);
}
@override
FutureOr<void> buildDefinitionForFunction(FunctionDeclaration function,
FunctionDefinitionBuilder builder) async {
checkFunctionDeclaration(function);
await checkIdentifierResolver(builder);
await checkTypeDeclarationResolver(builder, {function.identifier: null});
}
}
@@ -1,24 +0,0 @@
{
"configVersion": 2,
"packages": [
{
"name": "macro_api_test",
"rootUri": "../../../../_fe_analyzer_shared/test/macros/api/"
},
{
"name": "meta",
"rootUri": "../../../../meta/",
"packageUri": "lib/"
},
{
"name": "macros",
"rootUri": "../../../../macros/",
"packageUri": "lib/"
},
{
"name": "_macros",
"rootUri": "../../../../_macros/",
"packageUri": "lib/"
}
]
}
@@ -1,816 +0,0 @@
// Copyright (c) 2024, 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 'package:_fe_analyzer_shared/src/macros/code_optimizer.dart';
import 'package:_fe_analyzer_shared/src/scanner/scanner.dart';
import 'package:test/test.dart';
void main() {
group('Function |', () {
test('From dart:core', () {
assertEdits(code: r'''
import 'dart:core' as prefix0;
void foo() {
prefix0.print(42);
}
''', expected: r'''
RemoveImportPrefixDeclarationEdit
18 +11 | as prefix0|
RemoveImportPrefixReferenceEdit
47 +8 |prefix0.|
----------------
import 'dart:core';
void foo() {
print(42);
}
''');
});
test('Shadowed by local variable', () {
assertEditsNoChanges(code: r'''
import 'dart:core' as prefix0;
void foo() {
prefix0.print(42);
int print;
}
''');
});
test('Shadowed by unqualified identifier', () {
assertEditsNoChanges(code: r'''
import 'dart:core' as prefix0;
class A {
void foo() {
prefix0.print(42);
print(); // could be from super
}
}
''');
});
});
group('Variable |', () {
test('From dart:math', () {
assertEdits(code: r'''
import 'dart:math' as prefix0;
void foo() {
prefix0.pi;
}
''', expected: r'''
RemoveImportPrefixDeclarationEdit
18 +11 | as prefix0|
RemoveImportPrefixReferenceEdit
47 +8 |prefix0.|
----------------
import 'dart:math';
void foo() {
pi;
}
''');
});
test('Shadowed by local variable', () {
assertEditsNoChanges(code: r'''
import 'dart:math' as prefix0;
void foo() {
prefix0.pi;
int pi;
}
''');
});
test('Shadowed by unqualified identifier', () {
assertEditsNoChanges(code: r'''
import 'dart:math' as prefix0;
class A {
void foo() {
prefix0.pi;
pi; // could be from super
}
}
''');
});
});
group('NamedType |', () {
group('Not shadowed |', () {
test('Last import, dart:core', () {
assertEdits(code: r'''
import 'dart:core' as prefix0;
class A {
prefix0.String foo() {}
}
''', expected: r'''
RemoveImportPrefixDeclarationEdit
18 +11 | as prefix0|
RemoveImportPrefixReferenceEdit
44 +8 |prefix0.|
----------------
import 'dart:core';
class A {
String foo() {}
}
''');
});
test('Last import, dart:math', () {
assertEdits(code: r'''
import 'dart:math' as prefix0;
class A {
prefix0.Random foo() {}
}
''', expected: r'''
RemoveImportPrefixDeclarationEdit
18 +11 | as prefix0|
RemoveImportPrefixReferenceEdit
44 +8 |prefix0.|
----------------
import 'dart:math';
class A {
Random foo() {}
}
''');
});
test('First import, dart:math', () {
assertEdits(code: r'''
import 'dart:core' as prefix0;
import 'dart:math' as prefix1;
class A {
prefix0.int foo(prefix1.Random a) {}
}
''', expected: r'''
RemoveImportPrefixDeclarationEdit
18 +11 | as prefix0|
RemoveImportPrefixDeclarationEdit
49 +11 | as prefix1|
RemoveImportPrefixReferenceEdit
75 +8 |prefix0.|
RemoveImportPrefixReferenceEdit
91 +8 |prefix1.|
----------------
import 'dart:core';
import 'dart:math';
class A {
int foo(Random a) {}
}
''');
});
});
group('Shadowed | ', () {
test('By library declaration name', () {
assertEditsNoChanges(code: r'''
import 'dart:core' as prefix0;
class A {
prefix0.String foo() {}
}
''', libraryDeclarationNames: {'String'});
});
test('By local class, before', () {
assertEditsNoChanges(code: r'''
import 'dart:core' as prefix0;
class String {}
class A {
prefix0.String foo() {}
}
''');
});
test('By local class, after', () {
assertEditsNoChanges(code: r'''
import 'dart:core' as prefix0;
class A {
prefix0.String foo() {}
}
class String {}
''');
});
test('By local enum', () {
assertEditsNoChanges(code: r'''
import 'dart:core' as prefix0;
class A {
prefix0.String foo() {}
}
enum String { v }
''');
});
test('By local extension', () {
assertEditsNoChanges(code: r'''
import 'dart:core' as prefix0;
class A {
prefix0.String foo() {}
}
extension String on A {}
''');
});
test('By local extension type', () {
assertEditsNoChanges(code: r'''
import 'dart:core' as prefix0;
class A {
prefix0.String foo() {}
}
extension type String(A it) {}
''');
});
test('By local function', () {
assertEditsNoChanges(code: r'''
import 'dart:core' as prefix0;
class A {
prefix0.String foo() {}
}
void String() {}
''');
});
test('By local mixin', () {
assertEditsNoChanges(code: r'''
import 'dart:core' as prefix0;
class A {
prefix0.String foo() {}
}
mixin String {}
''');
});
test('By local top-level variable, no initializer', () {
assertEditsNoChanges(code: r'''
import 'dart:core' as prefix0;
class A {
prefix0.String foo() {}
}
int? String;
''');
});
test('By local top-level variable, with initializer', () {
assertEditsNoChanges(code: r'''
import 'dart:core' as prefix0;
class A {
prefix0.String foo() {}
}
int String = 0;
''');
});
test('By local typedef', () {
assertEditsNoChanges(code: r'''
import 'dart:core' as prefix0;
class A {
prefix0.String foo() {}
}
typedef String = void Function();
''');
});
test('By class type parameter', () {
assertEditsNoChanges(code: r'''
import 'dart:core' as prefix0;
class A<String> {
prefix0.String foo() {}
}
''');
});
test('By method formal parameter', () {
assertEditsNoChanges(code: r'''
import 'dart:core' as prefix0;
class A {
prefix0.String foo<String>(String) {}
}
''');
});
test('By method type parameter', () {
assertEditsNoChanges(code: r'''
import 'dart:core' as prefix0;
class A {
prefix0.String foo<String>() {}
}
''');
});
test('By sibling getter', () {
assertEditsNoChanges(code: r'''
import 'dart:core' as prefix0;
class A {
prefix0.String foo() {}
int get String {}
}
''');
});
test('By sibling setter', () {
assertEditsNoChanges(code: r'''
import 'dart:core' as prefix0;
class A {
prefix0.String foo() {}
set String(_) {}
}
''');
});
test('By sibling method', () {
assertEditsNoChanges(code: r'''
import 'dart:core' as prefix0;
class A {
prefix0.String foo() {}
void String() {}
}
''');
});
test('Other class method', () {
// This rarely causes actual shadowing, but still might, if
// we invoke `String()` from a subclass `C` of `B`. If we import
// `dart:core` without an import prefix, inside `C` the meaning
// of `String()` will change to invoking the `dart:core@String`
// constructor.
assertEditsNoChanges(code: r'''
import 'dart:core' as prefix0;
class A {
prefix0.String foo() {}
}
class B {
void String() {}
}
''');
});
test('By sibling field, no initializer', () {
assertEditsNoChanges(code: r'''
import 'dart:core' as prefix0;
class A {
prefix0.String foo() {}
int String;
}
''');
});
test('By sibling field, with initializer', () {
assertEditsNoChanges(code: r'''
import 'dart:core' as prefix0;
class A {
prefix0.String foo() {}
int String = 0;
}
''');
});
test('Partial 1/3', () {
assertEdits(code: r'''
import 'dart:core' as prefix0;
class A {
prefix0.bool foo1() {}
prefix0.int foo2() {}
prefix0.String foo3() {}
}
class String {}
''', expected: r'''
ImportWithoutPrefixEdit
30 |\nimport 'dart:core' hide String;|
RemoveImportPrefixReferenceEdit
44 +8 |prefix0.|
RemoveImportPrefixReferenceEdit
69 +8 |prefix0.|
----------------
import 'dart:core' as prefix0;
import 'dart:core' hide String;
class A {
bool foo1() {}
int foo2() {}
prefix0.String foo3() {}
}
class String {}
''');
});
test('Other class type parameter', () {
assertEdits(code: r'''
import 'dart:core' as prefix0;
class A {
prefix0.String foo() {}
}
class B<String> {}
''', expected: r'''
----------------
import 'dart:core' as prefix0;
class A {
prefix0.String foo() {}
}
class B<String> {}
''');
});
test('Other class method type parameter', () {
assertEdits(code: r'''
import 'dart:core' as prefix0;
class A {
prefix0.String foo() {}
}
class B {
void bar<String>() {}
}
''', expected: r'''
----------------
import 'dart:core' as prefix0;
class A {
prefix0.String foo() {}
}
class B {
void bar<String>() {}
}
''');
});
test('Other method formal parameter', () {
assertEdits(code: r'''
import 'dart:core' as prefix0;
class A {
prefix0.String foo() {}
void bar(String) {}
}
''', expected: r'''
----------------
import 'dart:core' as prefix0;
class A {
prefix0.String foo() {}
void bar(String) {}
}
''');
});
test('Other method type parameter', () {
assertEdits(code: r'''
import 'dart:core' as prefix0;
class A {
prefix0.String foo() {}
void bar<String>() {}
}
''', expected: r'''
----------------
import 'dart:core' as prefix0;
class A {
prefix0.String foo() {}
void bar<String>() {}
}
''');
});
test('Sibling constructor', () {
assertEdits(code: r'''
import 'dart:core' as prefix0;
class A {
A.String();
prefix0.String foo() {}
}
''', expected: r'''
----------------
import 'dart:core' as prefix0;
class A {
A.String();
prefix0.String foo() {}
}
''');
});
test('Enum, type parameter', () {
assertEdits(code: r'''
import 'dart:core' as prefix0;
class A {
prefix0.String foo() {}
}
enum X<String> { v }
''', expected: r'''
----------------
import 'dart:core' as prefix0;
class A {
prefix0.String foo() {}
}
enum X<String> { v }
''');
});
test('Extension, type parameter', () {
assertEdits(code: r'''
import 'dart:core' as prefix0;
class A {
prefix0.String foo() {}
}
extension X<String> on A {}
''', expected: r'''
----------------
import 'dart:core' as prefix0;
class A {
prefix0.String foo() {}
}
extension X<String> on A {}
''');
});
test('Extension type, type parameter', () {
assertEdits(code: r'''
import 'dart:core' as prefix0;
class A {
prefix0.String foo() {}
}
extension type X<String>(A it) {}
''', expected: r'''
----------------
import 'dart:core' as prefix0;
class A {
prefix0.String foo() {}
}
extension type X<String>(A it) {}
''');
});
test('Typedef, type parameter', () {
assertEdits(code: r'''
import 'dart:core' as prefix0;
class A {
prefix0.String foo() {}
}
typedef F<String> = void Function();
''', expected: r'''
----------------
import 'dart:core' as prefix0;
class A {
prefix0.String foo() {}
}
typedef F<String> = void Function();
''');
});
test('By class field', () {
assertEditsNoChanges(code: r'''
import 'dart:core' as prefix0;
class A {
int String;
prefix0.String foo() {}
}
''');
});
test('By local variable', () {
assertEditsNoChanges(code: r'''
import 'dart:core' as prefix0;
prefix0.String foo() {
int String;
}
''');
});
test('By formal parameter', () {
assertEditsNoChanges(code: r'''
import 'dart:core' as prefix0;
prefix0.String foo(int String) {
}
''');
});
});
});
test('JsonSerializable', () {
assertEdits(
importedNames: {
'package:json_serializable/json_serializable.dart': {
'FromJson',
'ToJson'
},
},
withEdits: false,
code: r'''
augment library 'test.dart';
import 'package:json_serializable/json_serializable.dart' as prefix0;
import 'dart:core' as prefix1;
augment class User {
@prefix0.FromJson()
external User.fromJson(prefix1.Map<prefix1.String, prefix1.Object?> json);
@prefix0.ToJson()
external prefix1.Map<prefix1.String, prefix1.Object?> toJson();
augment User.fromJson(prefix1.Map<prefix1.String, prefix1.Object?> json, ) :
this.age = json["age"] as prefix1.int,
this.name = json["name"] as prefix1.String{}
augment prefix1.Map<prefix1.String, prefix1.Object?> toJson() => {
'age': this.age,
'name': this.name,
};
}
''',
expected: r'''
augment library 'test.dart';
import 'package:json_serializable/json_serializable.dart';
import 'dart:core';
augment class User {
@FromJson()
external User.fromJson(Map<String, Object?> json);
@ToJson()
external Map<String, Object?> toJson();
augment User.fromJson(Map<String, Object?> json, ) :
this.age = json["age"] as int,
this.name = json["name"] as String{}
augment Map<String, Object?> toJson() => {
'age': this.age,
'name': this.name,
};
}
''');
});
test('Update expectations', () {
// import '../../../analyzer/test/src/dart/resolution/node_text_expectations.dart';
// NodeTextExpectationsCollector.apply();
});
}
const _dartImports = {
'dart:core': {'bool', 'double', 'int', 'Map', 'Object', 'String', 'print'},
'dart:math': {'Random', 'pi'},
};
void assertEdits({
Map<String, Set<String>> importedNames = const {},
Set<String> libraryDeclarationNames = const {},
required String code,
required String expected,
bool withEdits = true,
bool throwIfHasErrors = true,
}) {
var optimizer = _CodeOptimizer(
importedNames: {
..._dartImports,
...importedNames,
},
);
var edits = optimizer.optimize(
code,
libraryDeclarationNames: libraryDeclarationNames,
scannerConfiguration: ScannerConfiguration(
forAugmentationLibrary: true,
),
throwIfHasErrors: throwIfHasErrors,
);
var buffer = StringBuffer();
if (withEdits) {
void writeRemoveEdit(RemoveEdit edit) {
buffer.write(' ${edit.offset} +${edit.length}');
var removed = code.substring(edit.offset, edit.offset + edit.length);
buffer.writeln(' |${escape(removed)}|');
}
for (var edit in edits) {
switch (edit) {
case RemoveDartCoreImportEdit():
buffer.writeln('RemoveDartCoreImportEdit');
writeRemoveEdit(edit);
case RemoveImportPrefixDeclarationEdit():
buffer.writeln('RemoveImportPrefixDeclarationEdit');
writeRemoveEdit(edit);
case RemoveImportPrefixReferenceEdit():
buffer.writeln('RemoveImportPrefixReferenceEdit');
writeRemoveEdit(edit);
case ImportWithoutPrefixEdit():
buffer.writeln('ImportWithoutPrefixEdit');
buffer.write(' ${edit.offset}');
buffer.writeln(' |${escape(edit.replacement)}|');
}
}
buffer.writeln('-' * 16);
}
// Apply in reverse order.
var optimized = Edit.applyList(edits, code);
buffer.write(optimized);
var actual = buffer.toString();
if (actual != expected) {
print('-------- Actual --------');
print('$actual------------------------');
// NodeTextExpectationsCollector.add(actual);
fail('Not as expected');
}
}
void assertEditsNoChanges({
Map<String, Set<String>> importedNames = const {},
Set<String> libraryDeclarationNames = const {},
required String code,
bool throwIfHasErrors = true,
}) {
assertEdits(
importedNames: importedNames,
libraryDeclarationNames: libraryDeclarationNames,
code: code,
throwIfHasErrors: throwIfHasErrors,
expected: '${'-' * 16}\n$code',
);
}
class _CodeOptimizer extends CodeOptimizer {
final Map<String, Set<String>> importedNames;
_CodeOptimizer({
required this.importedNames,
});
@override
Set<String> getImportedNames(String uriStr) {
return importedNames[uriStr] ?? (throw StateError('Unexpected: $uriStr'));
}
}
@@ -1,269 +0,0 @@
// Copyright (c) 2022, 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 'dart:typed_data';
import 'package:_fe_analyzer_shared/src/macros/compiler/request_channel.dart';
import 'package:_fe_analyzer_shared/src/macros/compiler/byte_data_serializer.dart';
import 'package:test/test.dart';
main() {
group('ByteDataSerializer', () {
group('addAny', () {
Uint8List write(Object? object) {
final serializer = ByteDataSerializer();
serializer.addAny(object);
return serializer.result;
}
Object? read(Uint8List bytes) {
final deserializer = ByteDataDeserializer(
new ByteData.sublistView(bytes),
);
return deserializer.expectAny();
}
void writeRead(Object? object) {
final bytes = write(object);
expect(read(bytes), object);
}
group('bool', () {
test('false', () {
writeRead(false);
});
test('true', () {
writeRead(true);
});
});
test('null', () {
writeRead(null);
});
group('int', () {
test('negative', () {
void writeReadInt(int value) {
expect(value, isNegative);
writeRead(value);
}
writeReadInt(-1);
writeReadInt(-2);
writeReadInt(-0x7F);
writeReadInt(-0xFF);
writeReadInt(-0xFFFF);
writeReadInt(-0xFFFFFF);
writeReadInt(-0xFFFFFFFF);
writeReadInt(-0xFFFFFFFFFF);
writeReadInt(-0xFFFFFFFFFFFF);
writeReadInt(-0xFFFFFFFFFFFFFF);
writeReadInt(-0x7FFFFFFFFFFFFFFF);
writeReadInt(0x8000000000000000);
});
test('non-negative', () {
void writeReadInt(int value) {
expect(value, isNonNegative);
writeRead(value);
}
writeReadInt(0);
writeReadInt(1);
writeReadInt(0x6F);
writeReadInt(0x7F);
writeReadInt(0xFF);
writeReadInt(0xFFFF);
writeReadInt(0xFFFFFF);
writeReadInt(0xFFFFFFFF);
writeReadInt(0xFFFFFFFFFF);
writeReadInt(0xFFFFFFFFFFFF);
writeReadInt(0xFFFFFFFFFFFFFF);
writeReadInt(0x7FFFFFFFFFFFFFFF);
});
});
group('String', () {
test('one-byte', () {
writeRead('test');
});
test('two-byte', () {
writeRead('проба');
});
});
group('list', () {
test('empty', () {
writeRead(<int>[]);
});
test('of int', () {
writeRead([1, 2, 3]);
});
test('of string', () {
writeRead(['a', 'b', 'c']);
});
test('mixed', () {
writeRead([true, 'abc', 0xAB]);
});
test('nested', () {
writeRead([
[1, 2, 3],
true,
['foo', 'bar'],
false,
]);
});
});
group('map', () {
test('empty', () {
writeRead(<String, int>{});
});
test('string to int', () {
writeRead({'a': 0x11, 'b': 0x22});
});
test('nested', () {
writeRead({
'foo': {0: 1, 2: 3},
'bar': {4: 5, 6: 7},
});
});
});
group('Uint8List', () {
test('empty', () {
writeRead(Uint8List(0));
});
test('5 elements', () {
writeRead(
Uint8List.fromList(
[0x11, 0x22, 0x33, 0x44, 0x55],
),
);
});
test('0..4096', () {
for (int length = 1; length < 4096; length++) {
Uint8List object = Uint8List(length);
for (int i = 0; i < object.length; i++) {
object[i] = i & 0xFF;
}
Uint8List bytes = write(object);
Uint8List readObject = read(bytes) as Uint8List;
for (int i = 0; i < object.length; i++) {
if (object[i] != readObject[i]) {
fail('[length: $length][i: $i]');
}
}
}
});
test('nested in Map', () {
writeRead({
'answer': 42,
'result': Uint8List.fromList(
[0x11, 0x22, 0x33, 0x44, 0x55],
),
'hasErrors': false,
});
});
});
group('JSON', () {
test('command', () {
writeRead({
'command': 'compile',
'argument': {
'uri': 'input.dart',
'additional': ['a', 'b', 'c'],
},
});
});
});
});
});
group('RequestChannel', () {
Future<void> runServerClient({
required Future<void> Function(RequestChannel channel) server,
required Future<void> Function(RequestChannel channel) client,
}) async {
ServerSocket serverSocket =
await ServerSocket.bind(InternetAddress.loopbackIPv4, 0);
serverSocket.listen((socket) async {
await serverSocket.close();
await server(
RequestChannel(socket),
);
});
var clientSocket = await Socket.connect(
InternetAddress.loopbackIPv4,
serverSocket.port,
);
try {
await client(
RequestChannel(clientSocket),
);
} finally {
clientSocket.destroy();
}
}
test('exception', () async {
await runServerClient(
server: (channel) async {
channel.add('throwIt', (argument) async {
throw 'Some error';
});
},
client: (channel) async {
try {
await channel.sendRequest('throwIt', {});
fail('Expected to throw RemoteException.');
} on RequestChannelException catch (e) {
expect(e.message, 'Some error');
expect(e.stackTrace, isNotEmpty);
}
},
);
});
test('no handler', () async {
await runServerClient(
server: (channel) async {},
client: (channel) async {
try {
await channel.sendRequest('noSuchHandler', {});
fail('Expected to throw RemoteException.');
} on RequestChannelException catch (e) {
expect(e.message, contains('noSuchHandler'));
expect(e.stackTrace, isNotEmpty);
}
},
);
});
test('two-way communication', () async {
await runServerClient(
server: (channel) async {
channel.add('add', (argument) async {
if (argument is List<Object?> && argument.length == 2) {
Object? a = argument[0];
Object? b = argument[1];
if (a is int && b is int) {
int more = await channel.sendRequest<int>('more', null);
return a + b + more;
}
}
return '<bad>';
});
},
client: (channel) async {
channel.add('more', (_) async => 4);
expect(await channel.sendRequest('add', [2, 3]), 9);
},
);
});
});
}
+1 -5
View File
@@ -6,8 +6,6 @@ import 'dart:typed_data';
import 'package:build_integration/file_system/multi_root.dart'
show MultiRootFileSystem;
import 'package:front_end/src/api_prototype/macros.dart' as macros
show isMacroLibraryUri;
import 'package:front_end/src/api_prototype/standard_file_system.dart'
show StandardFileSystem;
import 'package:front_end/src/api_unstable/vm.dart'
@@ -242,9 +240,7 @@ Future<CompilationResult> compileToModule(
if (depFile != null) {
writeDepfile(
compilerOptions.fileSystem,
// TODO(https://dartbug.com/55246): track macro deps when available.
component.uriToSource.keys
.where((uri) => !macros.isMacroLibraryUri(uri)),
component.uriToSource.keys,
options.outputFile,
depFile);
}
@@ -8,8 +8,6 @@ import 'dart:io';
import 'package:args/args.dart';
import 'package:build_integration/file_system/multi_root.dart';
import 'package:front_end/src/api_prototype/macros.dart' as macros
show isMacroLibraryUri;
import 'package:front_end/src/api_unstable/ddc.dart' as fe;
import 'package:kernel/binary/ast_from_binary.dart' as kernel
show BinaryBuilder;
@@ -1043,11 +1041,6 @@ Map<String, Object?> placeSourceMap(Map<String, Object?> sourceMap,
return sourcePath;
}
if (macros.isMacroLibraryUri(uri)) {
// TODO: https://github.com/dart-lang/sdk/issues/53913
return sourcePath;
}
if (uri.isScheme('http')) return sourcePath;
// Convert to a local file path if it's not.
@@ -1 +0,0 @@
export 'package:_fe_analyzer_shared/src/macros/uri.dart' show isMacroLibraryUri;
@@ -2,7 +2,6 @@
// 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:_fe_analyzer_shared/src/macros/uri.dart';
import 'package:_fe_analyzer_shared/src/testing/id.dart';
import 'package:_fe_analyzer_shared/src/testing/id_testing.dart';
import 'package:kernel/ast.dart';
@@ -238,15 +237,9 @@ Future<TestResult<T>> processCompiledResult<T, C extends TestConfig, R,
Map<Uri, Map<int, List<FormattedMessage>>> errorMap = {};
for (FormattedMessage error in errors) {
Uri? uri = error.uri;
bool isMacroLibrary = false;
if (uri != null && isMacroLibraryUri(uri)) {
isMacroLibrary = true;
uri = toOriginLibraryUri(uri);
}
Map<int, List<FormattedMessage>> map =
errorMap.putIfAbsent(uri ?? nullUri, () => {});
List<FormattedMessage> list =
map.putIfAbsent(isMacroLibrary ? -1 : error.charOffset, () => []);
List<FormattedMessage> list = map.putIfAbsent(error.charOffset, () => []);
list.add(error);
}
@@ -270,9 +263,6 @@ Future<TestResult<T>> processCompiledResult<T, C extends TestConfig, R,
Uri uri = node is Library
? node.fileUri
: (node is Member ? node.fileUri : node.location!.file);
if (isMacroLibraryUri(uri)) {
uri = toOriginLibraryUri(uri);
}
return actualMapForUri(uri);
}
+1 -5
View File
@@ -24,8 +24,6 @@ import 'package:dev_compiler/dev_compiler.dart'
ExpressionCompiler,
ModuleFormat,
parseModuleFormat;
import 'package:front_end/src/api_prototype/macros.dart' as macros
show isMacroLibraryUri;
import 'package:front_end/src/api_unstable/ddc.dart' as ddc
show IncrementalCompiler;
import 'package:front_end/src/api_unstable/vm.dart';
@@ -654,9 +652,7 @@ class FrontendCompiler implements CompilerInterface {
nativeAssetsLibrary: _nativeAssetsLibrary,
classHierarchy: compilerResult.classHierarchy,
coreTypes: compilerResult.coreTypes,
// TODO(https://dartbug.com/55246): track macro deps when available.
compiledSources: component.uriToSource.keys
.where((uri) => !macros.isMacroLibraryUri(uri)),
compiledSources: component.uriToSource.keys,
);
incrementalSerializer = _generator.incrementalSerializer;
+1 -5
View File
@@ -13,8 +13,6 @@ import 'package:args/args.dart' show ArgParser, ArgResults;
import 'package:build_integration/file_system/multi_root.dart'
show MultiRootFileSystem, MultiRootFileSystemEntity;
import 'package:crypto/crypto.dart';
import 'package:front_end/src/api_prototype/macros.dart' as macros
show isMacroLibraryUri;
import 'package:front_end/src/api_unstable/vm.dart'
show
CompilerContext,
@@ -551,9 +549,7 @@ Future<KernelCompilationResults> compileToKernel(
}
final Component? component = compilerResult?.component;
// TODO(https://dartbug.com/55246): track macro deps when available.
Iterable<Uri>? compiledSources = component?.uriToSource.keys
.where((uri) => !macros.isMacroLibraryUri(uri));
Iterable<Uri>? compiledSources = component?.uriToSource.keys;
Set<Library> loadedLibraries = createLoadedLibrariesSet(
compilerResult?.loadedComponents, compilerResult?.sdkComponent,