Support for element model tests with macros.

Change-Id: Ief2397b79aec91ff975f15d731ef251be9d6e075
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/237859
Reviewed-by: Samuel Rawlins <srawlins@google.com>
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
Commit-Queue: Konstantin Shcheglov <scheglov@google.com>
This commit is contained in:
Konstantin Shcheglov
2022-04-11 19:31:23 +00:00
committed by Commit Bot
parent 9a8b63d6fa
commit 7e7b271586
6 changed files with 320 additions and 4 deletions
@@ -203,7 +203,8 @@ class LibraryBuilder {
lineInfo: parseResult.lineInfo,
)
..enclosingElement = element
..isSynthetic = true;
..isSynthetic = true
..uri = unitUri.toString();
var unitReference = reference.getChild('@unit').getChild('$unitUri');
_bindReference(unitReference, unitElement);
@@ -224,6 +225,14 @@ class LibraryBuilder {
element: unitElement,
),
);
linker.macroGeneratedUnits.add(
LinkMacroGeneratedUnit(
uri: unitUri,
content: parseResult.content,
unit: parseResult.unit,
),
);
}
void resolveConstructors() {
+17
View File
@@ -37,6 +37,7 @@ Future<LinkResult> link(
await linker.link(inputLibraries);
return LinkResult(
resolutionBytes: linker.resolutionBytes,
macroGeneratedUnits: linker.macroGeneratedUnits,
);
}
@@ -62,6 +63,8 @@ class Linker {
late Uint8List resolutionBytes;
final List<LinkMacroGeneratedUnit> macroGeneratedUnits = [];
Linker(this.elementFactory, this.macroExecutor);
AnalysisContextImpl get analysisContext {
@@ -293,10 +296,24 @@ class LinkInputUnit {
String get uriStr => '$uri';
}
class LinkMacroGeneratedUnit {
final Uri uri;
final String content;
final ast.CompilationUnit unit;
LinkMacroGeneratedUnit({
required this.uri,
required this.content,
required this.unit,
});
}
class LinkResult {
final Uint8List resolutionBytes;
final List<LinkMacroGeneratedUnit> macroGeneratedUnits;
LinkResult({
required this.resolutionBytes,
required this.macroGeneratedUnits,
});
}
+2
View File
@@ -129,4 +129,6 @@ class MacroLibrary {
required this.path,
required this.classes,
});
String get uriStr => uri.toString();
}
@@ -68,11 +68,12 @@ class LibraryMacroApplier {
var macroExecutor = libraryBuilder.linker.macroExecutor;
if (macroExecutor != null && macroResults.isNotEmpty) {
return macroExecutor.buildAugmentationLibrary(
var code = macroExecutor.buildAugmentationLibrary(
macroResults,
_resolveIdentifier,
_inferOmittedType,
);
return code.trim();
}
return null;
}
@@ -4,12 +4,15 @@
import 'dart:typed_data';
import 'package:_fe_analyzer_shared/src/macros/executor/multi_executor.dart'
as macro;
import 'package:analyzer/dart/analysis/declared_variables.dart';
import 'package:analyzer/dart/analysis/features.dart';
import 'package:analyzer/dart/ast/ast.dart';
import 'package:analyzer/file_system/file_system.dart';
import 'package:analyzer/src/context/context.dart';
import 'package:analyzer/src/dart/analysis/session.dart';
import 'package:analyzer/src/dart/ast/ast.dart';
import 'package:analyzer/src/dart/element/class_hierarchy.dart';
import 'package:analyzer/src/dart/element/element.dart';
import 'package:analyzer/src/dart/element/inheritance_manager3.dart';
@@ -22,10 +25,12 @@ import 'package:analyzer/src/summary2/bundle_reader.dart';
import 'package:analyzer/src/summary2/informative_data.dart';
import 'package:analyzer/src/summary2/link.dart';
import 'package:analyzer/src/summary2/linked_element_factory.dart';
import 'package:analyzer/src/summary2/macro.dart';
import 'package:analyzer/src/summary2/reference.dart';
import 'package:analyzer/src/test_utilities/mock_sdk.dart';
import 'package:analyzer/src/test_utilities/resource_provider_mixin.dart';
import 'package:analyzer/src/util/uri.dart';
import 'package:path/path.dart' as package_path;
import 'package:test_reflective_loader/test_reflective_loader.dart';
import '../../util/feature_sets.dart';
@@ -37,6 +42,9 @@ abstract class ElementsBaseTest with ResourceProviderMixin {
/// The shared SDK bundle, computed once and shared among test invocations.
static _SdkBundle? _sdkBundle;
MacroKernelBuilder? macroKernelBuilder;
macro.MultiMacroExecutor? macroExecutor;
/// The set of features enabled in this test.
FeatureSet featureSet = FeatureSets.latestWithExperiments;
@@ -113,7 +121,9 @@ abstract class ElementsBaseTest with ResourceProviderMixin {
String get testPackageLibPath => '$testPackageRootPath/lib';
String get testPackageRootPath => '/home/test';
String get testPackageRootPath => '$workspaceRootPath/test';
String get workspaceRootPath => '/home';
void addSource(String path, String contents) {
newFile2(path, contents);
@@ -123,6 +133,7 @@ abstract class ElementsBaseTest with ResourceProviderMixin {
String text, {
bool allowErrors = false,
bool dumpSummaries = false,
List<Set<String>>? preBuildSequence,
}) async {
var testFile = newFile2(testFilePath, text);
var testUri = sourceFactory.pathToUri(testFile.path)!;
@@ -160,7 +171,22 @@ abstract class ElementsBaseTest with ResourceProviderMixin {
),
);
var linkResult = await link(elementFactory, inputLibraries);
_linkConfiguredLibraries(
elementFactory,
inputLibraries,
preBuildSequence,
);
var linkResult = await link(
elementFactory,
inputLibraries,
macroExecutor: macroExecutor,
);
for (var macroUnit in linkResult.macroGeneratedUnits) {
var informativeBytes = writeUnitInformative(macroUnit.unit);
unitsInformativeBytes[macroUnit.uri] = informativeBytes;
}
if (!keepLinkingLibraries) {
elementFactory.removeBundle(
@@ -279,6 +305,79 @@ abstract class ElementsBaseTest with ResourceProviderMixin {
}
}
/// If there are any [macroLibraries], build the kernel and prepare for
/// execution.
void _buildMacroLibraries(
LinkedElementFactory elementFactory,
List<MacroLibrary> macroLibraries,
) {
if (macroLibraries.isEmpty) {
return;
}
final macroKernelBuilder = this.macroKernelBuilder;
if (macroKernelBuilder == null) {
return;
}
final macroExecutor = this.macroExecutor;
if (macroExecutor == null) {
return;
}
var macroKernelBytes = macroKernelBuilder.build(
fileSystem: _MacroFileSystem(resourceProvider),
libraries: macroLibraries,
);
var bundleMacroExecutor = BundleMacroExecutor(
macroExecutor: macroExecutor,
kernelBytes: macroKernelBytes,
libraries: macroLibraries.map((e) => e.uri).toSet(),
);
for (var macroLibrary in macroLibraries) {
var uriStr = macroLibrary.uriStr;
var element = elementFactory.libraryOfUri2(uriStr);
element.bundleMacroExecutor = bundleMacroExecutor;
}
}
/// If there are any libraries in the [uriStrSetList], link these subsets
/// of [inputLibraries] (and remove from it), build macro kernels, prepare
/// for executing macros.
void _linkConfiguredLibraries(
LinkedElementFactory elementFactory,
List<LinkInputLibrary> inputLibraries,
List<Set<String>>? uriStrSetList,
) {
if (uriStrSetList == null) {
return;
}
for (var uriStrSet in uriStrSetList) {
var cycleInputLibraries = <LinkInputLibrary>[];
var macroLibraries = <MacroLibrary>[];
for (var inputLibrary in inputLibraries) {
if (uriStrSet.contains(inputLibrary.uriStr)) {
cycleInputLibraries.add(inputLibrary);
_addMacroLibrary(macroLibraries, inputLibrary);
}
}
link(
elementFactory,
cycleInputLibraries,
macroExecutor: macroExecutor,
);
_buildMacroLibraries(elementFactory, macroLibraries);
// Remove libraries that we just linked.
cycleInputLibraries.forEach(inputLibraries.remove);
}
}
String _readSafely(String path) {
try {
var file = resourceProvider.getFile(path);
@@ -287,6 +386,53 @@ abstract class ElementsBaseTest with ResourceProviderMixin {
return '';
}
}
/// If there are any macros in the [inputLibrary], add it.
static void _addMacroLibrary(
List<MacroLibrary> macroLibraries,
LinkInputLibrary inputLibrary,
) {
var macroClasses = <MacroClass>[];
for (var inputUnit in inputLibrary.units) {
for (var declaration in inputUnit.unit.declarations) {
if (declaration is ClassDeclarationImpl &&
declaration.macroKeyword != null) {
var constructors =
declaration.members.whereType<ConstructorDeclaration>().toList();
if (constructors.isEmpty) {
macroClasses.add(
MacroClass(
name: declaration.name.name,
constructors: [''],
),
);
} else {
var constructorNames = constructors
.map((e) => e.name?.name ?? '')
.where((e) => !e.startsWith('_'))
.toList();
if (constructorNames.isNotEmpty) {
macroClasses.add(
MacroClass(
name: declaration.name.name,
constructors: constructorNames,
),
);
}
}
}
}
}
if (macroClasses.isNotEmpty) {
macroLibraries.add(
MacroLibrary(
uri: inputLibrary.uri,
path: inputLibrary.source.fullName,
classes: macroClasses,
),
);
}
}
}
class _AnalysisSessionForLinking implements AnalysisSessionImpl {
@@ -300,6 +446,35 @@ class _AnalysisSessionForLinking implements AnalysisSessionImpl {
noSuchMethod(Invocation invocation) => super.noSuchMethod(invocation);
}
/// [MacroFileEntry] adapter for [File].
class _MacroFileEntry implements MacroFileEntry {
final File file;
_MacroFileEntry(this.file);
@override
String get content => file.readAsStringSync();
@override
bool get exists => file.exists;
}
/// [MacroFileSystem] adapter for [ResourceProvider].
class _MacroFileSystem implements MacroFileSystem {
final ResourceProvider resourceProvider;
_MacroFileSystem(this.resourceProvider);
@override
package_path.Context get pathContext => resourceProvider.pathContext;
@override
MacroFileEntry getFile(String path) {
var file = resourceProvider.getFile(path);
return _MacroFileEntry(file);
}
}
class _SdkBundle {
final Uint8List resolutionBytes;
@@ -2,12 +2,23 @@
// 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/executor/multi_executor.dart'
as macro;
import 'package:analyzer/src/test_utilities/package_config_file_builder.dart';
import 'package:test_reflective_loader/test_reflective_loader.dart';
import 'element_text.dart';
import 'elements_base.dart';
import 'repository_macro_kernel_builder.dart';
main() {
try {
MacrosEnvironment.instance;
} catch (_) {
print('Cannot initialize environment. Skip macros tests.');
return;
}
defineReflectiveSuite(() {
defineReflectiveTests(MacroElementsKeepLinkingTest);
defineReflectiveTests(MacroElementsFromBytesTest);
@@ -30,6 +41,79 @@ class MacroElementsTest extends ElementsBaseTest {
@override
bool get keepLinkingLibraries => false;
/// The path for external packages.
String get packagesRootPath => '/packages';
Future<void> setUp() async {
writeTestPackageConfig(
PackageConfigFileBuilder(),
macrosEnvironment: MacrosEnvironment.instance,
);
macroKernelBuilder = DartRepositoryMacroKernelBuilder(
MacrosEnvironment.instance.platformDillBytes,
);
macroExecutor = macro.MultiMacroExecutor();
}
Future<void> tearDown() async {
await macroExecutor?.close();
}
test_build_types() async {
newFile2('$testPackageLibPath/a.dart', r'''
import 'dart:async';
import 'package:_fe_analyzer_shared/src/macros/api.dart';
macro class MyMacro implements ClassTypesMacro {
FutureOr<void> buildTypesForClass(clazz, builder) {
builder.declareType(
'MyClass',
DeclarationCode.fromString('class MyClass {}'),
);
}
}
''');
var library = await buildLibrary(r'''
import 'a.dart';
@MyMacro()
class A {}
''', preBuildSequence: [
{'package:test/a.dart'}
]);
checkElementText(library, r'''
library
imports
package:test/a.dart
definingUnit
classes
class A @35
metadata
Annotation
atSign: @ @18
name: SimpleIdentifier
token: MyMacro @19
staticElement: package:test/a.dart::@class::MyMacro
staticType: null
arguments: ArgumentList
leftParenthesis: ( @26
rightParenthesis: ) @27
element: package:test/a.dart::@class::MyMacro::@constructor::•
constructors
synthetic @-1
parts
package:test/_macro_types.dart
classes
class MyClass @6
constructors
synthetic @-1
''');
}
test_class_macro() async {
var library = await buildLibrary(r'''
macro class A {}
@@ -72,4 +156,32 @@ library
Object
''');
}
void writeTestPackageConfig(
PackageConfigFileBuilder config, {
MacrosEnvironment? macrosEnvironment,
}) {
config = config.copy();
config.add(
name: 'test',
rootPath: testPackageRootPath,
);
if (macrosEnvironment != null) {
var packagesRootFolder = getFolder(packagesRootPath);
macrosEnvironment.packageSharedFolder.copyTo(packagesRootFolder);
config.add(
name: '_fe_analyzer_shared',
rootPath: getFolder('$packagesRootPath/_fe_analyzer_shared').path,
);
}
newPackageConfigJsonFile(
testPackageRootPath,
config.toContent(
toUriStr: toUriStr,
),
);
}
}