diff --git a/src/com/google/javascript/jscomp/DisambiguateProperties.java b/src/com/google/javascript/jscomp/DisambiguateProperties.java index 8c1b42c4d15..cd893c7fd18 100644 --- a/src/com/google/javascript/jscomp/DisambiguateProperties.java +++ b/src/com/google/javascript/jscomp/DisambiguateProperties.java @@ -307,14 +307,13 @@ boolean invalidate() { /** * Schedule the node to potentially be renamed. + * * @param node the node to rename - * @param type the highest type in the prototype chain for which the - * property is defined - * @return True if type was accepted without invalidation or if the property - * was already invalidated. False if this property was invalidated this - * time. + * @return True if type was accepted without invalidation or if the property was already + * invalidated. False if this property was invalidated this time. */ - boolean scheduleRenaming(Node node, JSType type) { + boolean scheduleRenaming(Node node, JSType targetType) { + JSType type = processProperty(targetType); if (!skipRenaming) { if (invalidatingTypes.isInvalidating(type)) { invalidate(); @@ -324,6 +323,47 @@ boolean scheduleRenaming(Node node, JSType type) { } return true; } + + @Nullable + private JSType processProperty(JSType type) { + return processProperty(type, null); + } + + /** + * Processes a property, adding it to the list of properties to rename. + * + * @param type a type this property is known to be on, not necessarily the highest type + * @param relatedType only for use inside this function, other callers should pass null. + * @return a representative type for the property reference, which will be the highest type on + * the prototype chain of the provided type. In the case of a union type, it will be the + * highest type on the prototype chain of one of the members of the union. Returns null if + * the property is marked as not renamable or the given type is invalidated + */ + @Nullable + private JSType processProperty(JSType type, @Nullable JSType relatedType) { + type = type.restrictByNotNullOrUndefined(); + if (this.skipRenaming || invalidatingTypes.isInvalidating(type)) { + return null; + } + Iterable alternatives = getTypeAlternatives(type); + if (alternatives != null) { + JSType firstType = relatedType; + for (JSType subType : alternatives) { + JSType lastType = processProperty(subType, firstType); + if (firstType == null) { + firstType = lastType; // maybe null + } + } + return firstType; + } else { + JSType topType = getTypeWithProperty(this.name, type); + if (invalidatingTypes.isInvalidating(topType)) { + return null; + } + this.addType(type, relatedType); + return topType; + } + } } private final Map properties = new LinkedHashMap<>(); @@ -461,7 +501,7 @@ private void handleGetProp(Node n) { String name = n.getLastChild().getString(); JSType type = getType(n.getFirstChild()); Property prop = getProperty(name); - if (!prop.scheduleRenaming(n.getLastChild(), processProperty(prop, type, null)) + if (!prop.scheduleRenaming(n.getLastChild(), type) && propertiesToErrorFor.containsKey(name)) { String suggestion = ""; if (type.isAllType() || type.isUnknownType()) { @@ -504,7 +544,7 @@ private void handleObjectLit(Node n) { String name = child.getString(); JSType objlitType = getType(n); Property prop = getProperty(name); - if (!prop.scheduleRenaming(child, processProperty(prop, objlitType, null))) { + if (!prop.scheduleRenaming(child, objlitType)) { // TODO(user): It doesn't look like the user can do much in this // case right now. if (propertiesToErrorFor.containsKey(name)) { @@ -553,8 +593,7 @@ private void handleClass(Node classNode) { Property prop = getProperty(name); JSType ownerType = member.isStaticMember() ? classType : classInstanceType; - if (!prop.scheduleRenaming(member, processProperty(prop, ownerType, null)) - && propertiesToErrorFor.containsKey(name)) { + if (!prop.scheduleRenaming(member, ownerType) && propertiesToErrorFor.containsKey(name)) { String suggestion = ""; List errors = new ArrayList<>(); printErrorLocations(errors, ownerType); @@ -594,7 +633,7 @@ private void handleObjectPattern(Node pattern) { } String name = stringKey.getString(); Property prop = getProperty(name); - if (!prop.scheduleRenaming(stringKey, processProperty(prop, objectPatternType, null)) + if (!prop.scheduleRenaming(stringKey, objectPatternType) && propertiesToErrorFor.containsKey(name)) { String suggestion = ""; if (objectPatternType.isAllType() || objectPatternType.isUnknownType()) { @@ -662,7 +701,7 @@ private void handlePropertyRenameFunctionCall(Node call, String renameFunctionNa Node obj = call.getChildAtIndex(2); JSType type = getType(obj); Property prop = getProperty(propName); - if (!prop.scheduleRenaming(call.getSecondChild(), processProperty(prop, type, null)) + if (!prop.scheduleRenaming(call.getSecondChild(), type) && propertiesToErrorFor.containsKey(propName)) { String suggestion = ""; if (type.isAllType() || type.isUnknownType()) { @@ -708,7 +747,7 @@ private void handleObjectDefineProperties(Node call) { String propName = key.getString(); Property prop = getProperty(propName); - prop.scheduleRenaming(key, processProperty(prop, type, null)); + prop.scheduleRenaming(key, type); } } @@ -732,42 +771,6 @@ private void printErrorLocations(List errors, JSType t) { errors.add(t + " at " + error.sourceName + ":" + error.lineNumber); } } - - /** - * Processes a property, adding it to the list of properties to rename. - * - * @param type a type this property is known to be on, not necessarily the highest type - * @param relatedType only for use inside this function, other callers should pass null. - * @return a representative type for the property reference, which will be the highest type on - * the prototype chain of the provided type. In the case of a union type, it will be the - * highest type on the prototype chain of one of the members of the union. Returns null if - * the property is marked as not renamable or the given type is invalidated - */ - @Nullable - private JSType processProperty(Property prop, JSType type, @Nullable JSType relatedType) { - type = type.restrictByNotNullOrUndefined(); - if (prop.skipRenaming || invalidatingTypes.isInvalidating(type)) { - return null; - } - Iterable alternatives = getTypeAlternatives(type); - if (alternatives != null) { - JSType firstType = relatedType; - for (JSType subType : alternatives) { - JSType lastType = processProperty(prop, subType, firstType); - if (lastType != null) { - firstType = firstType == null ? lastType : firstType; - } - } - return firstType; - } else { - JSType topType = getTypeWithProperty(prop.name, type); - if (invalidatingTypes.isInvalidating(topType)) { - return null; - } - prop.addType(type, relatedType); - return topType; - } - } } /** Renames all properties with references on more than one type. */