[_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:
committed by
Commit Queue
parent
ca16a4271c
commit
7c22f942aa
@@ -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) {
|
||||
|
||||
@@ -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);
|
||||
},
|
||||
);
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user