Version 2.18.0-12.0.dev
Merge commit 'b7257ef58ee7323cade1b4f3bd1ca19658d89415' into 'dev'
diff --git a/pkg/_fe_analyzer_shared/lib/src/deferred_closure_heuristic.dart b/pkg/_fe_analyzer_shared/lib/src/deferred_closure_heuristic.dart
index d5f823a..9bff8c0 100644
--- a/pkg/_fe_analyzer_shared/lib/src/deferred_closure_heuristic.dart
+++ b/pkg/_fe_analyzer_shared/lib/src/deferred_closure_heuristic.dart
@@ -4,136 +4,163 @@
import 'package:_fe_analyzer_shared/src/util/dependency_walker.dart';
-/// Data structure tracking the type inference dependencies between closures
-/// passed as invocation parameters.
+/// Data structure tracking the type inference dependencies between generic
+/// invocation parameters.
///
-/// [planClosureReconciliationStages] is used as part of support for
+/// [planReconciliationStages] is used as part of support for
/// https://github.com/dart-lang/language/issues/731 (improved inference for
/// fold etc.) to choose the proper order in which to recursively analyze
/// closures passed as invocation arguments.
-abstract class ClosureDependencies<TypeVariable, Closure> {
- final List<_ClosureNode<Closure>> _closureNodes = [];
+abstract class ClosureDependencies<TypeVariable, ParamInfo,
+ DeferredParamInfo extends ParamInfo> {
+ final List<_Node<ParamInfo>> _paramNodes = [];
/// Construct a [ClosureDependencies] object that's prepared to determine the
- /// order to resolve [closures] for a generic invocation involving the given
- /// [typeVariables].
+ /// order to resolve [deferredParams] for a generic invocation involving the
+ /// given [typeVariables].
+ ///
+ /// [unDeferredParams] should contain information about any parameters
+ /// corresponding to arguments that have already been type inferred.
ClosureDependencies(
- Iterable<Closure> closures, Iterable<TypeVariable> typeVariables) {
- Map<TypeVariable, Set<_ClosureNode<Closure>>> closuresDependingOnTypeVar =
- {};
- Map<TypeVariable, Set<_ClosureNode<Closure>>> closuresConstrainingTypeVar =
- {};
- for (Closure closure in closures) {
- _ClosureNode<Closure> closureNode = new _ClosureNode<Closure>(closure);
- _closureNodes.add(closureNode);
- for (TypeVariable v in typeVarsFreeInClosureArguments(closure)) {
- (closuresDependingOnTypeVar[v] ??= {}).add(closureNode);
+ Iterable<DeferredParamInfo> deferredParams,
+ Iterable<TypeVariable> typeVariables,
+ Iterable<ParamInfo> unDeferredParams) {
+ Map<TypeVariable, Set<_Node<ParamInfo>>> paramsDependingOnTypeVar = {};
+ Map<TypeVariable, Set<_Node<ParamInfo>>> paramsConstrainingTypeVar = {};
+ for (DeferredParamInfo param in deferredParams) {
+ _Node<ParamInfo> paramNode =
+ new _Node<ParamInfo>(param, isDeferred: true);
+ _paramNodes.add(paramNode);
+ for (TypeVariable v in typeVarsFreeInParamParams(param)) {
+ (paramsDependingOnTypeVar[v] ??= {}).add(paramNode);
}
- for (TypeVariable v in typeVarsFreeInClosureReturns(closure)) {
- (closuresConstrainingTypeVar[v] ??= {}).add(closureNode);
+ for (TypeVariable v in typeVarsFreeInParamReturns(param)) {
+ (paramsConstrainingTypeVar[v] ??= {}).add(paramNode);
+ }
+ }
+ for (ParamInfo param in unDeferredParams) {
+ _Node<ParamInfo> paramNode =
+ new _Node<ParamInfo>(param, isDeferred: false);
+ _paramNodes.add(paramNode);
+ // Note: for un-deferred parameters, we only care about
+ // typeVarsFreeInParamReturns, because these parameters have already been
+ // analyzed, so they can't depend on other parameters.
+ for (TypeVariable v in typeVarsFreeInParamReturns(param)) {
+ (paramsConstrainingTypeVar[v] ??= {}).add(paramNode);
}
}
for (TypeVariable typeVariable in typeVariables) {
- for (_ClosureNode<Closure> closureNode
- in closuresDependingOnTypeVar[typeVariable] ?? const {}) {
- closureNode.dependencies
- .addAll(closuresConstrainingTypeVar[typeVariable] ?? const {});
+ for (_Node<ParamInfo> paramNode
+ in paramsDependingOnTypeVar[typeVariable] ?? const {}) {
+ paramNode.dependencies
+ .addAll(paramsConstrainingTypeVar[typeVariable] ?? const {});
}
}
}
- /// Computes the order in which to resolve the closures passed to the
- /// constructor.
+ /// Computes the order in which to resolve the deferred parameters passed to
+ /// the constructor.
///
- /// Each entry in the returned list represents the set of closures that should
- /// be visited during a single stage of resolution; after each stage, the
- /// assignment of actual types to type variables should be refined.
+ /// Each entry in the returned list represents the set of parameters whose
+ /// corresponding arguments should be visited during a single stage of
+ /// resolution; after each stage, the assignment of actual types to type
+ /// variables should be refined.
///
- /// So, for example, if the closures in question are A, B, and C, and the
- /// returned list is `[{A, B}, {C}]`, then first closures A and B should be
+ /// So, for example, if the parameters in question are A, B, and C, and the
+ /// returned list is `[{A, B}, {C}]`, then first parameters A and B should be
/// resolved, then the assignment of actual types to type variables should be
/// refined, and then C should be resolved, and then the final assignment of
/// actual types to type variables should be computed.
- List<Set<Closure>> planClosureReconciliationStages() {
- _DependencyWalker<Closure> walker = new _DependencyWalker<Closure>();
- for (_ClosureNode<Closure> closureNode in _closureNodes) {
- walker.walk(closureNode);
+ ///
+ /// Note that the first stage may be empty; when this happens, it means that
+ /// the assignment of actual types to type variables should be refined before
+ /// doing any visiting.
+ List<Set<DeferredParamInfo>> planReconciliationStages() {
+ _DependencyWalker<ParamInfo, DeferredParamInfo> walker =
+ new _DependencyWalker<ParamInfo, DeferredParamInfo>();
+ for (_Node<ParamInfo> paramNode in _paramNodes) {
+ walker.walk(paramNode);
}
- return walker.closureReconciliationStages;
+ return walker.reconciliationStages;
}
- /// If the type of the parameter corresponding to [closure] is a function
- /// type, the set of type parameters referred to by the parameter types of
- /// that parameter. If the type of the parameter is not a function type, an
- /// empty iterable should be returned.
+ /// If the type of the parameter corresponding to [param] is a function type,
+ /// the set of type parameters referred to by the parameter types of that
+ /// parameter. If the type of the parameter is not a function type, an empty
+ /// iterable should be returned.
///
/// Should be overridden by the client.
- Iterable<TypeVariable> typeVarsFreeInClosureArguments(Closure closure);
+ Iterable<TypeVariable> typeVarsFreeInParamParams(DeferredParamInfo param);
- /// If the type of the parameter corresponding to [closure] is a function
- /// type, the set of type parameters referred to by the return type of that
+ /// If the type of the parameter corresponding to [param] is a function type,
+ /// the set of type parameters referred to by the return type of that
/// parameter. If the type of the parameter is not a function type, the set
/// type parameters referred to by the type of the parameter should be
/// returned.
///
/// Should be overridden by the client.
- Iterable<TypeVariable> typeVarsFreeInClosureReturns(Closure closure);
-}
-
-/// Node type representing a single [Closure] for purposes of walking the
-/// graph of type inference dependencies among closures.
-class _ClosureNode<Closure> extends Node<_ClosureNode<Closure>> {
- /// The [Closure] being represented by this node.
- final Closure closure;
-
- /// If not `null`, the index of the reconciliation stage to which this closure
- /// has been assigned.
- int? stageNum;
-
- /// The nodes for the closures depended on by this closure.
- final List<_ClosureNode<Closure>> dependencies = [];
-
- _ClosureNode(this.closure);
-
- @override
- bool get isEvaluated => stageNum != null;
-
- @override
- List<_ClosureNode<Closure>> computeDependencies() => dependencies;
+ Iterable<TypeVariable> typeVarsFreeInParamReturns(ParamInfo param);
}
/// Derived class of [DependencyWalker] capable of walking the graph of type
-/// inference dependencies among closures.
-class _DependencyWalker<Closure>
- extends DependencyWalker<_ClosureNode<Closure>> {
- /// The set of closure reconciliation stages accumulated so far.
- final List<Set<Closure>> closureReconciliationStages = [];
+/// inference dependencies among parameters.
+class _DependencyWalker<ParamInfo, DeferredParamInfo extends ParamInfo>
+ extends DependencyWalker<_Node<ParamInfo>> {
+ /// The set of reconciliation stages accumulated so far.
+ final List<Set<DeferredParamInfo>> reconciliationStages = [];
@override
- void evaluate(_ClosureNode v) => evaluateScc([v]);
+ void evaluate(_Node<ParamInfo> v) => evaluateScc([v]);
@override
- void evaluateScc(List<_ClosureNode> nodes) {
+ void evaluateScc(List<_Node<ParamInfo>> nodes) {
int stageNum = 0;
- for (_ClosureNode node in nodes) {
- for (_ClosureNode dependency in node.dependencies) {
+ for (_Node<ParamInfo> node in nodes) {
+ for (_Node<ParamInfo> dependency in node.dependencies) {
int? dependencyStageNum = dependency.stageNum;
if (dependencyStageNum != null && dependencyStageNum >= stageNum) {
stageNum = dependencyStageNum + 1;
}
}
}
- if (closureReconciliationStages.length <= stageNum) {
- closureReconciliationStages.add({});
+ if (reconciliationStages.length <= stageNum) {
+ reconciliationStages.add({});
// `stageNum` can't grow by more than 1 each time `evaluateScc` is called,
// so adding one stage is sufficient to make sure the list is now long
// enough.
- assert(stageNum < closureReconciliationStages.length);
+ assert(stageNum < reconciliationStages.length);
}
- Set<Closure> stage = closureReconciliationStages[stageNum];
- for (_ClosureNode node in nodes) {
+ Set<DeferredParamInfo> stage = reconciliationStages[stageNum];
+ for (_Node<ParamInfo> node in nodes) {
node.stageNum = stageNum;
- stage.add(node.closure);
+ if (node.isDeferred) {
+ stage.add(node.param as DeferredParamInfo);
+ }
}
}
}
+
+/// Node type representing a single parameter for purposes of walking the graph
+/// of type inference dependencies among parameters.
+class _Node<ParamInfo> extends Node<_Node<ParamInfo>> {
+ /// The [ParamInfo] represented by this node.
+ final ParamInfo param;
+
+ /// If not `null`, the index of the reconciliation stage to which this
+ /// parameter has been assigned.
+ int? stageNum;
+
+ /// The nodes for the parameters depended on by this parameter.
+ final List<_Node<ParamInfo>> dependencies = [];
+
+ /// Indicates whether this node represents a deferred parameter.
+ final bool isDeferred;
+
+ _Node(this.param, {required this.isDeferred});
+
+ @override
+ bool get isEvaluated => stageNum != null;
+
+ @override
+ List<_Node<ParamInfo>> computeDependencies() => dependencies;
+}
diff --git a/pkg/_fe_analyzer_shared/test/deferred_closure_heuristic_test.dart b/pkg/_fe_analyzer_shared/test/deferred_closure_heuristic_test.dart
index ab4b8f9..41ec79e 100644
--- a/pkg/_fe_analyzer_shared/test/deferred_closure_heuristic_test.dart
+++ b/pkg/_fe_analyzer_shared/test/deferred_closure_heuristic_test.dart
@@ -8,10 +8,10 @@
main() {
test('single', () {
// If there is just a single closure and no type variables, it is selected.
- var f = Closure('f');
+ var f = Param('f');
expect(
_TestClosureDeps(typeVars: [], closures: [f])
- .planClosureReconciliationStages(),
+ .planReconciliationStages(),
[
{f}
]);
@@ -19,11 +19,11 @@
test('simple dependency', () {
// If f depends on g, then g is selected first, and then f.
- var f = Closure('f', argTypes: ['T']);
- var g = Closure('g', retTypes: ['T']);
+ var f = Param('f', argTypes: ['T']);
+ var g = Param('g', retTypes: ['T']);
expect(
_TestClosureDeps(typeVars: ['T'], closures: [f, g])
- .planClosureReconciliationStages(),
+ .planReconciliationStages(),
[
{g},
{f}
@@ -33,12 +33,12 @@
test('long chain', () {
// If f depends on g and g depends on h, then we do three separate stages:
// h, then g, then f.
- var f = Closure('f', argTypes: ['T']);
- var g = Closure('g', argTypes: ['U'], retTypes: ['T']);
- var h = Closure('h', retTypes: ['U']);
+ var f = Param('f', argTypes: ['T']);
+ var g = Param('g', argTypes: ['U'], retTypes: ['T']);
+ var h = Param('h', retTypes: ['U']);
expect(
_TestClosureDeps(typeVars: ['T', 'U'], closures: [f, g, h])
- .planClosureReconciliationStages(),
+ .planReconciliationStages(),
[
{h},
{g},
@@ -49,12 +49,12 @@
test('unrelated closure', () {
// Closures that are independent of all the others are inferred during the
// first stage.
- var f = Closure('f', argTypes: ['T']);
- var g = Closure('g', retTypes: ['T']);
- var h = Closure('h');
+ var f = Param('f', argTypes: ['T']);
+ var g = Param('g', retTypes: ['T']);
+ var h = Param('h');
expect(
_TestClosureDeps(typeVars: ['T', 'U'], closures: [f, g, h])
- .planClosureReconciliationStages(),
+ .planReconciliationStages(),
[
{g, h},
{f}
@@ -64,13 +64,13 @@
test('independent chains', () {
// If f depends on g, and h depends on i, then g and i are selected first,
// and then f and h.
- var f = Closure('f', argTypes: ['T']);
- var g = Closure('g', retTypes: ['T']);
- var h = Closure('h', argTypes: ['U']);
- var i = Closure('i', retTypes: ['U']);
+ var f = Param('f', argTypes: ['T']);
+ var g = Param('g', retTypes: ['T']);
+ var h = Param('h', argTypes: ['U']);
+ var i = Param('i', retTypes: ['U']);
expect(
_TestClosureDeps(typeVars: ['T', 'U'], closures: [f, g, h, i])
- .planClosureReconciliationStages(),
+ .planReconciliationStages(),
[
{g, i},
{f, h}
@@ -80,13 +80,13 @@
test('diamond', () {
// Test a diamond dependency shape: f depends on g and h; g and h both
// depend on i.
- var f = Closure('f', argTypes: ['T', 'U']);
- var g = Closure('g', argTypes: ['V'], retTypes: ['T']);
- var h = Closure('h', argTypes: ['V'], retTypes: ['U']);
- var i = Closure('i', retTypes: ['V']);
+ var f = Param('f', argTypes: ['T', 'U']);
+ var g = Param('g', argTypes: ['V'], retTypes: ['T']);
+ var h = Param('h', argTypes: ['V'], retTypes: ['U']);
+ var i = Param('i', retTypes: ['V']);
expect(
_TestClosureDeps(typeVars: ['T', 'U', 'V'], closures: [f, g, h, i])
- .planClosureReconciliationStages(),
+ .planReconciliationStages(),
[
{i},
{g, h},
@@ -96,43 +96,59 @@
test('cycle', () {
// A dependency cycle is inferred all at once.
- var f = Closure('f', argTypes: ['T']);
- var g = Closure('g', argTypes: ['U']);
- var h = Closure('h', argTypes: ['U'], retTypes: ['T']);
- var i = Closure('i', argTypes: ['T'], retTypes: ['U']);
+ var f = Param('f', argTypes: ['T']);
+ var g = Param('g', argTypes: ['U']);
+ var h = Param('h', argTypes: ['U'], retTypes: ['T']);
+ var i = Param('i', argTypes: ['T'], retTypes: ['U']);
expect(
_TestClosureDeps(typeVars: ['T', 'U'], closures: [f, g, h, i])
- .planClosureReconciliationStages(),
+ .planReconciliationStages(),
[
{h, i},
{f, g}
]);
});
+
+ test('dependency on undeferred param', () {
+ var f = Param('f', argTypes: ['T']);
+ var x = Param('x', retTypes: ['T']);
+ expect(
+ _TestClosureDeps(typeVars: ['T'], closures: [f], undeferredParams: [x])
+ .planReconciliationStages(),
+ [
+ <Param>{},
+ {f}
+ ]);
+ });
}
-class Closure {
+class Param {
final String name;
final List<String> argTypes;
final List<String> retTypes;
- Closure(this.name, {this.argTypes = const [], this.retTypes = const []});
+ Param(this.name, {this.argTypes = const [], this.retTypes = const []});
@override
String toString() => name;
}
-class _TestClosureDeps extends ClosureDependencies<String, Closure> {
+class _TestClosureDeps extends ClosureDependencies<String, Param, Param> {
final List<String> typeVars;
- final List<Closure> closures;
+ final List<Param> closures;
+ final List<Param> undeferredParams;
- _TestClosureDeps({required this.typeVars, required this.closures})
- : super(closures, typeVars);
+ _TestClosureDeps(
+ {required this.typeVars,
+ required this.closures,
+ this.undeferredParams = const []})
+ : super(closures, typeVars, undeferredParams);
@override
- Set<String> typeVarsFreeInClosureArguments(Closure closure) =>
+ Set<String> typeVarsFreeInParamParams(Param closure) =>
closure.argTypes.toSet();
@override
- Set<String> typeVarsFreeInClosureReturns(Closure closure) =>
+ Set<String> typeVarsFreeInParamReturns(Param closure) =>
closure.retTypes.toSet();
}
diff --git a/pkg/analysis_server/lib/src/plugin/plugin_manager.dart b/pkg/analysis_server/lib/src/plugin/plugin_manager.dart
index ae25ec1..7dc3e98 100644
--- a/pkg/analysis_server/lib/src/plugin/plugin_manager.dart
+++ b/pkg/analysis_server/lib/src/plugin/plugin_manager.dart
@@ -632,13 +632,8 @@
.getChildAssumingFolder(file_paths.dotDartTool)
.getChildAssumingFile(file_paths.packageConfigJson);
if (pubCommand != null) {
- var vmPath = Platform.executable;
- var pubPath = path.join(path.dirname(vmPath), 'pub');
- if (Platform.isWindows) {
- // Process.run requires the `.bat` suffix on Windows
- pubPath = '$pubPath.bat';
- }
- var result = Process.runSync(pubPath, <String>[pubCommand],
+ var result = Process.runSync(
+ Platform.executable, <String>['pub', pubCommand],
stderrEncoding: utf8,
stdoutEncoding: utf8,
workingDirectory: pluginFolder.path,
diff --git a/pkg/analyzer/analysis_options.yaml b/pkg/analyzer/analysis_options.yaml
index 1d8a622..876098b 100644
--- a/pkg/analyzer/analysis_options.yaml
+++ b/pkg/analyzer/analysis_options.yaml
@@ -1,16 +1,9 @@
-include: package:pedantic/analysis_options.1.9.0.yaml
+include: package:lints/recommended.yaml
analyzer:
errors:
# Increase the severity of the unused_import hint.
unused_import: warning
- # There are currently 10k violations in test/ and tool/.
- always_declare_return_types: ignore
- # There are currently 5000 violations in lib/. This just does not fit well
- # with the analyzer team's style.
- omit_local_variable_types: ignore
- # There are currently 3360 violations in lib/.
- prefer_single_quotes: ignore
# "strict-inference" is enabled, but "unused" parameters named '_' are
# still reported. Re-evaluate after
@@ -21,6 +14,19 @@
# "ignored for test/**/*.dart".
inference_failure_on_function_return_type: ignore
+ # Lints from the recommended set that conflict w/ analyzer style or will
+ # require some work to reach compliance.
+ # See: https://github.com/dart-lang/sdk/issues/48784
+ avoid_renaming_method_parameters: ignore
+ camel_case_types: ignore
+ constant_identifier_names: ignore
+ hash_and_equals: ignore
+ non_constant_identifier_names: ignore
+ implementation_imports: ignore
+ library_private_types_in_public_api: ignore
+ overridden_fields: ignore
+ provide_deprecation_message: ignore
+
language:
strict-inference: true
@@ -31,10 +37,4 @@
- avoid_unused_constructor_parameters
- await_only_futures
- depend_on_referenced_packages
- - empty_statements
- - iterable_contains_unrelated_type
- - list_remove_unrelated_type
- - prefer_typing_uninitialized_variables
- - unnecessary_brace_in_string_interps
- - unnecessary_overrides
- unnecessary_parenthesis
diff --git a/pkg/analyzer/lib/src/dart/analysis/analysis_context_collection.dart b/pkg/analyzer/lib/src/dart/analysis/analysis_context_collection.dart
index 266ad41..e7ff31d 100644
--- a/pkg/analyzer/lib/src/dart/analysis/analysis_context_collection.dart
+++ b/pkg/analyzer/lib/src/dart/analysis/analysis_context_collection.dart
@@ -2,6 +2,8 @@
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
+import 'package:_fe_analyzer_shared/src/macros/executor/multi_executor.dart'
+ as macro;
import 'package:analyzer/dart/analysis/analysis_context_collection.dart';
import 'package:analyzer/dart/analysis/context_locator.dart';
import 'package:analyzer/dart/analysis/declared_variables.dart';
@@ -22,6 +24,9 @@
/// The resource provider used to access the file system.
final ResourceProvider resourceProvider;
+ /// The instance of macro executor that is used for all macros.
+ final macro.MultiMacroExecutor? macroExecutor = macro.MultiMacroExecutor();
+
/// The list of analysis contexts.
@override
final List<DriverBasedAnalysisContext> contexts = [];
@@ -77,6 +82,7 @@
updateAnalysisOptions: updateAnalysisOptions,
fileContentCache: fileContentCache,
macroKernelBuilder: macroKernelBuilder,
+ macroExecutor: macroExecutor,
);
contexts.add(context);
}
@@ -109,6 +115,13 @@
throw StateError('Unable to find the context to $path');
}
+ void dispose() {
+ for (var analysisContext in contexts) {
+ analysisContext.driver.dispose();
+ }
+ macroExecutor?.close();
+ }
+
/// Check every element with [_throwIfNotAbsoluteNormalizedPath].
void _throwIfAnyNotAbsoluteNormalizedPath(List<String> paths) {
for (var path in paths) {
diff --git a/pkg/analyzer/lib/src/dart/analysis/context_builder.dart b/pkg/analyzer/lib/src/dart/analysis/context_builder.dart
index ddb9cc9..c1185f2 100644
--- a/pkg/analyzer/lib/src/dart/analysis/context_builder.dart
+++ b/pkg/analyzer/lib/src/dart/analysis/context_builder.dart
@@ -2,6 +2,8 @@
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
+import 'package:_fe_analyzer_shared/src/macros/executor/multi_executor.dart'
+ as macro;
import 'package:analyzer/dart/analysis/context_builder.dart';
import 'package:analyzer/dart/analysis/context_root.dart';
import 'package:analyzer/dart/analysis/declared_variables.dart';
@@ -60,6 +62,7 @@
void Function(AnalysisOptionsImpl)? updateAnalysisOptions,
FileContentCache? fileContentCache,
MacroKernelBuilder? macroKernelBuilder,
+ macro.MultiMacroExecutor? macroExecutor,
}) {
// TODO(scheglov) Remove this, and make `sdkPath` required.
sdkPath ??= getSdkPath();
@@ -118,6 +121,7 @@
retainDataForTesting: retainDataForTesting,
fileContentCache: fileContentCache,
macroKernelBuilder: macroKernelBuilder,
+ macroExecutor: macroExecutor,
);
if (declaredVariables != null) {
diff --git a/pkg/analyzer/lib/src/dart/analysis/driver.dart b/pkg/analyzer/lib/src/dart/analysis/driver.dart
index 457e79d..5e40995 100644
--- a/pkg/analyzer/lib/src/dart/analysis/driver.dart
+++ b/pkg/analyzer/lib/src/dart/analysis/driver.dart
@@ -5,6 +5,8 @@
import 'dart:async';
import 'dart:typed_data';
+import 'package:_fe_analyzer_shared/src/macros/executor/multi_executor.dart'
+ as macro;
import 'package:analyzer/dart/analysis/analysis_context.dart' as api;
import 'package:analyzer/dart/analysis/declared_variables.dart';
import 'package:analyzer/dart/analysis/results.dart';
@@ -124,6 +126,9 @@
final MacroKernelBuilder? macroKernelBuilder;
+ /// The instance of macro executor that is used for all macros.
+ final macro.MultiMacroExecutor? macroExecutor;
+
/// The declared environment variables.
DeclaredVariables declaredVariables = DeclaredVariables();
@@ -258,6 +263,7 @@
required AnalysisOptionsImpl analysisOptions,
required Packages packages,
this.macroKernelBuilder,
+ this.macroExecutor,
FileContentCache? fileContentCache,
this.enableIndex = false,
SummaryDataStore? externalSummaries,
@@ -325,6 +331,7 @@
declaredVariables: declaredVariables,
sourceFactory: _sourceFactory,
macroKernelBuilder: macroKernelBuilder,
+ macroExecutor: macroExecutor,
externalSummaries: _externalSummaries,
fileSystemState: _fsState,
);
@@ -525,6 +532,7 @@
/// periodically.
@visibleForTesting
void clearLibraryContext() {
+ _libraryContext?.dispose();
_libraryContext = null;
}
@@ -578,6 +586,7 @@
@override
void dispose() {
_scheduler.remove(this);
+ clearLibraryContext();
}
/// Return the cached [ResolvedUnitResult] for the Dart file with the given
diff --git a/pkg/analyzer/lib/src/dart/analysis/library_context.dart b/pkg/analyzer/lib/src/dart/analysis/library_context.dart
index 96dcbb7..57da9e4 100644
--- a/pkg/analyzer/lib/src/dart/analysis/library_context.dart
+++ b/pkg/analyzer/lib/src/dart/analysis/library_context.dart
@@ -4,6 +4,8 @@
import 'dart:typed_data';
+import 'package:_fe_analyzer_shared/src/macros/executor/multi_executor.dart'
+ as macro;
import 'package:analyzer/dart/analysis/declared_variables.dart';
import 'package:analyzer/dart/element/element.dart'
show CompilationUnitElement, LibraryElement;
@@ -43,6 +45,7 @@
final ByteStore byteStore;
final FileSystemState fileSystemState;
final MacroKernelBuilder? macroKernelBuilder;
+ final macro.MultiMacroExecutor? macroExecutor;
final SummaryDataStore store = SummaryDataStore();
late final AnalysisContextImpl analysisContext;
@@ -57,7 +60,8 @@
required AnalysisOptionsImpl analysisOptions,
required DeclaredVariables declaredVariables,
required SourceFactory sourceFactory,
- this.macroKernelBuilder,
+ required this.macroKernelBuilder,
+ required this.macroExecutor,
required SummaryDataStore? externalSummaries,
}) {
var synchronousSession =
@@ -92,6 +96,10 @@
return element as CompilationUnitElement;
}
+ void dispose() {
+ elementFactory.dispose();
+ }
+
/// Get the [LibraryElement] for the given library.
LibraryElement getLibraryElement(Uri uri) {
_createElementFactoryTypeProvider();
@@ -152,7 +160,7 @@
}
}
- var resolutionKey = cycle.transitiveSignature + '.linked_bundle';
+ var resolutionKey = '${cycle.transitiveSignature}.linked_bundle';
var resolutionBytes = byteStore.get(resolutionKey);
if (resolutionBytes == null) {
@@ -206,7 +214,8 @@
LinkResult linkResult;
try {
timerLinking.start();
- linkResult = await link(elementFactory, inputLibraries);
+ linkResult = await link(elementFactory, inputLibraries,
+ macroExecutor: macroExecutor);
librariesLinked += cycle.libraries.length;
counterLinkedLibraries += inputLibraries.length;
timerLinking.stop();
@@ -235,13 +244,32 @@
final macroKernelBuilder = this.macroKernelBuilder;
if (macroKernelBuilder != null && macroLibraries.isNotEmpty) {
- var macroKernelKey = cycle.transitiveSignature + '.macro_kernel';
- var macroKernelBytes = macroKernelBuilder.build(
- fileSystem: _MacroFileSystem(fileSystemState),
- libraries: macroLibraries,
- );
- byteStore.put(macroKernelKey, macroKernelBytes);
- bytesPut += macroKernelBytes.length;
+ var macroKernelKey = '${cycle.transitiveSignature}.macro_kernel';
+ var macroKernelBytes = byteStore.get(macroKernelKey);
+ if (macroKernelBytes == null) {
+ macroKernelBytes = macroKernelBuilder.build(
+ fileSystem: _MacroFileSystem(fileSystemState),
+ libraries: macroLibraries,
+ );
+ byteStore.put(macroKernelKey, macroKernelBytes);
+ bytesPut += macroKernelBytes.length;
+ } else {
+ bytesGet += macroKernelBytes.length;
+ }
+
+ final macroExecutor = this.macroExecutor;
+ if (macroExecutor != null) {
+ var bundleMacroExecutor = BundleMacroExecutor(
+ macroExecutor: macroExecutor,
+ kernelBytes: macroKernelBytes,
+ libraries: cycle.libraries.map((e) => e.uri).toSet(),
+ );
+ for (var libraryFile in cycle.libraries) {
+ var libraryUriStr = libraryFile.uriStr;
+ var libraryElement = elementFactory.libraryOfUri2(libraryUriStr);
+ libraryElement.bundleMacroExecutor = bundleMacroExecutor;
+ }
+ }
}
}
diff --git a/pkg/analyzer/lib/src/dart/element/element.dart b/pkg/analyzer/lib/src/dart/element/element.dart
index f646b7e..f8e2426 100644
--- a/pkg/analyzer/lib/src/dart/element/element.dart
+++ b/pkg/analyzer/lib/src/dart/element/element.dart
@@ -40,6 +40,7 @@
import 'package:analyzer/src/generated/utilities_dart.dart';
import 'package:analyzer/src/summary2/ast_binary_tokens.dart';
import 'package:analyzer/src/summary2/bundle_reader.dart';
+import 'package:analyzer/src/summary2/macro.dart';
import 'package:analyzer/src/summary2/reference.dart';
import 'package:analyzer/src/task/inference_error.dart';
import 'package:collection/collection.dart';
@@ -3678,6 +3679,9 @@
/// The scope of this library, `null` if it has not been created yet.
LibraryScope? _scope;
+ /// The macro executor for the bundle to which this library belongs.
+ BundleMacroExecutor? bundleMacroExecutor;
+
/// Initialize a newly created library element in the given [context] to have
/// the given [name] and [offset].
LibraryElementImpl(this.context, this.session, String name, int offset,
diff --git a/pkg/analyzer/lib/src/dart/resolver/invocation_inferrer.dart b/pkg/analyzer/lib/src/dart/resolver/invocation_inferrer.dart
index 97dad9b..fe15873 100644
--- a/pkg/analyzer/lib/src/dart/resolver/invocation_inferrer.dart
+++ b/pkg/analyzer/lib/src/dart/resolver/invocation_inferrer.dart
@@ -18,6 +18,35 @@
import 'package:analyzer/src/error/codes.dart';
import 'package:analyzer/src/generated/resolver.dart';
+Set<Object> _computeExplicitlyTypedParameterSet(
+ FunctionExpression functionExpression) {
+ List<FormalParameter> parameters =
+ functionExpression.parameters?.parameters ?? const [];
+ Set<Object> result = {};
+ for (var formalParameter in parameters) {
+ int unnamedParameterIndex = 0;
+ var key = formalParameter.isNamed
+ ? formalParameter.identifier?.name ?? ''
+ : unnamedParameterIndex++;
+ if (formalParameter.isExplicitlyTyped) {
+ result.add(key);
+ }
+ }
+ return result;
+}
+
+/// Given an iterable of parameters, computes a map whose keys are either the
+/// parameter name (for named parameters) or the zero-based integer index (for
+/// unnamed parameters), and whose values are the parameters themselves.
+Map<Object, ParameterElement> _computeParameterMap(
+ Iterable<ParameterElement> parameters) {
+ int unnamedParameterIndex = 0;
+ return {
+ for (var parameter in parameters)
+ parameter.isNamed ? parameter.name : unnamedParameterIndex++: parameter
+ };
+}
+
/// Specialization of [InvocationInferrer] for performing type inference on AST
/// nodes of type [Annotation] that resolve to a constructor invocation.
class AnnotationInferrer extends FullInvocationInferrer<AnnotationImpl> {
@@ -192,15 +221,21 @@
List<EqualityInfo<PromotableElement, DartType>?>? identicalInfo =
_isIdentical ? [] : null;
+ var parameterMap = _computeParameterMap(rawType?.parameters ?? const []);
var deferredClosures = _visitArguments(
+ parameterMap: parameterMap,
identicalInfo: identicalInfo,
substitution: substitution,
inferrer: inferrer);
if (deferredClosures != null) {
- for (var stage in _ClosureDependencies(resolver.typeSystem,
- deferredClosures, rawType?.typeFormals.toSet() ?? const {})
- .planClosureReconciliationStages()) {
- if (inferrer != null) {
+ bool isFirstStage = true;
+ for (var stage in _ClosureDependencies(
+ resolver.typeSystem,
+ deferredClosures,
+ rawType?.typeFormals.toSet() ?? const {},
+ _computeUndeferredParamInfo(parameterMap, deferredClosures))
+ .planReconciliationStages()) {
+ if (inferrer != null && !isFirstStage) {
substitution = Substitution.fromPairs(
rawType!.typeFormals, inferrer.partialInfer());
}
@@ -209,6 +244,7 @@
identicalInfo: identicalInfo,
substitution: substitution,
inferrer: inferrer);
+ isFirstStage = false;
}
}
@@ -234,6 +270,22 @@
return returnType;
}
+ /// Computes a list of [_ParamInfo] objects corresponding to the invocation
+ /// parameters that were *not* deferred.
+ List<_ParamInfo> _computeUndeferredParamInfo(
+ Map<Object, ParameterElement> parameterMap,
+ List<_DeferredParamInfo> deferredClosures) {
+ if (rawType == null) return const [];
+ var parameterKeysAlreadyCovered = {
+ for (var closure in deferredClosures) closure.parameterKey
+ };
+ return [
+ for (var entry in parameterMap.entries)
+ if (!parameterKeysAlreadyCovered.contains(entry.key))
+ _ParamInfo(entry.value)
+ ];
+ }
+
DartType _refineReturnType(DartType returnType) => returnType;
void _reportWrongNumberOfTypeArguments(TypeArgumentList typeArgumentList,
@@ -401,7 +453,8 @@
/// Performs type inference on the invocation expression.
void resolveInvocation() {
- var deferredClosures = _visitArguments();
+ var deferredClosures = _visitArguments(
+ parameterMap: _computeParameterMap(rawType?.parameters ?? const []));
if (deferredClosures != null) {
_resolveDeferredClosures(deferredClosures: deferredClosures);
}
@@ -426,7 +479,7 @@
/// Resolves any closures that were deferred by [_visitArguments].
void _resolveDeferredClosures(
- {required Iterable<_DeferredClosure> deferredClosures,
+ {required Iterable<_DeferredParamInfo> deferredClosures,
List<EqualityInfo<PromotableElement, DartType>?>? identicalInfo,
Substitution? substitution,
GenericInferrer? inferrer}) {
@@ -460,48 +513,34 @@
/// Visits [argumentList], resolving each argument. If any arguments need to
/// be deferred due to the `inference-update-1` feature, a list of them is
/// returned.
- List<_DeferredClosure>? _visitArguments(
- {List<EqualityInfo<PromotableElement, DartType>?>? identicalInfo,
+ List<_DeferredParamInfo>? _visitArguments(
+ {required Map<Object, ParameterElement> parameterMap,
+ List<EqualityInfo<PromotableElement, DartType>?>? identicalInfo,
Substitution? substitution,
GenericInferrer? inferrer}) {
assert(whyNotPromotedList.isEmpty);
- List<_DeferredClosure>? deferredClosures;
- var parameters = rawType?.parameters;
- var namedParameters = <String, ParameterElement>{};
- if (parameters != null) {
- for (var i = 0; i < parameters.length; i++) {
- var parameter = parameters[i];
- if (parameter.isNamed) {
- namedParameters[parameter.name] = parameter;
- }
- }
- }
+ List<_DeferredParamInfo>? deferredClosures;
resolver.checkUnreachableNode(argumentList);
var flow = resolver.flowAnalysis.flow;
- var positionalParameterIndex = 0;
+ var unnamedArgumentIndex = 0;
var arguments = argumentList.arguments;
for (int i = 0; i < arguments.length; i++) {
var argument = arguments[i];
Expression value;
ParameterElement? parameter;
+ Object parameterKey;
if (argument is NamedExpression) {
value = argument.expression;
- parameter = namedParameters[argument.name.label.name];
+ parameterKey = argument.name.label.name;
} else {
value = argument;
- if (parameters != null) {
- while (positionalParameterIndex < parameters.length) {
- var candidate = parameters[positionalParameterIndex++];
- if (!candidate.isNamed) {
- parameter = candidate;
- break;
- }
- }
- }
+ parameterKey = unnamedArgumentIndex++;
}
+ parameter = parameterMap[parameterKey];
if (resolver.isInferenceUpdate1Enabled &&
value is FunctionExpressionImpl) {
- (deferredClosures ??= []).add(_DeferredClosure(parameter, value, i));
+ (deferredClosures ??= [])
+ .add(_DeferredParamInfo(parameter, value, i, parameterKey));
identicalInfo?.add(null);
// The "why not promoted" list isn't really relevant for closures
// because promoting a closure doesn't even make sense. So we store an
@@ -602,24 +641,31 @@
}
}
-class _ClosureDependencies
- extends ClosureDependencies<TypeParameterElement, _DeferredClosure> {
+class _ClosureDependencies extends ClosureDependencies<TypeParameterElement,
+ _ParamInfo, _DeferredParamInfo> {
final TypeSystemImpl _typeSystem;
final Set<TypeParameterElement> _typeVariables;
- _ClosureDependencies(this._typeSystem, Iterable<_DeferredClosure> closures,
- this._typeVariables)
- : super(closures, _typeVariables);
+ _ClosureDependencies(
+ this._typeSystem,
+ Iterable<_DeferredParamInfo> deferredParamInfo,
+ this._typeVariables,
+ List<_ParamInfo> undeferredParamInfo)
+ : super(deferredParamInfo, _typeVariables, undeferredParamInfo);
@override
- Iterable<TypeParameterElement> typeVarsFreeInClosureArguments(
- _DeferredClosure closure) {
- var type = closure.parameter?.type;
+ Iterable<TypeParameterElement> typeVarsFreeInParamParams(
+ _DeferredParamInfo paramInfo) {
+ var type = paramInfo.parameter?.type;
if (type is FunctionType) {
+ var parameterMap = _computeParameterMap(type.parameters);
+ var explicitlyTypedParameters =
+ _computeExplicitlyTypedParameterSet(paramInfo.value);
Set<TypeParameterElement> result = {};
- for (var parameter in type.parameters) {
- result.addAll(_typeSystem.getFreeParameters(parameter.type,
+ for (var entry in parameterMap.entries) {
+ if (explicitlyTypedParameters.contains(entry.key)) continue;
+ result.addAll(_typeSystem.getFreeParameters(entry.value.type,
candidates: _typeVariables) ??
const []);
}
@@ -630,9 +676,9 @@
}
@override
- Iterable<TypeParameterElement> typeVarsFreeInClosureReturns(
- _DeferredClosure closure) {
- var type = closure.parameter?.type;
+ Iterable<TypeParameterElement> typeVarsFreeInParamReturns(
+ _ParamInfo paramInfo) {
+ var type = paramInfo.parameter?.type;
if (type is FunctionType) {
return _typeSystem.getFreeParameters(type.returnType,
candidates: _typeVariables) ??
@@ -649,15 +695,27 @@
/// Information about an invocation argument that needs to be resolved later due
/// to the fact that it's a closure and the `inference-update-1` feature is
/// enabled.
-class _DeferredClosure {
- /// The [ParameterElement] the closure is being passed to.
- final ParameterElement? parameter;
-
+class _DeferredParamInfo extends _ParamInfo {
/// The closure expression.
final FunctionExpression value;
/// The index into the argument list of the closure expression.
final int index;
- _DeferredClosure(this.parameter, this.value, this.index);
+ final Object parameterKey;
+
+ _DeferredParamInfo(
+ ParameterElement? parameter, this.value, this.index, this.parameterKey)
+ : super(parameter);
+}
+
+/// Information about an invocation argument that may or may not have already
+/// been resolved, as part of the deferred resolution mechanism for the
+/// `inference-update-1` feature.
+class _ParamInfo {
+ /// The function parameter corresponding to the argument, or `null` if we are
+ /// resolving a dynamic invocation.
+ final ParameterElement? parameter;
+
+ _ParamInfo(this.parameter);
}
diff --git a/pkg/analyzer/lib/src/generated/declaration_resolver.dart b/pkg/analyzer/lib/src/generated/declaration_resolver.dart
index c20f28d..041fc62 100644
--- a/pkg/analyzer/lib/src/generated/declaration_resolver.dart
+++ b/pkg/analyzer/lib/src/generated/declaration_resolver.dart
@@ -2,6 +2,9 @@
// 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.
+// See: https://github.com/dart-lang/linter/issues/3345
+// ignore_for_file: prefer_initializing_formals
+
import 'package:analyzer/dart/element/element.dart';
import 'package:analyzer/src/dart/element/element.dart';
diff --git a/pkg/analyzer/lib/src/summary2/library_builder.dart b/pkg/analyzer/lib/src/summary2/library_builder.dart
index 323b31e..3f1f4a4 100644
--- a/pkg/analyzer/lib/src/summary2/library_builder.dart
+++ b/pkg/analyzer/lib/src/summary2/library_builder.dart
@@ -2,18 +2,22 @@
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
+import 'package:analyzer/dart/analysis/features.dart';
+import 'package:analyzer/dart/analysis/utilities.dart';
import 'package:analyzer/dart/ast/ast.dart' as ast;
import 'package:analyzer/dart/element/element.dart';
import 'package:analyzer/src/dart/ast/ast.dart' as ast;
import 'package:analyzer/src/dart/ast/mixin_super_invoked_names.dart';
import 'package:analyzer/src/dart/element/element.dart';
import 'package:analyzer/src/dart/resolver/scope.dart';
+import 'package:analyzer/src/generated/source.dart';
import 'package:analyzer/src/summary2/combinator.dart';
import 'package:analyzer/src/summary2/constructor_initializer_resolver.dart';
import 'package:analyzer/src/summary2/default_value_resolver.dart';
import 'package:analyzer/src/summary2/element_builder.dart';
import 'package:analyzer/src/summary2/export.dart';
import 'package:analyzer/src/summary2/link.dart';
+import 'package:analyzer/src/summary2/macro_application.dart';
import 'package:analyzer/src/summary2/metadata_resolver.dart';
import 'package:analyzer/src/summary2/reference.dart';
import 'package:analyzer/src/summary2/reference_resolver.dart';
@@ -57,6 +61,10 @@
required this.units,
});
+ SourceFactory get _sourceFactory {
+ return linker.elementFactory.analysisContext.sourceFactory;
+ }
+
void addExporters() {
for (var element in element.exports) {
var exportedLibrary = element.exportedLibrary;
@@ -169,6 +177,64 @@
}
}
+ Future<void> executeMacroTypesPhase() async {
+ if (!element.featureSet.isEnabled(Feature.macros)) {
+ return;
+ }
+
+ var applier = LibraryMacroApplier(this);
+ var augmentationLibrary = await applier.executeMacroTypesPhase();
+ if (augmentationLibrary == null) {
+ return;
+ }
+
+ var parseResult = parseString(
+ content: augmentationLibrary,
+ featureSet: element.featureSet,
+ throwIfDiagnostics: false,
+ );
+ var unitNode = parseResult.unit as ast.CompilationUnitImpl;
+
+ // For now we model augmentation libraries as parts.
+ var unitUri = uri.resolve('_macro_types.dart');
+ var unitElement = CompilationUnitElementImpl(
+ source: _sourceFactory.forUri2(unitUri)!,
+ librarySource: element.source,
+ lineInfo: parseResult.lineInfo,
+ )
+ ..enclosingElement = element
+ ..isSynthetic = true
+ ..uri = unitUri.toString();
+
+ var unitReference = reference.getChild('@unit').getChild('$unitUri');
+ _bindReference(unitReference, unitElement);
+
+ element.parts.add(unitElement);
+
+ ElementBuilder(
+ libraryBuilder: this,
+ unitReference: unitReference,
+ unitElement: unitElement,
+ ).buildDeclarationElements(unitNode);
+
+ units.add(
+ LinkingUnit(
+ isDefiningUnit: false,
+ reference: unitReference,
+ node: unitNode,
+ element: unitElement,
+ ),
+ );
+
+ linker.macroGeneratedUnits.add(
+ LinkMacroGeneratedUnit(
+ uri: unitUri,
+ content: parseResult.content,
+ unit: parseResult.unit,
+ ),
+ );
+ }
+
void resolveConstructors() {
ConstructorInitializerResolver(linker, element).resolve();
}
@@ -281,7 +347,6 @@
unitElements.add(unitElement);
linkingUnits.add(
LinkingUnit(
- input: inputUnit,
isDefiningUnit: isDefiningUnit,
reference: unitReference,
node: unitNode,
@@ -312,14 +377,12 @@
}
class LinkingUnit {
- final LinkInputUnit input;
final bool isDefiningUnit;
final Reference reference;
final ast.CompilationUnitImpl node;
final CompilationUnitElementImpl element;
LinkingUnit({
- required this.input,
required this.isDefiningUnit,
required this.reference,
required this.node,
diff --git a/pkg/analyzer/lib/src/summary2/link.dart b/pkg/analyzer/lib/src/summary2/link.dart
index d4b9866..3afe40b 100644
--- a/pkg/analyzer/lib/src/summary2/link.dart
+++ b/pkg/analyzer/lib/src/summary2/link.dart
@@ -4,6 +4,8 @@
import 'dart:typed_data';
+import 'package:_fe_analyzer_shared/src/macros/executor/multi_executor.dart'
+ as macro;
import 'package:analyzer/dart/analysis/declared_variables.dart';
import 'package:analyzer/dart/ast/ast.dart' as ast;
import 'package:analyzer/dart/element/element.dart';
@@ -28,12 +30,14 @@
/// Note that AST units and tokens of [inputLibraries] will be damaged.
Future<LinkResult> link(
LinkedElementFactory elementFactory,
- List<LinkInputLibrary> inputLibraries,
-) async {
- var linker = Linker(elementFactory);
- linker.link(inputLibraries);
+ List<LinkInputLibrary> inputLibraries, {
+ macro.MultiMacroExecutor? macroExecutor,
+}) async {
+ var linker = Linker(elementFactory, macroExecutor);
+ await linker.link(inputLibraries);
return LinkResult(
resolutionBytes: linker.resolutionBytes,
+ macroGeneratedUnits: linker.macroGeneratedUnits,
);
}
@@ -48,6 +52,7 @@
class Linker {
final LinkedElementFactory elementFactory;
+ final macro.MultiMacroExecutor? macroExecutor;
/// Libraries that are being linked.
final Map<Uri, LibraryBuilder> builders = {};
@@ -58,7 +63,9 @@
late Uint8List resolutionBytes;
- Linker(this.elementFactory);
+ final List<LinkMacroGeneratedUnit> macroGeneratedUnits = [];
+
+ Linker(this.elementFactory, this.macroExecutor);
AnalysisContextImpl get analysisContext {
return elementFactory.analysisContext;
@@ -81,12 +88,12 @@
return elementNodes[element];
}
- void link(List<LinkInputLibrary> inputLibraries) {
+ Future<void> link(List<LinkInputLibrary> inputLibraries) async {
for (var inputLibrary in inputLibraries) {
LibraryBuilder.build(this, inputLibrary);
}
- _buildOutlines();
+ await _buildOutlines();
timerLinkingLinkingBundle.start();
_writeLibraries();
@@ -99,9 +106,9 @@
}
}
- void _buildOutlines() {
+ Future<void> _buildOutlines() async {
_createTypeSystemIfNotLinkingDartCore();
- _computeLibraryScopes();
+ await _computeLibraryScopes();
_createTypeSystem();
_resolveTypes();
_buildEnumChildren();
@@ -121,12 +128,16 @@
}
}
- void _computeLibraryScopes() {
+ Future<void> _computeLibraryScopes() async {
for (var library in builders.values) {
library.buildElements();
}
for (var library in builders.values) {
+ await library.executeMacroTypesPhase();
+ }
+
+ for (var library in builders.values) {
library.buildInitialExportScope();
}
@@ -285,10 +296,24 @@
String get uriStr => '$uri';
}
+class LinkMacroGeneratedUnit {
+ final Uri uri;
+ final String content;
+ final ast.CompilationUnit unit;
+
+ LinkMacroGeneratedUnit({
+ required this.uri,
+ required this.content,
+ required this.unit,
+ });
+}
+
class LinkResult {
final Uint8List resolutionBytes;
+ final List<LinkMacroGeneratedUnit> macroGeneratedUnits;
LinkResult({
required this.resolutionBytes,
+ required this.macroGeneratedUnits,
});
}
diff --git a/pkg/analyzer/lib/src/summary2/linked_element_factory.dart b/pkg/analyzer/lib/src/summary2/linked_element_factory.dart
index 64cb301..c296291 100644
--- a/pkg/analyzer/lib/src/summary2/linked_element_factory.dart
+++ b/pkg/analyzer/lib/src/summary2/linked_element_factory.dart
@@ -114,6 +114,12 @@
}
}
+ void dispose() {
+ for (var libraryReference in rootReference.children) {
+ _disposeLibrary(libraryReference.element);
+ }
+ }
+
Element? elementOfReference(Reference reference) {
if (reference.element != null) {
return reference.element;
@@ -184,7 +190,8 @@
void removeLibraries(Set<String> uriStrSet) {
for (var uriStr in uriStrSet) {
_libraryReaders.remove(uriStr);
- rootReference.removeChild(uriStr);
+ var libraryReference = rootReference.removeChild(uriStr);
+ _disposeLibrary(libraryReference?.element);
}
analysisSession.classHierarchy.removeOfLibraries(uriStrSet);
@@ -238,4 +245,10 @@
libraryElement.createLoadLibraryFunction();
}
+
+ void _disposeLibrary(Element? libraryElement) {
+ if (libraryElement is LibraryElementImpl) {
+ libraryElement.bundleMacroExecutor?.dispose();
+ }
+ }
}
diff --git a/pkg/analyzer/lib/src/summary2/macro.dart b/pkg/analyzer/lib/src/summary2/macro.dart
index a3502d0..74f3692 100644
--- a/pkg/analyzer/lib/src/summary2/macro.dart
+++ b/pkg/analyzer/lib/src/summary2/macro.dart
@@ -2,10 +2,72 @@
// 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:isolate';
import 'dart:typed_data';
+import 'package:_fe_analyzer_shared/src/macros/api.dart' as macro;
+import 'package:_fe_analyzer_shared/src/macros/executor.dart' as macro;
+import 'package:_fe_analyzer_shared/src/macros/executor/isolated_executor.dart'
+ as isolated_executor;
+import 'package:_fe_analyzer_shared/src/macros/executor/multi_executor.dart'
+ as macro;
+import 'package:_fe_analyzer_shared/src/macros/executor/serialization.dart'
+ as macro;
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;
+ final Uint8List kernelBytes;
+ Uri? _kernelUriCached;
+
+ BundleMacroExecutor({
+ required this.macroExecutor,
+ required Uint8List kernelBytes,
+ required Set<Uri> libraries,
+ }) : kernelBytes = Uint8List.fromList(kernelBytes) {
+ _executorFactoryToken = macroExecutor.registerExecutorFactory(
+ () => isolated_executor.start(
+ macro.SerializationMode.byteDataServer,
+ _kernelUri,
+ ),
+ libraries,
+ );
+ }
+
+ Uri get _kernelUri {
+ return _kernelUriCached ??=
+ // ignore: avoid_dynamic_calls
+ (Isolate.current as dynamic).createUriForKernelBlob(kernelBytes);
+ }
+
+ void dispose() {
+ macroExecutor.unregisterExecutorFactory(_executorFactoryToken);
+ final kernelUriCached = _kernelUriCached;
+ if (kernelUriCached != null) {
+ // ignore: avoid_dynamic_calls
+ (Isolate.current as dynamic).unregisterKernelBlobUri(kernelUriCached);
+ _kernelUriCached = null;
+ }
+ }
+
+ Future<MacroClassInstance> instantiate({
+ required Uri libraryUri,
+ required String className,
+ required String constructorName,
+ required macro.Arguments arguments,
+ required macro.Declaration declaration,
+ required macro.IdentifierResolver identifierResolver,
+ }) async {
+ var instanceIdentifier = await macroExecutor.instantiateMacro(
+ libraryUri, className, constructorName, arguments);
+ return MacroClassInstance._(
+ this, identifierResolver, declaration, instanceIdentifier);
+ }
+}
+
class MacroClass {
final String name;
final List<String> constructors;
@@ -16,6 +78,26 @@
});
}
+class MacroClassInstance {
+ final BundleMacroExecutor _bundleExecutor;
+ final macro.IdentifierResolver _identifierResolver;
+ final macro.Declaration _declaration;
+ final macro.MacroInstanceIdentifier _instanceIdentifier;
+
+ MacroClassInstance._(
+ this._bundleExecutor,
+ this._identifierResolver,
+ this._declaration,
+ this._instanceIdentifier,
+ );
+
+ Future<macro.MacroExecutionResult> executeTypesPhase() async {
+ macro.MacroExecutor executor = _bundleExecutor.macroExecutor;
+ return await executor.executeTypesPhase(
+ _instanceIdentifier, _declaration, _identifierResolver);
+ }
+}
+
abstract class MacroFileEntry {
String get content;
@@ -47,4 +129,6 @@
required this.path,
required this.classes,
});
+
+ String get uriStr => uri.toString();
}
diff --git a/pkg/analyzer/lib/src/summary2/macro_application.dart b/pkg/analyzer/lib/src/summary2/macro_application.dart
new file mode 100644
index 0000000..5ef25c0
--- /dev/null
+++ b/pkg/analyzer/lib/src/summary2/macro_application.dart
@@ -0,0 +1,148 @@
+// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'package:_fe_analyzer_shared/src/macros/api.dart' as macro;
+import 'package:_fe_analyzer_shared/src/macros/executor.dart' as macro;
+import 'package:_fe_analyzer_shared/src/macros/executor/introspection_impls.dart'
+ as macro;
+import 'package:_fe_analyzer_shared/src/macros/executor/remote_instance.dart'
+ as macro;
+import 'package:analyzer/dart/ast/ast.dart';
+import 'package:analyzer/src/dart/element/element.dart';
+import 'package:analyzer/src/summary2/library_builder.dart';
+import 'package:analyzer/src/summary2/macro.dart';
+
+class LibraryMacroApplier {
+ final LibraryBuilder libraryBuilder;
+
+ final Map<ClassDeclaration, macro.ClassDeclaration> _classDeclarations = {};
+
+ LibraryMacroApplier(this.libraryBuilder);
+
+ /// TODO(scheglov) check `shouldExecute`.
+ /// TODO(scheglov) check `supportsDeclarationKind`.
+ Future<String?> executeMacroTypesPhase() async {
+ var macroResults = <macro.MacroExecutionResult>[];
+ for (var unitElement in libraryBuilder.element.units) {
+ for (var classElement in unitElement.classes) {
+ var classNode = libraryBuilder.linker.elementNodes[classElement];
+ // TODO(scheglov) support other declarations
+ if (classNode is ClassDeclaration) {
+ for (var annotation in classNode.metadata) {
+ var annotationNameNode = annotation.name;
+ if (annotationNameNode is SimpleIdentifier &&
+ annotation.arguments != null) {
+ // TODO(scheglov) Create a Scope.
+ for (var import in libraryBuilder.element.imports) {
+ var importedLibrary = import.importedLibrary;
+ if (importedLibrary is LibraryElementImpl) {
+ var importedUri = importedLibrary.source.uri;
+ if (!libraryBuilder.linker.builders
+ .containsKey(importedUri)) {
+ var lookupResult = importedLibrary.scope.lookup(
+ annotationNameNode.name,
+ );
+ var getter = lookupResult.getter;
+ if (getter is ClassElementImpl && getter.isMacro) {
+ var macroExecutor = importedLibrary.bundleMacroExecutor;
+ if (macroExecutor != null) {
+ var macroResult = await _runSingleMacro(
+ macroExecutor,
+ getClassDeclaration(classNode),
+ getter,
+ );
+ if (macroResult.isNotEmpty) {
+ macroResults.add(macroResult);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ var macroExecutor = libraryBuilder.linker.macroExecutor;
+ if (macroExecutor != null && macroResults.isNotEmpty) {
+ var code = macroExecutor.buildAugmentationLibrary(
+ macroResults,
+ _resolveIdentifier,
+ _inferOmittedType,
+ );
+ return code.trim();
+ }
+ return null;
+ }
+
+ macro.ClassDeclaration getClassDeclaration(ClassDeclaration node) {
+ return _classDeclarations[node] ??= _createClassDeclaration(node);
+ }
+
+ macro.ClassDeclaration _createClassDeclaration(ClassDeclaration node) {
+ return macro.ClassDeclarationImpl(
+ id: macro.RemoteInstance.uniqueId,
+ identifier: _IdentifierImpl(
+ id: macro.RemoteInstance.uniqueId,
+ name: node.name.name,
+ ),
+ // TODO(scheglov): Support typeParameters
+ typeParameters: [],
+ // TODO(scheglov): Support interfaces
+ interfaces: [],
+ isAbstract: node.abstractKeyword != null,
+ isExternal: false,
+ // TODO(scheglov): Support mixins
+ mixins: [],
+ // TODO(scheglov): Support superclass
+ superclass: null,
+ );
+ }
+
+ macro.TypeAnnotation _inferOmittedType(
+ macro.OmittedTypeAnnotation omittedType,
+ ) {
+ throw UnimplementedError();
+ }
+
+ macro.ResolvedIdentifier _resolveIdentifier(macro.Identifier identifier) {
+ throw UnimplementedError();
+ }
+
+ Future<macro.MacroExecutionResult> _runSingleMacro(
+ BundleMacroExecutor macroExecutor,
+ macro.Declaration declaration,
+ ClassElementImpl classElement,
+ ) async {
+ var macroInstance = await macroExecutor.instantiate(
+ libraryUri: classElement.librarySource.uri,
+ className: classElement.name,
+ constructorName: '', // TODO
+ arguments: Arguments([], {}), // TODO
+ declaration: declaration,
+ identifierResolver: _FakeIdentifierResolver(),
+ );
+ return await macroInstance.executeTypesPhase();
+ }
+}
+
+class _FakeIdentifierResolver extends macro.IdentifierResolver {
+ @override
+ Future<macro.Identifier> resolveIdentifier(Uri library, String name) {
+ // TODO: implement resolveIdentifier
+ throw UnimplementedError();
+ }
+}
+
+class _IdentifierImpl extends macro.IdentifierImpl {
+ _IdentifierImpl({required int id, required String name})
+ : super(id: id, name: name);
+}
+
+extension on macro.MacroExecutionResult {
+ bool get isNotEmpty =>
+ libraryAugmentations.isNotEmpty || classAugmentations.isNotEmpty;
+}
diff --git a/pkg/analyzer/lib/src/util/lru_map.dart b/pkg/analyzer/lib/src/util/lru_map.dart
index 66f6129..fe2cb1e 100644
--- a/pkg/analyzer/lib/src/util/lru_map.dart
+++ b/pkg/analyzer/lib/src/util/lru_map.dart
@@ -37,7 +37,7 @@
_map[key] = value;
if (_map.length > _maxSize) {
K evictedKey = _map.keys.first;
- V evictedValue = _map.remove(evictedKey)!;
+ V evictedValue = _map.remove(evictedKey) as V;
if (_handler != null) {
_handler!.call(evictedKey, evictedValue);
}
diff --git a/pkg/analyzer/pubspec.yaml b/pkg/analyzer/pubspec.yaml
index ae91994..e0b366d 100644
--- a/pkg/analyzer/pubspec.yaml
+++ b/pkg/analyzer/pubspec.yaml
@@ -29,6 +29,7 @@
kernel:
path: ../kernel
linter: ^1.12.0
+ lints: ^2.0.0
matcher: ^0.12.10
test: ^1.16.0
test_reflective_loader: ^0.2.0
diff --git a/pkg/analyzer/test/src/dart/resolution/context_collection_resolution.dart b/pkg/analyzer/test/src/dart/resolution/context_collection_resolution.dart
index 5b9d9a4..85b0d8c 100644
--- a/pkg/analyzer/test/src/dart/resolution/context_collection_resolution.dart
+++ b/pkg/analyzer/test/src/dart/resolution/context_collection_resolution.dart
@@ -177,7 +177,9 @@
}
void disposeAnalysisContextCollection() {
- if (_analysisContextCollection != null) {
+ final analysisContextCollection = _analysisContextCollection;
+ if (analysisContextCollection != null) {
+ analysisContextCollection.dispose();
_analysisContextCollection = null;
}
}
@@ -216,6 +218,10 @@
);
}
+ Future<void> tearDown() async {
+ disposeAnalysisContextCollection();
+ }
+
/// Override this method to update [analysisOptions] for every context root,
/// the default or already updated with `analysis_options.yaml` file.
void updateAnalysisOptions(AnalysisOptionsImpl analysisOptions) {}
diff --git a/pkg/analyzer/test/src/dart/resolution/macro_test.dart b/pkg/analyzer/test/src/dart/resolution/macro_test.dart
index 2b41573..a3d12b2 100644
--- a/pkg/analyzer/test/src/dart/resolution/macro_test.dart
+++ b/pkg/analyzer/test/src/dart/resolution/macro_test.dart
@@ -34,6 +34,9 @@
void setUp() {
super.setUp();
+ // TODO(scheglov) Dependency tracking for macros is not right yet.
+ useEmptyByteStore();
+
writeTestPackageConfig(
PackageConfigFileBuilder(),
macrosEnvironment: MacrosEnvironment.instance,
@@ -41,14 +44,30 @@
}
test_0() async {
- await assertNoErrorsInCode(r'''
+ newFile2('$testPackageLibPath/a.dart', r'''
import 'dart:async';
import 'package:_fe_analyzer_shared/src/macros/api.dart';
macro class EmptyMacro implements ClassTypesMacro {
const EmptyMacro();
- FutureOr<void> buildTypesForClass(clazz, builder) {}
+
+ FutureOr<void> buildTypesForClass(clazz, builder) {
+ var targetName = clazz.identifier.name;
+ builder.declareType(
+ '${targetName}_Macro',
+ DeclarationCode.fromString('class ${targetName}_Macro {}'),
+ );
+ }
}
''');
+
+ await assertNoErrorsInCode('''
+import 'a.dart';
+
+@EmptyMacro()
+class A {}
+
+void f(A_Macro a) {}
+''');
}
}
diff --git a/pkg/analyzer/test/src/dart/resolution/type_inference/inference_update_1_test.dart b/pkg/analyzer/test/src/dart/resolution/type_inference/inference_update_1_test.dart
index f6700c42..81d22f9 100644
--- a/pkg/analyzer/test/src/dart/resolution/type_inference/inference_update_1_test.dart
+++ b/pkg/analyzer/test/src/dart/resolution/type_inference/inference_update_1_test.dart
@@ -205,6 +205,66 @@
_isEnabled ? 'int' : 'Object?');
}
+ test_horizontal_inference_unnecessary_due_to_explicit_parameter_type() async {
+ // In this example, there is no need for horizontal type inference because
+ // the type of `x` is explicit.
+ await assertErrorsInCode('''
+test(List<int> list) {
+ var a = list.fold(null, (int? x, y) => (x ?? 0) + y);
+}
+''', [
+ error(HintCode.UNUSED_LOCAL_VARIABLE, 29, 1),
+ ]);
+ assertType(findElement.localVar('a').type, 'int?');
+ assertType(findElement.parameter('x').type, 'int?');
+ assertType(findElement.parameter('y').type, 'int');
+ expect(findNode.binary('+ y').staticElement!.enclosingElement.name, 'num');
+ }
+
+ test_horizontal_inference_unnecessary_due_to_explicit_parameter_type_named() async {
+ // In this example, there is no need for horizontal type inference because
+ // the type of `x` is explicit.
+ await assertErrorsInCode('''
+T f<T>(T a, T Function({required T x, required int y}) b) => throw '';
+test() {
+ var a = f(null, ({int? x, required y}) => (x ?? 0) + y);
+}
+''', [
+ error(HintCode.UNUSED_LOCAL_VARIABLE, 86, 1),
+ ]);
+ assertType(findElement.localVar('a').type, 'int?');
+ assertType(findElement.parameter('x').type, 'int?');
+ assertType(findElement.parameter('y').type, 'int');
+ expect(findNode.binary('+ y').staticElement!.enclosingElement.name, 'num');
+ }
+
+ test_horizontal_inference_unnecessary_due_to_no_dependency() async {
+ // In this example, there is no dependency between the two parameters of
+ // `f`, so there should be no horizontal type inference between inferring
+ // `null` and inferring `() => 0`. (If there were horizontal type inference
+ // between them, that would be a problem, because we would infer a type of
+ // `null` for `T`).
+ await assertNoErrorsInCode('''
+void f<T>(T Function() g, T t) {}
+test() => f(() => 0, null);
+''');
+ assertType(
+ findNode.methodInvocation('f(').typeArgumentTypes!.single, 'int?');
+ assertType(findNode.methodInvocation('f(').staticInvokeType,
+ 'void Function(int? Function(), int?)');
+ }
+
+ test_horizontal_inference_with_callback() async {
+ await assertNoErrorsInCode('''
+test(void Function<T>(T, void Function(T)) f) {
+ f(0, (x) {
+ x;
+ });
+}
+''');
+ assertType(findNode.simple('x;'), _isEnabled ? 'int' : 'Object?');
+ }
+
test_write_capture_deferred() async {
await assertNoErrorsInCode('''
test(int? i) {
diff --git a/pkg/analyzer/test/src/fasta/recovery/partial_code/instance_creation_test.dart b/pkg/analyzer/test/src/fasta/recovery/partial_code/instance_creation_test.dart
index 9289e3a..28a3686 100644
--- a/pkg/analyzer/test/src/fasta/recovery/partial_code/instance_creation_test.dart
+++ b/pkg/analyzer/test/src/fasta/recovery/partial_code/instance_creation_test.dart
@@ -27,7 +27,7 @@
return <TestDescriptor>[
TestDescriptor(
'${keyword}_keyword',
- '$keyword',
+ keyword,
[
ParserErrorCode.MISSING_IDENTIFIER,
ParserErrorCode.EXPECTED_TOKEN,
diff --git a/pkg/analyzer/test/src/summary/element_text.dart b/pkg/analyzer/test/src/summary/element_text.dart
index d8bae60..5790c59 100644
--- a/pkg/analyzer/test/src/summary/element_text.dart
+++ b/pkg/analyzer/test/src/summary/element_text.dart
@@ -919,7 +919,7 @@
if (uri.isScheme('file')) {
uriStr = uri.pathSegments.last;
}
- buffer.write('$uriStr');
+ buffer.write(uriStr);
} else {
buffer.write('<unresolved>');
}
diff --git a/pkg/analyzer/test/src/summary/elements_base.dart b/pkg/analyzer/test/src/summary/elements_base.dart
index f3d2095..d0e24164 100644
--- a/pkg/analyzer/test/src/summary/elements_base.dart
+++ b/pkg/analyzer/test/src/summary/elements_base.dart
@@ -4,12 +4,15 @@
import 'dart:typed_data';
+import 'package:_fe_analyzer_shared/src/macros/executor/multi_executor.dart'
+ as macro;
import 'package:analyzer/dart/analysis/declared_variables.dart';
import 'package:analyzer/dart/analysis/features.dart';
import 'package:analyzer/dart/ast/ast.dart';
import 'package:analyzer/file_system/file_system.dart';
import 'package:analyzer/src/context/context.dart';
import 'package:analyzer/src/dart/analysis/session.dart';
+import 'package:analyzer/src/dart/ast/ast.dart';
import 'package:analyzer/src/dart/element/class_hierarchy.dart';
import 'package:analyzer/src/dart/element/element.dart';
import 'package:analyzer/src/dart/element/inheritance_manager3.dart';
@@ -22,10 +25,12 @@
import 'package:analyzer/src/summary2/informative_data.dart';
import 'package:analyzer/src/summary2/link.dart';
import 'package:analyzer/src/summary2/linked_element_factory.dart';
+import 'package:analyzer/src/summary2/macro.dart';
import 'package:analyzer/src/summary2/reference.dart';
import 'package:analyzer/src/test_utilities/mock_sdk.dart';
import 'package:analyzer/src/test_utilities/resource_provider_mixin.dart';
import 'package:analyzer/src/util/uri.dart';
+import 'package:path/path.dart' as package_path;
import 'package:test_reflective_loader/test_reflective_loader.dart';
import '../../util/feature_sets.dart';
@@ -37,6 +42,9 @@
/// The shared SDK bundle, computed once and shared among test invocations.
static _SdkBundle? _sdkBundle;
+ MacroKernelBuilder? macroKernelBuilder;
+ macro.MultiMacroExecutor? macroExecutor;
+
/// The set of features enabled in this test.
FeatureSet featureSet = FeatureSets.latestWithExperiments;
@@ -113,7 +121,9 @@
String get testPackageLibPath => '$testPackageRootPath/lib';
- String get testPackageRootPath => '/home/test';
+ String get testPackageRootPath => '$workspaceRootPath/test';
+
+ String get workspaceRootPath => '/home';
void addSource(String path, String contents) {
newFile2(path, contents);
@@ -123,6 +133,7 @@
String text, {
bool allowErrors = false,
bool dumpSummaries = false,
+ List<Set<String>>? preBuildSequence,
}) async {
var testFile = newFile2(testFilePath, text);
var testUri = sourceFactory.pathToUri(testFile.path)!;
@@ -160,7 +171,22 @@
),
);
- var linkResult = await link(elementFactory, inputLibraries);
+ _linkConfiguredLibraries(
+ elementFactory,
+ inputLibraries,
+ preBuildSequence,
+ );
+
+ var linkResult = await link(
+ elementFactory,
+ inputLibraries,
+ macroExecutor: macroExecutor,
+ );
+
+ for (var macroUnit in linkResult.macroGeneratedUnits) {
+ var informativeBytes = writeUnitInformative(macroUnit.unit);
+ unitsInformativeBytes[macroUnit.uri] = informativeBytes;
+ }
if (!keepLinkingLibraries) {
elementFactory.removeBundle(
@@ -279,6 +305,79 @@
}
}
+ /// If there are any [macroLibraries], build the kernel and prepare for
+ /// execution.
+ void _buildMacroLibraries(
+ LinkedElementFactory elementFactory,
+ List<MacroLibrary> macroLibraries,
+ ) {
+ if (macroLibraries.isEmpty) {
+ return;
+ }
+
+ final macroKernelBuilder = this.macroKernelBuilder;
+ if (macroKernelBuilder == null) {
+ return;
+ }
+
+ final macroExecutor = this.macroExecutor;
+ if (macroExecutor == null) {
+ return;
+ }
+
+ var macroKernelBytes = macroKernelBuilder.build(
+ fileSystem: _MacroFileSystem(resourceProvider),
+ libraries: macroLibraries,
+ );
+
+ var bundleMacroExecutor = BundleMacroExecutor(
+ macroExecutor: macroExecutor,
+ kernelBytes: macroKernelBytes,
+ libraries: macroLibraries.map((e) => e.uri).toSet(),
+ );
+
+ for (var macroLibrary in macroLibraries) {
+ var uriStr = macroLibrary.uriStr;
+ var element = elementFactory.libraryOfUri2(uriStr);
+ element.bundleMacroExecutor = bundleMacroExecutor;
+ }
+ }
+
+ /// If there are any libraries in the [uriStrSetList], link these subsets
+ /// of [inputLibraries] (and remove from it), build macro kernels, prepare
+ /// for executing macros.
+ void _linkConfiguredLibraries(
+ LinkedElementFactory elementFactory,
+ List<LinkInputLibrary> inputLibraries,
+ List<Set<String>>? uriStrSetList,
+ ) {
+ if (uriStrSetList == null) {
+ return;
+ }
+
+ for (var uriStrSet in uriStrSetList) {
+ var cycleInputLibraries = <LinkInputLibrary>[];
+ var macroLibraries = <MacroLibrary>[];
+ for (var inputLibrary in inputLibraries) {
+ if (uriStrSet.contains(inputLibrary.uriStr)) {
+ cycleInputLibraries.add(inputLibrary);
+ _addMacroLibrary(macroLibraries, inputLibrary);
+ }
+ }
+
+ link(
+ elementFactory,
+ cycleInputLibraries,
+ macroExecutor: macroExecutor,
+ );
+
+ _buildMacroLibraries(elementFactory, macroLibraries);
+
+ // Remove libraries that we just linked.
+ cycleInputLibraries.forEach(inputLibraries.remove);
+ }
+ }
+
String _readSafely(String path) {
try {
var file = resourceProvider.getFile(path);
@@ -287,6 +386,53 @@
return '';
}
}
+
+ /// If there are any macros in the [inputLibrary], add it.
+ static void _addMacroLibrary(
+ List<MacroLibrary> macroLibraries,
+ LinkInputLibrary inputLibrary,
+ ) {
+ var macroClasses = <MacroClass>[];
+ for (var inputUnit in inputLibrary.units) {
+ for (var declaration in inputUnit.unit.declarations) {
+ if (declaration is ClassDeclarationImpl &&
+ declaration.macroKeyword != null) {
+ var constructors =
+ declaration.members.whereType<ConstructorDeclaration>().toList();
+ if (constructors.isEmpty) {
+ macroClasses.add(
+ MacroClass(
+ name: declaration.name.name,
+ constructors: [''],
+ ),
+ );
+ } else {
+ var constructorNames = constructors
+ .map((e) => e.name?.name ?? '')
+ .where((e) => !e.startsWith('_'))
+ .toList();
+ if (constructorNames.isNotEmpty) {
+ macroClasses.add(
+ MacroClass(
+ name: declaration.name.name,
+ constructors: constructorNames,
+ ),
+ );
+ }
+ }
+ }
+ }
+ }
+ if (macroClasses.isNotEmpty) {
+ macroLibraries.add(
+ MacroLibrary(
+ uri: inputLibrary.uri,
+ path: inputLibrary.source.fullName,
+ classes: macroClasses,
+ ),
+ );
+ }
+ }
}
class _AnalysisSessionForLinking implements AnalysisSessionImpl {
@@ -300,6 +446,35 @@
noSuchMethod(Invocation invocation) => super.noSuchMethod(invocation);
}
+/// [MacroFileEntry] adapter for [File].
+class _MacroFileEntry implements MacroFileEntry {
+ final File file;
+
+ _MacroFileEntry(this.file);
+
+ @override
+ String get content => file.readAsStringSync();
+
+ @override
+ bool get exists => file.exists;
+}
+
+/// [MacroFileSystem] adapter for [ResourceProvider].
+class _MacroFileSystem implements MacroFileSystem {
+ final ResourceProvider resourceProvider;
+
+ _MacroFileSystem(this.resourceProvider);
+
+ @override
+ package_path.Context get pathContext => resourceProvider.pathContext;
+
+ @override
+ MacroFileEntry getFile(String path) {
+ var file = resourceProvider.getFile(path);
+ return _MacroFileEntry(file);
+ }
+}
+
class _SdkBundle {
final Uint8List resolutionBytes;
diff --git a/pkg/analyzer/test/src/summary/macro_test.dart b/pkg/analyzer/test/src/summary/macro_test.dart
index be36b5a..c46dce0 100644
--- a/pkg/analyzer/test/src/summary/macro_test.dart
+++ b/pkg/analyzer/test/src/summary/macro_test.dart
@@ -2,12 +2,25 @@
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
+import 'package:_fe_analyzer_shared/src/macros/executor/multi_executor.dart'
+ as macro;
+import 'package:analyzer/src/test_utilities/package_config_file_builder.dart';
+import 'package:test/test.dart';
import 'package:test_reflective_loader/test_reflective_loader.dart';
import 'element_text.dart';
import 'elements_base.dart';
+import 'repository_macro_kernel_builder.dart';
main() {
+ try {
+ MacrosEnvironment.instance;
+ } catch (_) {
+ print('Cannot initialize environment. Skip macros tests.');
+ test('fake', () {});
+ return;
+ }
+
defineReflectiveSuite(() {
defineReflectiveTests(MacroElementsKeepLinkingTest);
defineReflectiveTests(MacroElementsFromBytesTest);
@@ -30,6 +43,79 @@
@override
bool get keepLinkingLibraries => false;
+ /// The path for external packages.
+ String get packagesRootPath => '/packages';
+
+ Future<void> setUp() async {
+ writeTestPackageConfig(
+ PackageConfigFileBuilder(),
+ macrosEnvironment: MacrosEnvironment.instance,
+ );
+
+ macroKernelBuilder = DartRepositoryMacroKernelBuilder(
+ MacrosEnvironment.instance.platformDillBytes,
+ );
+
+ macroExecutor = macro.MultiMacroExecutor();
+ }
+
+ Future<void> tearDown() async {
+ await macroExecutor?.close();
+ }
+
+ test_build_types() async {
+ newFile2('$testPackageLibPath/a.dart', r'''
+import 'dart:async';
+import 'package:_fe_analyzer_shared/src/macros/api.dart';
+
+macro class MyMacro implements ClassTypesMacro {
+ FutureOr<void> buildTypesForClass(clazz, builder) {
+ builder.declareType(
+ 'MyClass',
+ DeclarationCode.fromString('class MyClass {}'),
+ );
+ }
+}
+''');
+
+ var library = await buildLibrary(r'''
+import 'a.dart';
+
+@MyMacro()
+class A {}
+''', preBuildSequence: [
+ {'package:test/a.dart'}
+ ]);
+
+ checkElementText(library, r'''
+library
+ imports
+ package:test/a.dart
+ definingUnit
+ classes
+ class A @35
+ metadata
+ Annotation
+ atSign: @ @18
+ name: SimpleIdentifier
+ token: MyMacro @19
+ staticElement: package:test/a.dart::@class::MyMacro
+ staticType: null
+ arguments: ArgumentList
+ leftParenthesis: ( @26
+ rightParenthesis: ) @27
+ element: package:test/a.dart::@class::MyMacro::@constructor::•
+ constructors
+ synthetic @-1
+ parts
+ package:test/_macro_types.dart
+ classes
+ class MyClass @6
+ constructors
+ synthetic @-1
+''');
+ }
+
test_class_macro() async {
var library = await buildLibrary(r'''
macro class A {}
@@ -72,4 +158,32 @@
Object
''');
}
+
+ void writeTestPackageConfig(
+ PackageConfigFileBuilder config, {
+ MacrosEnvironment? macrosEnvironment,
+ }) {
+ config = config.copy();
+
+ config.add(
+ name: 'test',
+ rootPath: testPackageRootPath,
+ );
+
+ if (macrosEnvironment != null) {
+ var packagesRootFolder = getFolder(packagesRootPath);
+ macrosEnvironment.packageSharedFolder.copyTo(packagesRootFolder);
+ config.add(
+ name: '_fe_analyzer_shared',
+ rootPath: getFolder('$packagesRootPath/_fe_analyzer_shared').path,
+ );
+ }
+
+ newPackageConfigJsonFile(
+ testPackageRootPath,
+ config.toContent(
+ toUriStr: toUriStr,
+ ),
+ );
+ }
}
diff --git a/pkg/analyzer/test/src/summary/repository_macro_kernel_builder.dart b/pkg/analyzer/test/src/summary/repository_macro_kernel_builder.dart
index 615a927..8af654a 100644
--- a/pkg/analyzer/test/src/summary/repository_macro_kernel_builder.dart
+++ b/pkg/analyzer/test/src/summary/repository_macro_kernel_builder.dart
@@ -92,7 +92,7 @@
/// Just like [DartRepositoryMacroKernelBuilder], this is a temporary
/// implementation.
class MacrosEnvironment {
- static late final instance = MacrosEnvironment._();
+ static final instance = MacrosEnvironment._();
final _resourceProvider = MemoryResourceProvider(context: package_path.posix);
late final Uint8List platformDillBytes;
diff --git a/pkg/analyzer/test/src/task/strong/checker_test.dart b/pkg/analyzer/test/src/task/strong/checker_test.dart
index db57448..afb2f16 100644
--- a/pkg/analyzer/test/src/task/strong/checker_test.dart
+++ b/pkg/analyzer/test/src/task/strong/checker_test.dart
@@ -2890,9 +2890,9 @@
class L<T> {}
class M<T> extends L<T> {}
// L<dynamic|Object>
-// / \
+// /
// M<dynamic|Object> L<A>
-// \ /
+// /
// M<A>
// In normal Dart, there are additional edges
// from M<A> to M<dynamic>
diff --git a/pkg/analyzer/test/src/util/glob_test.dart b/pkg/analyzer/test/src/util/glob_test.dart
index 30ba6c7..3810b76 100644
--- a/pkg/analyzer/test/src/util/glob_test.dart
+++ b/pkg/analyzer/test/src/util/glob_test.dart
@@ -33,7 +33,7 @@
void test_specialChars() {
Glob glob = Glob(r'/', r'*.dart');
expect(glob.matches(r'a.dart'), isTrue);
- expect(glob.matches('_-\a.dart'), isTrue);
+ expect(glob.matches('_-a.dart'), isTrue);
expect(glob.matches(r'^$*?.dart'), isTrue);
expect(glob.matches(r'()[]{}.dart'), isTrue);
expect(glob.matches('\u2665.dart'), isTrue);
@@ -99,7 +99,7 @@
void test_specialChars() {
Glob glob = Glob(r'\', r'*.dart');
expect(glob.matches(r'a.dart'), isTrue);
- expect(glob.matches('_-\a.dart'), isTrue);
+ expect(glob.matches('_-a.dart'), isTrue);
expect(glob.matches(r'^$*?.dart'), isTrue);
expect(glob.matches(r'()[]{}.dart'), isTrue);
expect(glob.matches('\u2665.dart'), isTrue);
diff --git a/pkg/analyzer/test/src/workspace/bazel_watcher_test.dart b/pkg/analyzer/test/src/workspace/bazel_watcher_test.dart
index 23817f4..e33dd39 100644
--- a/pkg/analyzer/test/src/workspace/bazel_watcher_test.dart
+++ b/pkg/analyzer/test/src/workspace/bazel_watcher_test.dart
@@ -159,7 +159,7 @@
];
_MockPollTrigger? trigger1;
_MockPollTrigger? trigger2;
- var triggerFactory = (String workspace) {
+ _MockPollTrigger triggerFactory(String workspace) {
if (workspace == convertPath('/workspace1')) {
trigger1 = _MockPollTrigger();
return trigger1!;
@@ -169,7 +169,8 @@
} else {
throw ArgumentError('Got unexpected workspace: `$workspace`');
}
- };
+ }
+
var recPort = ReceivePort();
// Note that we provide below a dummy `ReceivePort` that will *not* be used.
// We'll directly call `handleRequest` to avoid any problems with various
@@ -240,8 +241,8 @@
// The `_addResources`/`_deleteResources` functions recognize a folder by a
// trailing `/`, but everywhere else we need to use normalized paths.
- var addFolder = (path) => _addResources(['$path/']);
- var deleteFolder = (path) => _deleteResources(['$path/']);
+ void addFolder(path) => _addResources(['$path/']);
+ void deleteFolder(path) => _deleteResources(['$path/']);
var candidates = [
convertPath('/workspace/bazel-out'),
diff --git a/pkg/analyzer/tool/analysis_driver/inspect_exception.dart b/pkg/analyzer/tool/analysis_driver/inspect_exception.dart
index 4eda86e..e0d467c 100644
--- a/pkg/analyzer/tool/analysis_driver/inspect_exception.dart
+++ b/pkg/analyzer/tool/analysis_driver/inspect_exception.dart
@@ -38,9 +38,9 @@
for (var file in context.files) {
print("=" * 40);
- print('${file.path}');
+ print(file.path);
print("-" * 40);
- print('${file.content}');
+ print(file.content);
print('');
print('');
print('');
diff --git a/pkg/analyzer/tool/messages/error_code_info.dart b/pkg/analyzer/tool/messages/error_code_info.dart
index d64f3b7..0b1d48e 100644
--- a/pkg/analyzer/tool/messages/error_code_info.dart
+++ b/pkg/analyzer/tool/messages/error_code_info.dart
@@ -99,7 +99,7 @@
/// strings. TODO(paulberry): share this regexp (and the code for interpreting
/// it) between the CFE and analyzer.
final RegExp _placeholderPattern =
- RegExp("#\([-a-zA-Z0-9_]+\)(?:%\([0-9]*\)\.\([0-9]+\))?");
+ RegExp("#([-a-zA-Z0-9_]+)(?:%([0-9]*).([0-9]+))?");
/// Convert a CFE template string (which uses placeholders like `#string`) to
/// an analyzer template string (which uses placeholders like `{0}`).
diff --git a/pkg/analyzer/tool/summary/mini_ast.dart b/pkg/analyzer/tool/summary/mini_ast.dart
index ec11a1e..20c7dd4 100644
--- a/pkg/analyzer/tool/summary/mini_ast.dart
+++ b/pkg/analyzer/tool/summary/mini_ast.dart
@@ -361,8 +361,7 @@
var constructorName = popIfNotNull(periodBeforeName) as String?;
pop(); // Type arguments
var name = pop() as String;
- push(Annotation(name, constructorName,
- arguments == null ? null : arguments.cast<Expression>()));
+ push(Annotation(name, constructorName, arguments?.cast<Expression>()));
}
@override
diff --git a/runtime/tests/vm/dart/finalizer/finalizer_zone_run_gc_test.dart b/runtime/tests/vm/dart/finalizer/finalizer_zone_run_gc_test.dart
index bcc6d9e1..3c5dfa1 100644
--- a/runtime/tests/vm/dart/finalizer/finalizer_zone_run_gc_test.dart
+++ b/runtime/tests/vm/dart/finalizer/finalizer_zone_run_gc_test.dart
@@ -45,6 +45,9 @@
// Now we have.
Expect.equals(expectedZone, actualZone);
+
+ // Make sure finalizer is still reachable.
+ reachabilityFence(finalizer);
}
Future<void> testFinalizerException() async {
@@ -70,4 +73,7 @@
Expect.isNull(caughtError);
await yieldToMessageLoop();
Expect.isNotNull(caughtError);
+
+ // Make sure finalizer is still reachable.
+ reachabilityFence(finalizer);
}
diff --git a/runtime/tests/vm/dart/finalizer/helpers.dart b/runtime/tests/vm/dart/finalizer/helpers.dart
index d776098..3fdb3de 100644
--- a/runtime/tests/vm/dart/finalizer/helpers.dart
+++ b/runtime/tests/vm/dart/finalizer/helpers.dart
@@ -53,3 +53,10 @@
_namedPrint(name)('Await done.');
return null;
}
+
+// Uses [object] to guarantee it is reachable.
+@pragma('vm:never-inline')
+void reachabilityFence(Object? object) {
+ // Make sure [object] parameter is used and not tree shaken.
+ object.toString();
+}
diff --git a/runtime/tests/vm/dart_2/finalizer/finalizer_zone_run_gc_test.dart b/runtime/tests/vm/dart_2/finalizer/finalizer_zone_run_gc_test.dart
index 17646d1..d5b0f66 100644
--- a/runtime/tests/vm/dart_2/finalizer/finalizer_zone_run_gc_test.dart
+++ b/runtime/tests/vm/dart_2/finalizer/finalizer_zone_run_gc_test.dart
@@ -47,6 +47,9 @@
// Now we have.
Expect.equals(expectedZone, actualZone);
+
+ // Make sure finalizer is still reachable.
+ reachabilityFence(finalizer);
}
Future<void> testFinalizerException() async {
@@ -72,4 +75,7 @@
Expect.isNull(caughtError);
await yieldToMessageLoop();
Expect.isNotNull(caughtError);
+
+ // Make sure finalizer is still reachable.
+ reachabilityFence(finalizer);
}
diff --git a/runtime/tests/vm/dart_2/finalizer/helpers.dart b/runtime/tests/vm/dart_2/finalizer/helpers.dart
index a82646c..653f8e5 100644
--- a/runtime/tests/vm/dart_2/finalizer/helpers.dart
+++ b/runtime/tests/vm/dart_2/finalizer/helpers.dart
@@ -55,3 +55,10 @@
_namedPrint(name)('Await done.');
return null;
}
+
+// Uses [object] to guarantee it is reachable.
+@pragma('vm:never-inline')
+void reachabilityFence(Object object) {
+ // Make sure [object] parameter is used and not tree shaken.
+ object.toString();
+}
diff --git a/tests/language/inference_update_1/horizontal_inference_disabled_test.dart b/tests/language/inference_update_1/horizontal_inference_disabled_test.dart
index e335aee..c63166b 100644
--- a/tests/language/inference_update_1/horizontal_inference_disabled_test.dart
+++ b/tests/language/inference_update_1/horizontal_inference_disabled_test.dart
@@ -44,4 +44,19 @@
f(0, (x) => [x]).expectStaticType<Exactly<List<Object?>>>();
}
+testUnnecessaryDueToNoDependency(T Function<T>(T Function(), T) f) {
+ f(() => 0, null).expectStaticType<Exactly<int?>>();
+}
+
+testUnnecessaryDueToExplicitParameterType(List<int> list) {
+ var a = list.fold(null, (int? x, y) => (x ?? 0) + y);
+ a.expectStaticType<Exactly<int?>>();
+}
+
+testUnnecessaryDueToExplicitParameterTypeNamed(
+ T Function<T>(T, T Function({required T x, required int y})) f) {
+ var a = f(null, ({int? x, required y}) => (x ?? 0) + y);
+ a.expectStaticType<Exactly<int?>>();
+}
+
main() {}
diff --git a/tests/language/inference_update_1/horizontal_inference_enabled_test.dart b/tests/language/inference_update_1/horizontal_inference_enabled_test.dart
index 33b32df..6191c06 100644
--- a/tests/language/inference_update_1/horizontal_inference_enabled_test.dart
+++ b/tests/language/inference_update_1/horizontal_inference_enabled_test.dart
@@ -107,4 +107,19 @@
});
}
+testUnnecessaryDueToNoDependency(T Function<T>(T Function(), T) f) {
+ f(() => 0, null).expectStaticType<Exactly<int?>>();
+}
+
+testUnnecessaryDueToExplicitParameterType(List<int> list) {
+ var a = list.fold(null, (int? x, y) => (x ?? 0) + y);
+ a.expectStaticType<Exactly<int?>>();
+}
+
+testUnnecessaryDueToExplicitParameterTypeNamed(
+ T Function<T>(T, T Function({required T x, required int y})) f) {
+ var a = f(null, ({int? x, required y}) => (x ?? 0) + y);
+ a.expectStaticType<Exactly<int?>>();
+}
+
main() {}
diff --git a/tools/VERSION b/tools/VERSION
index 88dbd87..84d1f8d 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
MAJOR 2
MINOR 18
PATCH 0
-PRERELEASE 11
+PRERELEASE 12
PRERELEASE_PATCH 0
\ No newline at end of file