Fine. Record opaque API usages.
For some getters or methods we don't have good way to track them.
So, we just record them, and the requirements will always fail.
The idea is that for practical purposes we also don't need this.
One example is accessible `firstFragment`, with the counter-argument
that for analysis you (analyzer, linter) should only only use elements.
Anyway, this is necessary for correctness, and we can improve step
by step precision later. FWIW, the current state forces 9 files to be
analyzer, 7 of them because of these "opaque" requirement, vs. 1427
without fine-grained dependencies, when analyzing analyzer/ itself.
Change-Id: I6277ae6a7d1760765fdc758a178e520451510fbd
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/444704
Reviewed-by: Johnni Winther <johnniwinther@google.com>
Commit-Queue: Konstantin Shcheglov <scheglov@google.com>
diff --git a/pkg/analyzer/lib/src/dart/analysis/driver.dart b/pkg/analyzer/lib/src/dart/analysis/driver.dart
index a4ce007..630b5a9 100644
--- a/pkg/analyzer/lib/src/dart/analysis/driver.dart
+++ b/pkg/analyzer/lib/src/dart/analysis/driver.dart
@@ -109,7 +109,7 @@
// TODO(scheglov): Clean up the list of implicitly analyzed files.
class AnalysisDriver {
/// The version of data format, should be incremented on every format change.
- static const int DATA_VERSION = 512;
+ static const int DATA_VERSION = 515;
/// The number of exception contexts allowed to write. Once this field is
/// zero, we stop writing any new exception contexts in this process.
@@ -1359,6 +1359,7 @@
RequirementsManifest? requirements;
if (withFineDependencies) {
requirements = RequirementsManifest();
+ requirements.addExcludedLibraries([library.file.uri]);
globalResultRequirements = requirements;
}
diff --git a/pkg/analyzer/lib/src/dart/element/element.dart b/pkg/analyzer/lib/src/dart/element/element.dart
index fa29459..5bbbe46 100644
--- a/pkg/analyzer/lib/src/dart/element/element.dart
+++ b/pkg/analyzer/lib/src/dart/element/element.dart
@@ -206,14 +206,14 @@
@override
@trackedDirectlyDisable
ClassFragmentImpl get firstFragment {
- globalResultRequirements?.record_disable(this, 'firstFragment');
+ globalResultRequirements?.recordOpaqueApiUse(this, 'firstFragment');
return _firstFragment;
}
@override
@trackedDirectlyDisable
List<ClassFragmentImpl> get fragments {
- globalResultRequirements?.record_disable(this, 'fragments');
+ globalResultRequirements?.recordOpaqueApiUse(this, 'fragments');
return [
for (
ClassFragmentImpl? fragment = firstFragment;
@@ -389,7 +389,11 @@
@override
@trackedIncludedIntoId
- bool get isMixinApplication => firstFragment.isMixinApplication;
+ bool get isMixinApplication {
+ return globalResultRequirements.includedInId(() {
+ return firstFragment.isMixinApplication;
+ });
+ }
@override
@trackedIncludedIntoId
@@ -421,7 +425,7 @@
@override
@trackedDirectlyDisable
T? accept<T>(ElementVisitor2<T> visitor) {
- globalResultRequirements?.record_disable(this, 'accept2');
+ globalResultRequirements?.recordOpaqueApiUse(this, 'accept2');
return visitor.visitClassElement(this);
}
@@ -1786,8 +1790,11 @@
bool get isPublic => !isPrivate;
@override
+ @trackedIncludedIntoId
LibraryElementImpl? get library {
- return firstFragment.libraryFragment?.element as LibraryElementImpl?;
+ return globalResultRequirements.includedInId(() {
+ return firstFragment.libraryFragment?.element as LibraryElementImpl?;
+ });
}
@override
@@ -4026,7 +4033,12 @@
}
@override
- String get displayName => firstFragment.displayName;
+ @trackedIncludedIntoId
+ String get displayName {
+ return globalResultRequirements.includedInId(() {
+ return firstFragment.displayName;
+ });
+ }
@override
String? get documentationComment => firstFragment.documentationComment;
@@ -4078,10 +4090,20 @@
bool get isPublic => firstFragment.isPublic;
@override
- bool get isSimplyBounded => firstFragment.isSimplyBounded;
+ @trackedIncludedIntoId
+ bool get isSimplyBounded {
+ return globalResultRequirements.includedInId(() {
+ return firstFragment.isSimplyBounded;
+ });
+ }
@override
- bool get isSynthetic => firstFragment.isSynthetic;
+ @trackedIncludedIntoId
+ bool get isSynthetic {
+ return globalResultRequirements.includedInId(() {
+ return firstFragment.isSynthetic;
+ });
+ }
@override
LibraryElementImpl get library => super.library!;
@@ -4091,7 +4113,12 @@
LibraryElementImpl get library2 => library;
@override
- MetadataImpl get metadata => firstFragment.metadata;
+ @trackedIncludedIntoId
+ MetadataImpl get metadata {
+ return globalResultRequirements.includedInId(() {
+ return firstFragment.metadata;
+ });
+ }
@Deprecated('Use metadata instead')
@override
@@ -4114,7 +4141,12 @@
List<MethodElementImpl> get methods2 => methods;
@override
- String? get name => firstFragment.name;
+ @trackedIncludedIntoId
+ String? get name {
+ return globalResultRequirements.includedInId(() {
+ return firstFragment.name;
+ });
+ }
@Deprecated('Use name instead')
@override
@@ -4142,8 +4174,14 @@
List<SetterElementImpl> get setters2 => setters;
@override
- List<TypeParameterElementImpl> get typeParameters =>
- firstFragment.typeParameters.map((fragment) => fragment.element).toList();
+ @trackedIncludedIntoId
+ List<TypeParameterElementImpl> get typeParameters {
+ return globalResultRequirements.includedInId(() {
+ return firstFragment.typeParameters
+ .map((fragment) => fragment.element)
+ .toList();
+ });
+ }
@Deprecated('Use typeParameters instead')
@override
@@ -4180,15 +4218,9 @@
name: name,
);
- return globalResultRequirements.withoutRecording(
- reason: r'''
-The result depends only on the requested field, which we have already
-recorded above.
-''',
- operation: () {
- return fields.firstWhereOrNull((e) => e.name == name);
- },
- );
+ return globalResultRequirements.alreadyRecorded(() {
+ return fields.firstWhereOrNull((e) => e.name == name);
+ });
}
@Deprecated('Use getField instead')
@@ -4203,15 +4235,9 @@
name: name,
);
- return globalResultRequirements.withoutRecording(
- reason: r'''
-The result depends only on the requested getter, which we have already
-recorded above.
-''',
- operation: () {
- return getters.firstWhereOrNull((e) => e.name == name);
- },
- );
+ return globalResultRequirements.alreadyRecorded(() {
+ return getters.firstWhereOrNull((e) => e.name == name);
+ });
}
@Deprecated('Use getGetter instead')
@@ -4226,15 +4252,9 @@
name: name,
);
- return globalResultRequirements.withoutRecording(
- reason: r'''
-The result depends only on the requested method, which we have already
-recorded above.
-''',
- operation: () {
- return methods.firstWhereOrNull((e) => e.lookupName == name);
- },
- );
+ return globalResultRequirements.alreadyRecorded(() {
+ return methods.firstWhereOrNull((e) => e.lookupName == name);
+ });
}
@Deprecated('Use getMethod instead')
@@ -4249,15 +4269,9 @@
name: name,
);
- return globalResultRequirements.withoutRecording(
- reason: r'''
-The result depends only on the requested setter, which we have already
-recorded above.
-''',
- operation: () {
- return setters.firstWhereOrNull((e) => e.name == name);
- },
- );
+ return globalResultRequirements.alreadyRecorded(() {
+ return setters.firstWhereOrNull((e) => e.name == name);
+ });
}
@Deprecated('Use getSetter instead')
@@ -4393,7 +4407,7 @@
yield getter as InternalPropertyAccessorElement;
}
}
- var supertype = element.firstFragment.supertype;
+ var supertype = element.supertype;
supertype as InterfaceTypeImpl?;
element = supertype?.element;
}
@@ -4417,7 +4431,7 @@
yield method as InternalMethodElement;
}
}
- var supertype = element.firstFragment.supertype;
+ var supertype = element.supertype;
supertype as InterfaceTypeImpl?;
element = supertype?.element;
}
@@ -4443,7 +4457,7 @@
yield setter as InternalPropertyAccessorElement;
}
}
- var supertype = element.firstFragment.supertype;
+ var supertype = element.supertype;
supertype as InterfaceTypeImpl?;
element = supertype?.element;
}
@@ -4709,8 +4723,11 @@
.map;
@override
+ @trackedIncludedIntoId
List<InterfaceTypeImpl> get interfaces {
- return firstFragment.interfaces;
+ return globalResultRequirements.includedInId(() {
+ return firstFragment.interfaces;
+ });
}
/// Return `true` if this class represents the class '_Enum' defined in the
@@ -4726,12 +4743,20 @@
}
@override
+ @trackedIncludedIntoId
List<InterfaceTypeImpl> get mixins {
- return firstFragment.mixins;
+ return globalResultRequirements.includedInId(() {
+ return firstFragment.mixins;
+ });
}
@override
- InterfaceTypeImpl? get supertype => firstFragment.supertype;
+ @trackedIncludedIntoId
+ InterfaceTypeImpl? get supertype {
+ return globalResultRequirements.includedInId(() {
+ return firstFragment.supertype;
+ });
+ }
@override
InterfaceTypeImpl get thisType {
@@ -4794,15 +4819,9 @@
name: name,
);
- return globalResultRequirements.withoutRecording(
- reason: r'''
-The result depends only on the requested constructor, which we have already
-recorded above.
-''',
- operation: () {
- return constructors.firstWhereOrNull((e) => e.name == name);
- },
- );
+ return globalResultRequirements.alreadyRecorded(() {
+ return constructors.firstWhereOrNull((e) => e.name == name);
+ });
}
@Deprecated('Use getNamedConstructor instead')
@@ -4945,6 +4964,7 @@
/// This method should be used only for error recovery during analysis,
/// when instance access to a static class member, defined in this class,
/// or a superclass.
+ @trackedIndirectly
InternalGetterElement? lookupStaticGetter(
String name,
LibraryElement library,
@@ -4961,6 +4981,7 @@
/// This method should be used only for error recovery during analysis,
/// when instance access to a static class member, defined in this class,
/// or a superclass.
+ @trackedIndirectly
InternalMethodElement? lookupStaticMethod(
String name,
LibraryElement library,
@@ -4975,6 +4996,7 @@
/// This method should be used only for error recovery during analysis,
/// when instance access to a static class member, defined in this class,
/// or a superclass.
+ @trackedIndirectly
InternalSetterElement? lookupStaticSetter(
String name,
LibraryElement library,
diff --git a/pkg/analyzer/lib/src/fine/requirement_failure.dart b/pkg/analyzer/lib/src/fine/requirement_failure.dart
index 57b51b3..63f3166 100644
--- a/pkg/analyzer/lib/src/fine/requirement_failure.dart
+++ b/pkg/analyzer/lib/src/fine/requirement_failure.dart
@@ -4,6 +4,7 @@
import 'package:analyzer/src/fine/lookup_name.dart';
import 'package:analyzer/src/fine/manifest_id.dart';
+import 'package:analyzer/src/fine/requirements.dart';
final class ExportCountMismatch extends ExportFailure {
final Uri fragmentUri;
@@ -220,6 +221,17 @@
}
}
+final class OpaqueApiUseFailure extends RequirementFailure {
+ final List<OpaqueApiUse> uses;
+
+ OpaqueApiUseFailure({required this.uses});
+
+ @override
+ String toString() {
+ return 'OpaqueApiUseFailure(uses: $uses)';
+ }
+}
+
sealed class RequirementFailure {}
sealed class TopLevelFailure extends RequirementFailure {
diff --git a/pkg/analyzer/lib/src/fine/requirements.dart b/pkg/analyzer/lib/src/fine/requirements.dart
index 23774bf..c0840bd 100644
--- a/pkg/analyzer/lib/src/fine/requirements.dart
+++ b/pkg/analyzer/lib/src/fine/requirements.dart
@@ -346,6 +346,44 @@
}
}
+/// Information about an API usage that is not supported by fine-grained
+/// dependencies. If such API is used, we have to decide that the requirements
+/// are not satisfied, because we don't know for sure.
+class OpaqueApiUse {
+ final String targetRuntimeType;
+ final String methodName;
+ final Uri? targetElementLibraryUri;
+ final String? targetElementName;
+
+ OpaqueApiUse({
+ required this.targetRuntimeType,
+ required this.methodName,
+ this.targetElementLibraryUri,
+ this.targetElementName,
+ });
+
+ factory OpaqueApiUse.read(SummaryDataReader reader) {
+ return OpaqueApiUse(
+ targetRuntimeType: reader.readStringUtf8(),
+ methodName: reader.readStringUtf8(),
+ targetElementLibraryUri: reader.readOptionalObject(
+ () => reader.readUri(),
+ ),
+ targetElementName: reader.readOptionalStringUtf8(),
+ );
+ }
+
+ void write(BufferedSink sink) {
+ sink.writeStringUtf8(targetRuntimeType);
+ sink.writeStringUtf8(methodName);
+ sink.writeOptionalObject(
+ targetElementLibraryUri,
+ (uri) => sink.writeUri(uri),
+ );
+ sink.writeOptionalStringUtf8(targetElementName);
+ }
+}
+
class RequirementsManifest {
/// LibraryUri => TopName => ID
final Map<Uri, Map<LookupName, ManifestItemId?>> topLevels = {};
@@ -358,6 +396,11 @@
final List<LibraryExportRequirements> exportRequirements = [];
+ /// If this list is not empty, [isSatisfied] returns `false`.
+ final List<OpaqueApiUse> opaqueApiUses = [];
+
+ final Set<Uri> _excludedLibraries = {};
+
int _recordingLockLevel = 0;
RequirementsManifest();
@@ -400,9 +443,17 @@
reader.readTypedList(() => LibraryExportRequirements.read(reader)),
);
+ result.opaqueApiUses.addAll(
+ reader.readTypedList(() => OpaqueApiUse.read(reader)),
+ );
+
return result;
}
+ void addExcludedLibraries(Iterable<Uri> libraries) {
+ _excludedLibraries.addAll(libraries);
+ }
+
/// Adds requirements to exports from libraries.
///
/// We have already computed manifests for each library.
@@ -447,6 +498,10 @@
required LinkedElementFactory elementFactory,
required Map<Uri, LibraryManifest> libraryManifests,
}) {
+ if (opaqueApiUses.isNotEmpty) {
+ return OpaqueApiUseFailure(uses: opaqueApiUses);
+ }
+
for (var libraryEntry in topLevels.entries) {
var libraryUri = libraryEntry.key;
@@ -724,10 +779,6 @@
// TODO(scheglov): implement.
}
- void record_disable(Object target, String method) {
- // TODO(scheglov): implement.
- }
-
/// Record that [id] was looked up in the import prefix scope that
/// imports [importedLibraries].
void record_importPrefixScope_lookup({
@@ -986,6 +1037,40 @@
}
}
+ void recordOpaqueApiUse(Object target, String method) {
+ if (_recordingLockLevel != 0) {
+ return;
+ }
+
+ Uri? targetElementLibraryUri;
+ String? targetElementName;
+ if (target case ElementImpl targetElement) {
+ targetElementLibraryUri = targetElement.library?.uri;
+ targetElementName = targetElement.name;
+ if (_excludedLibraries.contains(targetElementLibraryUri)) {
+ return;
+ }
+ }
+
+ untracked(
+ reason: 'We are recording failure',
+ operation: () {
+ // TODO(scheglov): remove after adding all tracking
+ // print('[${target.runtimeType}.$method]');
+ // print(StackTrace.current);
+
+ opaqueApiUses.add(
+ OpaqueApiUse(
+ targetRuntimeType: target.runtimeType.toString(),
+ methodName: method,
+ targetElementName: targetElementName,
+ targetElementLibraryUri: targetElementLibraryUri,
+ ),
+ );
+ },
+ );
+ }
+
/// This method is invoked after linking of a library cycle, to exclude
/// requirements to the libraries of this same library cycle. We already
/// link these libraries together, so only requirements to the previous
@@ -1041,6 +1126,8 @@
exportRequirements,
(requirement) => requirement.write(sink),
);
+
+ sink.writeList(opaqueApiUses, (usage) => usage.write(sink));
}
void _addExports(LibraryElementImpl libraryElement) {
@@ -1219,10 +1306,26 @@
}
extension RequirementsManifestExtension on RequirementsManifest? {
- T withoutRecording<T>({
- required String reason,
- required T Function() operation,
- }) {
+ /// Executes the given [operation] without recording dependencies, because
+ /// the dependency has already been recorded at a higher level of
+ /// granularity.
+ T alreadyRecorded<T>(T Function() operation) {
+ return untracked(
+ reason: 'The dependency has already been recorded',
+ operation: operation,
+ );
+ }
+
+ /// Executes the given [operation] without recording dependencies.
+ ///
+ /// This is used for getters on elements that are considered part of the
+ /// element's identity. Since a change to such a getter implies a change to
+ /// the element's identity, separate dependency tracking is not necessary.
+ T includedInId<T>(T Function() operation) {
+ return untracked(reason: 'Included in ID', operation: operation);
+ }
+
+ T untracked<T>({required String reason, required T Function() operation}) {
var self = this;
if (self == null) {
return operation();
diff --git a/pkg/analyzer/lib/src/summary2/link.dart b/pkg/analyzer/lib/src/summary2/link.dart
index 28e7bd0..5484462 100644
--- a/pkg/analyzer/lib/src/summary2/link.dart
+++ b/pkg/analyzer/lib/src/summary2/link.dart
@@ -13,6 +13,7 @@
import 'package:analyzer/src/dart/element/inheritance_manager3.dart';
import 'package:analyzer/src/dart/element/name_union.dart';
import 'package:analyzer/src/fine/library_manifest.dart';
+import 'package:analyzer/src/fine/requirements.dart';
import 'package:analyzer/src/summary2/bundle_writer.dart';
import 'package:analyzer/src/summary2/detach_nodes.dart';
import 'package:analyzer/src/summary2/enclosing_type_parameters_flag.dart';
@@ -87,6 +88,10 @@
return elementNodes[fragment];
}
+ bool isLinkingElement(Element element) {
+ return builders.containsKey(element.library?.uri);
+ }
+
void link({
required OperationPerformanceImpl performance,
required List<LibraryFileKind> inputLibraries,
@@ -221,6 +226,10 @@
_computeLibraryScopes(performance: performance);
});
+ globalResultRequirements?.addExcludedLibraries(
+ builders.values.map((builder) => builder.uri),
+ );
+
_createTypeSystem();
_resolveTypes();
_setDefaultSupertypes();
@@ -257,9 +266,14 @@
}
void _computeLibraryScopes({required OperationPerformanceImpl performance}) {
- for (var library in builders.values) {
- library.buildElements();
- }
+ globalResultRequirements.untracked(
+ reason: 'No complete elements yet',
+ operation: () {
+ for (var library in builders.values) {
+ library.buildElements();
+ }
+ },
+ );
_buildExportScopes();
}
diff --git a/pkg/analyzer/lib/src/summary2/type_alias.dart b/pkg/analyzer/lib/src/summary2/type_alias.dart
index 83bb8ac..bca0000 100644
--- a/pkg/analyzer/lib/src/summary2/type_alias.dart
+++ b/pkg/analyzer/lib/src/summary2/type_alias.dart
@@ -92,31 +92,33 @@
return;
}
- var typeNode = linker.getLinkingNode2(element.firstFragment);
- if (typeNode != null) {
- if (typeNode == self) {
- hasSelfReference = true;
- return;
- }
- if (typeNode is ClassDeclaration) {
- if (visited.add(typeNode)) {
- _typeParameterList(typeNode.typeParameters);
+ if (linker.isLinkingElement(element)) {
+ var typeNode = linker.getLinkingNode2(element.firstFragment);
+ if (typeNode != null) {
+ if (typeNode == self) {
+ hasSelfReference = true;
+ return;
}
- } else if (typeNode is ClassTypeAlias) {
- if (visited.add(typeNode)) {
- _typeParameterList(typeNode.typeParameters);
- }
- } else if (typeNode is FunctionTypeAlias) {
- if (visited.add(typeNode)) {
- functionTypeAlias(typeNode);
- }
- } else if (typeNode is GenericTypeAlias) {
- if (visited.add(typeNode)) {
- genericTypeAlias(typeNode);
- }
- } else if (typeNode is MixinDeclaration) {
- if (visited.add(typeNode)) {
- _typeParameterList(typeNode.typeParameters);
+ if (typeNode is ClassDeclaration) {
+ if (visited.add(typeNode)) {
+ _typeParameterList(typeNode.typeParameters);
+ }
+ } else if (typeNode is ClassTypeAlias) {
+ if (visited.add(typeNode)) {
+ _typeParameterList(typeNode.typeParameters);
+ }
+ } else if (typeNode is FunctionTypeAlias) {
+ if (visited.add(typeNode)) {
+ functionTypeAlias(typeNode);
+ }
+ } else if (typeNode is GenericTypeAlias) {
+ if (visited.add(typeNode)) {
+ genericTypeAlias(typeNode);
+ }
+ } else if (typeNode is MixinDeclaration) {
+ if (visited.add(typeNode)) {
+ _typeParameterList(typeNode.typeParameters);
+ }
}
}
}
diff --git a/pkg/analyzer/test/src/dart/analysis/driver_test.dart b/pkg/analyzer/test/src/dart/analysis/driver_test.dart
index 7f6a48f..d563531 100644
--- a/pkg/analyzer/test/src/dart/analysis/driver_test.dart
+++ b/pkg/analyzer/test/src/dart/analysis/driver_test.dart
@@ -24757,6 +24757,118 @@
);
}
+ test_dependency_opaqueApiUse_firstFragment() async {
+ _ManualRequirements.install((state) {
+ var A = state.singleUnit.scopeInterfaceElement('A');
+ A.firstFragment;
+ });
+
+ await _runChangeScenarioTA(
+ initialA: r'''
+class A {}
+''',
+ testCode: r'''
+import 'a.dart';
+''',
+ operation: _FineOperationTestFileGetErrors(),
+ expectedInitialEvents: r'''
+[status] working
+[operation] linkLibraryCycle SDK
+[future] getErrors T1
+ ErrorsResult #0
+ path: /home/test/lib/test.dart
+ uri: package:test/test.dart
+ flags: isLibrary
+ errors
+ 7 +8 UNUSED_IMPORT
+[operation] linkLibraryCycle
+ package:test/a.dart
+ declaredClasses
+ A: #M0
+ interface: #M1
+ requirements
+[operation] linkLibraryCycle
+ package:test/test.dart
+ requirements
+[operation] analyzeFile
+ file: /home/test/lib/test.dart
+ library: /home/test/lib/test.dart
+[stream]
+ ResolvedUnitResult #1
+ path: /home/test/lib/test.dart
+ uri: package:test/test.dart
+ flags: exists isLibrary
+ errors
+ 7 +8 UNUSED_IMPORT
+[operation] analyzedLibrary
+ file: /home/test/lib/test.dart
+ requirements
+ topLevels
+ dart:core
+ A: <null>
+ package:test/a.dart
+ A: #M0
+ opaqueApiUses
+ ClassElementImpl.firstFragment
+ targetElementLibraryUri: package:test/a.dart
+ targetElementName: A
+[status] idle
+''',
+ updatedA: r'''
+class A {}
+class B {}
+''',
+ expectedUpdatedEvents: r'''
+[status] working
+[operation] linkLibraryCycle
+ package:test/a.dart
+ declaredClasses
+ A: #M0
+ interface: #M1
+ B: #M2
+ interface: #M3
+ requirements
+[future] getErrors T2
+ ErrorsResult #2
+ path: /home/test/lib/test.dart
+ uri: package:test/test.dart
+ flags: isLibrary
+ errors
+ 7 +8 UNUSED_IMPORT
+[operation] readLibraryCycleBundle
+ package:test/test.dart
+[operation] getErrorsCannotReuse
+ opaqueApiUseFailure
+ ClassElementImpl.firstFragment
+ targetElementLibraryUri: package:test/a.dart
+ targetElementName: A
+[operation] analyzeFile
+ file: /home/test/lib/test.dart
+ library: /home/test/lib/test.dart
+[stream]
+ ResolvedUnitResult #3
+ path: /home/test/lib/test.dart
+ uri: package:test/test.dart
+ flags: exists isLibrary
+ errors
+ 7 +8 UNUSED_IMPORT
+[operation] analyzedLibrary
+ file: /home/test/lib/test.dart
+ requirements
+ topLevels
+ dart:core
+ A: <null>
+ package:test/a.dart
+ A: #M0
+ opaqueApiUses
+ ClassElementImpl.firstFragment
+ targetElementLibraryUri: package:test/a.dart
+ targetElementName: A
+[status] idle
+''',
+ );
+ }
+
test_dependency_topLevelFunction_change_invoked() async {
await _runChangeScenarioTA(
initialA: r'''
diff --git a/pkg/analyzer/test/src/dart/analysis/result_printer.dart b/pkg/analyzer/test/src/dart/analysis/result_printer.dart
index 7da2227..fde8f53 100644
--- a/pkg/analyzer/test/src/dart/analysis/result_printer.dart
+++ b/pkg/analyzer/test/src/dart/analysis/result_printer.dart
@@ -49,6 +49,7 @@
_writeInstanceItems(requirements);
_writeInterfaceItems(requirements);
_writeExportRequirements(requirements);
+ _writeOpaqueApiUses(requirements);
});
}
@@ -217,6 +218,23 @@
}
}
+ void _writeOpaqueApiUses(RequirementsManifest requirements) {
+ var usages = requirements.opaqueApiUses.sortedBy((e) {
+ return '${e.targetRuntimeType}.${e.methodName}';
+ });
+ sink.writeElements('opaqueApiUses', usages, (usage) {
+ sink.writelnWithIndent('${usage.targetRuntimeType}.${usage.methodName}');
+ sink.withIndent(() {
+ if (usage.targetElementLibraryUri case var libraryUri?) {
+ sink.writelnWithIndent('targetElementLibraryUri: $libraryUri');
+ }
+ if (usage.targetElementName case var elementName?) {
+ sink.writelnWithIndent('targetElementName: $elementName');
+ }
+ });
+ });
+ }
+
void _writeTopLevels(RequirementsManifest requirements) {
var libEntries = requirements.topLevels.sorted;
sink.writeElements('topLevels', libEntries, (libEntry) {
@@ -568,6 +586,21 @@
case TopLevelNotInterface():
// TODO(scheglov): Handle this case.
throw UnimplementedError();
+ case OpaqueApiUseFailure():
+ var sortedUses = failure.uses.sortedBy((e) {
+ return '${e.targetRuntimeType}.${e.methodName}';
+ });
+ sink.writeElements('opaqueApiUseFailure', sortedUses, (use) {
+ sink.writelnWithIndent('${use.targetRuntimeType}.${use.methodName}');
+ sink.withIndent(() {
+ if (use.targetElementLibraryUri case var libraryUri?) {
+ sink.writelnWithIndent('targetElementLibraryUri: $libraryUri');
+ }
+ if (use.targetElementName case var elementName?) {
+ sink.writelnWithIndent('targetElementName: $elementName');
+ }
+ });
+ });
}
}