@@ -194,12 +194,14 @@ public final class JSTypeRegistry {
194
194
private final transient Set <String > forwardDeclaredTypes ;
195
195
196
196
// A map of properties to the types on which those properties have been declared.
197
- private transient Multimap <String , JSType > typesIndexedByProperty =
197
+ // "Reference" types are excluded because those already exist in eachRefTypeIndexedByProperty to
198
+ // avoid blowing up the size of this map.
199
+ private final transient SetMultimap <String , JSType > nonRefTypesIndexedByProperty =
198
200
MultimapBuilder .hashKeys ().linkedHashSetValues ().build ();
199
201
200
202
private JSType sentinelObjectLiteral ;
201
203
202
- // To avoid blowing up the size of typesIndexedByProperty , we use the sentinel object
204
+ // To avoid blowing up the size of nonRefTypesIndexedByProperty , we use the sentinel object
203
205
// literal instead of registering arbitrarily many types.
204
206
// But because of the way unions are constructed, some properties of record types in unions
205
207
// are getting dropped and cause spurious "non-existent property" warnings.
@@ -209,7 +211,7 @@ public final class JSTypeRegistry {
209
211
// canPropertyBeDefined, if the type has a property in propertiesOfSupertypesInUnions, we
210
212
// consider it to possibly have any property in droppedPropertiesOfUnions. This is a loose
211
213
// check, but we restrict it to records that may be present in unions, and it allows us to
212
- // keep typesIndexedByProperty small.
214
+ // keep nonRefTypesIndexedByProperty small.
213
215
private final Set <String > propertiesOfSupertypesInUnions = new HashSet <>();
214
216
private final Set <String > droppedPropertiesOfUnions = new HashSet <>();
215
217
@@ -1080,31 +1082,22 @@ void registerDroppedPropertiesInUnion(RecordType subtype, RecordType supertype)
1080
1082
* show up in the type registry").
1081
1083
*/
1082
1084
public void registerPropertyOnType (String propertyName , JSType type ) {
1083
- if (isObjectLiteralThatCanBeSkipped (type )) {
1084
- type = getSentinelObjectLiteral ();
1085
- }
1086
-
1087
1085
if (type .isUnionType ()) {
1088
- typesIndexedByProperty .putAll (propertyName , type .toMaybeUnionType ().getAlternates ());
1089
- } else {
1090
- typesIndexedByProperty .put (propertyName , type );
1086
+ for (JSType alternate : type .toMaybeUnionType ().getAlternates ()) {
1087
+ registerPropertyOnType (propertyName , alternate );
1088
+ }
1089
+ return ;
1091
1090
}
1092
1091
1093
- addReferenceTypeIndexedByProperty (propertyName , type );
1094
- }
1092
+ if (isObjectLiteralThatCanBeSkipped (type )) {
1093
+ type = getSentinelObjectLiteral ();
1094
+ }
1095
1095
1096
- private void addReferenceTypeIndexedByProperty (
1097
- String propertyName , JSType type ) {
1098
1096
if (type instanceof ObjectType && ((ObjectType ) type ).hasReferenceName ()) {
1099
1097
ObjectType objType = (ObjectType ) type ;
1100
1098
eachRefTypeIndexedByProperty .put (propertyName , objType );
1101
- } else if (type instanceof NamedType ) {
1102
- addReferenceTypeIndexedByProperty (
1103
- propertyName , ((NamedType ) type ).getReferencedType ());
1104
- } else if (type .isUnionType ()) {
1105
- for (JSType alternate : type .toMaybeUnionType ().getAlternates ()) {
1106
- addReferenceTypeIndexedByProperty (propertyName , alternate );
1107
- }
1099
+ } else {
1100
+ nonRefTypesIndexedByProperty .put (propertyName , type );
1108
1101
}
1109
1102
}
1110
1103
@@ -1148,19 +1141,26 @@ public PropDefinitionKind canPropertyBeDefined(JSType type, String propertyName)
1148
1141
}
1149
1142
}
1150
1143
1151
- if (typesIndexedByProperty .containsKey (propertyName )) {
1152
- for (JSType alternative : typesIndexedByProperty .get (propertyName )) {
1153
- JSType greatestSubtype = alternative .getGreatestSubtype (type );
1154
- if (!greatestSubtype .isEmptyType ()) {
1155
- // We've found a type with this property. Now we just have to make
1156
- // sure it's not a type used for internal bookkeeping.
1157
- RecordType maybeRecordType = greatestSubtype .toMaybeRecordType ();
1158
- if (maybeRecordType != null && maybeRecordType .isSynthetic ()) {
1159
- continue ;
1160
- }
1144
+ Iterable <JSType > associatedTypes = ImmutableList .of ();
1145
+ if (nonRefTypesIndexedByProperty .containsKey (propertyName )) {
1146
+ associatedTypes = nonRefTypesIndexedByProperty .get (propertyName );
1147
+ }
1148
+ if (eachRefTypeIndexedByProperty .containsKey (propertyName )) {
1149
+ associatedTypes =
1150
+ Iterables .concat (associatedTypes , eachRefTypeIndexedByProperty .get (propertyName ));
1151
+ }
1161
1152
1162
- return PropDefinitionKind .LOOSE ;
1153
+ for (JSType alternative : associatedTypes ) {
1154
+ JSType greatestSubtype = alternative .getGreatestSubtype (type );
1155
+ if (!greatestSubtype .isEmptyType ()) {
1156
+ // We've found a type with this property. Now we just have to make
1157
+ // sure it's not a type used for internal bookkeeping.
1158
+ RecordType maybeRecordType = greatestSubtype .toMaybeRecordType ();
1159
+ if (maybeRecordType != null && maybeRecordType .isSynthetic ()) {
1160
+ continue ;
1163
1161
}
1162
+
1163
+ return PropDefinitionKind .LOOSE ;
1164
1164
}
1165
1165
}
1166
1166
0 commit comments