Skip to content

Commit

Permalink
[NTI] Add a ToStringContext for adapting how JSTypes are stringified.
Browse files Browse the repository at this point in the history
This addresses two problems at once:
1. Issue #2499 where type variables are ambiguated, making warnings difficult to understand
2. Migrating `TypedCodeGenerator` to NTI requires printing types in a "for annotations" mode

Rather than add various booleans, the context object allows plumbing any configuration all the way through the recursive stringify operation, and is extensible if further configuration is needed later.  Moreover, context instances may be passed to multiple stringify operations (i.e. to ensure typevars are printed consistently throughout an entire message).

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=156936000
  • Loading branch information
shicks authored and blickly committed May 25, 2017
1 parent 1623666 commit 269e13b
Show file tree
Hide file tree
Showing 7 changed files with 101 additions and 38 deletions.
6 changes: 6 additions & 0 deletions src/com/google/javascript/jscomp/newtypes/EnumType.java
Expand Up @@ -235,4 +235,10 @@ static boolean areSubtypes(JSType t1, JSType t2, SubtypeCache subSuperMap) {
public Collection<JSType> getSubtypesWithProperty(QualifiedName qname) { public Collection<JSType> getSubtypesWithProperty(QualifiedName qname) {
return declaredType.getSubtypesWithProperty(qname); return declaredType.getSubtypesWithProperty(qname);
} }

public String toString(ToStringContext ctx) {
return ctx.forAnnotation()
? this.declaredType.appendTo(new StringBuilder(), ctx).toString()
: this.toString();
}
} }
23 changes: 12 additions & 11 deletions src/com/google/javascript/jscomp/newtypes/FunctionType.java
Expand Up @@ -1428,36 +1428,37 @@ public int hashCode() {


@Override @Override
public String toString() { public String toString() {
return appendTo(new StringBuilder()).toString(); return appendTo(new StringBuilder(), ToStringContext.TO_STRING).toString();
} }


/** /**
* Returns a transformed collection of type parameter names, mapped back * Returns a transformed collection of type parameter names, mapped back
* to original (pre-uniquified) names. * to original (pre-uniquified) names.
*/ */
private static Collection<String> getPrettyTypeParams(List<String> typeParams) { private static Collection<String> getPrettyTypeParams(
List<String> typeParams, final ToStringContext ctx) {
return Collections2.transform( return Collections2.transform(
typeParams, typeParams,
new Function<String, String>() { new Function<String, String>() {
@Override @Override
public String apply(String typeParam) { public String apply(String typeParam) {
return UniqueNameGenerator.getOriginalName(typeParam); return ctx.formatTypeVar(typeParam);
} }
}); });
} }


@SuppressWarnings("ReferenceEquality") @SuppressWarnings("ReferenceEquality")
public StringBuilder appendTo(StringBuilder builder) { public StringBuilder appendTo(StringBuilder builder, ToStringContext ctx) {
if (this == this.commonTypes.LOOSE_TOP_FUNCTION) { if (this == this.commonTypes.LOOSE_TOP_FUNCTION) {
return builder.append("LOOSE_TOP_FUNCTION"); return builder.append("LOOSE_TOP_FUNCTION");
} else if (this == this.commonTypes.TOP_FUNCTION) { } else if (this == this.commonTypes.TOP_FUNCTION) {
return builder.append("TOP_FUNCTION"); return builder.append("TOP_FUNCTION");
} else if (isQmarkFunction()) { } else if (isQmarkFunction()) {
return builder.append("Function"); return builder.append(ctx.forAnnotation() ? "!" : "").append("Function");
} }
if (!this.typeParameters.isEmpty()) { if (!this.typeParameters.isEmpty()) {
builder.append("<"); builder.append("<");
builder.append(Joiner.on(",").join(getPrettyTypeParams(this.typeParameters))); builder.append(Joiner.on(",").join(getPrettyTypeParams(this.typeParameters, ctx)));
builder.append(">"); builder.append(">");
} }
builder.append("function("); builder.append("function(");
Expand All @@ -1471,16 +1472,16 @@ public StringBuilder appendTo(StringBuilder builder) {
builder.append(','); builder.append(',');
} }
for (int i = 0; i < requiredFormals.size(); ++i) { for (int i = 0; i < requiredFormals.size(); ++i) {
requiredFormals.get(i).appendTo(builder); requiredFormals.get(i).appendTo(builder, ctx);
builder.append(','); builder.append(',');
} }
for (int i = 0; i < optionalFormals.size(); ++i) { for (int i = 0; i < optionalFormals.size(); ++i) {
optionalFormals.get(i).appendTo(builder); optionalFormals.get(i).appendTo(builder, ctx);
builder.append("=,"); builder.append("=,");
} }
if (restFormals != null) { if (restFormals != null) {
builder.append("..."); builder.append("...");
restFormals.appendTo(builder); restFormals.appendTo(builder, ctx);
} }
// Delete the trailing comma, if present // Delete the trailing comma, if present
if (builder.charAt(builder.length() - 1) == ',') { if (builder.charAt(builder.length() - 1) == ',') {
Expand All @@ -1489,7 +1490,7 @@ public StringBuilder appendTo(StringBuilder builder) {
builder.append(')'); builder.append(')');
if (returnType != null) { if (returnType != null) {
builder.append(':'); builder.append(':');
returnType.appendTo(builder); returnType.appendTo(builder, ctx);
} }
if (isLoose()) { if (isLoose()) {
builder.append(" (loose)"); builder.append(" (loose)");
Expand All @@ -1503,7 +1504,7 @@ public StringBuilder appendTo(StringBuilder builder) {
} }
builder.append(entry.getKey()); builder.append(entry.getKey());
builder.append('='); builder.append('=');
entry.getValue().appendTo(builder); entry.getValue().appendTo(builder, ctx);
} }
builder.append('}'); builder.append('}');
} }
Expand Down
26 changes: 14 additions & 12 deletions src/com/google/javascript/jscomp/newtypes/JSType.java
Expand Up @@ -1515,17 +1515,19 @@ public final String toString() {
if (mockToString) { if (mockToString) {
return ""; return "";
} }
return appendTo(new StringBuilder()).toString(); return appendTo(new StringBuilder(), ToStringContext.TO_STRING).toString();
} }


public final StringBuilder appendTo(StringBuilder builder) { /** For use in {@link #appendTo} */
return typeToString(builder);
}

/** For use in {@link #typeToString} */
private static final Joiner PIPE_JOINER = Joiner.on("|"); private static final Joiner PIPE_JOINER = Joiner.on("|");


private StringBuilder typeToString(StringBuilder builder) { /**
* Appends this type to the `builder`. If `forAnnotations` is true, then
* the type will be in a format suitable for type annotations during
* code generation.
*/
StringBuilder appendTo(StringBuilder builder, ToStringContext ctx) {
// TODO(sdh): Add checkState(!forAnnotations) calls in places where annotations are nonsense.
switch (getMask()) { switch (getMask()) {
case BOTTOM_MASK: case BOTTOM_MASK:
return builder.append("bottom"); return builder.append("bottom");
Expand Down Expand Up @@ -1570,16 +1572,16 @@ private StringBuilder typeToString(StringBuilder builder) {
tags &= ~UNDEFINED_MASK; tags &= ~UNDEFINED_MASK;
continue; continue;
case TYPEVAR_MASK: case TYPEVAR_MASK:
builder.append(UniqueNameGenerator.getOriginalName(getTypeVar())); builder.append(ctx.formatTypeVar(getTypeVar()));
tags &= ~TYPEVAR_MASK; tags &= ~TYPEVAR_MASK;
continue; continue;
case NON_SCALAR_MASK: { case NON_SCALAR_MASK: {
if (getObjs().size() == 1) { if (getObjs().size() == 1) {
Iterables.getOnlyElement(getObjs()).appendTo(builder); Iterables.getOnlyElement(getObjs()).appendTo(builder, ctx);
} else { } else {
Set<String> strReps = new TreeSet<>(); Set<String> strReps = new TreeSet<>();
for (ObjectType obj : getObjs()) { for (ObjectType obj : getObjs()) {
strReps.add(obj.toString()); strReps.add(obj.toString(ctx));
} }
PIPE_JOINER.appendTo(builder, strReps); PIPE_JOINER.appendTo(builder, strReps);
} }
Expand All @@ -1588,11 +1590,11 @@ private StringBuilder typeToString(StringBuilder builder) {
} }
case ENUM_MASK: { case ENUM_MASK: {
if (getEnums().size() == 1) { if (getEnums().size() == 1) {
builder.append(Iterables.getOnlyElement(getEnums())); builder.append(Iterables.getOnlyElement(getEnums()).toString(ctx));
} else { } else {
Set<String> strReps = new TreeSet<>(); Set<String> strReps = new TreeSet<>();
for (EnumType e : getEnums()) { for (EnumType e : getEnums()) {
strReps.add(e.toString()); strReps.add(e.toString(ctx));
} }
PIPE_JOINER.appendTo(builder, strReps); PIPE_JOINER.appendTo(builder, strReps);
} }
Expand Down
9 changes: 6 additions & 3 deletions src/com/google/javascript/jscomp/newtypes/NominalType.java
Expand Up @@ -676,10 +676,13 @@ static boolean equalRawTypes(NominalType n1, NominalType n2) {


@Override @Override
public String toString() { public String toString() {
return appendTo(new StringBuilder()).toString(); return appendTo(new StringBuilder(), ToStringContext.TO_STRING).toString();
} }


StringBuilder appendTo(StringBuilder builder) { StringBuilder appendTo(StringBuilder builder, ToStringContext ctx) {
if (ctx.forAnnotation()) {
builder.append("!");
}
if (this.typeMap.isEmpty()) { if (this.typeMap.isEmpty()) {
return this.rawType.appendTo(builder); return this.rawType.appendTo(builder);
} }
Expand All @@ -695,7 +698,7 @@ StringBuilder appendTo(StringBuilder builder) {
builder.append(','); builder.append(',');
} }
JSType concrete = this.typeMap.get(typeParam); JSType concrete = this.typeMap.get(typeParam);
Preconditions.checkNotNull(concrete).appendTo(builder); Preconditions.checkNotNull(concrete).appendTo(builder, ctx);
} }
builder.append('>'); builder.append('>');
return builder; return builder;
Expand Down
18 changes: 11 additions & 7 deletions src/com/google/javascript/jscomp/newtypes/ObjectType.java
Expand Up @@ -1382,23 +1382,27 @@ boolean isPropDefinedOnSubtype(QualifiedName pname) {


@Override @Override
public String toString() { public String toString() {
return appendTo(new StringBuilder()).toString(); return appendTo(new StringBuilder(), ToStringContext.TO_STRING).toString();
} }


StringBuilder appendTo(StringBuilder builder) { String toString(ToStringContext ctx) {
return appendTo(new StringBuilder(), ctx).toString();
}

StringBuilder appendTo(StringBuilder builder, ToStringContext ctx) {
if (isPrototypeObject()) { if (isPrototypeObject()) {
return builder.append(getOwnerFunction().getThisType()).append(".prototype"); return builder.append(getOwnerFunction().getThisType()).append(".prototype");
} }
if (!hasNonPrototypeProperties()) { if (!hasNonPrototypeProperties()) {
if (fn != null) { if (fn != null) {
return fn.appendTo(builder); return fn.appendTo(builder, ctx);
} }
return this.nominalType.appendTo(builder); return this.nominalType.appendTo(builder, ctx);
} }
if (!nominalType.getName().equals("Function") if (!nominalType.getName().equals("Function")
&& !nominalType.getName().equals("Object") && !nominalType.getName().equals("Object")
&& !nominalType.getName().equals(JSTypes.OBJLIT_CLASS_NAME)) { && !nominalType.getName().equals(JSTypes.OBJLIT_CLASS_NAME)) {
nominalType.appendTo(builder); nominalType.appendTo(builder, ctx);
} else if (isStruct()) { } else if (isStruct()) {
builder.append("struct"); builder.append("struct");
} else if (isDict()) { } else if (isDict()) {
Expand All @@ -1408,7 +1412,7 @@ StringBuilder appendTo(StringBuilder builder) {
} }
if (this.fn != null) { if (this.fn != null) {
builder.append("<|"); builder.append("<|");
fn.appendTo(builder); fn.appendTo(builder, ctx);
builder.append("|>"); builder.append("|>");
} }
if (ns == null || !props.isEmpty()) { if (ns == null || !props.isEmpty()) {
Expand All @@ -1422,7 +1426,7 @@ StringBuilder appendTo(StringBuilder builder) {
} }
builder.append(pname); builder.append(pname);
builder.append(':'); builder.append(':');
props.get(pname).appendTo(builder); props.get(pname).appendTo(builder, ctx);
} }
builder.append('}'); builder.append('}');
} }
Expand Down
10 changes: 5 additions & 5 deletions src/com/google/javascript/jscomp/newtypes/Property.java
Expand Up @@ -216,17 +216,17 @@ Property substituteGenerics(Map<String, JSType> concreteTypes) {


@Override @Override
public String toString() { public String toString() {
return appendTo(new StringBuilder()).toString(); return appendTo(new StringBuilder(), ToStringContext.TO_STRING).toString();
} }


public StringBuilder appendTo(StringBuilder builder) { public StringBuilder appendTo(StringBuilder builder, ToStringContext ctx) {
switch (attribute) { switch (attribute) {
case CONSTANT: case CONSTANT:
return inferredType.appendTo(builder).append('^'); return inferredType.appendTo(builder, ctx).append('^');
case REQUIRED: case REQUIRED:
return inferredType.appendTo(builder); return inferredType.appendTo(builder, ctx);
case OPTIONAL: case OPTIONAL:
return inferredType.appendTo(builder).append('='); return inferredType.appendTo(builder, ctx).append('=');
default: default:
throw new RuntimeException("Unknown Attribute value " + attribute); throw new RuntimeException("Unknown Attribute value " + attribute);
} }
Expand Down
47 changes: 47 additions & 0 deletions src/com/google/javascript/jscomp/newtypes/ToStringContext.java
@@ -0,0 +1,47 @@
/*
* Copyright 2017 The Closure Compiler Authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.google.javascript.jscomp.newtypes;

/**
* Configuration options for {@link JSType#toString}. This object is
* passed around to all internal types.
*/
public class ToStringContext {

/** The default context for toString(). */
static final ToStringContext TO_STRING = new ToStringContext();

/** The default context for toAnnotationString(). */
static final ToStringContext FOR_ANNOTATION =
new ToStringContext() {
@Override
boolean forAnnotation() {
return true;
}
};

// Nobody else should be making instances.
private ToStringContext() {}

String formatTypeVar(String typeVar) {
return UniqueNameGenerator.getOriginalName(typeVar);
}

boolean forAnnotation() {
return false;
}
}

0 comments on commit 269e13b

Please sign in to comment.