Version 2.18.0-27.0.dev
Merge commit '61c05151e7235664d9faee8a618a4ad5763c7020' into 'dev'
diff --git a/pkg/analysis_server/test/analysis/notification_navigation_test.dart b/pkg/analysis_server/test/analysis/notification_navigation_test.dart
index f4b8b23..399eb4a 100644
--- a/pkg/analysis_server/test/analysis/notification_navigation_test.dart
+++ b/pkg/analysis_server/test/analysis/notification_navigation_test.dart
@@ -720,7 +720,7 @@
''');
await prepareNavigation();
assertHasRegion('index');
- assertHasTargetInDartCore('index;');
+ assertHasTargetInDartCore('index; // Enum');
}
Future<void> test_enum_method() async {
diff --git a/pkg/analyzer/lib/src/summary2/macro.dart b/pkg/analyzer/lib/src/summary2/macro.dart
index 2ebb62e..27130c3 100644
--- a/pkg/analyzer/lib/src/summary2/macro.dart
+++ b/pkg/analyzer/lib/src/summary2/macro.dart
@@ -17,8 +17,6 @@
import 'package:analyzer/src/summary2/kernel_compilation_service.dart';
import 'package:path/path.dart' as package_path;
-export 'package:_fe_analyzer_shared/src/macros/executor.dart' show Arguments;
-
class BundleMacroExecutor {
final macro.MultiMacroExecutor macroExecutor;
late final macro.ExecutorFactoryToken _executorFactoryToken;
diff --git a/pkg/analyzer/lib/src/summary2/macro_application.dart b/pkg/analyzer/lib/src/summary2/macro_application.dart
index 2e9a109..847d5be 100644
--- a/pkg/analyzer/lib/src/summary2/macro_application.dart
+++ b/pkg/analyzer/lib/src/summary2/macro_application.dart
@@ -9,6 +9,7 @@
import 'package:_fe_analyzer_shared/src/macros/executor/remote_instance.dart'
as macro;
import 'package:analyzer/dart/ast/ast.dart';
+import 'package:analyzer/dart/ast/token.dart';
import 'package:analyzer/src/dart/element/element.dart';
import 'package:analyzer/src/summary2/library_builder.dart';
import 'package:analyzer/src/summary2/macro.dart';
@@ -31,8 +32,9 @@
if (classNode is ClassDeclaration) {
for (var annotation in classNode.metadata) {
var annotationNameNode = annotation.name;
+ var argumentsNode = annotation.arguments;
if (annotationNameNode is SimpleIdentifier &&
- annotation.arguments != null) {
+ argumentsNode != null) {
// TODO(scheglov) Create a Scope.
for (var import in libraryBuilder.element.imports) {
var importedLibrary = import.importedLibrary;
@@ -51,6 +53,7 @@
macroExecutor,
getClassDeclaration(classNode),
getter,
+ _buildArguments(argumentsNode),
);
if (macroResult.isNotEmpty) {
macroResults.add(macroResult);
@@ -96,18 +99,34 @@
BundleMacroExecutor macroExecutor,
macro.Declaration declaration,
ClassElementImpl classElement,
+ macro.Arguments arguments,
) async {
var macroInstance = await macroExecutor.instantiate(
libraryUri: classElement.librarySource.uri,
className: classElement.name,
constructorName: '', // TODO
- arguments: Arguments([], {}), // TODO
+ arguments: arguments,
declaration: declaration,
identifierResolver: _FakeIdentifierResolver(),
);
return await macroInstance.executeTypesPhase();
}
+ static macro.Arguments _buildArguments(ArgumentList node) {
+ final positional = <Object?>[];
+ final named = <String, Object?>{};
+ for (final argument in node.arguments) {
+ if (argument is NamedExpression) {
+ final value = _evaluateArgument(argument.expression);
+ named[argument.name.label.name] = value;
+ } else {
+ final value = _evaluateArgument(argument);
+ positional.add(value);
+ }
+ }
+ return macro.Arguments(positional, named);
+ }
+
static macro.ClassDeclarationImpl _buildClassDeclaration(
ClassDeclaration node,
) {
@@ -180,6 +199,44 @@
return const [];
}
}
+
+ static Object? _evaluateArgument(Expression node) {
+ if (node is BooleanLiteral) {
+ return node.value;
+ } else if (node is DoubleLiteral) {
+ return node.value;
+ } else if (node is IntegerLiteral) {
+ return node.value;
+ } else if (node is ListLiteral) {
+ return node.elements.cast<Expression>().map(_evaluateArgument).toList();
+ } else if (node is NullLiteral) {
+ return null;
+ } else if (node is PrefixExpression &&
+ node.operator.type == TokenType.MINUS) {
+ final operandValue = _evaluateArgument(node.operand);
+ if (operandValue is double) {
+ return -operandValue;
+ } else if (operandValue is int) {
+ return -operandValue;
+ }
+ } else if (node is SetOrMapLiteral) {
+ final result = <Object?, Object?>{};
+ for (final element in node.elements) {
+ if (element is! MapLiteralEntry) {
+ throw ArgumentError(
+ 'Not supported: (${element.runtimeType}) $element',
+ );
+ }
+ final key = _evaluateArgument(element.key);
+ final value = _evaluateArgument(element.value);
+ result[key] = value;
+ }
+ return result;
+ } else if (node is SimpleStringLiteral) {
+ return node.value;
+ }
+ throw ArgumentError('Not supported: (${node.runtimeType}) $node');
+ }
}
class _FakeIdentifierResolver extends macro.IdentifierResolver {
diff --git a/pkg/analyzer/lib/src/test_utilities/mock_sdk.dart b/pkg/analyzer/lib/src/test_utilities/mock_sdk.dart
index ba0dc02..b462020 100644
--- a/pkg/analyzer/lib/src/test_utilities/mock_sdk.dart
+++ b/pkg/analyzer/lib/src/test_utilities/mock_sdk.dart
@@ -258,6 +258,7 @@
library dart.core;
import "dart:_internal" hide Symbol;
+import "dart:_internal" as internal show Symbol;
export 'dart:async' show Future, Stream;
@@ -279,7 +280,8 @@
// In the SDK this is an abstract class.
class BigInt implements Comparable<BigInt> {
- static BigInt parse(String source, {int radix}) => BigInt();
+ int compareTo(BigInt other) => 0;
+ static BigInt parse(String source, {int? radix}) => throw 0;
}
abstract class bool extends Object {
@@ -351,7 +353,9 @@
external static double? tryParse(String source);
}
-class Duration implements Comparable<Duration> {}
+class Duration implements Comparable<Duration> {
+ int compareTo(Duration other) => 0;
+}
class Error {
Error();
@@ -457,11 +461,11 @@
void add(E value) {}
void addAll(Iterable<E> iterable) {}
- Map<int, E> asMap() {}
+ Map<int, E> asMap() => throw 0;
void clear() {}
int indexOf(Object element);
bool remove(Object? value);
- E removeLast() {}
+ E removeLast() => throw 0;
noSuchMethod(Invocation invocation) => null;
}
@@ -557,11 +561,14 @@
}
abstract class Enum {
- int get index;
+ int get index; // Enum
+ String get _name;
}
-abstract class _Enum extends Enum {
- String _name;
+abstract class _Enum implements Enum {
+ final int index;
+ final String _name;
+ const _Enum(this.index, this._name);
}
abstract class Pattern {
@@ -621,7 +628,7 @@
}
class Symbol {
- const factory Symbol(String name) = _SymbolImpl;
+ const factory Symbol(String name) = internal.Symbol;
}
class Type {}
@@ -633,8 +640,8 @@
}
class Uri {
- static List<int> parseIPv6Address(String host, [int start = 0, int end]) {
- return null;
+ static List<int> parseIPv6Address(String host, [int start = 0, int? end]) {
+ throw 0;
}
}
@@ -646,8 +653,9 @@
const _Proxy();
}
-class _SymbolImpl {
- const _SymbolImpl(String name);
+@Since("2.15")
+extension EnumName on Enum {
+ String get name => _name;
}
''',
)
@@ -1078,7 +1086,8 @@
'''
library dart._internal;
-class Symbol {}
+import 'dart:core' hide Symbol;
+import 'dart:core' as core show Symbol;
class EmptyIterable<E> implements Iterable<E> {
const EmptyIterable();
@@ -1088,6 +1097,16 @@
final String name;
const ExternalName(this.name);
}
+
+@Since("2.2")
+class Since {
+ final String version;
+ const Since(this.version);
+}
+
+class Symbol implements core.Symbol {
+ external const Symbol(String name);
+}
''',
)
],
diff --git a/pkg/analyzer/test/src/summary/macro_test.dart b/pkg/analyzer/test/src/summary/macro_test.dart
index 42e0a59..36592d2 100644
--- a/pkg/analyzer/test/src/summary/macro_test.dart
+++ b/pkg/analyzer/test/src/summary/macro_test.dart
@@ -75,6 +75,165 @@
);
}
+ test_arguments_typesPhase_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_arguments_typesPhase_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_arguments_typesPhase_kind_requiredNamed() async {
+ await _assertTypesPhaseArgumentsText(
+ fields: {'foo': 'int'},
+ constructorParametersCode: '({required this.foo})',
+ argumentsCode: '(foo: 42)',
+ expected: r'''
+foo: 42
+''',
+ );
+ }
+
+ test_arguments_typesPhase_kind_requiredPositional() async {
+ await _assertTypesPhaseArgumentsText(
+ fields: {'foo': 'int'},
+ constructorParametersCode: '(this.foo)',
+ argumentsCode: '(42)',
+ expected: r'''
+foo: 42
+''',
+ );
+ }
+
+ test_arguments_typesPhase_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_arguments_typesPhase_type_double() async {
+ await _assertTypesPhaseArgumentsText(
+ fields: {'foo': 'double'},
+ constructorParametersCode: '(this.foo)',
+ argumentsCode: '(1.2)',
+ expected: r'''
+foo: 1.2
+''',
+ );
+ }
+
+ test_arguments_typesPhase_type_double_negative() async {
+ await _assertTypesPhaseArgumentsText(
+ fields: {'foo': 'double'},
+ constructorParametersCode: '(this.foo)',
+ argumentsCode: '(-1.2)',
+ expected: r'''
+foo: -1.2
+''',
+ );
+ }
+
+ test_arguments_typesPhase_type_int() async {
+ await _assertTypesPhaseArgumentsText(
+ fields: {'foo': 'int'},
+ constructorParametersCode: '(this.foo)',
+ argumentsCode: '(42)',
+ expected: r'''
+foo: 42
+''',
+ );
+ }
+
+ test_arguments_typesPhase_type_int_negative() async {
+ await _assertTypesPhaseArgumentsText(
+ fields: {'foo': 'int'},
+ constructorParametersCode: '(this.foo)',
+ argumentsCode: '(-42)',
+ expected: r'''
+foo: -42
+''',
+ );
+ }
+
+ test_arguments_typesPhase_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_arguments_typesPhase_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_arguments_typesPhase_type_null() async {
+ await _assertTypesPhaseArgumentsText(
+ fields: {'foo': 'Object?'},
+ constructorParametersCode: '(this.foo)',
+ argumentsCode: '(null)',
+ expected: r'''
+foo: null
+''',
+ );
+ }
+
+ test_arguments_typesPhase_type_string() async {
+ await _assertTypesPhaseArgumentsText(
+ fields: {'foo': 'String'},
+ constructorParametersCode: '(this.foo)',
+ argumentsCode: "('aaa')",
+ expected: r'''
+foo: aaa
+''',
+ );
+ }
+
test_build_types() async {
newFile2('$testPackageLibPath/a.dart', r'''
import 'dart:async';
@@ -274,6 +433,64 @@
);
}
+ /// 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,
+ required String expected,
+ }) async {
+ final dumpCode = fields.keys.map((name) {
+ return "$name: \$$name\\\\n";
+ }).join('');
+
+ newFile2('$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 {}
+ ''', preBuildSequence: [
+ {'package:test/arguments_text.dart'}
+ ]);
+
+ final x = library.parts.single.topLevelVariables.single;
+ expect(x.name, 'x');
+ x as ConstTopLevelVariableElementImpl;
+ final actual = (x.constantInitializer as SimpleStringLiteral).value;
+
+ if (actual != expected) {
+ print(actual);
+ }
+ expect(actual, expected);
+ }
+
/// Assert that the textual dump of the introspection information for
/// the first declaration in [declarationCode] is the same as [expected].
Future<void> _assertTypesPhaseIntrospectionText(
diff --git a/tools/VERSION b/tools/VERSION
index ad16b8c..2ca8e5a 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
MAJOR 2
MINOR 18
PATCH 0
-PRERELEASE 26
+PRERELEASE 27
PRERELEASE_PATCH 0
\ No newline at end of file