blob: 72a2b6dcdbc98780e1db6976247af03118297440 [file] [log] [blame]
// 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' as io;
import 'dart:typed_data';
import 'package:_fe_analyzer_shared/src/macros/bootstrap.dart' as macro;
import 'package:_fe_analyzer_shared/src/macros/executor/serialization.dart'
as macro;
import 'package:analyzer/dart/ast/ast.dart';
import 'package:analyzer/dart/element/element.dart';
import 'package:analyzer/dart/element/visitor.dart';
import 'package:analyzer/file_system/file_system.dart';
import 'package:analyzer/file_system/physical_file_system.dart';
import 'package:analyzer/src/dart/element/element.dart';
import 'package:analyzer/src/summary2/macro.dart';
import 'package:analyzer/src/summary2/macro_application_error.dart';
import 'package:analyzer/src/test_utilities/mock_packages.dart';
import 'package:analyzer/src/test_utilities/package_config_file_builder.dart';
import 'package:path/path.dart' as package_path;
import 'package:test/test.dart';
import 'package:test_reflective_loader/test_reflective_loader.dart';
import '../dart/resolution/node_text_expectations.dart';
import 'element_text.dart';
import 'elements_base.dart';
import 'macros_environment.dart';
main() {
try {
MacrosEnvironment.instance;
} catch (_) {
print('Cannot initialize environment. Skip macros tests.');
test('fake', () {});
return;
}
defineReflectiveSuite(() {
defineReflectiveTests(MacroArgumentsTest);
defineReflectiveTests(MacroTypesIntrospectTest);
defineReflectiveTests(MacroTypesTest_keepLinking);
defineReflectiveTests(MacroTypesTest_fromBytes);
defineReflectiveTests(MacroDeclarationsIntrospectTest);
defineReflectiveTests(MacroDeclarationsTest_keepLinking);
defineReflectiveTests(MacroDeclarationsTest_fromBytes);
defineReflectiveTests(MacroElementsTest_keepLinking);
defineReflectiveTests(MacroElementsTest_fromBytes);
defineReflectiveTests(MacroApplicationOrderTest);
defineReflectiveTests(UpdateNodeTextExpectations);
});
}
@reflectiveTest
class MacroApplicationOrderTest extends MacroElementsBaseTest {
@override
bool get keepLinkingLibraries => true;
String get _orderCode {
var code = MacrosEnvironment.instance.packageAnalyzerFolder
.getChildAssumingFile('test/src/summary/macro/order.dart')
.readAsStringSync();
return code.replaceAll('/*macro*/', 'macro');
}
test_declarations_class_interfaces_backward() async {
_newOrderMacrosFile();
var library = await buildLibrary(r'''
import 'order.dart';
@AddFunction('f31')
@AddFunction('f32')
class X3 {}
@AddFunction('f21')
@AddFunction('f22')
class X2 {}
@AddFunction('f11')
@AddFunction('f12')
class X1 implements X2, X3 {}
''');
configuration.forOrder();
checkElementText(library, r'''
library
imports
package:test/order.dart
augmentationImports
package:test/test.macro.dart
macroGeneratedCode
---
library augment 'test.dart';
void f32() {}
void f31() {}
void f22() {}
void f21() {}
void f12() {}
void f11() {}
---
''');
}
test_declarations_class_interfaces_forward() async {
_newOrderMacrosFile();
var library = await buildLibrary(r'''
import 'order.dart';
@AddFunction('f11')
@AddFunction('f12')
class X1 implements X2, X3 {}
@AddFunction('f21')
@AddFunction('f22')
class X2 {}
@AddFunction('f31')
@AddFunction('f32')
class X3 {}
''');
configuration.forOrder();
checkElementText(library, r'''
library
imports
package:test/order.dart
augmentationImports
package:test/test.macro.dart
macroGeneratedCode
---
library augment 'test.dart';
void f22() {}
void f21() {}
void f32() {}
void f31() {}
void f12() {}
void f11() {}
---
''');
}
test_declarations_class_interfaces_forward2() async {
_newOrderMacrosFile();
var library = await buildLibrary(r'''
import 'order.dart';
@AddFunction('f11')
@AddFunction('f12')
class X1 implements X3, X4 {}
@AddFunction('f21')
@AddFunction('f22')
class X2 implements X3, X4 {}
@AddFunction('f31')
@AddFunction('f32')
class X3 {}
@AddFunction('f41')
@AddFunction('f42')
class X4 {}
''');
configuration.forOrder();
checkElementText(library, r'''
library
imports
package:test/order.dart
augmentationImports
package:test/test.macro.dart
macroGeneratedCode
---
library augment 'test.dart';
void f32() {}
void f31() {}
void f42() {}
void f41() {}
void f12() {}
void f11() {}
void f22() {}
void f21() {}
---
''');
}
test_declarations_class_interfaces_forward3() async {
_newOrderMacrosFile();
var library = await buildLibrary(r'''
import 'order.dart';
@AddFunction('f11')
@AddFunction('f12')
class X1 implements X2, X4 {}
@AddFunction('f21')
@AddFunction('f22')
class X2 implements X3 {}
@AddFunction('f31')
@AddFunction('f32')
class X3 {}
@AddFunction('f41')
@AddFunction('f42')
class X4 {}
''');
configuration.forOrder();
checkElementText(library, r'''
library
imports
package:test/order.dart
augmentationImports
package:test/test.macro.dart
macroGeneratedCode
---
library augment 'test.dart';
void f32() {}
void f31() {}
void f22() {}
void f21() {}
void f42() {}
void f41() {}
void f12() {}
void f11() {}
---
''');
}
test_declarations_class_mixins_backward() async {
_newOrderMacrosFile();
var library = await buildLibrary(r'''
import 'order.dart';
@AddFunction('f31')
@AddFunction('f32')
mixin X3 {}
@AddFunction('f21')
@AddFunction('f22')
mixin X2 {}
@AddFunction('f11')
@AddFunction('f12')
class X1 with X2, X3 {}
''');
configuration.forOrder();
checkElementText(library, r'''
library
imports
package:test/order.dart
augmentationImports
package:test/test.macro.dart
macroGeneratedCode
---
library augment 'test.dart';
void f32() {}
void f31() {}
void f22() {}
void f21() {}
void f12() {}
void f11() {}
---
''');
}
test_declarations_class_mixins_forward() async {
_newOrderMacrosFile();
var library = await buildLibrary(r'''
import 'order.dart';
@AddFunction('f11')
@AddFunction('f12')
class X1 with X2, X3 {}
@AddFunction('f21')
@AddFunction('f22')
mixin X2 {}
@AddFunction('f31')
@AddFunction('f32')
mixin X3 {}
''');
configuration.forOrder();
checkElementText(library, r'''
library
imports
package:test/order.dart
augmentationImports
package:test/test.macro.dart
macroGeneratedCode
---
library augment 'test.dart';
void f22() {}
void f21() {}
void f32() {}
void f31() {}
void f12() {}
void f11() {}
---
''');
}
test_declarations_class_superclass_backward() async {
_newOrderMacrosFile();
var library = await buildLibrary(r'''
import 'order.dart';
@AddFunction('f3')
class X3 extends X2 {
@AddFunction('f31')
void foo() {}
@AddFunction('f32')
void bar() {}
}
@AddFunction('f2')
class X2 extends X1 {
@AddFunction('f21')
void foo() {}
@AddFunction('f22')
void bar() {}
}
@AddFunction('f1')
class X1 {
@AddFunction('f11')
void foo() {}
@AddFunction('f12')
void bar() {}
}
''');
configuration.forOrder();
checkElementText(library, r'''
library
imports
package:test/order.dart
augmentationImports
package:test/test.macro.dart
macroGeneratedCode
---
library augment 'test.dart';
void f11() {}
void f12() {}
void f1() {}
void f21() {}
void f22() {}
void f2() {}
void f31() {}
void f32() {}
void f3() {}
---
''');
}
test_declarations_class_superclass_forward() async {
_newOrderMacrosFile();
var library = await buildLibrary(r'''
import 'order.dart';
@AddFunction('f1')
class X1 {
@AddFunction('f11')
void foo() {}
@AddFunction('f12')
void bar() {}
}
@AddFunction('f2')
class X2 extends X1 {
@AddFunction('f21')
void foo() {}
@AddFunction('f22')
void bar() {}
}
@AddFunction('f3')
class X3 extends X2 {
@AddFunction('f31')
void foo() {}
@AddFunction('f32')
void bar() {}
}
''');
configuration.forOrder();
checkElementText(library, r'''
library
imports
package:test/order.dart
augmentationImports
package:test/test.macro.dart
macroGeneratedCode
---
library augment 'test.dart';
void f11() {}
void f12() {}
void f1() {}
void f21() {}
void f22() {}
void f2() {}
void f31() {}
void f32() {}
void f3() {}
---
''');
}
test_declarations_class_superClass_mixins_interfaces_backward() async {
_newOrderMacrosFile();
var library = await buildLibrary(r'''
import 'order.dart';
@AddFunction('f41')
@AddFunction('f42')
class X4 {}
@AddFunction('f31')
@AddFunction('f32')
mixin X3 {}
@AddFunction('f21')
@AddFunction('f22')
class X2 {}
@AddFunction('f11')
@AddFunction('f12')
class X1 extends X2 with X3 implements X4 {}
''');
configuration.forOrder();
checkElementText(library, r'''
library
imports
package:test/order.dart
augmentationImports
package:test/test.macro.dart
macroGeneratedCode
---
library augment 'test.dart';
void f42() {}
void f41() {}
void f32() {}
void f31() {}
void f22() {}
void f21() {}
void f12() {}
void f11() {}
---
''');
}
test_declarations_class_superClass_mixins_interfaces_forward() async {
_newOrderMacrosFile();
var library = await buildLibrary(r'''
import 'order.dart';
@AddFunction('f11')
@AddFunction('f12')
class X1 extends X2 with X3 implements X4 {}
@AddFunction('f21')
@AddFunction('f22')
class X2 {}
@AddFunction('f31')
@AddFunction('f32')
mixin X3 {}
@AddFunction('f41')
@AddFunction('f42')
class X4 {}
''');
configuration.forOrder();
checkElementText(library, r'''
library
imports
package:test/order.dart
augmentationImports
package:test/test.macro.dart
macroGeneratedCode
---
library augment 'test.dart';
void f22() {}
void f21() {}
void f32() {}
void f31() {}
void f42() {}
void f41() {}
void f12() {}
void f11() {}
---
''');
}
test_declarations_libraryCycle_class_interfaces() async {
useEmptyByteStore();
_newOrderMacrosFile();
newFile('$testPackageLibPath/x2.dart', r'''
import 'test.dart';
import 'order.dart';
@AddHierarchyMethod('f211')
@AddHierarchyMethod('f212')
class X21 {}
@AddHierarchyMethod('f221')
@AddHierarchyMethod('f222')
class X22 {}
''');
final testLibrary = await buildLibrary(r'''
import 'order.dart';
import 'x2.dart';
@AddHierarchyMethod('f11')
@AddHierarchyMethod('f12')
class X1 implements X22 {}
''');
// When we process `X1`, we see macro generated methods of `X22`.
// This shows that we processed `X22` before `X1`.
configuration.forOrder();
checkElementText(testLibrary, r'''
library
imports
package:test/order.dart
package:test/x2.dart
augmentationImports
package:test/test.macro.dart
macroGeneratedCode
---
library augment 'test.dart';
augment class X1 {
void f222_f221_f12() {}
void f222_f221_f11() {}
}
---
''');
// There are no dependencies between `X21` and `X22`, so they are
// processed in the source order.
// We see `f212` before `f211`, this shows that we process annotations
// from right to left.
final x2Library = await testContextLibrary('package:test/x2.dart');
checkElementText(x2Library, r'''
library
imports
package:test/test.dart
package:test/order.dart
augmentationImports
package:test/x2.macro.dart
macroGeneratedCode
---
library augment 'x2.dart';
augment class X21 {
void f212() {}
void f211() {}
}
augment class X22 {
void f222() {}
void f221() {}
}
---
''');
}
test_declarations_mixin_interfaces_backward() async {
_newOrderMacrosFile();
var library = await buildLibrary(r'''
import 'order.dart';
@AddFunction('f31')
@AddFunction('f32')
class X3 {}
@AddFunction('f21')
@AddFunction('f22')
class X2 {}
@AddFunction('f11')
@AddFunction('f12')
mixin X1 implements X2, X3 {}
''');
configuration.forOrder();
checkElementText(library, r'''
library
imports
package:test/order.dart
augmentationImports
package:test/test.macro.dart
macroGeneratedCode
---
library augment 'test.dart';
void f32() {}
void f31() {}
void f22() {}
void f21() {}
void f12() {}
void f11() {}
---
''');
}
test_declarations_mixin_interfaces_forward() async {
_newOrderMacrosFile();
var library = await buildLibrary(r'''
import 'order.dart';
@AddFunction('f11')
@AddFunction('f12')
mixin X1 implements X2, X3 {}
@AddFunction('f21')
@AddFunction('f22')
class X2 {}
@AddFunction('f31')
@AddFunction('f32')
class X3 {}
''');
configuration.forOrder();
checkElementText(library, r'''
library
imports
package:test/order.dart
augmentationImports
package:test/test.macro.dart
macroGeneratedCode
---
library augment 'test.dart';
void f22() {}
void f21() {}
void f32() {}
void f31() {}
void f12() {}
void f11() {}
---
''');
}
test_declarations_mixin_superclassConstraints_backward() async {
_newOrderMacrosFile();
var library = await buildLibrary(r'''
import 'order.dart';
@AddFunction('f31')
@AddFunction('f32')
class X3 {}
@AddFunction('f21')
@AddFunction('f22')
class X2 {}
@AddFunction('f11')
@AddFunction('f12')
mixin X1 on X2, X3 {}
''');
configuration.forOrder();
checkElementText(library, r'''
library
imports
package:test/order.dart
augmentationImports
package:test/test.macro.dart
macroGeneratedCode
---
library augment 'test.dart';
void f32() {}
void f31() {}
void f22() {}
void f21() {}
void f12() {}
void f11() {}
---
''');
}
test_declarations_mixin_superclassConstraints_forward() async {
_newOrderMacrosFile();
var library = await buildLibrary(r'''
import 'order.dart';
@AddFunction('f11')
@AddFunction('f12')
mixin X1 on X2, X3 {}
@AddFunction('f21')
@AddFunction('f22')
class X2 {}
@AddFunction('f31')
@AddFunction('f32')
class X3 {}
''');
configuration.forOrder();
checkElementText(library, r'''
library
imports
package:test/order.dart
augmentationImports
package:test/test.macro.dart
macroGeneratedCode
---
library augment 'test.dart';
void f22() {}
void f21() {}
void f32() {}
void f31() {}
void f12() {}
void f11() {}
---
''');
}
test_declarations_mixin_superclassConstraints_interfaces_backward() async {
_newOrderMacrosFile();
var library = await buildLibrary(r'''
import 'order.dart';
@AddFunction('f31')
@AddFunction('f32')
class X3 {}
@AddFunction('f21')
@AddFunction('f22')
class X2 {}
@AddFunction('f11')
@AddFunction('f12')
mixin X1 on X2 implements X3 {}
''');
configuration.forOrder();
checkElementText(library, r'''
library
imports
package:test/order.dart
augmentationImports
package:test/test.macro.dart
macroGeneratedCode
---
library augment 'test.dart';
void f32() {}
void f31() {}
void f22() {}
void f21() {}
void f12() {}
void f11() {}
---
''');
}
test_declarations_mixin_superclassConstraints_interfaces_forward() async {
_newOrderMacrosFile();
var library = await buildLibrary(r'''
import 'order.dart';
@AddFunction('f11')
@AddFunction('f12')
mixin X1 on X2 implements X3 {}
@AddFunction('f21')
@AddFunction('f22')
class X2 {}
@AddFunction('f31')
@AddFunction('f32')
class X3 {}
''');
configuration.forOrder();
checkElementText(library, r'''
library
imports
package:test/order.dart
augmentationImports
package:test/test.macro.dart
macroGeneratedCode
---
library augment 'test.dart';
void f22() {}
void f21() {}
void f32() {}
void f31() {}
void f12() {}
void f11() {}
---
''');
}
test_phases_class_types_declarations() async {
_newOrderMacrosFile();
var library = await buildLibrary(r'''
import 'order.dart';
@AddClass('A1')
@AddFunction('f1')
class X {}
''');
configuration.forOrder();
checkElementText(library, r'''
library
imports
package:test/order.dart
augmentationImports
package:test/test.macro.dart
macroGeneratedCode
---
library augment 'test.dart';
class A1 {}
void f1() {}
---
''');
}
test_types_class_method_rightToLeft() async {
_newOrderMacrosFile();
var library = await buildLibrary(r'''
import 'order.dart';
class X {
@AddClass('A1')
@AddClass('A2')
void foo() {}
}
''');
configuration.forOrder();
checkElementText(library, r'''
library
imports
package:test/order.dart
augmentationImports
package:test/test.macro.dart
macroGeneratedCode
---
library augment 'test.dart';
class A2 {}
class A1 {}
---
''');
}
test_types_class_method_sourceOrder() async {
_newOrderMacrosFile();
var library = await buildLibrary(r'''
import 'order.dart';
class X {
@AddClass('A1')
void foo() {}
@AddClass('A2')
void bar() {}
}
''');
configuration.forOrder();
checkElementText(library, r'''
library
imports
package:test/order.dart
augmentationImports
package:test/test.macro.dart
macroGeneratedCode
---
library augment 'test.dart';
class A1 {}
class A2 {}
---
''');
}
test_types_class_rightToLeft() async {
_newOrderMacrosFile();
var library = await buildLibrary(r'''
import 'order.dart';
@AddClass('A1')
@AddClass('A2')
class X {}
''');
configuration.forOrder();
checkElementText(library, r'''
library
imports
package:test/order.dart
augmentationImports
package:test/test.macro.dart
macroGeneratedCode
---
library augment 'test.dart';
class A2 {}
class A1 {}
---
''');
}
test_types_class_sourceOrder() async {
_newOrderMacrosFile();
var library = await buildLibrary(r'''
import 'order.dart';
@AddClass('A1')
class X1 {}
@AddClass('A2')
class X2 {}
''');
configuration.forOrder();
checkElementText(library, r'''
library
imports
package:test/order.dart
augmentationImports
package:test/test.macro.dart
macroGeneratedCode
---
library augment 'test.dart';
class A1 {}
class A2 {}
---
''');
}
test_types_innerBeforeOuter_class_method() async {
_newOrderMacrosFile();
var library = await buildLibrary(r'''
import 'order.dart';
@AddClass('A1')
class X {
@AddClass('A2')
void foo() {}
}
''');
configuration.forOrder();
checkElementText(library, r'''
library
imports
package:test/order.dart
augmentationImports
package:test/test.macro.dart
macroGeneratedCode
---
library augment 'test.dart';
class A2 {}
class A1 {}
---
''');
}
test_types_innerBeforeOuter_mixin_method() async {
_newOrderMacrosFile();
var library = await buildLibrary(r'''
import 'order.dart';
@AddClass('A1')
mixin X {
@AddClass('A2')
void foo() {}
}
''');
configuration.forOrder();
checkElementText(library, r'''
library
imports
package:test/order.dart
augmentationImports
package:test/test.macro.dart
macroGeneratedCode
---
library augment 'test.dart';
class A2 {}
class A1 {}
---
''');
}
test_types_mixin_method_rightToLeft() async {
_newOrderMacrosFile();
var library = await buildLibrary(r'''
import 'order.dart';
mixin X {
@AddClass('A1')
@AddClass('A2')
void foo() {}
}
''');
configuration.forOrder();
checkElementText(library, r'''
library
imports
package:test/order.dart
augmentationImports
package:test/test.macro.dart
macroGeneratedCode
---
library augment 'test.dart';
class A2 {}
class A1 {}
---
''');
}
test_types_mixin_method_sourceOrder() async {
_newOrderMacrosFile();
var library = await buildLibrary(r'''
import 'order.dart';
mixin X {
@AddClass('A1')
void foo() {}
@AddClass('A2')
void bar() {}
}
''');
configuration.forOrder();
checkElementText(library, r'''
library
imports
package:test/order.dart
augmentationImports
package:test/test.macro.dart
macroGeneratedCode
---
library augment 'test.dart';
class A1 {}
class A2 {}
---
''');
}
test_types_mixin_rightToLeft() async {
_newOrderMacrosFile();
var library = await buildLibrary(r'''
import 'order.dart';
@AddClass('A1')
@AddClass('A2')
mixin X {}
''');
configuration.forOrder();
checkElementText(library, r'''
library
imports
package:test/order.dart
augmentationImports
package:test/test.macro.dart
macroGeneratedCode
---
library augment 'test.dart';
class A2 {}
class A1 {}
---
''');
}
test_types_mixin_sourceOrder() async {
_newOrderMacrosFile();
var library = await buildLibrary(r'''
import 'order.dart';
@AddClass('A1')
mixin X1 {}
@AddClass('A2')
mixin X2 {}
''');
configuration.forOrder();
checkElementText(library, r'''
library
imports
package:test/order.dart
augmentationImports
package:test/test.macro.dart
macroGeneratedCode
---
library augment 'test.dart';
class A1 {}
class A2 {}
---
''');
}
void _newOrderMacrosFile() {
newFile('$testPackageLibPath/order.dart', _orderCode);
}
}
@reflectiveTest
class MacroArgumentsTest extends MacroElementsBaseTest {
@override
bool get keepLinkingLibraries => true;
test_error() async {
await _assertTypesPhaseArgumentsText(
fields: {
'foo': 'Object',
'bar': 'Object',
},
constructorParametersCode: '(this.foo, this.bar)',
argumentsCode: '(0, const Object())',
expectedErrors: 'Argument(annotation: 0, argument: 1, '
'message: Not supported: InstanceCreationExpressionImpl)',
);
}
test_kind_optionalNamed() async {
await _assertTypesPhaseArgumentsText(
fields: {
'foo': 'int',
'bar': 'int',
},
constructorParametersCode: '({this.foo = -1, this.bar = -2})',
argumentsCode: '(foo: 1)',
expected: r'''
foo: 1
bar: -2
''',
);
}
test_kind_optionalPositional() async {
await _assertTypesPhaseArgumentsText(
fields: {
'foo': 'int',
'bar': 'int',
},
constructorParametersCode: '([this.foo = -1, this.bar = -2])',
argumentsCode: '(1)',
expected: r'''
foo: 1
bar: -2
''',
);
}
test_kind_requiredNamed() async {
await _assertTypesPhaseArgumentsText(
fields: {'foo': 'int'},
constructorParametersCode: '({required this.foo})',
argumentsCode: '(foo: 42)',
expected: r'''
foo: 42
''',
);
}
test_kind_requiredPositional() async {
await _assertTypesPhaseArgumentsText(
fields: {'foo': 'int'},
constructorParametersCode: '(this.foo)',
argumentsCode: '(42)',
expected: r'''
foo: 42
''',
);
}
test_type_bool() async {
await _assertTypesPhaseArgumentsText(
fields: {
'foo': 'bool',
'bar': 'bool',
},
constructorParametersCode: '(this.foo, this.bar)',
argumentsCode: '(true, false)',
expected: r'''
foo: true
bar: false
''',
);
}
test_type_double() async {
await _assertTypesPhaseArgumentsText(
fields: {'foo': 'double'},
constructorParametersCode: '(this.foo)',
argumentsCode: '(1.2)',
expected: r'''
foo: 1.2
''',
);
}
test_type_double_negative() async {
await _assertTypesPhaseArgumentsText(
fields: {'foo': 'double'},
constructorParametersCode: '(this.foo)',
argumentsCode: '(-1.2)',
expected: r'''
foo: -1.2
''',
);
}
test_type_int() async {
await _assertTypesPhaseArgumentsText(
fields: {'foo': 'int'},
constructorParametersCode: '(this.foo)',
argumentsCode: '(42)',
expected: r'''
foo: 42
''',
);
}
test_type_int_negative() async {
await _assertTypesPhaseArgumentsText(
fields: {'foo': 'int'},
constructorParametersCode: '(this.foo)',
argumentsCode: '(-42)',
expected: r'''
foo: -42
''',
);
}
test_type_list() async {
await _assertTypesPhaseArgumentsText(
fields: {
'foo': 'List<Object?>',
},
constructorParametersCode: '(this.foo)',
argumentsCode: '([1, 2, true, 3, 4.2])',
expected: r'''
foo: [1, 2, true, 3, 4.2]
''',
);
}
test_type_map() async {
await _assertTypesPhaseArgumentsText(
fields: {
'foo': 'Map<Object?, Object?>',
},
constructorParametersCode: '(this.foo)',
argumentsCode: '({1: true, "abc": 2.3})',
expected: r'''
foo: {1: true, abc: 2.3}
''',
);
}
test_type_null() async {
await _assertTypesPhaseArgumentsText(
fields: {'foo': 'Object?'},
constructorParametersCode: '(this.foo)',
argumentsCode: '(null)',
expected: r'''
foo: null
''',
);
}
test_type_set() async {
await _assertTypesPhaseArgumentsText(
fields: {
'foo': 'Set<Object?>',
},
constructorParametersCode: '(this.foo)',
argumentsCode: '({1, 2, 3})',
expected: r'''
foo: {1, 2, 3}
''',
);
}
test_type_string() async {
await _assertTypesPhaseArgumentsText(
fields: {'foo': 'String'},
constructorParametersCode: '(this.foo)',
argumentsCode: "('aaa')",
expected: r'''
foo: aaa
''',
);
}
test_type_string_adjacent() async {
await _assertTypesPhaseArgumentsText(
fields: {'foo': 'String'},
constructorParametersCode: '(this.foo)',
argumentsCode: "('aaa' 'bbb' 'ccc')",
expected: r'''
foo: aaabbbccc
''',
);
}
/// Build a macro with specified [fields], initialized in the constructor
/// with [constructorParametersCode], and apply this macro with
/// [argumentsCode] to an empty class.
///
/// The macro generates exactly one top-level constant `x`, with a textual
/// dump of the field values. So, we check that the analyzer built these
/// values, and the macro executor marshalled these values to the running
/// macro isolate.
Future<void> _assertTypesPhaseArgumentsText({
required Map<String, String> fields,
required String constructorParametersCode,
required String argumentsCode,
String? expected,
String? expectedErrors,
}) async {
final dumpCode = fields.keys.map((name) {
return "$name: \$$name\\\\n";
}).join('');
newFile('$testPackageLibPath/arguments_text.dart', '''
import 'dart:async';
import 'package:_fe_analyzer_shared/src/macros/api.dart';
macro class ArgumentsTextMacro implements ClassTypesMacro {
${fields.entries.map((e) => ' final ${e.value} ${e.key};').join('\n')}
const ArgumentsTextMacro${constructorParametersCode.trim()};
FutureOr<void> buildTypesForClass(clazz, builder) {
builder.declareType(
'x',
DeclarationCode.fromString(
"const x = '$dumpCode';",
),
);
}
}
''');
final library = await buildLibrary('''
import 'arguments_text.dart';
@ArgumentsTextMacro$argumentsCode
class A {}
''');
if (expectedErrors != null) {
expect(library.macroErrorsStr, expectedErrors);
return;
} else {
library.assertNoMacroErrors();
}
if (expected != null) {
final macroAugmentation = library.augmentations.first;
final macroUnit = macroAugmentation.definingCompilationUnit;
final x = macroUnit.topLevelVariables.single;
expect(x.name, 'x');
x as ConstTopLevelVariableElementImpl;
final actual = (x.constantInitializer as SimpleStringLiteral).value;
if (actual != expected) {
print(actual);
}
expect(actual, expected);
} else {
fail("Either 'expected' or 'expectedErrors' must be provided.");
}
}
}
@reflectiveTest
class MacroDeclarationsIntrospectTest extends MacroElementsBaseTest {
@override
bool get keepLinkingLibraries => true;
String get _appendMacrosCode {
var code = MacrosEnvironment.instance.packageAnalyzerFolder
.getChildAssumingFile('test/src/summary/macro/append.dart')
.readAsStringSync();
return code.replaceAll('/*macro*/', 'macro');
}
/// Return the code for `IntrospectDeclarationsPhaseMacro`.
String get _introspectDeclarationsCode {
final code = MacrosEnvironment.instance.packageAnalyzerFolder
.getChildAssumingFolder('test/src/summary/macro')
.getChildAssumingFile('introspect_declarations_phase.dart')
.readAsStringSync();
return code.replaceAll('/*macro*/', 'macro');
}
test_element_class_field_flag_hasExternal() async {
newFile('$testPackageLibPath/a.dart', r'''
class A {
external int foo;
}
''');
await _assertIntrospectText(r'''
import 'a.dart';
@IntrospectDeclarationsPhaseMacro(
withDetailsFor: {'A'},
)
class X extends A {}
''', r'''
class X
superclass: A
class A
superclass: Object
fields
foo
flags: hasExternal
type: int
''');
}
test_element_class_field_flag_hasFinal() async {
newFile('$testPackageLibPath/a.dart', r'''
class A {
final int foo = 0;
}
''');
await _assertIntrospectText(r'''
import 'a.dart';
@IntrospectDeclarationsPhaseMacro(
withDetailsFor: {'A'},
)
class X extends A {}
''', r'''
class X
superclass: A
class A
superclass: Object
fields
foo
flags: hasFinal
type: int
''');
}
test_element_class_field_flag_hasLate() async {
newFile('$testPackageLibPath/a.dart', r'''
class A {
late int foo;
}
''');
await _assertIntrospectText(r'''
import 'a.dart';
@IntrospectDeclarationsPhaseMacro(
withDetailsFor: {'A'},
)
class X extends A {}
''', r'''
class X
superclass: A
class A
superclass: Object
fields
foo
flags: hasLate
type: int
''');
}
test_element_class_field_flag_isStatic() async {
newFile('$testPackageLibPath/a.dart', r'''
class A {
static int foo = 0;
}
''');
await _assertIntrospectText(r'''
import 'a.dart';
@IntrospectDeclarationsPhaseMacro(
withDetailsFor: {'A'},
)
class X extends A {}
''', r'''
class X
superclass: A
class A
superclass: Object
fields
foo
flags: isStatic
type: int
''');
}
test_element_class_field_metadata_identifier() async {
newFile('$testPackageLibPath/a.dart', r'''
const a = 0;
class A {
@a
int? foo;
}
''');
await _assertIntrospectText(r'''
import 'a.dart';
@IntrospectDeclarationsPhaseMacro(
withDetailsFor: {'A'},
withMetadata: true,
)
class X extends A {}
''', r'''
class X
metadata
ConstructorMetadataAnnotation
type: IntrospectDeclarationsPhaseMacro
superclass: A
class A
superclass: Object
fields
foo
metadata
IdentifierMetadataAnnotation
identifier: a
type: int?
''');
}
test_element_class_field_metadata_identifier_imported() async {
newFile('$testPackageLibPath/a.dart', r'''
const a = 0;
''');
newFile('$testPackageLibPath/b.dart', r'''
import 'a.dart';
class A {
@a
int? foo;
}
''');
await _assertIntrospectText(r'''
import 'b.dart';
@IntrospectDeclarationsPhaseMacro(
withDetailsFor: {'A'},
withMetadata: true,
)
class X extends A {}
''', r'''
class X
metadata
ConstructorMetadataAnnotation
type: IntrospectDeclarationsPhaseMacro
superclass: A
class A
superclass: Object
fields
foo
metadata
IdentifierMetadataAnnotation
identifier: a
type: int?
''');
}
test_element_class_flags_hasAbstract() async {
newFile('$testPackageLibPath/a.dart', r'''
abstract class A {}
''');
await _assertIntrospectText(r'''
import 'a.dart';
@IntrospectDeclarationsPhaseMacro(
withDetailsFor: {'A'},
)
class X extends A {}
''', r'''
class X
superclass: A
class A
flags: hasAbstract
superclass: Object
''');
}
test_element_class_interfaces() async {
newFile('$testPackageLibPath/a.dart', r'''
class A {}
class B {}
class C implements A, B {}
''');
await _assertIntrospectText(r'''
import 'a.dart';
@IntrospectDeclarationsPhaseMacro(
withDetailsFor: {'C'},
)
class X extends C {}
''', r'''
class X
superclass: C
class C
superclass: Object
interfaces
A
B
''');
}
test_element_class_metadata_augmented() async {
newFile('$testPackageLibPath/a.dart', r'''
const a = 0;
const b = 1;
import augment 'b.dart';
@a
class A {}
''');
newFile('$testPackageLibPath/b.dart', r'''
library augment 'a.dart';
@b
augment class A {}
''');
await _assertIntrospectText(r'''
import 'a.dart';
@IntrospectDeclarationsPhaseMacro(
withDetailsFor: {'A'},
withMetadata: true,
)
class X extends A {}
''', r'''
class X
metadata
ConstructorMetadataAnnotation
type: IntrospectDeclarationsPhaseMacro
superclass: A
class A
metadata
IdentifierMetadataAnnotation
identifier: a
IdentifierMetadataAnnotation
identifier: b
superclass: Object
''');
}
test_element_class_metadata_constructor_named() async {
newFile('$testPackageLibPath/a.dart', r'''
class A {
final int f;
const A.named(this.f)
}
@A.named(42)
class B {}
''');
await _assertIntrospectText(r'''
import 'a.dart';
@IntrospectDeclarationsPhaseMacro(
withDetailsFor: {'B'},
withMetadata: true,
)
class X extends B {}
''', r'''
class X
metadata
ConstructorMetadataAnnotation
type: IntrospectDeclarationsPhaseMacro
superclass: B
class B
metadata
ConstructorMetadataAnnotation
type: A
constructorName: named
superclass: Object
''');
}
test_element_class_metadata_constructor_named_imported() async {
newFile('$testPackageLibPath/a.dart', r'''
class A {
final int f;
const A.named(this.f)
}
''');
newFile('$testPackageLibPath/b.dart', r'''
import 'a.dart';
@A.named(42)
class B {}
''');
await _assertIntrospectText(r'''
import 'b.dart';
@IntrospectDeclarationsPhaseMacro(
withDetailsFor: {'B'},
withMetadata: true,
)
class X extends B {}
''', r'''
class X
metadata
ConstructorMetadataAnnotation
type: IntrospectDeclarationsPhaseMacro
superclass: B
class B
metadata
ConstructorMetadataAnnotation
type: A
constructorName: named
superclass: Object
''');
}
test_element_class_metadata_constructor_named_imported_withPrefix() async {
newFile('$testPackageLibPath/a.dart', r'''
class A {
final int f;
const A.named(this.f)
}
''');
newFile('$testPackageLibPath/b.dart', r'''
import 'a.dart' as prefix;
@prefix.A.named(42)
class B {}
''');
await _assertIntrospectText(r'''
import 'b.dart';
@IntrospectDeclarationsPhaseMacro(
withDetailsFor: {'B'},
withMetadata: true,
)
class X extends B {}
''', r'''
class X
metadata
ConstructorMetadataAnnotation
type: IntrospectDeclarationsPhaseMacro
superclass: B
class B
metadata
ConstructorMetadataAnnotation
type: A
constructorName: named
superclass: Object
''');
}
test_element_class_metadata_constructor_unnamed() async {
newFile('$testPackageLibPath/a.dart', r'''
class A {
final int f;
const A(this.f)
}
@A(42)
class B {}
''');
await _assertIntrospectText(r'''
import 'a.dart';
@IntrospectDeclarationsPhaseMacro(
withDetailsFor: {'B'},
withMetadata: true,
)
class X extends B {}
''', r'''
class X
metadata
ConstructorMetadataAnnotation
type: IntrospectDeclarationsPhaseMacro
superclass: B
class B
metadata
ConstructorMetadataAnnotation
type: A
superclass: Object
''');
}
test_element_class_metadata_constructor_unnamed_imported() async {
newFile('$testPackageLibPath/a.dart', r'''
class A {
final int f;
const A(this.f)
}
''');
newFile('$testPackageLibPath/b.dart', r'''
import 'a.dart';
@A(42)
class B {}
''');
await _assertIntrospectText(r'''
import 'b.dart';
@IntrospectDeclarationsPhaseMacro(
withDetailsFor: {'B'},
withMetadata: true,
)
class X extends B {}
''', r'''
class X
metadata
ConstructorMetadataAnnotation
type: IntrospectDeclarationsPhaseMacro
superclass: B
class B
metadata
ConstructorMetadataAnnotation
type: A
superclass: Object
''');
}
test_element_class_metadata_constructor_unnamed_imported_withPrefix() async {
newFile('$testPackageLibPath/a.dart', r'''
class A {
final int f;
const A(this.f)
}
''');
newFile('$testPackageLibPath/b.dart', r'''
import 'a.dart' as prefix;
@prefix.A(42)
class B {}
''');
await _assertIntrospectText(r'''
import 'b.dart';
@IntrospectDeclarationsPhaseMacro(
withDetailsFor: {'B'},
withMetadata: true,
)
class X extends B {}
''', r'''
class X
metadata
ConstructorMetadataAnnotation
type: IntrospectDeclarationsPhaseMacro
superclass: B
class B
metadata
ConstructorMetadataAnnotation
type: A
superclass: Object
''');
}
test_element_class_metadata_identifier() async {
newFile('$testPackageLibPath/a.dart', r'''
const a = 0;
@a
class A {}
''');
await _assertIntrospectText(r'''
import 'a.dart';
@IntrospectDeclarationsPhaseMacro(
withDetailsFor: {'A'},
withMetadata: true,
)
class X extends A {}
''', r'''
class X
metadata
ConstructorMetadataAnnotation
type: IntrospectDeclarationsPhaseMacro
superclass: A
class A
metadata
IdentifierMetadataAnnotation
identifier: a
superclass: Object
''');
}
test_element_class_metadata_identifier_imported() async {
newFile('$testPackageLibPath/a.dart', r'''
const a = 0;
''');
newFile('$testPackageLibPath/b.dart', r'''
import 'a.dart';
@a
class A {}
''');
await _assertIntrospectText(r'''
import 'b.dart';
@IntrospectDeclarationsPhaseMacro(
withDetailsFor: {'A'},
withMetadata: true,
)
class X extends A {}
''', r'''
class X
metadata
ConstructorMetadataAnnotation
type: IntrospectDeclarationsPhaseMacro
superclass: A
class A
metadata
IdentifierMetadataAnnotation
identifier: a
superclass: Object
''');
}
test_element_class_metadata_identifier_imported_withPrefix() async {
newFile('$testPackageLibPath/a.dart', r'''
const a = 0;
''');
newFile('$testPackageLibPath/b.dart', r'''
import 'a.dart' as prefix;
@prefix.a
class A {}
''');
await _assertIntrospectText(r'''
import 'b.dart';
@IntrospectDeclarationsPhaseMacro(
withDetailsFor: {'A'},
withMetadata: true,
)
class X extends A {}
''', r'''
class X
metadata
ConstructorMetadataAnnotation
type: IntrospectDeclarationsPhaseMacro
superclass: A
class A
metadata
IdentifierMetadataAnnotation
identifier: a
superclass: Object
''');
}
test_element_class_mixins() async {
newFile('$testPackageLibPath/a.dart', r'''
mixin M1 {}
mixin M2 {}
class C with M1, M2 {}
''');
await _assertIntrospectText(r'''
import 'a.dart';
@IntrospectDeclarationsPhaseMacro(
withDetailsFor: {'C'},
)
class X extends C {}
''', r'''
class X
superclass: C
class C
superclass: Object
mixins
M1
M2
''');
}
test_element_class_superclass() async {
newFile('$testPackageLibPath/a.dart', r'''
class A<T> {}
class B<U> extends A<U> {}
''');
await _assertIntrospectText(r'''
import 'a.dart';
@IntrospectDeclarationsPhaseMacro(
withDetailsFor: {'A', 'B'},
)
class X extends B<int> {}
''', r'''
class X
superclass: B<int>
class B
superclass: A<U>
class A
superclass: Object
typeParameters
T
typeParameters
U
''');
}
test_element_class_typeParameters() async {
newFile('$testPackageLibPath/a.dart', r'''
class A<T, U extends List<T>> {}
''');
await _assertIntrospectText(r'''
import 'a.dart';
@IntrospectDeclarationsPhaseMacro(
withDetailsFor: {'A'},
)
class X extends A {}
''', r'''
class X
superclass: A
class A
superclass: Object
typeParameters
T
U
bound: List<T>
''');
}
test_element_mixin_field_metadata_identifier() async {
newFile('$testPackageLibPath/a.dart', r'''
const a = 0;
mixin A {
@a
int? foo;
}
''');
await _assertIntrospectText(r'''
import 'a.dart';
@IntrospectDeclarationsPhaseMacro(
withDetailsFor: {'A'},
withMetadata: true,
)
class X with A {}
''', r'''
class X
metadata
ConstructorMetadataAnnotation
type: IntrospectDeclarationsPhaseMacro
mixins
A
mixin A
superclassConstraints
Object
fields
foo
metadata
IdentifierMetadataAnnotation
identifier: a
type: int?
''');
}
test_element_mixin_field_metadata_identifier_imported() async {
newFile('$testPackageLibPath/a.dart', r'''
const a = 0;
''');
newFile('$testPackageLibPath/b.dart', r'''
import 'a.dart';
mixin A {
@a
int? foo;
}
''');
await _assertIntrospectText(r'''
import 'b.dart';
@IntrospectDeclarationsPhaseMacro(
withDetailsFor: {'A'},
withMetadata: true,
)
class X with A {}
''', r'''
class X
metadata
ConstructorMetadataAnnotation
type: IntrospectDeclarationsPhaseMacro
mixins
A
mixin A
superclassConstraints
Object
fields
foo
metadata
IdentifierMetadataAnnotation
identifier: a
type: int?
''');
}
test_element_mixin_metadata_augmented() async {
newFile('$testPackageLibPath/a.dart', r'''
const a = 0;
const b = 1;
import augment 'b.dart';
@a
mixin A {}
''');
newFile('$testPackageLibPath/b.dart', r'''
library augment 'a.dart';
@b
augment mixin A {}
''');
await _assertIntrospectText(r'''
import 'a.dart';
@IntrospectDeclarationsPhaseMacro(
withDetailsFor: {'A'},
withMetadata: true,
)
class X extends A {}
''', r'''
class X
metadata
ConstructorMetadataAnnotation
type: IntrospectDeclarationsPhaseMacro
superclass: A
mixin A
metadata
IdentifierMetadataAnnotation
identifier: a
IdentifierMetadataAnnotation
identifier: b
superclassConstraints
Object
''');
}
test_element_mixin_metadata_identifier() async {
newFile('$testPackageLibPath/a.dart', r'''
const a = 0;
@a
mixin A {}
''');
await _assertIntrospectText(r'''
import 'a.dart';
@IntrospectDeclarationsPhaseMacro(
withDetailsFor: {'A'},
withMetadata: true,
)
class X with A {}
''', r'''
class X
metadata
ConstructorMetadataAnnotation
type: IntrospectDeclarationsPhaseMacro
mixins
A
mixin A
metadata
IdentifierMetadataAnnotation
identifier: a
superclassConstraints
Object
''');
}
test_node_class_appendInterfaces() async {
_newAppendMacrosFile();
await _assertIntrospectText(r'''
import 'append.dart';
@IntrospectDeclarationsPhaseMacro()
@AppendInterfaceA()
class X {}
''', r'''
class X
interfaces
A
''');
}
test_node_class_appendMixins() async {
_newAppendMacrosFile();
await _assertIntrospectText(r'''
import 'append.dart';
@IntrospectDeclarationsPhaseMacro()
@AppendMixinA()
class X {}
''', r'''
class X
mixins
A
''');
}
test_node_class_field_flags_hasExternal() async {
await _assertIntrospectText(r'''
@IntrospectDeclarationsPhaseMacro(
withDetailsFor: {'X'},
)
class X {
external int a;
int b = 0;
}
''', r'''
class X
fields
a
flags: hasExternal
type: int
b
type: int
''');
}
test_node_class_field_flags_hasFinal() async {
await _assertIntrospectText(r'''
@IntrospectDeclarationsPhaseMacro(
withDetailsFor: {'X'},
)
class X {
final int a = 0;
int b = 0;
}
''', r'''
class X
fields
a
flags: hasFinal
type: int
b
type: int
''');
}
test_node_class_field_flags_hasLate() async {
await _assertIntrospectText(r'''
@IntrospectDeclarationsPhaseMacro(
withDetailsFor: {'X'},
)
class X {
late final int a;
final int b = 0;
}
''', r'''
class X
fields
a
flags: hasFinal hasLate
type: int
b
flags: hasFinal
type: int
''');
}
test_node_class_field_flags_isStatic() async {
await _assertIntrospectText(r'''
@IntrospectDeclarationsPhaseMacro(
withDetailsFor: {'X'},
)
class X {
static int a = 0;
int b = 0;
}
''', r'''
class X
fields
a
flags: isStatic
type: int
b
type: int
''');
}
test_node_class_field_type_explicit() async {
await _assertIntrospectText(r'''
@IntrospectDeclarationsPhaseMacro(
withDetailsFor: {'X'},
)
class X {
int a = 0;
List<String> b = [];
}
''', r'''
class X
fields
a
type: int
b
type: List<String>
''');
}
test_node_class_superclassOf() async {
await _assertIntrospectText(r'''
class A {}
@IntrospectDeclarationsPhaseMacro(
withDetailsFor: {'A'},
)
class X extends A {}
''', r'''
class X
superclass: A
class A
superclass: Object
''');
}
test_node_class_superclassOf_implicit() async {
await _assertIntrospectText(r'''
@IntrospectDeclarationsPhaseMacro()
class X {}
''', r'''
class X
''');
}
test_node_class_superclassOf_unresolved() async {
await _assertIntrospectText(r'''
@IntrospectDeclarationsPhaseMacro()
class X extends A {}
''', r'''
class X
superclass: A
noDeclaration
''');
}
test_node_mixin_appendInterfaces() async {
_newAppendMacrosFile();
await _assertIntrospectText(r'''
import 'append.dart';
@IntrospectDeclarationsPhaseMacro()
@AppendInterfaceA()
mixin X {}
''', r'''
mixin X
interfaces
A
''');
}
test_node_mixin_field_flags_hasFinal() async {
await _assertIntrospectText(r'''
@IntrospectDeclarationsPhaseMacro(
withDetailsFor: {'X'},
)
mixin X {
final int a = 0;
int b = 0;
}
''', r'''
mixin X
fields
a
flags: hasFinal
type: int
b
type: int
''');
}
/// Assert that the textual dump of the introspection information produced
/// by `IntrospectDeclarationsPhaseMacro` in [code], is the [expected].
Future<void> _assertIntrospectText(String code, String expected) async {
var actual = await _getIntrospectText(code);
if (actual != expected) {
print(actual);
NodeTextExpectationsCollector.add(actual);
}
expect(actual, expected);
}
/// The [code] is expected to have exactly one application of
/// `IntrospectDeclarationsPhaseMacro`. It may contain arbitrary code otherwise.
///
/// The macro generates a top-level constant `x`, with a string literal
/// initializer - the textual dump of the introspection.
Future<String> _getIntrospectText(String code) async {
newFile(
'$testPackageLibPath/introspect_shared.dart',
_introspectSharedCode,
);
newFile(
'$testPackageLibPath/introspect_declarations_phase.dart',
_introspectDeclarationsCode,
);
var library = await buildLibrary('''
import 'introspect_declarations_phase.dart';
$code
''');
library.assertNoMacroErrors();
return library.topLevelElements
.whereType<ConstTopLevelVariableElementImpl>()
.where((e) => e.name.startsWith('introspect_'))
.map((e) => (e.constantInitializer as SimpleStringLiteral).value)
.join('\n');
}
void _newAppendMacrosFile() {
newFile(
'$testPackageLibPath/append.dart',
_appendMacrosCode,
);
}
}
abstract class MacroDeclarationsTest extends MacroElementsBaseTest {
test_addClass_addMethod_addMethod() async {
newFile('$testPackageLibPath/a.dart', r'''
import 'dart:async';
import 'package:_fe_analyzer_shared/src/macros/api.dart';
macro class AddClassB implements ClassTypesMacro {
const AddClassB();
FutureOr<void> buildTypesForClass(clazz, builder) async {
final identifier = await builder.resolveIdentifier(
Uri.parse('package:test/a.dart'),
'AddMethodFoo',
);
builder.declareType(
'MyClass',
DeclarationCode.fromParts([
'@',
identifier,
'()\nclass B {}\n',
]),
);
}
}
macro class AddMethodFoo implements ClassDeclarationsMacro {
const AddMethodFoo();
buildDeclarationsForClass(clazz, builder) async {
final identifier = await builder.resolveIdentifier(
Uri.parse('package:test/a.dart'),
'AddMethodBar',
);
builder.declareInType(
DeclarationCode.fromParts([
' @',
identifier,
'()\n void foo() {}',
]),
);
}
}
macro class AddMethodBar implements MethodDeclarationsMacro {
const AddMethodBar();
buildDeclarationsForMethod(method, builder) async {
builder.declareInType(
DeclarationCode.fromString(' void bar() {}'),
);
}
}
''');
var library = await buildLibrary(r'''
import 'a.dart';
@AddClassB()
class A {}
''');
configuration
..withConstructors = false
..withReferences = true;
checkElementText(library, r'''
library
reference: self
imports
package:test/a.dart
definingUnit
reference: self
classes
class A @37
reference: self::@class::A
metadata
Annotation
atSign: @ @18
name: SimpleIdentifier
token: AddClassB @19
staticElement: package:test/a.dart::@class::AddClassB
staticType: null
arguments: ArgumentList
leftParenthesis: ( @28
rightParenthesis: ) @29
element: package:test/a.dart::@class::AddClassB::@constructor::new
augmentationImports
package:test/test.macro.dart
reference: self::@augmentation::package:test/test.macro.dart
macroGeneratedCode
---
library augment 'test.dart';
import 'package:test/a.dart' as prefix0;
@prefix0.AddMethodFoo()
class B {}
augment class B {
@prefix0.AddMethodBar()
void foo() {}
void bar() {}
}
---
imports
package:test/a.dart as prefix0 @62
definingUnit
reference: self::@augmentation::package:test/test.macro.dart
classes
class B @102
reference: self::@augmentation::package:test/test.macro.dart::@class::B
metadata
Annotation
atSign: @ @72
name: PrefixedIdentifier
prefix: SimpleIdentifier
token: prefix0 @73
staticElement: <null>
staticType: null
period: . @80
identifier: SimpleIdentifier
token: AddMethodFoo @81
staticElement: <null>
staticType: null
staticElement: <null>
staticType: null
arguments: ArgumentList
leftParenthesis: ( @93
rightParenthesis: ) @94
element: <null>
augmentation: self::@augmentation::package:test/test.macro.dart::@classAugmentation::B
augmented
constructors
self::@augmentation::package:test/test.macro.dart::@class::B::@constructor::new
methods
self::@augmentation::package:test/test.macro.dart::@classAugmentation::B::@method::bar
self::@augmentation::package:test/test.macro.dart::@classAugmentation::B::@method::foo
augment class B @122
reference: self::@augmentation::package:test/test.macro.dart::@classAugmentation::B
augmentationTarget: self::@augmentation::package:test/test.macro.dart::@class::B
methods
foo @159
reference: self::@augmentation::package:test/test.macro.dart::@classAugmentation::B::@method::foo
metadata
Annotation
atSign: @ @128
name: PrefixedIdentifier
prefix: SimpleIdentifier
token: prefix0 @129
staticElement: self::@augmentation::package:test/test.macro.dart::@prefix::prefix0
staticType: null
period: . @136
identifier: SimpleIdentifier
token: AddMethodBar @137
staticElement: package:test/a.dart::@class::AddMethodBar
staticType: null
staticElement: package:test/a.dart::@class::AddMethodBar
staticType: null
arguments: ArgumentList
leftParenthesis: ( @149
rightParenthesis: ) @150
element: package:test/a.dart::@class::AddMethodBar::@constructor::new
returnType: void
bar @175
reference: self::@augmentation::package:test/test.macro.dart::@classAugmentation::B::@method::bar
returnType: void
''');
}
/// TODO(scheglov) Not quite correct - we should not add a synthetic one.
test_class_constructor_add() async {
newFile('$testPackageLibPath/a.dart', r'''
import 'package:_fe_analyzer_shared/src/macros/api.dart';
macro class MyMacro implements ClassDeclarationsMacro {
const MyMacro();
buildDeclarationsForClass(clazz, builder) async {
builder.declareInType(
DeclarationCode.fromString(' A.named(int a);'),
);
}
}
''');
var library = await buildLibrary(r'''
import 'a.dart';
@MyMacro()
class A {}
''');
configuration
..withMetadata = false
..withReferences = true;
checkElementText(library, r'''
library
reference: self
imports
package:test/a.dart
definingUnit
reference: self
classes
class A @35
reference: self::@class::A
augmentation: self::@augmentation::package:test/test.macro.dart::@classAugmentation::A
constructors
synthetic @-1
reference: self::@class::A::@constructor::new
augmented
constructors
self::@class::A::@constructor::new
self::@augmentation::package:test/test.macro.dart::@classAugmentation::A::@constructor::named
augmentationImports
package:test/test.macro.dart
reference: self::@augmentation::package:test/test.macro.dart
macroGeneratedCode
---
library augment 'test.dart';
augment class A {
A.named(int a);
}
---
definingUnit
reference: self::@augmentation::package:test/test.macro.dart
classes
augment class A @44
reference: self::@augmentation::package:test/test.macro.dart::@classAugmentation::A
augmentationTarget: self::@class::A
constructors
named @52
reference: self::@augmentation::package:test/test.macro.dart::@classAugmentation::A::@constructor::named
periodOffset: 51
nameEnd: 57
parameters
requiredPositional a @62
type: int
''');
}
test_class_field_add() async {
newFile('$testPackageLibPath/a.dart', r'''
import 'package:_fe_analyzer_shared/src/macros/api.dart';
macro class MyMacro implements ClassDeclarationsMacro {
const MyMacro();
buildDeclarationsForClass(clazz, builder) async {
builder.declareInType(
DeclarationCode.fromString(' int foo = 0;'),
);
}
}
''');
var library = await buildLibrary(r'''
import 'a.dart';
@MyMacro()
class A {}
''');
configuration
..withConstructors = false
..withMetadata = false
..withReferences = true;
checkElementText(library, r'''
library
reference: self
imports
package:test/a.dart
definingUnit
reference: self
classes
class A @35
reference: self::@class::A
augmentation: self::@augmentation::package:test/test.macro.dart::@classAugmentation::A
augmented
fields
self::@augmentation::package:test/test.macro.dart::@classAugmentation::A::@field::foo
constructors
self::@class::A::@constructor::new
accessors
self::@augmentation::package:test/test.macro.dart::@classAugmentation::A::@getter::foo
self::@augmentation::package:test/test.macro.dart::@classAugmentation::A::@setter::foo
augmentationImports
package:test/test.macro.dart
reference: self::@augmentation::package:test/test.macro.dart
macroGeneratedCode
---
library augment 'test.dart';
augment class A {
int foo = 0;
}
---
definingUnit
reference: self::@augmentation::package:test/test.macro.dart
classes
augment class A @44
reference: self::@augmentation::package:test/test.macro.dart::@classAugmentation::A
augmentationTarget: self::@class::A
fields
foo @54
reference: self::@augmentation::package:test/test.macro.dart::@classAugmentation::A::@field::foo
type: int
shouldUseTypeForInitializerInference: true
accessors
synthetic get foo @-1
reference: self::@augmentation::package:test/test.macro.dart::@classAugmentation::A::@getter::foo
returnType: int
synthetic set foo= @-1
reference: self::@augmentation::package:test/test.macro.dart::@classAugmentation::A::@setter::foo
parameters
requiredPositional _foo @-1
type: int
returnType: void
''');
}
test_class_getter_add() async {
newFile('$testPackageLibPath/a.dart', r'''
import 'package:_fe_analyzer_shared/src/macros/api.dart';
macro class MyMacro implements ClassDeclarationsMacro {
const MyMacro();
buildDeclarationsForClass(clazz, builder) async {
builder.declareInType(
DeclarationCode.fromString(' int get foo => 0;'),
);
}
}
''');
var library = await buildLibrary(r'''
import 'a.dart';
@MyMacro()
class A {}
''');
configuration
..withConstructors = false
..withMetadata = false
..withReferences = true;
checkElementText(library, r'''
library
reference: self
imports
package:test/a.dart
definingUnit
reference: self
classes
class A @35
reference: self::@class::A
augmentation: self::@augmentation::package:test/test.macro.dart::@classAugmentation::A
augmented
fields
self::@augmentation::package:test/test.macro.dart::@classAugmentation::A::@field::foo
constructors
self::@class::A::@constructor::new
accessors
self::@augmentation::package:test/test.macro.dart::@classAugmentation::A::@getter::foo
augmentationImports
package:test/test.macro.dart
reference: self::@augmentation::package:test/test.macro.dart
macroGeneratedCode
---
library augment 'test.dart';
augment class A {
int get foo => 0;
}
---
definingUnit
reference: self::@augmentation::package:test/test.macro.dart
classes
augment class A @44
reference: self::@augmentation::package:test/test.macro.dart::@classAugmentation::A
augmentationTarget: self::@class::A
fields
synthetic foo @-1
reference: self::@augmentation::package:test/test.macro.dart::@classAugmentation::A::@field::foo
type: int
accessors
get foo @58
reference: self::@augmentation::package:test/test.macro.dart::@classAugmentation::A::@getter::foo
returnType: int
''');
}
test_class_method_add() async {
newFile('$testPackageLibPath/a.dart', r'''
import 'package:_fe_analyzer_shared/src/macros/api.dart';
macro class MyMacro implements ClassDeclarationsMacro {
const MyMacro();
buildDeclarationsForClass(clazz, builder) async {
builder.declareInType(
DeclarationCode.fromString(' int foo(double a) => 0;'),
);
}
}
''');
var library = await buildLibrary(r'''
import 'a.dart';
@MyMacro()
class A {}
''');
configuration
..withConstructors = false
..withMetadata = false
..withReferences = true;
checkElementText(library, r'''
library
reference: self
imports
package:test/a.dart
definingUnit
reference: self
classes
class A @35
reference: self::@class::A
augmentation: self::@augmentation::package:test/test.macro.dart::@classAugmentation::A
augmented
constructors
self::@class::A::@constructor::new
methods
self::@augmentation::package:test/test.macro.dart::@classAugmentation::A::@method::foo
augmentationImports
package:test/test.macro.dart
reference: self::@augmentation::package:test/test.macro.dart
macroGeneratedCode
---
library augment 'test.dart';
augment class A {
int foo(double a) => 0;
}
---
definingUnit
reference: self::@augmentation::package:test/test.macro.dart
classes
augment class A @44
reference: self::@augmentation::package:test/test.macro.dart::@classAugmentation::A
augmentationTarget: self::@class::A
methods
foo @54
reference: self::@augmentation::package:test/test.macro.dart::@classAugmentation::A::@method::foo
parameters
requiredPositional a @65
type: double
returnType: int
''');
}
test_class_setter_add() async {
newFile('$testPackageLibPath/a.dart', r'''
import 'package:_fe_analyzer_shared/src/macros/api.dart';
macro class MyMacro implements ClassDeclarationsMacro {
const MyMacro();
buildDeclarationsForClass(clazz, builder) async {
builder.declareInType(
DeclarationCode.fromString(' set foo(int a) {}'),
);
}
}
''');
var library = await buildLibrary(r'''
import 'a.dart';
@MyMacro()
class A {}
''');
configuration
..withConstructors = false
..withMetadata = false
..withReferences = true;
checkElementText(library, r'''
library
reference: self
imports
package:test/a.dart
definingUnit
reference: self
classes
class A @35
reference: self::@class::A
augmentation: self::@augmentation::package:test/test.macro.dart::@classAugmentation::A
augmented
fields
self::@augmentation::package:test/test.macro.dart::@classAugmentation::A::@field::foo
constructors
self::@class::A::@constructor::new
accessors
self::@augmentation::package:test/test.macro.dart::@classAugmentation::A::@setter::foo
augmentationImports
package:test/test.macro.dart
reference: self::@augmentation::package:test/test.macro.dart
macroGeneratedCode
---
library augment 'test.dart';
augment class A {
set foo(int a) {}
}
---
definingUnit
reference: self::@augmentation::package:test/test.macro.dart
classes
augment class A @44
reference: self::@augmentation::package:test/test.macro.dart::@classAugmentation::A
augmentationTarget: self::@class::A
fields
synthetic foo @-1
reference: self::@augmentation::package:test/test.macro.dart::@classAugmentation::A::@field::foo
type: int
accessors
set foo= @54
reference: self::@augmentation::package:test/test.macro.dart::@classAugmentation::A::@setter::foo
parameters
requiredPositional a @62
type: int
returnType: void
''');
}
test_unit_variable_add() async {
newFile('$testPackageLibPath/a.dart', r'''
import 'package:_fe_analyzer_shared/src/macros/api.dart';
macro class MyMacro implements ClassDeclarationsMacro {
const MyMacro();
buildDeclarationsForClass(clazz, builder) async {
builder.declareInLibrary(
DeclarationCode.fromString('final x = 42;'),
);
}
}
''');
var library = await buildLibrary(r'''
import 'a.dart';
@MyMacro()
class A {}
''');
configuration
..withConstructors = false
..withMetadata = false
..withPropertyLinking = true
..withReferences = true;
checkElementText(library, r'''
library
reference: self
imports
package:test/a.dart
definingUnit
reference: self
classes
class A @35
reference: self::@class::A
augmentationImports
package:test/test.macro.dart
reference: self::@augmentation::package:test/test.macro.dart
macroGeneratedCode
---
library augment 'test.dart';
final x = 42;
---
definingUnit
reference: self::@augmentation::package:test/test.macro.dart
topLevelVariables
static final x @36
reference: self::@augmentation::package:test/test.macro.dart::@topLevelVariable::x
type: int
shouldUseTypeForInitializerInference: false
id: variable_0
getter: getter_0
accessors
synthetic static get x @-1
reference: self::@augmentation::package:test/test.macro.dart::@accessor::x
returnType: int
id: getter_0
variable: variable_0
''');
}
}
@reflectiveTest
class MacroDeclarationsTest_fromBytes extends MacroDeclarationsTest {
@override
bool get keepLinkingLibraries => false;
}
@reflectiveTest
class MacroDeclarationsTest_keepLinking extends MacroDeclarationsTest {
@override
bool get keepLinkingLibraries => true;
}
abstract class MacroElementsBaseTest extends ElementsBaseTest {
String get _introspectSharedCode {
return MacrosEnvironment.instance.packageAnalyzerFolder
.getChildAssumingFile('test/src/summary/macro/introspect_shared.dart')
.readAsStringSync();
}
@override
Future<void> setUp() async {
super.setUp();
writeTestPackageConfig(
PackageConfigFileBuilder(),
macrosEnvironment: MacrosEnvironment.instance,
);
}
}
abstract class MacroElementsTest extends MacroElementsBaseTest {
@FailingTest(reason: 'Fails because exceptions are reported as diagnostics')
test_macroApplicationErrors_declarationsPhase_throwsException() async {
newFile('$testPackageLibPath/a.dart', r'''
import 'package:_fe_analyzer_shared/src/macros/api.dart';
macro class MyMacro implements ClassDeclarationsMacro {
const MyMacro();
buildDeclarationsForClass(clazz, builder) async {
throw 'foo bar';
}
}
''');
final library = await buildLibrary(r'''
import 'a.dart';
@MyMacro()
class A {}
''');
final A = library.getClass('A') as ClassElementImpl;
final error = A.macroApplicationErrors.single;
error as UnknownMacroApplicationError;
expect(error.annotationIndex, 0);
expect(error.message, 'foo bar');
expect(error.stackTrace, contains('MyMacro.buildDeclarationsForClass'));
}
@FailingTest(reason: 'Fails because exceptions are reported as diagnostics')
test_macroApplicationErrors_typesPhase_compileTimeError() async {
newFile('$testPackageLibPath/a.dart', r'''
import 'package:_fe_analyzer_shared/src/macros/api.dart';
macro class MyMacro implements ClassTypesMacro {
const MyMacro();
buildTypesForClass(clazz, builder) {
unresolved;
}
}
''');
final library = await buildLibrary(r'''
import 'a.dart';
@MyMacro()
class A {}
''');
final A = library.getClass('A') as ClassElementImpl;
final error = A.macroApplicationErrors.single;
error as UnknownMacroApplicationError;
expect(error.annotationIndex, 0);
expect(error.message, contains('unresolved'));
expect(error.stackTrace, contains('executeTypesMacro'));
}
@FailingTest(reason: 'Fails because exceptions are reported as diagnostics')
test_macroApplicationErrors_typesPhase_throwsException() async {
newFile('$testPackageLibPath/a.dart', r'''
import 'package:_fe_analyzer_shared/src/macros/api.dart';
macro class MyMacro implements ClassTypesMacro {
const MyMacro();
buildTypesForClass(clazz, builder) {
throw 'foo bar';
}
}
''');
final library = await buildLibrary(r'''
import 'a.dart';
@MyMacro()
class A {}
''');
final A = library.getClass('A') as ClassElementImpl;
final error = A.macroApplicationErrors.single;
error as UnknownMacroApplicationError;
expect(error.annotationIndex, 0);
expect(error.message, 'foo bar');
expect(error.stackTrace, contains('MyMacro.buildTypesForClass'));
}
test_macroFlag_class() async {
var library = await buildLibrary(r'''
macro class A {}
''');
checkElementText(library, r'''
library
definingUnit
classes
macro class A @12
constructors
synthetic @-1
''');
}
test_macroFlag_classAlias() async {
var library = await buildLibrary(r'''
mixin M {}
macro class A = Object with M;
''');
checkElementText(library, r'''
library
definingUnit
classes
macro class alias A @23
supertype: Object
mixins
M
constructors
synthetic const @-1
constantInitializers
SuperConstructorInvocation
superKeyword: super @0
argumentList: ArgumentList
leftParenthesis: ( @0
rightParenthesis: ) @0
staticElement: dart:core::@class::Object::@constructor::new
mixins
mixin M @6
superclassConstraints
Object
''');
}
}
@reflectiveTest
class MacroElementsTest_fromBytes extends MacroElementsTest {
@override
bool get keepLinkingLibraries => false;
}
@reflectiveTest
class MacroElementsTest_keepLinking extends MacroElementsTest {
@override
bool get keepLinkingLibraries => true;
}
@reflectiveTest
class MacroTypesIntrospectTest extends MacroElementsBaseTest {
@override
bool get keepLinkingLibraries => true;
/// Return the code for `IntrospectTypesPhaseMacro`.
String get _introspectTypesCode {
final code = MacrosEnvironment.instance.packageAnalyzerFolder
.getChildAssumingFolder('test/src/summary/macro')
.getChildAssumingFile('introspect_types_phase.dart')
.readAsStringSync();
return code.replaceAll('/*macro*/', 'macro');
}
test_class_flags_hasAbstract() async {
await _assertIntrospectText(r'''
@IntrospectTypesPhaseMacro()
abstract class A {}
''', r'''
class A
flags: hasAbstract
''');
}
test_class_getter() async {
await _assertIntrospectText(r'''
abstract class A {
@IntrospectTypesPhaseMacro()
int get foo => 0;
}
''', r'''
foo
flags: hasBody isGetter
returnType: int
''');
}
test_class_interfaces() async {
await _assertIntrospectText(r'''
@IntrospectTypesPhaseMacro()
class A implements B, C<int, String> {}
''', r'''
class A
interfaces
B
C<int, String>
''');
}
test_class_metadata_constructor_named() async {
await _assertIntrospectText(r'''
@IntrospectTypesPhaseMacro(withMetadata: true)
@A.named(42)
class X {}
class A {
final int f;
const A.named(this.f)
}
''', r'''
class X
metadata
ConstructorMetadataAnnotation
type: IntrospectTypesPhaseMacro
ConstructorMetadataAnnotation
type: A
constructorName: named
''');
}
test_class_metadata_constructor_named_imported() async {
newFile('$testPackageLibPath/a.dart', r'''
class A {
final int f;
const A.named(this.f)
}
''');
await _assertIntrospectText(r'''
import 'a.dart';
@IntrospectTypesPhaseMacro(withMetadata: true)
@A.named(42)
class X {}
''', r'''
class X
metadata
ConstructorMetadataAnnotation
type: IntrospectTypesPhaseMacro
ConstructorMetadataAnnotation
type: A
constructorName: named
''');
}
test_class_metadata_constructor_named_imported_withPrefix() async {
newFile('$testPackageLibPath/a.dart', r'''
class A {
final int f;
const A.named(this.f)
}
''');
await _assertIntrospectText(r'''
import 'a.dart' as prefix;
@IntrospectTypesPhaseMacro(withMetadata: true)
@prefix.A.named(42)
class X {}
''', r'''
class X
metadata
ConstructorMetadataAnnotation
type: IntrospectTypesPhaseMacro
ConstructorMetadataAnnotation
type: A
constructorName: named
''');
}
test_class_metadata_constructor_unnamed() async {
await _assertIntrospectText(r'''
@IntrospectTypesPhaseMacro(withMetadata: true)
@A(42)
class X {}
class A {
final int f;
const A(this.f)
}
''', r'''
class X
metadata
ConstructorMetadataAnnotation
type: IntrospectTypesPhaseMacro
ConstructorMetadataAnnotation
type: A
''');
}
test_class_metadata_constructor_unnamed_imported() async {
newFile('$testPackageLibPath/a.dart', r'''
class A {
final int f;
const A(this.f)
}
''');
await _assertIntrospectText(r'''
import 'a.dart';
@IntrospectTypesPhaseMacro(withMetadata: true)
@A(42)
class X {}
''', r'''
class X
metadata
ConstructorMetadataAnnotation
type: IntrospectTypesPhaseMacro
ConstructorMetadataAnnotation
type: A
''');
}
test_class_metadata_constructor_unnamed_imported_withPrefix() async {
newFile('$testPackageLibPath/a.dart', r'''
class A {
final int f;
const A(this.f)
}
''');
await _assertIntrospectText(r'''
import 'a.dart' as prefix;
@IntrospectTypesPhaseMacro(withMetadata: true)
@prefix.A(42)
class X {}
''', r'''
class X
metadata
ConstructorMetadataAnnotation
type: IntrospectTypesPhaseMacro
ConstructorMetadataAnnotation
type: A
''');
}
test_class_metadata_identifier() async {
await _assertIntrospectText(r'''
@IntrospectTypesPhaseMacro(withMetadata: true)
@a1
@a2
class X {}
const a1 = 0;
const a2 = 0;
''', r'''
class X
metadata
ConstructorMetadataAnnotation
type: IntrospectTypesPhaseMacro
IdentifierMetadataAnnotation
identifier: a1
IdentifierMetadataAnnotation
identifier: a2
''');
}
test_class_metadata_identifier_imported() async {
newFile('$testPackageLibPath/a.dart', r'''
const a1 = 0;
const a2 = 0;
''');
await _assertIntrospectText(r'''
import 'a.dart';
@IntrospectTypesPhaseMacro(withMetadata: true)
@a1
@a2
class X {}
''', r'''
class X
metadata
ConstructorMetadataAnnotation
type: IntrospectTypesPhaseMacro
IdentifierMetadataAnnotation
identifier: a1
IdentifierMetadataAnnotation
identifier: a2
''');
}
test_class_metadata_identifier_imported_withPrefix() async {
newFile('$testPackageLibPath/a.dart', r'''
const a1 = 0;
const a2 = 0;
''');
await _assertIntrospectText(r'''
import 'a.dart' as prefix;
@IntrospectTypesPhaseMacro(withMetadata: true)
@prefix.a1
@prefix.a2
class X {}
''', r'''
class X
metadata
ConstructorMetadataAnnotation
type: IntrospectTypesPhaseMacro
IdentifierMetadataAnnotation
identifier: a1
IdentifierMetadataAnnotation
identifier: a2
''');
}
test_class_method_flags_hasBody_false() async {
await _assertIntrospectText(r'''
abstract class A {
@IntrospectTypesPhaseMacro()
void foo();
}
''', r'''
foo
returnType: void
''');
}
test_class_method_flags_hasExternal() async {
await _assertIntrospectText(r'''
abstract class A {
@IntrospectTypesPhaseMacro()
external void foo();
}
''', r'''
foo
flags: hasExternal
returnType: void
''');
}
test_class_method_flags_isStatic() async {
await _assertIntrospectText(r'''
class A {
@IntrospectTypesPhaseMacro()
static void foo() {}
}
''', r'''
foo
flags: hasBody isStatic
returnType: void
''');
}
test_class_method_metadata() async {
newFile('$testPackageLibPath/a.dart', r'''
const a = 0;
''');
await _assertIntrospectText(r'''
import 'a.dart';
class X {
@IntrospectTypesPhaseMacro(withMetadata: true)
@a
void foo() {}
}
''', r'''
foo
flags: hasBody
metadata
ConstructorMetadataAnnotation
type: IntrospectTypesPhaseMacro
IdentifierMetadataAnnotation
identifier: a
returnType: void
''');
}
test_class_method_namedParameters() async {
await _assertIntrospectText(r'''
abstract class A {
@IntrospectTypesPhaseMacro()
void foo({required int a, String? b}) {}
}
''', r'''
foo
flags: hasBody
namedParameters
a
flags: isNamed isRequired
type: int
b
flags: isNamed
type: String?
returnType: void
''');
}
test_class_method_namedParameters_metadata() async {
newFile('$testPackageLibPath/a.dart', r'''
const a = 0;
''');
await _assertIntrospectText(r'''
import 'a.dart';
abstract class A {
@IntrospectTypesPhaseMacro(withMetadata: true)
void foo({@a required int x}) {}
}
''', r'''
foo
flags: hasBody
metadata
ConstructorMetadataAnnotation
type: IntrospectTypesPhaseMacro
namedParameters
x
flags: isNamed isRequired
metadata
IdentifierMetadataAnnotation
identifier: a
type: int
returnType: void
''');
}
test_class_method_positionalParameters() async {
await _assertIntrospectText(r'''
abstract class A {
@IntrospectTypesPhaseMacro()
void foo(int a, [String? b]) {}
}
''', r'''
foo
flags: hasBody
positionalParameters
a
flags: isRequired
type: int
b
type: String?
returnType: void
''');
}
test_class_method_positionalParameters_metadata() async {
newFile('$testPackageLibPath/a.dart', r'''
const a = 0;
''');
await _assertIntrospectText(r'''
import 'a.dart';
abstract class A {
@IntrospectTypesPhaseMacro(withMetadata: true)
void foo(@a int x) {}
}
''', r'''
foo
flags: hasBody
metadata
ConstructorMetadataAnnotation
type: IntrospectTypesPhaseMacro
positionalParameters
x
flags: isRequired
metadata
IdentifierMetadataAnnotation
identifier: a
type: int
returnType: void
''');
}
test_class_mixin_method() async {
await _assertIntrospectText(r'''
mixin A {
@IntrospectTypesPhaseMacro()
void foo() {}
}
''', r'''
foo
flags: hasBody
returnType: void
''');
}
test_class_mixins() async {
await _assertIntrospectText(r'''
@IntrospectTypesPhaseMacro()
class A with B, C<int, String> {}
''', r'''
class A
mixins
B
C<int, String>
''');
}
test_class_setter() async {
await _assertIntrospectText(r'''
abstract class A {
@IntrospectTypesPhaseMacro()
set foo(int value) {}
}
''', r'''
foo
flags: hasBody isSetter
positionalParameters
value
flags: isRequired
type: int
returnType: OmittedType
''');
}
test_class_superclass() async {
await _assertIntrospectText(r'''
@IntrospectTypesPhaseMacro()
class A extends B {}
''', r'''
class A
superclass: B
''');
}
test_class_superclass_nullable() async {
await _assertIntrospectText(r'''
@IntrospectTypesPhaseMacro()
class A extends B<int?> {}
''', r'''
class A
superclass: B<int?>
''');
}
test_class_superclass_typeArguments() async {
await _assertIntrospectText(r'''
@IntrospectTypesPhaseMacro()
class A extends B<String, List<int>> {}
''', r'''
class A
superclass: B<String, List<int>>
''');
}
test_class_typeParameter_metadata_identifier_imported() async {
newFile('$testPackageLibPath/a.dart', r'''
const a = 0;
''');
await _assertIntrospectText(r'''
import 'a.dart';
@IntrospectTypesPhaseMacro(withMetadata: true)
class A<@a T> {}
''', r'''
class A
metadata
ConstructorMetadataAnnotation
type: IntrospectTypesPhaseMacro
typeParameters
T
metadata
IdentifierMetadataAnnotation
identifier: a
''');
}
test_class_typeParameters() async {
await _assertIntrospectText(r'''
@IntrospectTypesPhaseMacro()
class A<T, U extends List<T>> {}
''', r'''
class A
typeParameters
T
U
bound: List<T>
''');
}
test_functionTypeAnnotation_formalParameters_namedOptional_simpleFormalParameter() async {
await _assertIntrospectText(r'''
@IntrospectTypesPhaseMacro()
class A extends B<void Function(int a, {int? b, int? c})> {}
''', r'''
class A
superclass: B<void Function(int a, {int? b}, {int? c})>
''');
}
test_functionTypeAnnotation_formalParameters_namedRequired_simpleFormalParameter() async {
await _assertIntrospectText(r'''
@IntrospectTypesPhaseMacro()
class A extends B<void Function(int a, {required int b, required int c})> {}
''', r'''
class A
superclass: B<void Function(int a, {required int b}, {required int c})>
''');
}
test_functionTypeAnnotation_formalParameters_positionalOptional_simpleFormalParameter() async {
await _assertIntrospectText(r'''
@IntrospectTypesPhaseMacro()
class A extends B<void Function(int a, [int b, int c])> {}
''', r'''
class A
superclass: B<void Function(int a, [int b], [int c])>
''');
}
/// TODO(scheglov) Tests for unnamed positional formal parameters.
test_functionTypeAnnotation_formalParameters_positionalRequired_simpleFormalParameter() async {
await _assertIntrospectText(r'''
@IntrospectTypesPhaseMacro()
class A extends B<void Function(int a, double b)> {}
''', r'''
class A
superclass: B<void Function(int a, double b)>
''');
}
test_functionTypeAnnotation_nullable() async {
await _assertIntrospectText(r'''
@IntrospectTypesPhaseMacro()
class A extends B<void Function()?> {}
''', r'''
class A
superclass: B<void Function()?>
''');
}
test_functionTypeAnnotation_returnType() async {
await _assertIntrospectText(r'''
@IntrospectTypesPhaseMacro()
class A extends B<void Function()> {}
''', r'''
class A
superclass: B<void Function()>
''');
}
test_functionTypeAnnotation_returnType_omitted() async {
await _assertIntrospectText(r'''
@IntrospectTypesPhaseMacro()
class A extends B<Function()> {}
''', r'''
class A
superclass: B<OmittedType Function()>
''');
}
test_functionTypeAnnotation_typeParameters() async {
await _assertIntrospectText(r'''
@IntrospectTypesPhaseMacro()
class A extends B<void Function<T, U extends num>()> {}
''', r'''
class A
superclass: B<void Function<T, U extends num>()>
''');
}
test_mixin_flags_hasBase() async {
await _assertIntrospectText(r'''
@IntrospectTypesPhaseMacro()
base mixin A {}
''', r'''
mixin A
flags: hasBase
''');
}
test_mixin_getter() async {
await _assertIntrospectText(r'''
mixin A {
@IntrospectTypesPhaseMacro()
int get foo => 0;
}
''', r'''
foo
flags: hasBody isGetter
returnType: int
''');
}
test_mixin_interfaces() async {
await _assertIntrospectText(r'''
@IntrospectTypesPhaseMacro()
mixin A implements B, C {}
''', r'''
mixin A
interfaces
B
C
''');
}
test_mixin_metadata_identifier_imported() async {
newFile('$testPackageLibPath/a.dart', r'''
const a = 0;
''');
await _assertIntrospectText(r'''
import 'a.dart';
@IntrospectTypesPhaseMacro(withMetadata: true)
@a
mixin X {}
''', r'''
mixin X
metadata
ConstructorMetadataAnnotation
type: IntrospectTypesPhaseMacro
IdentifierMetadataAnnotation
identifier: a
''');
}
test_mixin_method() async {
await _assertIntrospectText(r'''
mixin A {
@IntrospectTypesPhaseMacro()
void foo() {}
}
''', r'''
foo
flags: hasBody
returnType: void
''');
}
test_mixin_setter() async {
await _assertIntrospectText(r'''
mixin A {
@IntrospectTypesPhaseMacro()
set foo(int value) {}
}
''', r'''
foo
flags: hasBody isSetter
positionalParameters
value
flags: isRequired
type: int
returnType: OmittedType
''');
}
test_mixin_superclassConstraints() async {
await _assertIntrospectText(r'''
@IntrospectTypesPhaseMacro()
mixin A on B, C {}
''', r'''
mixin A
superclassConstraints
B
C
''');
}
test_mixin_typeParameters() async {
await _assertIntrospectText(r'''
@IntrospectTypesPhaseMacro()
mixin A<T, U extends List<T>> {}
''', r'''
mixin A
typeParameters
T
U
bound: List<T>
''');
}
test_namedTypeAnnotation_prefixed() async {
await _assertIntrospectText(r'''
@IntrospectTypesPhaseMacro()
class A extends prefix.B {}
''', r'''
class A
superclass: B
''');
}
/// Assert that the textual dump of the introspection information produced
/// by `IntrospectTypesPhaseMacro` in [code], is the [expected].
Future<void> _assertIntrospectText(
String code,
String expected,
) async {
var actual = await _getIntrospectText(code);
if (actual != expected) {
NodeTextExpectationsCollector.add(actual);
print(actual);
}
expect(actual, expected);
}
/// The [code] is expected to have exactly one application of
/// `IntrospectTypesPhaseMacro`. It may contain arbitrary code otherwise.
///
/// The macro generates a top-level constant `x`, with a string literal
/// initializer - the textual dump of the introspection.
Future<String> _getIntrospectText(String code) async {
newFile(
'$testPackageLibPath/introspect_shared.dart',
_introspectSharedCode,
);
newFile(
'$testPackageLibPath/introspect_types_phase.dart',
_introspectTypesCode,
);
var library = await buildLibrary('''
import 'introspect_types_phase.dart';
$code
''');
library.assertNoMacroErrors();
final macroAugmentation = library.augmentations.first;
final macroUnit = macroAugmentation.definingCompilationUnit;
final x = macroUnit.topLevelVariables.single;
expect(x.name, 'x');
x as ConstTopLevelVariableElementImpl;
var x_literal = x.constantInitializer as SimpleStringLiteral;
return x_literal.value;
}
}
abstract class MacroTypesTest extends MacroElementsBaseTest {
final List<io.Directory> _ioDirectoriesToDelete = [];
@override
bool get retainDataForTesting => true;
@override
Future<void> tearDown() async {
for (final directory in _ioDirectoriesToDelete) {
try {
directory.deleteSync(
recursive: true,
);
} catch (_) {}
}
return super.tearDown();
}
test_application_newInstance_withoutPrefix() async {
newFile('$testPackageLibPath/a.dart', r'''
import 'dart:async';
import 'package:_fe_analyzer_shared/src/macros/api.dart';
macro class MyMacro implements ClassTypesMacro {
const MyMacro();
FutureOr<void> buildTypesForClass(clazz, builder) {
builder.declareType(
'MyClass',
DeclarationCode.fromString('class MyClass {}'),
);
}
}
''');
var library = await buildLibrary(r'''
import 'a.dart';
@MyMacro()
class A {}
''');
configuration
..withConstructors = false
..withMetadata = false
..withReferences = true;
checkElementText(library, r'''
library
reference: self
imports
package:test/a.dart
definingUnit
reference: self
classes
class A @35
reference: self::@class::A
augmentationImports
package:test/test.macro.dart
reference: self::@augmentation::package:test/test.macro.dart
macroGeneratedCode
---
library augment 'test.dart';
class MyClass {}
---
definingUnit
reference: self::@augmentation::package:test/test.macro.dart
classes
class MyClass @36
reference: self::@augmentation::package:test/test.macro.dart::@class::MyClass
''');
}
test_application_newInstance_withoutPrefix_namedConstructor() async {
newFile('$testPackageLibPath/a.dart', r'''
import 'dart:async';
import 'package:_fe_analyzer_shared/src/macros/api.dart';
macro class MyMacro implements ClassTypesMacro {
const MyMacro.named();
FutureOr<void> buildTypesForClass(clazz, builder) {
builder.declareType(
'MyClass',
DeclarationCode.fromString('class MyClass {}'),
);
}
}
''');
var library = await buildLibrary(r'''
import 'a.dart';
@MyMacro.named()
class A {}
''');
configuration.withMetadata = false;
checkElementText(library, r'''
library
imports
package:test/a.dart
definingUnit
classes
class A @41
constructors
synthetic @-1
augmentationImports
package:test/test.macro.dart
macroGeneratedCode
---
library augment 'test.dart';
class MyClass {}
---
definingUnit
classes
class MyClass @36
constructors
synthetic @-1
''');
}
test_application_newInstance_withPrefix() async {
newFile('$testPackageLibPath/a.dart', r'''
import 'package:_fe_analyzer_shared/src/macros/api.dart';
macro class MyMacro implements ClassTypesMacro {
const MyMacro();
buildTypesForClass(clazz, builder) {
builder.declareType(
'MyClass',
DeclarationCode.fromString('class MyClass {}'),
);
}
}
''');
var library = await buildLibrary(r'''
import 'a.dart' as prefix;
@prefix.MyMacro()
class A {}
''');
configuration.withMetadata = false;
checkElementText(library, r'''
library
imports
package:test/a.dart as prefix @19
definingUnit
classes
class A @52
constructors
synthetic @-1
augmentationImports
package:test/test.macro.dart
macroGeneratedCode
---
library augment 'test.dart';
class MyClass {}
---
definingUnit
classes
class MyClass @36
constructors
synthetic @-1
''');
}
test_application_newInstance_withPrefix_namedConstructor() async {
newFile('$testPackageLibPath/a.dart', r'''
import 'package:_fe_analyzer_shared/src/macros/api.dart';
macro class MyMacro implements ClassTypesMacro {
const MyMacro.named();
buildTypesForClass(clazz, builder) {
builder.declareType(
'MyClass',
DeclarationCode.fromString('class MyClass {}'),
);
}
}
''');
var library = await buildLibrary(r'''
import 'a.dart' as prefix;
@prefix.MyMacro.named()
class A {}
''');
configuration.withMetadata = false;
checkElementText(library, r'''
library
imports
package:test/a.dart as prefix @19
definingUnit
classes
class A @58
constructors
synthetic @-1
augmentationImports
package:test/test.macro.dart
macroGeneratedCode
---
library augment 'test.dart';
class MyClass {}
---
definingUnit
classes
class MyClass @36
constructors
synthetic @-1
''');
}
test_executable() async {
// We use AOT executables only on Linux.
if (resourceProvider.pathContext.style != package_path.Style.posix) {
return;
}
// No need to verify reading elements for this test.
if (!keepLinkingLibraries) {
return;
}
const macroCode = r'''
import 'dart:async';
import 'package:_fe_analyzer_shared/src/macros/api.dart';
macro class MyMacro implements ClassTypesMacro {
const MyMacro();
FutureOr<void> buildTypesForClass(clazz, builder) {
builder.declareType(
'MyClass',
DeclarationCode.fromString('class MyClass {}'),
);
}
}
''';
// Compile the macro to executable.
io.File macroExecutable;
{
final macroMainContent = macro.bootstrapMacroIsolate(
{
'package:test/a.dart': {
'MyMacro': ['']
},
},
macro.SerializationMode.byteData,
);
final tempCompileDirectory =
io.Directory.systemTemp.createTempSync('dartAnalyzerMacro');
_ioDirectoriesToDelete.add(tempCompileDirectory);
final fileSystem = PhysicalResourceProvider.INSTANCE;
final compileRoot = fileSystem.getFolder(tempCompileDirectory.path);
final testRoot = compileRoot.getChildAssumingFolder('test');
testRoot.newFile('lib/a.dart').writeAsStringSync(macroCode);
final testBin = testRoot.getChildAssumingFolder('bin');
final testMain = testBin.newFile('main.dart');
testMain.writeAsStringSync(macroMainContent);
final metaDir = compileRoot.getChildAssumingFolder('meta');
MockPackages.addMetaPackageFiles(metaDir);
MacrosEnvironment.instance.packageSharedFolder.copyTo(compileRoot);
compileRoot
.newFile('.dart_tool/package_config.json')
.writeAsStringSync(r'''
{
"configVersion": 2,
"packages": [
{
"name": "test",
"rootUri": "../test",
"packageUri": "lib/"
},
{
"name": "_fe_analyzer_shared",
"rootUri": "../_fe_analyzer_shared",
"packageUri": "lib/"
},
{
"name": "meta",
"rootUri": "../meta",
"packageUri": "lib/"
}
]
}
''');
final process = await io.Process.start(
io.Platform.executable,
['compile', 'exe', '--enable-experiment=macros', testMain.path],
);
final exitCode = await process.exitCode;
if (exitCode == 255) {
markTestSkipped('Skip because cannot compile.');
return;
}
expect(exitCode, isZero);
final executable = testBin.getChildAssumingFile('main.exe');
expect(executable.exists, isTrue);
// Convert to io.File
macroExecutable = io.File(executable.path);
}
// Build the summary for `a.dart`, with the macro.
// We always have summaries for libraries with macro executable.
final Uint8List aBundleBytes;
{
final a = newFile('$testPackageLibPath/a.dart', macroCode);
// Disable compilation to kernel.
macroSupport = ExecutableMacroSupport();
final analysisDriver = driverFor(a);
aBundleBytes = await analysisDriver.buildPackageBundle(
uriList: [
Uri.parse('package:_fe_analyzer_shared/src/macros/api.dart'),
Uri.parse('package:test/a.dart'),
],
);
// We should not read the file anyway, but we make it explicit.
a.delete();
}
await disposeAnalysisContextCollection();
useEmptyByteStore();
// Configure summaries.
{
sdkSummaryFile = await writeSdkSummary();
final aBundleFile = getFile('/home/summaries/a.sum');
aBundleFile.writeAsBytesSync(aBundleBytes);
librarySummaryFiles = [aBundleFile];
}
// Configure the macro executor.
macroSupport = ExecutableMacroSupport()
..add(
executable: macroExecutable,
libraries: {
Uri.parse('package:test/a.dart'),
},
);
// Verify that we can use the executable to run the macro.
{
var library = await buildLibrary(r'''
import 'a.dart';
@MyMacro()
class A {}
''');
configuration
..withConstructors = false
..withMetadata = false
..withReferences = true;
checkElementText(library, r'''
library
reference: self
imports
package:test/a.dart
definingUnit
reference: self
classes
class A @35
reference: self::@class::A
augmentationImports
package:test/test.macro.dart
reference: self::@augmentation::package:test/test.macro.dart
macroGeneratedCode
---
library augment 'test.dart';
class MyClass {}
---
definingUnit
reference: self::@augmentation::package:test/test.macro.dart
classes
class MyClass @36
reference: self::@augmentation::package:test/test.macro.dart::@class::MyClass
''');
}
}
test_imports_class() async {
useEmptyByteStore();
newFile('$testPackageLibPath/a.dart', r'''
class A {}
''');
newFile('$testPackageLibPath/b.dart', r'''
import 'dart:async';
import 'package:_fe_analyzer_shared/src/macros/api.dart';
import 'a.dart';
macro class MyMacro implements ClassTypesMacro {
const MyMacro();
FutureOr<void> buildTypesForClass(clazz, ClassTypeBuilder builder) async {
final identifier = await builder.resolveIdentifier(
Uri.parse('package:test/a.dart'),
'A',
);
builder.declareType(
'MyClass',
DeclarationCode.fromParts([
'class MyClass {\n void foo(',
identifier,
' _) {}\n}',
]),
);
}
}
''');
var library = await buildLibrary(r'''
import 'b.dart';
@MyMacro()
class X {}
''');
configuration
..withConstructors = false
..withMetadata = false;
checkElementText(library, r'''
library
imports
package:test/b.dart
definingUnit
classes
class X @35
augmentationImports
package:test/test.macro.dart
macroGeneratedCode
---
library augment 'test.dart';
import 'package:test/a.dart' as prefix0;
class MyClass {
void foo(prefix0.A _) {}
}
---
imports
package:test/a.dart as prefix0 @62
definingUnit
classes
class MyClass @78
methods
foo @95
parameters
requiredPositional _ @109
type: A
returnType: void
''');
analyzerStatePrinterConfiguration.filesToPrintContent.add(
getFile('$testPackageLibPath/test.macro.dart'),
);
if (keepLinkingLibraries) {
assertDriverStateString(testFile, r'''
files
/home/test/lib/a.dart
uri: package:test/a.dart
current
id: file_0
kind: library_0
libraryImports
library_10 dart:core synthetic
cycle_0
dependencies: dart:core
libraries: library_0
apiSignature_0
users: cycle_1
referencingFiles: file_1 file_3
unlinkedKey: k00
/home/test/lib/b.dart
uri: package:test/b.dart
current
id: file_1
kind: library_1
libraryImports
library_12 dart:async
library_4 package:macro/api.dart
library_0
library_10 dart:core synthetic
cycle_1
dependencies: cycle_0 dart:core package:macro/api.dart
libraries: library_1
apiSignature_1
users: cycle_2
referencingFiles: file_2
unlinkedKey: k01
/home/test/lib/test.dart
uri: package:test/test.dart
current
id: file_2
kind: library_2
libraryImports
library_1
library_10 dart:core synthetic
augmentationImports
augmentation_3
cycle_2
dependencies: cycle_1 dart:core
libraries: library_2
apiSignature_2
unlinkedKey: k02
/home/test/lib/test.macro.dart
uri: package:test/test.macro.dart
current
id: file_3
content
---
library augment 'test.dart';
import 'package:test/a.dart' as prefix0;
class MyClass {
void foo(prefix0.A _) {}
}
---
kind: augmentation_3
augmented: library_2
library: library_2
libraryImports
library_0
library_10 dart:core synthetic
referencingFiles: file_2
unlinkedKey: k03
libraryCycles
/home/test/lib/a.dart
current: cycle_0
key: k04
get: []
put: [k04]
/home/test/lib/b.dart
current: cycle_1
key: k05
get: []
put: [k05]
/home/test/lib/test.dart
current: cycle_2
key: k06
get: []
put: [k06]
elementFactory
hasElement
package:test/a.dart
package:test/b.dart
package:test/test.dart
''');
// When we discard the library, we remove its macro file.
driverFor(testFile).changeFile(testFile.path);
await driverFor(testFile).applyPendingFileChanges();
assertDriverStateString(testFile, r'''
files
/home/test/lib/a.dart
uri: package:test/a.dart
current
id: file_0
kind: library_0
libraryImports
library_10 dart:core synthetic
cycle_0
dependencies: dart:core
libraries: library_0
apiSignature_0
users: cycle_1
referencingFiles: file_1
unlinkedKey: k00
/home/test/lib/b.dart
uri: package:test/b.dart
current
id: file_1
kind: library_1
libraryImports
library_12 dart:async
library_4 package:macro/api.dart
library_0
library_10 dart:core synthetic
cycle_1
dependencies: cycle_0 dart:core package:macro/api.dart
libraries: library_1
apiSignature_1
users: cycle_6
referencingFiles: file_2
unlinkedKey: k01
/home/test/lib/test.dart
uri: package:test/test.dart
current
id: file_2
kind: library_16
libraryImports
library_1
library_10 dart:core synthetic
cycle_6
dependencies: cycle_1 dart:core
libraries: library_16
apiSignature_2
unlinkedKey: k02
/home/test/lib/test.macro.dart
uri: package:test/test.macro.dart
libraryCycles
/home/test/lib/a.dart
current: cycle_0
key: k04
get: []
put: [k04]
/home/test/lib/b.dart
current: cycle_1
key: k05
get: []
put: [k05]
/home/test/lib/test.dart
get: []
put: [k06]
elementFactory
hasElement
package:test/a.dart
package:test/b.dart
''');
} else {
assertDriverStateString(testFile, r'''
files
/home/test/lib/a.dart
uri: package:test/a.dart
current
id: file_0
kind: library_0
libraryImports
library_10 dart:core synthetic
cycle_0
dependencies: dart:core
libraries: library_0
apiSignature_0
users: cycle_1
referencingFiles: file_1 file_3
unlinkedKey: k00
/home/test/lib/b.dart
uri: package:test/b.dart
current
id: file_1
kind: library_1
libraryImports
library_12 dart:async
library_4 package:macro/api.dart
library_0
library_10 dart:core synthetic
cycle_1
dependencies: cycle_0 dart:core package:macro/api.dart
libraries: library_1
apiSignature_1
users: cycle_2
referencingFiles: file_2
unlinkedKey: k01
/home/test/lib/test.dart
uri: package:test/test.dart
current
id: file_2
kind: library_2
libraryImports
library_1
library_10 dart:core synthetic
augmentationImports
augmentation_3
cycle_2
dependencies: cycle_1 dart:core
libraries: library_2
apiSignature_2
unlinkedKey: k02
/home/test/lib/test.macro.dart
uri: package:test/test.macro.dart
current
id: file_3
content
---
library augment 'test.dart';
import 'package:test/a.dart' as prefix0;
class MyClass {
void foo(prefix0.A _) {}
}
---
kind: augmentation_3
augmented: library_2
library: library_2
libraryImports
library_0
library_10 dart:core synthetic
referencingFiles: file_2
unlinkedKey: k03
libraryCycles
/home/test/lib/a.dart
current: cycle_0
key: k04
get: []
put: [k04]
/home/test/lib/b.dart
current: cycle_1
key: k05
get: []
put: [k05]
/home/test/lib/test.dart
current: cycle_2
key: k06
get: [k06]
put: [k06]
elementFactory
hasElement
package:test/a.dart
package:test/b.dart
package:test/test.dart
hasReader
package:test/test.dart
''');
}
}
test_iterate_merge() async {
useEmptyByteStore();
newFile('$testPackageLibPath/a.dart', r'''
import 'dart:async';
import 'package:_fe_analyzer_shared/src/macros/api.dart';
macro class AddClassA implements ClassTypesMacro {
const AddClassA();
FutureOr<void> buildTypesForClass(clazz, builder) async {
final identifier = await builder.resolveIdentifier(
Uri.parse('package:test/a.dart'),
'AddClassB',
);
builder.declareType(
'MyClass',
DeclarationCode.fromParts([
'@',
identifier,
'()\nclass A {}\n',
]),
);
}
}
macro class AddClassB implements ClassTypesMacro {
const AddClassB();
FutureOr<void> buildTypesForClass(clazz, builder) async {
builder.declareType(
'B',
DeclarationCode.fromString('class B {}\n'),
);
}
}
''');
var library = await buildLibrary(r'''
import 'a.dart';
@AddClassA()
class X {}
''');
configuration
..withConstructors = false
..withMetadata = false;
checkElementText(library, r'''
library
imports
package:test/a.dart
definingUnit
classes
class X @37
augmentationImports
package:test/test.macro.dart
macroGeneratedCode
---
library augment 'test.dart';
import 'package:test/a.dart' as prefix0;
@prefix0.AddClassB()
class A {}
class B {}
---
imports
package:test/a.dart as prefix0 @62
definingUnit
classes
class A @99
class B @111
''');
analyzerStatePrinterConfiguration.filesToPrintContent.add(
getFile('$testPackageLibPath/test.macro.dart'),
);
if (keepLinkingLibraries) {
assertDriverStateString(testFile, r'''
files
/home/test/lib/a.dart
uri: package:test/a.dart
current
id: file_0
kind: library_0
libraryImports
library_11 dart:async
library_3 package:macro/api.dart
library_9 dart:core synthetic
cycle_0
dependencies: dart:core package:macro/api.dart
libraries: library_0
apiSignature_0
users: cycle_1
referencingFiles: file_1 file_2
unlinkedKey: k00
/home/test/lib/test.dart
uri: package:test/test.dart
current
id: file_1
kind: library_1
libraryImports
library_0
library_9 dart:core synthetic
augmentationImports
augmentation_2
cycle_1
dependencies: cycle_0 dart:core
libraries: library_1
apiSignature_1
unlinkedKey: k01
/home/test/lib/test.macro.dart
uri: package:test/test.macro.dart
current
id: file_2
content
---
library augment 'test.dart';
import 'package:test/a.dart' as prefix0;
@prefix0.AddClassB()
class A {}
class B {}
---
kind: augmentation_2
augmented: library_1
library: library_1
libraryImports
library_0
library_9 dart:core synthetic
referencingFiles: file_1
unlinkedKey: k02
libraryCycles
/home/test/lib/a.dart
current: cycle_0
key: k03
get: []
put: [k03]
/home/test/lib/test.dart
current: cycle_1
key: k04
get: []
put: [k04]
elementFactory
hasElement
package:test/a.dart
package:test/test.dart
''');
} else {
assertDriverStateString(testFile, r'''
files
/home/test/lib/a.dart
uri: package:test/a.dart
current
id: file_0
kind: library_0
libraryImports
library_11 dart:async
library_3 package:macro/api.dart
library_9 dart:core synthetic
cycle_0
dependencies: dart:core package:macro/api.dart
libraries: library_0
apiSignature_0
users: cycle_1
referencingFiles: file_1 file_2
unlinkedKey: k00
/home/test/lib/test.dart
uri: package:test/test.dart
current
id: file_1
kind: library_1
libraryImports
library_0
library_9 dart:core synthetic
augmentationImports
augmentation_2
cycle_1
dependencies: cycle_0 dart:core
libraries: library_1
apiSignature_1
unlinkedKey: k01
/home/test/lib/test.macro.dart
uri: package:test/test.macro.dart
current
id: file_2
content
---
library augment 'test.dart';
import 'package:test/a.dart' as prefix0;
@prefix0.AddClassB()
class A {}
class B {}
---
kind: augmentation_2
augmented: library_1
library: library_1
libraryImports
library_0
library_9 dart:core synthetic
referencingFiles: file_1
unlinkedKey: k02
libraryCycles
/home/test/lib/a.dart
current: cycle_0
key: k03
get: []
put: [k03]
/home/test/lib/test.dart
current: cycle_1
key: k04
get: [k04]
put: [k04]
elementFactory
hasElement
package:test/a.dart
package:test/test.dart
hasReader
package:test/test.dart
''');
}
}
}
@reflectiveTest
class MacroTypesTest_fromBytes extends MacroTypesTest {
@override
bool get keepLinkingLibraries => false;
}
@reflectiveTest
class MacroTypesTest_keepLinking extends MacroTypesTest {
@override
bool get keepLinkingLibraries => true;
}
class _MacroApplicationErrorsCollector
extends GeneralizingElementVisitor<void> {
final List<MacroApplicationError> errors = [];
@override
void visitElement(Element element) {
if (element case final MacroTargetElement element) {
errors.addAll(element.macroApplicationErrors);
}
super.visitElement(element);
}
}
extension on LibraryElement {
List<MacroApplicationError> get macroErrors {
final collector = _MacroApplicationErrorsCollector();
accept(collector);
return collector.errors;
}
String get macroErrorsStr {
return macroErrors.map((e) {
return e.toStringForTest();
}).join('\n');
}
void assertNoMacroErrors() {
expect(macroErrorsStr, isEmpty);
}
}
extension on Folder {
File newFile(String relPath) {
final file = getChildAssumingFile(relPath);
file.parent.create();
return file;
}
}
extension on ElementTextConfiguration {
void forOrder() {
filter = (element) {
if (element is CompilationUnitElement) {
return false;
// return element.source.uri != Uri.parse('package:test/test.dart');
}
return true;
};
withConstructors = false;
withMetadata = false;
withReturnType = false;
}
}