Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(145)

Unified Diff: pkg/analyzer/test/src/summary/summary_test.dart

Issue 1610043002: Add propagated types to summary files. (Closed) Base URL: git@github.com:dart-lang/sdk.git@master
Patch Set: Created 4 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « pkg/analyzer/test/src/summary/resynthesize_test.dart ('k') | pkg/analyzer/tool/summary/idl.dart » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: pkg/analyzer/test/src/summary/summary_test.dart
diff --git a/pkg/analyzer/test/src/summary/summary_test.dart b/pkg/analyzer/test/src/summary/summary_test.dart
index 3d8d0623887c8fa40749346cf861949ee905e94d..bed50c30cc9ef9134c02640878786fe23d30ea8f 100644
--- a/pkg/analyzer/test/src/summary/summary_test.dart
+++ b/pkg/analyzer/test/src/summary/summary_test.dart
@@ -137,6 +137,9 @@ class PrelinkerTest extends SummarizeElementsTest {
bool get expectAbsoluteUrisInDependencies => false;
@override
+ bool get skipFullyLinkedData => true;
+
+ @override
Source addNamedSource(String filePath, String contents) {
Source source = super.addNamedSource(filePath, contents);
uriToPublicNamespace[absUri(filePath)] =
@@ -218,6 +221,9 @@ class SummarizeElementsTest extends ResolverTestCase with SummaryTest {
bool get expectAbsoluteUrisInDependencies => true;
@override
+ bool get skipFullyLinkedData => false;
+
+ @override
Source addNamedSource(String filePath, String contents) {
Source source = super.addNamedSource(filePath, contents);
_fileContents[source] = contents;
@@ -264,7 +270,7 @@ class SummarizeElementsTest extends ResolverTestCase with SummaryTest {
expect(linked.units.length, unlinkedUnits.length);
for (int i = 0; i < linked.units.length; i++) {
expect(unlinkedUnits[i].references.length,
- linked.units[i].references.length);
+ lessThanOrEqualTo(linked.units[i].references.length));
}
verifyPublicNamespace();
}
@@ -347,6 +353,12 @@ abstract class SummaryTest {
LinkedLibrary get linked;
/**
+ * `true` if the linked portion of the summary only contains prelinked data.
+ * This happens because we don't yet have a full linker; only a prelinker.
+ */
+ bool get skipFullyLinkedData;
+
+ /**
* Get access to the unlinked compilation unit summaries that result from
* serializing and deserializing the library under test.
*/
@@ -448,11 +460,14 @@ abstract class SummaryTest {
/**
* Verify that the dependency table contains an entry for a file reachable
- * via the given [absoluteUri] and [relativeUri].
+ * via the given [absoluteUri] and [relativeUri]. If [fullyLinked] is
+ * `true`, then the dependency should be a fully-linked dependency; otherwise
+ * it should be a prelinked dependency.
*
- * The [LinkedDependency] is returned.
+ * The index of the [LinkedDependency] is returned.
*/
- LinkedDependency checkHasDependency(String absoluteUri, String relativeUri) {
+ int checkHasDependency(String absoluteUri, String relativeUri,
+ {bool fullyLinked: false}) {
if (expectAbsoluteUrisInDependencies) {
// The element model doesn't (yet) store enough information to recover
// relative URIs, so we have to use the absolute URI.
@@ -460,9 +475,15 @@ abstract class SummaryTest {
relativeUri = absoluteUri;
}
List<String> found = <String>[];
- for (LinkedDependency dep in linked.dependencies) {
+ for (int i = 0; i < linked.dependencies.length; i++) {
+ LinkedDependency dep = linked.dependencies[i];
if (dep.uri == relativeUri) {
- return dep;
+ if (fullyLinked) {
+ expect(i, greaterThanOrEqualTo(linked.numPrelinkedDependencies));
+ } else {
+ expect(i, lessThan(linked.numPrelinkedDependencies));
+ }
+ return i;
}
found.add(dep.uri);
}
@@ -489,6 +510,87 @@ abstract class SummaryTest {
}
/**
+ * Verify that the given [typeRef] represents a reference to a type declared
+ * in a file reachable via [absoluteUri] and [relativeUri], having name
+ * [expectedName]. If [allowTypeParameters] is true, allow the type
+ * reference to supply type parameters. [expectedKind] is the kind of object
+ * referenced. [linkedSourceUnit] and [unlinkedSourceUnit] refer to the
+ * compilation unit within which the [typeRef] appears; if not specified they
+ * are assumed to refer to the defining compilation unit.
+ * [expectedTargetUnit] is the index of the compilation unit in which the
+ * target of the [typeRef] is expected to appear; if not specified it is
+ * assumed to be the defining compilation unit. [numTypeParameters] is the
+ * number of type parameters of the thing being referred to.
+ */
+ void checkLinkedTypeRef(TypeRef typeRef, String absoluteUri,
+ String relativeUri, String expectedName,
+ {bool allowTypeParameters: false,
+ ReferenceKind expectedKind: ReferenceKind.classOrEnum,
+ int expectedTargetUnit: 0,
+ LinkedUnit linkedSourceUnit,
+ UnlinkedUnit unlinkedSourceUnit,
+ int numTypeParameters: 0}) {
+ linkedSourceUnit ??= definingUnit;
+ expect(typeRef, isNotNull,
+ reason: 'No entry in linkedSourceUnit.types matching slotId');
+ expect(typeRef.paramReference, 0);
+ int index = typeRef.reference;
+ if (!allowTypeParameters) {
+ expect(typeRef.typeArguments, isEmpty);
+ }
+ checkTypeRefCommonElements(
+ index,
+ absoluteUri,
+ relativeUri,
+ expectedName,
+ expectedKind,
+ expectedTargetUnit,
+ linkedSourceUnit,
+ unlinkedSourceUnit,
+ numTypeParameters);
+ }
+
+ /**
+ * Verify that the given [slotId] represents a reference to a type declared
+ * in a file reachable via [absoluteUri] and [relativeUri], having name
+ * [expectedName]. If [allowTypeParameters] is true, allow the type
+ * reference to supply type parameters. [expectedKind] is the kind of object
+ * referenced. [linkedSourceUnit] and [unlinkedSourceUnit] refer to the
+ * compilation unit within which the [typeRef] appears; if not specified they
+ * are assumed to refer to the defining compilation unit.
+ * [expectedTargetUnit] is the index of the compilation unit in which the
+ * target of the [typeRef] is expected to appear; if not specified it is
+ * assumed to be the defining compilation unit. [numTypeParameters] is the
+ * number of type parameters of the thing being referred to.
+ */
+ void checkLinkedTypeSlot(
+ int slotId, String absoluteUri, String relativeUri, String expectedName,
+ {bool allowTypeParameters: false,
+ ReferenceKind expectedKind: ReferenceKind.classOrEnum,
+ int expectedTargetUnit: 0,
+ LinkedUnit linkedSourceUnit,
+ UnlinkedUnit unlinkedSourceUnit,
+ int numTypeParameters: 0}) {
+ // Slot ids should be nonzero, since zero means "no associated slot".
+ expect(slotId, isNot(0));
+ if (skipFullyLinkedData) {
+ return;
+ }
+ linkedSourceUnit ??= definingUnit;
+ checkLinkedTypeRef(
+ getTypeRefForSlot(slotId, linkedSourceUnit: linkedSourceUnit),
+ absoluteUri,
+ relativeUri,
+ expectedName,
+ allowTypeParameters: allowTypeParameters,
+ expectedKind: expectedKind,
+ expectedTargetUnit: expectedTargetUnit,
+ linkedSourceUnit: linkedSourceUnit,
+ unlinkedSourceUnit: unlinkedSourceUnit,
+ numTypeParameters: numTypeParameters);
+ }
+
+ /**
* Verify that the given [typeRef] represents a reference to a type parameter
* having the given [deBruijnIndex].
*/
@@ -536,45 +638,97 @@ abstract class SummaryTest {
UnlinkedUnit unlinkedSourceUnit,
int numTypeParameters: 0}) {
linkedSourceUnit ??= definingUnit;
- unlinkedSourceUnit ??= unlinkedUnits[0];
expect(typeRef, new isInstanceOf<TypeRef>());
expect(typeRef.paramReference, 0);
int index = typeRef.reference;
- UnlinkedReference reference = unlinkedSourceUnit.references[index];
- LinkedReference referenceResolution = linkedSourceUnit.references[index];
- if (index == 0) {
- // Index 0 is reserved for "dynamic".
- expect(reference.name, isEmpty);
+ if (!allowTypeParameters) {
+ expect(typeRef.typeArguments, isEmpty);
+ }
+ UnlinkedReference reference = checkTypeRefCommonElements(
+ index,
+ absoluteUri,
+ relativeUri,
+ expectedName,
+ expectedKind,
+ expectedTargetUnit,
+ linkedSourceUnit,
+ unlinkedSourceUnit,
+ numTypeParameters);
+ expect(reference, isNotNull,
+ reason: 'Unlinked type refs must refer to an explicit reference');
+ if (expectedKind == ReferenceKind.unresolved && !checkAstDerivedData) {
+ // summarize_elements.dart isn't yet able to record the prefix of
+ // unresolved references. TODO(paulberry): fix this.
+ expect(reference.prefixReference, 0);
+ } else if (expectedPrefix == null) {
expect(reference.prefixReference, 0);
+ } else {
+ checkPrefix(reference.prefixReference, expectedPrefix);
+ }
+ }
+
+ /**
+ * Check the data structures that are common between [checkTypeRef] and
+ * [checkLinkedTypeRef]. If the type reference in question is an explicit
+ * reference, return the [UnlinkedReference] that is used to make the
+ * explicit reference. If the type reference in question is an implicit
+ * reference, return `null`.
+ */
+ UnlinkedReference checkTypeRefCommonElements(
+ int referenceIndex,
+ String absoluteUri,
+ String relativeUri,
+ String expectedName,
+ ReferenceKind expectedKind,
+ int expectedTargetUnit,
+ LinkedUnit linkedSourceUnit,
+ UnlinkedUnit unlinkedSourceUnit,
+ int numTypeParameters) {
+ unlinkedSourceUnit ??= unlinkedUnits[0];
+ LinkedReference referenceResolution =
+ linkedSourceUnit.references[referenceIndex];
+ String name;
+ UnlinkedReference reference;
+ if (referenceIndex < unlinkedSourceUnit.references.length) {
+ // This is an explicit reference, so its name and prefix should be in
+ // [UnlinkedUnit.references].
+ expect(referenceResolution.name, isEmpty);
+ reference = unlinkedSourceUnit.references[referenceIndex];
+ name = reference.name;
+ if (reference.prefixReference != 0) {
+ // Prefixes should appear in the references table before any reference
+ // that uses them.
+ expect(reference.prefixReference, lessThan(referenceIndex));
+ }
+ } else {
+ // This is an implicit reference, so its name should be in
+ // [LinkedUnit.references].
+ name = referenceResolution.name;
+ }
+ if (referenceIndex == 0) {
+ // Index 0 is reserved for "dynamic".
+ expect(name, isEmpty);
}
if (absoluteUri == null) {
expect(referenceResolution.dependency, 0);
} else {
checkDependency(referenceResolution.dependency, absoluteUri, relativeUri);
}
- if (!allowTypeParameters) {
- expect(typeRef.typeArguments, isEmpty);
- }
if (expectedKind == ReferenceKind.unresolved && !checkAstDerivedData) {
- // summarize_elements.dart isn't yet able to record the name or prefix of
+ // summarize_elements.dart isn't yet able to record the name of
// unresolved references. TODO(paulberry): fix this.
- expect(reference.name, '*unresolved*');
- expect(reference.prefixReference, 0);
+ expect(name, '*unresolved*');
} else {
if (expectedName == null) {
- expect(reference.name, isEmpty);
- } else {
- expect(reference.name, expectedName);
- }
- if (expectedPrefix == null) {
- expect(reference.prefixReference, 0);
+ expect(name, isEmpty);
} else {
- checkPrefix(reference.prefixReference, expectedPrefix);
+ expect(name, expectedName);
}
}
expect(referenceResolution.kind, expectedKind);
expect(referenceResolution.unit, expectedTargetUnit);
expect(referenceResolution.numTypeParameters, numTypeParameters);
+ return reference;
}
/**
@@ -739,6 +893,19 @@ enum E {
}
/**
+ * Find the entry in [linkedSourceUnit.types] matching [slotId].
+ */
+ TypeRef getTypeRefForSlot(int slotId, {LinkedUnit linkedSourceUnit}) {
+ linkedSourceUnit ??= definingUnit;
+ for (TypeRef typeRef in linkedSourceUnit.types) {
+ if (typeRef.slot == slotId) {
+ return typeRef;
+ }
+ }
+ return null;
+ }
+
+ /**
* Serialize the given library [text] and return the summary of the class
* with the given [className].
*/
@@ -2248,9 +2415,9 @@ class C {
addNamedSource('/b.dart', 'part of a;');
addNamedSource('/c.dart', 'part of a;');
serializeLibraryText('import "a.dart"; A a;');
- LinkedDependency dep = checkHasDependency(absUri('/a.dart'), 'a.dart');
- checkDependencyParts(
- dep, [absUri('/b.dart'), absUri('/c.dart')], ['b.dart', 'c.dart']);
+ int dep = checkHasDependency(absUri('/a.dart'), 'a.dart');
+ checkDependencyParts(linked.dependencies[dep],
+ [absUri('/b.dart'), absUri('/c.dart')], ['b.dart', 'c.dart']);
}
test_dependencies_parts_relative_to_importing_library() {
@@ -2260,10 +2427,9 @@ class C {
addNamedSource('/a/c/e/f.dart', 'part of d;');
addNamedSource('/a/c/g/h.dart', 'part of d;');
serializeLibraryText('import "a/b.dart"; D d;');
- LinkedDependency dep =
- checkHasDependency(absUri('/a/c/d.dart'), 'a/c/d.dart');
+ int dep = checkHasDependency(absUri('/a/c/d.dart'), 'a/c/d.dart');
checkDependencyParts(
- dep,
+ linked.dependencies[dep],
[absUri('/a/c/e/f.dart'), absUri('/a/c/g/h.dart')],
['a/c/e/f.dart', 'a/c/g/h.dart']);
}
@@ -3049,12 +3215,36 @@ class C {
expect(variable.isFinal, isTrue);
}
+ test_field_propagated_type_final_immediate() {
+ UnlinkedVariable v =
+ serializeClassText('class C { final v = 0; }').fields[0];
+ checkLinkedTypeSlot(v.propagatedTypeSlot, 'dart:core', 'dart:core', 'int');
+ }
+
test_field_static() {
UnlinkedVariable variable =
serializeClassText('class C { static int i; }').fields[0];
expect(variable.isStatic, isTrue);
}
+ test_fully_linked_references_follow_other_references() {
+ if (skipFullyLinkedData) {
+ return;
+ }
+ serializeLibraryText('final x = 0; String y;');
+ checkLinkedTypeSlot(unlinkedUnits[0].variables[0].propagatedTypeSlot,
+ 'dart:core', 'dart:core', 'int');
+ checkTypeRef(
+ unlinkedUnits[0].variables[1].type, 'dart:core', 'dart:core', 'String');
+ // Even though the definition of y follows the definition of x, the linked
+ // type reference for x should use a higher numbered reference than the
+ // unlinked type reference for y.
+ TypeRef propagatedType =
+ getTypeRefForSlot(unlinkedUnits[0].variables[0].propagatedTypeSlot);
+ expect(unlinkedUnits[0].variables[1].type.reference,
+ lessThan(propagatedType.reference));
+ }
+
test_function_documented() {
String text = '''
// Extra comment so doc comment offset != 0
@@ -3089,6 +3279,23 @@ get f => null;''';
checkDocumentationComment(executable.documentationComment, text);
}
+ test_implicit_dependencies_follow_other_dependencies() {
+ if (skipFullyLinkedData) {
+ return;
+ }
+ addNamedSource('/a.dart', 'import "b.dart"; class C {} D f() => null;');
+ addNamedSource('/b.dart', 'class D {}');
+ serializeLibraryText('import "a.dart"; final x = f(); C y;');
+ // The dependency on b.dart is implicit, so it should be placed at the end
+ // of the dependency list, after a.dart, even though the code that refers
+ // to b.dart comes before the code that refers to a.dart.
+ int aDep =
+ checkHasDependency(absUri('/a.dart'), 'a.dart', fullyLinked: false);
+ int bDep =
+ checkHasDependency(absUri('/b.dart'), 'b.dart', fullyLinked: true);
+ expect(aDep, lessThan(bDep));
+ }
+
test_import_deferred() {
serializeLibraryText(
'import "dart:async" deferred as a; main() { print(a.Future); }');
@@ -3398,6 +3605,39 @@ library foo;''';
expectedTargetUnit: 2);
}
+ test_linked_reference_reuse() {
+ if (skipFullyLinkedData) {
+ return;
+ }
+ // When the reference for a linked type is the same as an explicitly
+ // referenced type, the explicit reference should be re-used.
+ addNamedSource('/a.dart', 'class C {}');
+ addNamedSource('/b.dart', 'import "a.dart"; C f() => null;');
+ serializeLibraryText(
+ 'import "a.dart"; import "b.dart"; C c1; final c2 = f();');
+ int explicitReference = findVariable('c1').type.reference;
+ expect(getTypeRefForSlot(findVariable('c2').propagatedTypeSlot).reference,
+ explicitReference);
+ }
+
+ test_linked_type_dependency_reuse() {
+ if (skipFullyLinkedData) {
+ return;
+ }
+ // When the dependency for a linked type is the same as an explicit
+ // dependency, the explicit dependency should be re-used.
+ addNamedSource('/a.dart', 'class C {} class D {}');
+ addNamedSource('/b.dart', 'import "a.dart"; D f() => null;');
+ serializeLibraryText(
+ 'import "a.dart"; import "b.dart"; C c; final d = f();');
+ int cReference = findVariable('c').type.reference;
+ int explicitDependency = linked.units[0].references[cReference].dependency;
+ int dReference =
+ getTypeRefForSlot(findVariable('d').propagatedTypeSlot).reference;
+ expect(
+ linked.units[0].references[dReference].dependency, explicitDependency);
+ }
+
test_local_names_take_precedence_over_imported_names() {
addNamedSource('/a.dart', 'class C {} class D {}');
serializeLibraryText('''
@@ -3469,6 +3709,15 @@ void set f(value) {}''';
checkDocumentationComment(executable.documentationComment, text);
}
+ test_slot_reuse() {
+ // Different compilation units have independent notions of slot id, so slot
+ // ids should be reused.
+ addNamedSource('/a.dart', 'part of foo; final v = 0;');
+ serializeLibraryText('library foo; part "a.dart"; final w = 0;');
+ expect(unlinkedUnits[0].variables[0].propagatedTypeSlot, 1);
+ expect(unlinkedUnits[1].variables[0].propagatedTypeSlot, 1);
+ }
+
test_type_arguments_explicit() {
TypeRef typeRef = serializeTypeText('List<int>');
checkTypeRef(typeRef, 'dart:core', 'dart:core', 'List',
@@ -4025,6 +4274,59 @@ var v;''';
expect(unlinkedUnits[0].publicNamespace.names, isEmpty);
}
+ test_variable_propagated_type_final_immediate() {
+ UnlinkedVariable v = serializeVariableText('final v = 0;');
+ checkLinkedTypeSlot(v.propagatedTypeSlot, 'dart:core', 'dart:core', 'int');
+ }
+
+ test_variable_propagated_type_new_reference() {
+ if (skipFullyLinkedData) {
+ return;
+ }
+ UnlinkedVariable v = serializeVariableText('final v = 0;');
+ // Since the propagated type of `v` is `int`, and there are no references
+ // to `int` elsewhere in the source file, a new linked reference should
+ // have been created for it, with no associated unlinked reference.
+ expect(v.propagatedTypeSlot, isNot(0));
+ TypeRef type = getTypeRefForSlot(v.propagatedTypeSlot);
+ expect(type, isNotNull);
+ expect(type.reference,
+ greaterThanOrEqualTo(unlinkedUnits[0].references.length));
+ }
+
+ test_variable_propagated_type_omit_dynamic() {
+ if (skipFullyLinkedData) {
+ return;
+ }
+ UnlinkedVariable v = serializeVariableText('final v = <int, dynamic>{};');
+ TypeRef type = getTypeRefForSlot(v.propagatedTypeSlot);
+ checkLinkedTypeRef(type, 'dart:core', 'dart:core', 'Map',
+ allowTypeParameters: true, numTypeParameters: 2);
+ expect(type.typeArguments, hasLength(1));
+ checkLinkedTypeRef(type.typeArguments[0], 'dart:core', 'dart:core', 'int');
+ }
+
+ test_variable_propagatedTypeSlot_const() {
+ // Const variables are propagable so they have a nonzero
+ // propagatedTypeSlot.
+ UnlinkedVariable variable = serializeVariableText('const v = 0;');
+ expect(variable.propagatedTypeSlot, isNot(0));
+ }
+
+ test_variable_propagatedTypeSlot_final() {
+ // Final variables are propagable so they have a nonzero
+ // propagatedTypeSlot.
+ UnlinkedVariable variable = serializeVariableText('final v = 0;');
+ expect(variable.propagatedTypeSlot, isNot(0));
+ }
+
+ test_variable_propagatedTypeSlot_non_propagable() {
+ // Non-final non-const variables aren't propagable so they don't have a
+ // propagatedTypeSlot.
+ UnlinkedVariable variable = serializeVariableText('var v;');
+ expect(variable.propagatedTypeSlot, 0);
+ }
+
test_variable_static() {
UnlinkedVariable variable =
serializeClassText('class C { static int i; }').fields[0];
@@ -4081,6 +4383,9 @@ class UnlinkedSummarizeAstTest extends Object with SummaryTest {
bool get expectAbsoluteUrisInDependencies => false;
@override
+ bool get skipFullyLinkedData => true;
+
+ @override
addNamedSource(String filePath, String contents) {
CompilationUnit unit = _parseText(contents);
UnlinkedUnit unlinkedUnit =
« no previous file with comments | « pkg/analyzer/test/src/summary/resynthesize_test.dart ('k') | pkg/analyzer/tool/summary/idl.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698