aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--src/main/java/com/google/devtools/build/lib/packages/MethodLibrary.java33
-rw-r--r--src/main/java/com/google/devtools/build/lib/packages/Type.java7
-rw-r--r--src/main/java/com/google/devtools/build/lib/query2/output/OutputFormatter.java7
-rw-r--r--src/main/java/com/google/devtools/build/lib/syntax/BinaryOperatorExpression.java7
-rw-r--r--src/main/java/com/google/devtools/build/lib/syntax/DotExpression.java2
-rw-r--r--src/main/java/com/google/devtools/build/lib/syntax/EvalUtils.java285
-rw-r--r--src/main/java/com/google/devtools/build/lib/syntax/FuncallExpression.java10
-rw-r--r--src/main/java/com/google/devtools/build/lib/syntax/FunctionSignature.java8
-rw-r--r--src/main/java/com/google/devtools/build/lib/syntax/LValue.java2
-rw-r--r--src/main/java/com/google/devtools/build/lib/syntax/ListComprehension.java2
-rw-r--r--src/main/java/com/google/devtools/build/lib/syntax/Printer.java350
-rw-r--r--src/main/java/com/google/devtools/build/lib/syntax/SkylarkList.java7
-rw-r--r--src/main/java/com/google/devtools/build/lib/syntax/SkylarkNestedSet.java2
-rw-r--r--src/main/java/com/google/devtools/build/lib/syntax/SkylarkSignatureProcessor.java2
-rw-r--r--src/test/java/com/google/devtools/build/lib/packages/RuleClassTest.java2
-rw-r--r--src/test/java/com/google/devtools/build/lib/packages/TypeTest.java55
-rw-r--r--src/test/java/com/google/devtools/build/lib/syntax/BaseFunctionTest.java8
-rw-r--r--src/test/java/com/google/devtools/build/lib/syntax/EvalUtilsTest.java170
-rw-r--r--src/test/java/com/google/devtools/build/lib/syntax/EvaluationTest.java2
-rw-r--r--src/test/java/com/google/devtools/build/lib/syntax/PrinterTest.java214
-rw-r--r--src/test/java/com/google/devtools/build/lib/syntax/SkylarkShell.java2
21 files changed, 641 insertions, 536 deletions
diff --git a/src/main/java/com/google/devtools/build/lib/packages/MethodLibrary.java b/src/main/java/com/google/devtools/build/lib/packages/MethodLibrary.java
index f2d1b4fe87..f67b0feec9 100644
--- a/src/main/java/com/google/devtools/build/lib/packages/MethodLibrary.java
+++ b/src/main/java/com/google/devtools/build/lib/packages/MethodLibrary.java
@@ -32,6 +32,7 @@ import com.google.devtools.build.lib.syntax.Environment;
import com.google.devtools.build.lib.syntax.EvalException;
import com.google.devtools.build.lib.syntax.EvalUtils;
import com.google.devtools.build.lib.syntax.FuncallExpression;
+import com.google.devtools.build.lib.syntax.Printer;
import com.google.devtools.build.lib.syntax.SelectorList;
import com.google.devtools.build.lib.syntax.SelectorValue;
import com.google.devtools.build.lib.syntax.SkylarkEnvironment;
@@ -472,7 +473,7 @@ public class MethodLibrary {
int res = stringFind(false, self, sub, start, end, "'end' argument to rindex");
if (res < 0) {
throw new EvalException(loc, String.format("substring %s not found in %s",
- EvalUtils.prettyPrintValue(sub), EvalUtils.prettyPrintValue(self)));
+ Printer.repr(sub), Printer.repr(self)));
}
return res;
}
@@ -498,7 +499,7 @@ public class MethodLibrary {
int res = stringFind(true, self, sub, start, end, "'end' argument to index");
if (res < 0) {
throw new EvalException(loc, String.format("substring %s not found in %s",
- EvalUtils.prettyPrintValue(sub), EvalUtils.prettyPrintValue(self)));
+ Printer.repr(sub), Printer.repr(self)));
}
return res;
}
@@ -578,7 +579,7 @@ public class MethodLibrary {
if (!kwargs.containsKey(word)) {
throw new EvalException(loc, "No replacement found for '" + word + "'");
}
- matcher.appendReplacement(result, EvalUtils.printValue(kwargs.get(word)));
+ matcher.appendReplacement(result, Printer.str(kwargs.get(word)));
}
matcher.appendTail(result);
return result.toString();
@@ -718,7 +719,7 @@ public class MethodLibrary {
Map<?, ?> dictionary = (Map<?, ?>) self;
if (!dictionary.containsKey(key)) {
throw new EvalException(loc, String.format("Key %s not found in dictionary",
- EvalUtils.prettyPrintValue(key)));
+ Printer.repr(key)));
}
return dictionary.get(key);
@@ -872,8 +873,17 @@ public class MethodLibrary {
"Converts any object to string. This is useful for debugging.",
mandatoryPositionals = {@Param(name = "x", doc = "The object to convert.")})
private static BuiltinFunction str = new BuiltinFunction("str") {
- public String invoke(Object x) throws EvalException {
- return EvalUtils.printValue(x);
+ public String invoke(Object x) {
+ return Printer.str(x);
+ }
+ };
+
+ @SkylarkSignature(name = "repr", returnType = String.class, doc =
+ "Converts any object to a string representation. This is useful for debugging.",
+ mandatoryPositionals = {@Param(name = "x", doc = "The object to convert.")})
+ private static BuiltinFunction repr = new BuiltinFunction("repr") {
+ public String invoke(Object x) {
+ return Printer.repr(x);
}
};
@@ -908,12 +918,11 @@ public class MethodLibrary {
return Integer.parseInt((String) x);
} catch (NumberFormatException e) {
throw new EvalException(loc,
- "invalid literal for int(): " + EvalUtils.prettyPrintValue(x));
+ "invalid literal for int(): " + Printer.repr(x));
}
} else {
throw new EvalException(loc,
- String.format("argument must be string, int, or bool, not '%s'",
- EvalUtils.prettyPrintValue(x)));
+ String.format("%s is not of type string or int or bool", Printer.repr(x)));
}
}
};
@@ -1113,7 +1122,7 @@ public class MethodLibrary {
return defaultValue;
} else {
throw new EvalException(loc, String.format("Object of type '%s' has no field %s",
- EvalUtils.getDataTypeName(obj), EvalUtils.prettyPrintValue(name)));
+ EvalUtils.getDataTypeName(obj), Printer.repr(name)));
}
}
return result;
@@ -1189,7 +1198,7 @@ public class MethodLibrary {
new com.google.common.base.Function<Object, String>() {
@Override
public String apply(Object input) {
- return EvalUtils.printValue(input);
+ return Printer.str(input);
}}));
env.handleEvent(Event.warn(loc, msg));
return Environment.NONE;
@@ -1298,7 +1307,7 @@ public class MethodLibrary {
items, get, keys, values);
private static final List<BaseFunction> pureGlobalFunctions = ImmutableList.<BaseFunction>of(
- bool, int_, len, minus, select, sorted, str);
+ bool, int_, len, minus, repr, select, sorted, str);
private static final List<BaseFunction> skylarkGlobalFunctions =
ImmutableList.<BaseFunction>builder()
diff --git a/src/main/java/com/google/devtools/build/lib/packages/Type.java b/src/main/java/com/google/devtools/build/lib/packages/Type.java
index 593838bf63..7998d93cfe 100644
--- a/src/main/java/com/google/devtools/build/lib/packages/Type.java
+++ b/src/main/java/com/google/devtools/build/lib/packages/Type.java
@@ -28,6 +28,7 @@ import com.google.devtools.build.lib.syntax.EvalUtils;
import com.google.devtools.build.lib.syntax.FilesetEntry;
import com.google.devtools.build.lib.syntax.GlobList;
import com.google.devtools.build.lib.syntax.Label;
+import com.google.devtools.build.lib.syntax.Printer;
import com.google.devtools.build.lib.syntax.SelectorValue;
import com.google.devtools.build.lib.syntax.SkylarkList;
import com.google.devtools.build.lib.util.LoggingUtil;
@@ -367,9 +368,9 @@ public abstract class Type<T> {
if (what != null) {
builder.append(" for ").append(what);
}
- builder.append(", but got '");
- EvalUtils.printValue(value, builder);
- builder.append("' (").append(EvalUtils.getDataTypeName(value)).append(")");
+ builder.append(", but got ");
+ Printer.write(builder, value);
+ builder.append(" (").append(EvalUtils.getDataTypeName(value)).append(")");
return builder.toString();
}
diff --git a/src/main/java/com/google/devtools/build/lib/query2/output/OutputFormatter.java b/src/main/java/com/google/devtools/build/lib/query2/output/OutputFormatter.java
index e47fcb70da..26432465d4 100644
--- a/src/main/java/com/google/devtools/build/lib/query2/output/OutputFormatter.java
+++ b/src/main/java/com/google/devtools/build/lib/query2/output/OutputFormatter.java
@@ -27,6 +27,7 @@ import com.google.devtools.build.lib.packages.Rule;
import com.google.devtools.build.lib.packages.Target;
import com.google.devtools.build.lib.syntax.EvalUtils;
import com.google.devtools.build.lib.syntax.Label;
+import com.google.devtools.build.lib.syntax.Printer;
import com.google.devtools.build.lib.util.BinaryPredicate;
import com.google.devtools.build.lib.util.Pair;
import com.google.devtools.common.options.EnumConverter;
@@ -309,7 +310,7 @@ public abstract class OutputFormatter implements Serializable {
// Display it as a list (and not as a tuple). Attributes can never be tuples.
value = new ArrayList<>((List<?>) value);
}
- EvalUtils.prettyPrintValue(value, out);
+ Printer.write(out, value);
out.println(",");
}
out.printf(")\n%n");
@@ -345,7 +346,7 @@ public abstract class OutputFormatter implements Serializable {
* shows the lowest rank for a given node, i.e. the length of the shortest
* path from a zero-rank node to it.
*
- * If the result came from a <code>deps(x)</code> query, then the MINRANKs
+ * <p>If the result came from a <code>deps(x)</code> query, then the MINRANKs
* correspond to the shortest path from x to each of its prerequisites.
*/
private static class MinrankOutputFormatter extends OutputFormatter {
@@ -396,7 +397,7 @@ public abstract class OutputFormatter implements Serializable {
* highest rank for a given node, i.e. the length of the longest non-cyclic
* path from a zero-rank node to it.
*
- * If the result came from a <code>deps(x)</code> query, then the MAXRANKs
+ * <p>If the result came from a <code>deps(x)</code> query, then the MAXRANKs
* correspond to the longest path from x to each of its prerequisites.
*/
private static class MaxrankOutputFormatter extends OutputFormatter {
diff --git a/src/main/java/com/google/devtools/build/lib/syntax/BinaryOperatorExpression.java b/src/main/java/com/google/devtools/build/lib/syntax/BinaryOperatorExpression.java
index 23e3e16b19..b7937ad98c 100644
--- a/src/main/java/com/google/devtools/build/lib/syntax/BinaryOperatorExpression.java
+++ b/src/main/java/com/google/devtools/build/lib/syntax/BinaryOperatorExpression.java
@@ -190,19 +190,18 @@ public final class BinaryOperatorExpression extends Expression {
if (rval instanceof List<?>) {
List<?> rlist = (List<?>) rval;
if (EvalUtils.isTuple(rlist)) {
- return EvalUtils.formatString(pattern, rlist);
+ return Printer.format(pattern, rlist);
}
/* string % list: fall thru */
}
if (rval instanceof SkylarkList) {
SkylarkList rlist = (SkylarkList) rval;
if (rlist.isTuple()) {
- return EvalUtils.formatString(pattern, rlist.toList());
+ return Printer.format(pattern, rlist.toList());
}
}
- return EvalUtils.formatString(pattern,
- Collections.singletonList(rval));
+ return Printer.format(pattern, Collections.singletonList(rval));
} catch (IllegalFormatException e) {
throw new EvalException(getLocation(), e.getMessage());
}
diff --git a/src/main/java/com/google/devtools/build/lib/syntax/DotExpression.java b/src/main/java/com/google/devtools/build/lib/syntax/DotExpression.java
index 0b3271ef49..bcdd4e8ce3 100644
--- a/src/main/java/com/google/devtools/build/lib/syntax/DotExpression.java
+++ b/src/main/java/com/google/devtools/build/lib/syntax/DotExpression.java
@@ -60,7 +60,7 @@ public final class DotExpression extends Expression {
}
}
throw new EvalException(getLocation(), String.format("Object of type '%s' has no field %s",
- EvalUtils.getDataTypeName(objValue), EvalUtils.prettyPrintValue(name)));
+ EvalUtils.getDataTypeName(objValue), Printer.repr(name)));
}
return result;
}
diff --git a/src/main/java/com/google/devtools/build/lib/syntax/EvalUtils.java b/src/main/java/com/google/devtools/build/lib/syntax/EvalUtils.java
index 74206b4e43..8db7648f44 100644
--- a/src/main/java/com/google/devtools/build/lib/syntax/EvalUtils.java
+++ b/src/main/java/com/google/devtools/build/lib/syntax/EvalUtils.java
@@ -14,32 +14,20 @@
package com.google.devtools.build.lib.syntax;
import com.google.common.annotations.VisibleForTesting;
-import com.google.common.base.Function;
-import com.google.common.base.Joiner;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
-import com.google.common.collect.Lists;
import com.google.common.collect.Ordering;
import com.google.devtools.build.lib.collect.nestedset.NestedSet;
-import com.google.devtools.build.lib.collect.nestedset.Order;
import com.google.devtools.build.lib.concurrent.ThreadSafety.Immutable;
import com.google.devtools.build.lib.events.Location;
-import com.google.devtools.build.lib.vfs.PathFragment;
-import java.io.IOException;
-import java.util.ArrayList;
import java.util.Collection;
-import java.util.Collections;
import java.util.Comparator;
-import java.util.Formattable;
-import java.util.Formatter;
-import java.util.IllegalFormatException;
import java.util.List;
import java.util.Map;
-import java.util.MissingFormatWidthException;
import java.util.Set;
/**
@@ -316,187 +304,6 @@ public abstract class EvalUtils {
return isTuple ? ImmutableList.copyOf(seq) : seq;
}
- /**
- * Print build-language value 'o' in display format into the specified buffer.
- */
- public static void printValue(Object o, Appendable buffer) {
- // Exception-swallowing wrapper due to annoying Appendable interface.
- try {
- printValueX(o, buffer);
- } catch (IOException e) {
- throw new AssertionError(e); // can't happen
- }
- }
-
- private static void printValueX(Object o, Appendable buffer)
- throws IOException {
- if (o == null) {
- throw new NullPointerException(); // Java null is not a build language value.
- } else if (o instanceof String || o instanceof Integer || o instanceof Double) {
- buffer.append(o.toString());
-
- } else if (o == Environment.NONE) {
- buffer.append("None");
-
- } else if (o == Boolean.TRUE) {
- buffer.append("True");
-
- } else if (o == Boolean.FALSE) {
- buffer.append("False");
-
- } else if (o instanceof List<?>) {
- List<?> seq = (List<?>) o;
- printList(seq, isImmutable(seq), buffer);
-
- } else if (o instanceof SkylarkList) {
- SkylarkList list = (SkylarkList) o;
- printList(list.toList(), list.isTuple(), buffer);
-
- } else if (o instanceof Map<?, ?>) {
- Map<?, ?> dict = (Map<?, ?>) o;
- printList(dict.entrySet(), "{", ", ", "}", null, buffer);
-
- } else if (o instanceof Map.Entry<?, ?>) {
- Map.Entry<?, ?> entry = (Map.Entry<?, ?>) o;
- prettyPrintValue(entry.getKey(), buffer);
- buffer.append(": ");
- prettyPrintValue(entry.getValue(), buffer);
-
- } else if (o instanceof SkylarkNestedSet) {
- SkylarkNestedSet set = (SkylarkNestedSet) o;
- buffer.append("set(");
- printList(set, "[", ", ", "]", null, buffer);
- Order order = set.getOrder();
- if (order != Order.STABLE_ORDER) {
- buffer.append(", order = \"" + order.getName() + "\"");
- }
- buffer.append(")");
-
- } else if (o instanceof BaseFunction) {
- BaseFunction func = (BaseFunction) o;
- buffer.append("<function " + func.getName() + ">");
-
- } else if (o instanceof FilesetEntry) {
- FilesetEntry entry = (FilesetEntry) o;
- buffer.append("FilesetEntry(srcdir = ");
- prettyPrintValue(entry.getSrcLabel().toString(), buffer);
- buffer.append(", files = ");
- prettyPrintValue(makeStringList(entry.getFiles()), buffer);
- buffer.append(", excludes = ");
- prettyPrintValue(makeList(entry.getExcludes()), buffer);
- buffer.append(", destdir = ");
- prettyPrintValue(entry.getDestDir().getPathString(), buffer);
- buffer.append(", strip_prefix = ");
- prettyPrintValue(entry.getStripPrefix(), buffer);
- buffer.append(", symlinks = \"");
- buffer.append(entry.getSymlinkBehavior().toString());
- buffer.append("\")");
- } else if (o instanceof PathFragment) {
- buffer.append(((PathFragment) o).getPathString());
- } else {
- buffer.append(o.toString());
- }
- }
-
- public static void printList(Iterable<?> list,
- String before, String separator, String after, String singletonTerminator, Appendable buffer)
- throws IOException {
- boolean printSeparator = false; // don't print the separator before the first element
- int len = 0;
- buffer.append(before);
- for (Object o : list) {
- if (printSeparator) {
- buffer.append(separator);
- }
- prettyPrintValue(o, buffer);
- printSeparator = true;
- len++;
- }
- if (singletonTerminator != null && len == 1) {
- buffer.append(singletonTerminator);
- }
- buffer.append(after);
- }
-
- public static void printList(Iterable<?> list, boolean isTuple, Appendable buffer)
- throws IOException {
- if (isTuple) {
- printList(list, "(", ", ", ")", ",", buffer);
- } else {
- printList(list, "[", ", ", "]", null, buffer);
- }
- }
-
- private static List<?> makeList(Collection<?> list) {
- return list == null ? Lists.newArrayList() : Lists.newArrayList(list);
- }
-
- private static List<String> makeStringList(List<Label> labels) {
- if (labels == null) { return Collections.emptyList(); }
- List<String> strings = Lists.newArrayListWithCapacity(labels.size());
- for (Label label : labels) {
- strings.add(label.toString());
- }
- return strings;
- }
-
- /**
- * Print build-language value 'o' in parseable format into the specified
- * buffer. (Only differs from printValueX in treatment of strings at toplevel,
- * i.e. not within a sequence or dict)
- */
- public static void prettyPrintValue(Object o, Appendable buffer) {
- // Exception-swallowing wrapper due to annoying Appendable interface.
- try {
- prettyPrintValueX(o, buffer);
- } catch (IOException e) {
- throw new AssertionError(e); // can't happen
- }
- }
-
- private static void prettyPrintValueX(Object o, Appendable buffer)
- throws IOException {
- if (o instanceof Label) {
- o = o.toString(); // Pretty-print a label like a string
- }
- if (o instanceof String) {
- Printer.writeString(buffer, (String) o);
- } else {
- printValueX(o, buffer);
- }
- }
-
- /**
- * Pretty-print value 'o' to a string. Convenience overloading of
- * prettyPrintValue(Object, Appendable).
- */
- public static String prettyPrintValue(Object o) {
- StringBuilder buffer = new StringBuilder();
- prettyPrintValue(o, buffer);
- return buffer.toString();
- }
-
- /**
- * Pretty-print values of 'o' separated by the separator.
- */
- public static String prettyPrintValues(String separator, Iterable<Object> o) {
- return Joiner.on(separator).join(Iterables.transform(o, new Function<Object, String>() {
- @Override
- public String apply(Object input) {
- return prettyPrintValue(input);
- }
- }));
- }
-
- /**
- * Print value 'o' to a string. Convenience overloading of printValue(Object, Appendable).
- */
- public static String printValue(Object o) {
- StringBuilder buffer = new StringBuilder();
- printValue(o, buffer);
- return buffer.toString();
- }
-
public static Object checkNotNull(Expression expr, Object obj) throws EvalException {
if (obj == null) {
throw new EvalException(expr.getLocation(),
@@ -507,98 +314,6 @@ public abstract class EvalUtils {
}
/**
- * Convert BUILD language objects to Formattable so JDK can render them correctly.
- * Don't do this for numeric or string types because we want %d, %x, %s to work.
- */
- private static Object makeFormattable(final Object o) {
- if (o instanceof Integer || o instanceof Double || o instanceof String) {
- return o;
- } else {
- return new Formattable() {
- @Override
- public String toString() {
- return "Formattable[" + o + "]";
- }
-
- @Override
- public void formatTo(Formatter formatter, int flags, int width,
- int precision) {
- printValue(o, formatter.out());
- }
- };
- }
- }
-
- private static final Object[] EMPTY = new Object[0];
-
- /*
- * N.B. MissingFormatWidthException is the only kind of IllegalFormatException
- * whose constructor can take and display arbitrary error message, hence its use below.
- */
-
- /**
- * Perform Python-style string formatting. Implemented by delegation to Java's
- * own string formatting routine to avoid reinventing the wheel. In more
- * obscure cases, semantics follow JDK (not Python) rules.
- *
- * @param pattern a format string.
- * @param tuple a tuple containing positional arguments
- */
- public static String formatString(String pattern, List<?> tuple)
- throws IllegalFormatException {
- int count = countPlaceholders(pattern);
- if (count < tuple.size()) {
- throw new MissingFormatWidthException(
- "not all arguments converted during string formatting");
- }
-
- List<Object> args = new ArrayList<>();
-
- for (Object o : tuple) {
- args.add(makeFormattable(o));
- }
-
- try {
- return String.format(pattern, args.toArray(EMPTY));
- } catch (IllegalFormatException e) {
- throw new MissingFormatWidthException(
- "invalid arguments for format string");
- }
- }
-
- private static int countPlaceholders(String pattern) {
- int length = pattern.length();
- boolean afterPercent = false;
- int i = 0;
- int count = 0;
- while (i < length) {
- switch (pattern.charAt(i)) {
- case 's':
- case 'd':
- if (afterPercent) {
- count++;
- afterPercent = false;
- }
- break;
-
- case '%':
- afterPercent = !afterPercent;
- break;
-
- default:
- if (afterPercent) {
- throw new MissingFormatWidthException("invalid arguments for format string");
- }
- afterPercent = false;
- break;
- }
- i++;
- }
-
- return count;
- }
-
- /**
* @return the truth value of an object, according to Python rules.
* http://docs.python.org/2/library/stdtypes.html#truth-value-testing
*/
diff --git a/src/main/java/com/google/devtools/build/lib/syntax/FuncallExpression.java b/src/main/java/com/google/devtools/build/lib/syntax/FuncallExpression.java
index 5f2c512252..21df356d12 100644
--- a/src/main/java/com/google/devtools/build/lib/syntax/FuncallExpression.java
+++ b/src/main/java/com/google/devtools/build/lib/syntax/FuncallExpression.java
@@ -26,7 +26,6 @@ import com.google.devtools.build.lib.events.Location;
import com.google.devtools.build.lib.syntax.EvalException.EvalExceptionWithJavaCause;
import com.google.devtools.build.lib.util.StringUtilities;
-import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
@@ -270,12 +269,7 @@ public final class FuncallExpression extends Expression {
if (obj != null) {
sb.append(obj).append(".");
}
- sb.append(func);
- try {
- EvalUtils.printList(args, "(", ", ", ")", null, sb);
- } catch (IOException x) {
- throw new RuntimeException("Error while printing", x);
- }
+ Printer.printList(sb.append(func), args, "(", ", ", ")", null);
return sb.toString();
}
@@ -331,7 +325,7 @@ public final class FuncallExpression extends Expression {
} else {
throw new EvalException(loc,
"Method invocation returned None, please contact Skylark developers: " + methodName
- + "(" + EvalUtils.prettyPrintValues(", ", ImmutableList.copyOf(args)) + ")");
+ + Printer.listString(ImmutableList.copyOf(args), "(", ", ", ")", null));
}
}
result = SkylarkType.convertToSkylark(result, method);
diff --git a/src/main/java/com/google/devtools/build/lib/syntax/FunctionSignature.java b/src/main/java/com/google/devtools/build/lib/syntax/FunctionSignature.java
index 3792e21fbc..272fc5e7d3 100644
--- a/src/main/java/com/google/devtools/build/lib/syntax/FunctionSignature.java
+++ b/src/main/java/com/google/devtools/build/lib/syntax/FunctionSignature.java
@@ -389,8 +389,12 @@ public abstract class FunctionSignature implements Serializable {
}
public void optional(int i) {
mandatory(i);
- sb.append(" = ").append((defaultValues == null)
- ? "?" : EvalUtils.prettyPrintValue(defaultValues.get(j++)));
+ sb.append(" = ");
+ if (defaultValues == null) {
+ sb.append("?");
+ } else {
+ Printer.write(sb, defaultValues.get(j++));
+ }
}
};
Show show = new Show();
diff --git a/src/main/java/com/google/devtools/build/lib/syntax/LValue.java b/src/main/java/com/google/devtools/build/lib/syntax/LValue.java
index fcaf3a8342..42b5b097df 100644
--- a/src/main/java/com/google/devtools/build/lib/syntax/LValue.java
+++ b/src/main/java/com/google/devtools/build/lib/syntax/LValue.java
@@ -97,7 +97,7 @@ public class LValue implements Serializable {
&& !variableType.equals(Environment.NoneType.class)) {
throw new EvalException(loc, String.format("Incompatible variable types, "
+ "trying to assign %s (type of %s) to variable %s which is already %s",
- EvalUtils.prettyPrintValue(result),
+ Printer.repr(result),
EvalUtils.getDataTypeName(result),
ident.getName(),
EvalUtils.getDataTypeNameFromClass(variableType)));
diff --git a/src/main/java/com/google/devtools/build/lib/syntax/ListComprehension.java b/src/main/java/com/google/devtools/build/lib/syntax/ListComprehension.java
index bd83e21821..b3d68178cc 100644
--- a/src/main/java/com/google/devtools/build/lib/syntax/ListComprehension.java
+++ b/src/main/java/com/google/devtools/build/lib/syntax/ListComprehension.java
@@ -120,7 +120,7 @@ public final class ListComprehension extends Expression {
@Override
public String toString() {
- return String.format("for %s in %s", variables.toString(), EvalUtils.prettyPrintValue(list));
+ return String.format("for %s in %s", variables.toString(), Printer.repr(list));
}
}
diff --git a/src/main/java/com/google/devtools/build/lib/syntax/Printer.java b/src/main/java/com/google/devtools/build/lib/syntax/Printer.java
index 259b34bb9d..8739be8c65 100644
--- a/src/main/java/com/google/devtools/build/lib/syntax/Printer.java
+++ b/src/main/java/com/google/devtools/build/lib/syntax/Printer.java
@@ -13,7 +13,21 @@
// limitations under the License.
package com.google.devtools.build.lib.syntax;
+import com.google.common.collect.Lists;
+
+import com.google.devtools.build.lib.collect.nestedset.Order;
+import com.google.devtools.build.lib.vfs.PathFragment;
+
import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Formattable;
+import java.util.Formatter;
+import java.util.IllegalFormatException;
+import java.util.List;
+import java.util.Map;
+import java.util.MissingFormatWidthException;
/**
* (Pretty) Printing of Skylark values
@@ -23,12 +37,157 @@ public final class Printer {
private Printer() {
}
- private static Appendable backslashChar(Appendable buffer, char c) throws IOException {
- return buffer.append('\\').append(c);
+ /**
+ * Get an informal representation of object x.
+ * Currently only differs from repr in the behavior for strings and labels at top-level,
+ * that are returned as is rather than quoted.
+ * @return the representation.
+ */
+ public static String str(Object x) {
+ return print(new StringBuilder(), x).toString();
+ }
+
+ /**
+ * Get an official representation of object x.
+ * For regular data structures, the value should be parsable back into an equal data structure.
+ * @return the representation.
+ */
+ public static String repr(Object x) {
+ return write(new StringBuilder(), x).toString();
+ }
+
+ // In absence of a Python naming tradition, the write() vs print() function names
+ // follow the Lisp tradition: print() displays the informal representation (as in Python str)
+ // whereas write() displays a readable representation (as in Python repr).
+ /**
+ * Print an informal representation of object x.
+ * Currently only differs from repr in the behavior for strings and labels at top-level,
+ * that are returned as is rather than quoted.
+ * @param buffer the Appendable to which to print the representation
+ * @param o the object
+ * @return the buffer, in fluent style
+ */
+ public static Appendable print(Appendable buffer, Object o) {
+ if (o instanceof Label) {
+ return append(buffer, o.toString()); // Pretty-print a label like a string
+ }
+ if (o instanceof String) {
+ return append(buffer, (String) o);
+ }
+ return write(buffer, o);
+ }
+
+ /**
+ * Print an official representation of object x.
+ * For regular data structures, the value should be parsable back into an equal data structure.
+ * @param buffer the Appendable to write to.
+ * @param o the string a representation of which to write.
+ * @return the Appendable, in fluent style.
+ */
+ public static Appendable write(Appendable buffer, Object o) {
+ if (o == null) {
+ throw new NullPointerException(); // Java null is not a build language value.
+
+ } else if (o instanceof String) {
+ writeString(buffer, (String) o);
+
+ } else if (o instanceof Integer || o instanceof Double) {
+ append(buffer, o.toString());
+
+ } else if (o == Environment.NONE) {
+ append(buffer, "None");
+
+ } else if (o == Boolean.TRUE) {
+ append(buffer, "True");
+
+ } else if (o == Boolean.FALSE) {
+ append(buffer, "False");
+
+ } else if (o instanceof List<?>) {
+ List<?> seq = (List<?>) o;
+ printList(buffer, seq, EvalUtils.isImmutable(seq));
+
+ } else if (o instanceof SkylarkList) {
+ SkylarkList list = (SkylarkList) o;
+ printList(buffer, list.toList(), list.isTuple());
+
+ } else if (o instanceof Map<?, ?>) {
+ Map<?, ?> dict = (Map<?, ?>) o;
+ printList(buffer, dict.entrySet(), "{", ", ", "}", null);
+
+ } else if (o instanceof Map.Entry<?, ?>) {
+ Map.Entry<?, ?> entry = (Map.Entry<?, ?>) o;
+ write(buffer, entry.getKey());
+ append(buffer, ": ");
+ write(buffer, entry.getValue());
+
+ } else if (o instanceof SkylarkNestedSet) {
+ SkylarkNestedSet set = (SkylarkNestedSet) o;
+ append(buffer, "set(");
+ printList(buffer, set, "[", ", ", "]", null);
+ Order order = set.getOrder();
+ if (order != Order.STABLE_ORDER) {
+ append(buffer, ", order = \"" + order.getName() + "\"");
+ }
+ append(buffer, ")");
+
+ } else if (o instanceof BaseFunction) {
+ BaseFunction func = (BaseFunction) o;
+ append(buffer, "<function " + func.getName() + ">");
+
+ } else if (o instanceof Label) {
+ write(buffer, o.toString());
+
+ } else if (o instanceof FilesetEntry) {
+ FilesetEntry entry = (FilesetEntry) o;
+ append(buffer, "FilesetEntry(srcdir = ");
+ write(buffer, entry.getSrcLabel().toString());
+ append(buffer, ", files = ");
+ write(buffer, makeStringList(entry.getFiles()));
+ append(buffer, ", excludes = ");
+ write(buffer, makeList(entry.getExcludes()));
+ append(buffer, ", destdir = ");
+ write(buffer, entry.getDestDir().getPathString());
+ append(buffer, ", strip_prefix = ");
+ write(buffer, entry.getStripPrefix());
+ append(buffer, ", symlinks = \"");
+ append(buffer, entry.getSymlinkBehavior().toString());
+ append(buffer, "\")");
+
+ } else if (o instanceof PathFragment) {
+ append(buffer, ((PathFragment) o).getPathString());
+
+ } else {
+ append(buffer, o.toString());
+ }
+
+ return buffer;
+ }
+
+ // Throughout this file, we transform IOException into AssertionError.
+ // During normal operations, we only use in-memory Appendable-s that
+ // cannot cause an IOException.
+ private static Appendable append(Appendable buffer, char c) {
+ try {
+ return buffer.append(c);
+ } catch (IOException e) {
+ throw new AssertionError(e);
+ }
+ }
+
+ private static Appendable append(Appendable buffer, CharSequence s) {
+ try {
+ return buffer.append(s);
+ } catch (IOException e) {
+ throw new AssertionError(e);
+ }
+ }
+
+ private static Appendable backslashChar(Appendable buffer, char c) {
+ return append(append(buffer, '\\'), c);
}
- private static Appendable escapeCharacter(Appendable buffer, char c, char quote)
- throws IOException {
+ private static Appendable escapeCharacter(Appendable buffer, char c, char quote) {
if (c == quote) {
return backslashChar(buffer, c);
}
@@ -43,29 +202,28 @@ public final class Printer {
return backslashChar(buffer, 't');
default:
if (c < 32) {
- return buffer.append(String.format("\\x%02x", (int) c));
+ return append(buffer, String.format("\\x%02x", (int) c));
}
- return buffer.append(c); // no need to support UTF-8
+ return append(buffer, c); // no need to support UTF-8
} // endswitch
}
/**
* Write a properly escaped Skylark representation of a string to a buffer.
*
- * @param buffer the Appendable we're writing to.
+ * @param buffer the Appendable to write to.
* @param s the string a representation of which to write.
* @param quote the quote character to use, '"' or '\''.
* @return the Appendable, in fluent style.
*/
- public static Appendable writeString(Appendable buffer, String s, char quote)
- throws IOException {
- buffer.append(quote);
+ public static Appendable writeString(Appendable buffer, String s, char quote) {
+ append(buffer, quote);
int len = s.length();
for (int i = 0; i < len; i++) {
char c = s.charAt(i);
escapeCharacter(buffer, c, quote);
}
- return buffer.append(quote);
+ return append(buffer, quote);
}
/**
@@ -77,7 +235,175 @@ public final class Printer {
* @param s the string a representation of which to write.
* @return the buffer, in fluent style.
*/
- public static Appendable writeString(Appendable buffer, String s) throws IOException {
+ public static Appendable writeString(Appendable buffer, String s) {
return writeString(buffer, s, '"');
}
+
+ /**
+ * Print a list of object representations
+ * @param buffer an appendable buffer onto which to write the list.
+ * @param list the list of objects to write (each as with repr)
+ * @param before a string to print before the list
+ * @param separator a separator to print between each object
+ * @param after a string to print after the list
+ * @param singletonTerminator null or a string to print after the list if it is a singleton
+ * The singleton case is notably relied upon in python syntax to distinguish
+ * a tuple of size one such as ("foo",) from a merely parenthesized object such as ("foo").
+ * @return the Appendable, in fluent style.
+ */
+ public static Appendable printList(Appendable buffer, Iterable<?> list,
+ String before, String separator, String after, String singletonTerminator) {
+ boolean printSeparator = false; // don't print the separator before the first element
+ int len = 0;
+ append(buffer, before);
+ for (Object o : list) {
+ if (printSeparator) {
+ append(buffer, separator);
+ }
+ write(buffer, o);
+ printSeparator = true;
+ len++;
+ }
+ if (singletonTerminator != null && len == 1) {
+ append(buffer, singletonTerminator);
+ }
+ return append(buffer, after);
+ }
+
+ /**
+ * Print a Skylark list or tuple of object representations
+ * @param buffer an appendable buffer onto which to write the list.
+ * @param list the contents of the list or tuple
+ * @param isTuple is it a tuple or a list?
+ * @return the Appendable, in fluent style.
+ */
+ public static Appendable printList(Appendable buffer, Iterable<?> list, boolean isTuple) {
+ if (isTuple) {
+ return printList(buffer, list, "(", ", ", ")", ",");
+ } else {
+ return printList(buffer, list, "[", ", ", "]", null);
+ }
+ }
+
+ /**
+ * Print a list of object representations
+ * @param list the list of objects to write (each as with repr)
+ * @param before a string to print before the list
+ * @param separator a separator to print between each object
+ * @param after a string to print after the list
+ * @param singletonTerminator null or a string to print after the list if it is a singleton
+ * The singleton case is notably relied upon in python syntax to distinguish
+ * a tuple of size one such as ("foo",) from a merely parenthesized object such as ("foo").
+ * @return a String, the representation.
+ */
+ public static String listString(Iterable<?> list,
+ String before, String separator, String after, String singletonTerminator) {
+ return printList(new StringBuilder(), list, before, separator, after, singletonTerminator)
+ .toString();
+ }
+
+ private static List<?> makeList(Collection<?> list) {
+ return list == null ? Lists.newArrayList() : Lists.newArrayList(list);
+ }
+
+ private static List<String> makeStringList(List<Label> labels) {
+ if (labels == null) {
+ return Collections.emptyList();
+ }
+ List<String> strings = Lists.newArrayListWithCapacity(labels.size());
+ for (Label label : labels) {
+ strings.add(label.toString());
+ }
+ return strings;
+ }
+
+ /**
+ * Convert BUILD language objects to Formattable so JDK can render them correctly.
+ * Don't do this for numeric or string types because we want %d, %x, %s to work.
+ */
+ private static Object strFormattable(final Object o) {
+ if (o instanceof Integer || o instanceof Double || o instanceof String) {
+ return o;
+ } else {
+ return new Formattable() {
+ @Override
+ public String toString() {
+ return str(o);
+ }
+
+ @Override
+ public void formatTo(Formatter formatter, int flags, int width, int precision) {
+ print(formatter.out(), o);
+ }
+ };
+ }
+ }
+
+ private static final Object[] EMPTY = new Object[0];
+
+ /*
+ * N.B. MissingFormatWidthException is the only kind of IllegalFormatException
+ * whose constructor can take and display arbitrary error message, hence its use below.
+ */
+
+ /**
+ * Perform Python-style string formatting. Implemented by delegation to Java's
+ * own string formatting routine to avoid reinventing the wheel. In more
+ * obscure cases, semantics follow JDK (not Python) rules.
+ *
+ * @param pattern a format string.
+ * @param tuple a tuple containing positional arguments
+ */
+ public static String format(String pattern, List<?> tuple) throws IllegalFormatException {
+ int count = countPlaceholders(pattern);
+ if (count != tuple.size()) {
+ throw new MissingFormatWidthException(
+ "not all arguments converted during string formatting");
+ }
+
+ List<Object> args = new ArrayList<>();
+
+ for (Object o : tuple) {
+ args.add(strFormattable(o));
+ }
+
+ try {
+ return String.format(pattern, args.toArray(EMPTY));
+ } catch (IllegalFormatException e) {
+ throw new MissingFormatWidthException(
+ "invalid arguments for format string");
+ }
+ }
+
+ private static int countPlaceholders(String pattern) {
+ int length = pattern.length();
+ boolean afterPercent = false;
+ int i = 0;
+ int count = 0;
+ while (i < length) {
+ switch (pattern.charAt(i)) {
+ case 's':
+ case 'd':
+ if (afterPercent) {
+ count++;
+ afterPercent = false;
+ }
+ break;
+
+ case '%':
+ afterPercent = !afterPercent;
+ break;
+
+ default:
+ if (afterPercent) {
+ throw new MissingFormatWidthException("invalid arguments for format string");
+ }
+ afterPercent = false;
+ break;
+ }
+ i++;
+ }
+
+ return count;
+ }
}
diff --git a/src/main/java/com/google/devtools/build/lib/syntax/SkylarkList.java b/src/main/java/com/google/devtools/build/lib/syntax/SkylarkList.java
index 2d6f39722e..cbcf21a95c 100644
--- a/src/main/java/com/google/devtools/build/lib/syntax/SkylarkList.java
+++ b/src/main/java/com/google/devtools/build/lib/syntax/SkylarkList.java
@@ -80,7 +80,7 @@ public abstract class SkylarkList implements Iterable<Object> {
@Override
public String toString() {
- return EvalUtils.prettyPrintValue(this);
+ return Printer.repr(this);
}
@Override
@@ -197,11 +197,6 @@ public abstract class SkylarkList implements Iterable<Object> {
public List<Object> toList() {
return isTuple() ? list : Lists.newArrayList(list);
}
-
- @Override
- public String toString() {
- return EvalUtils.prettyPrintValue(this);
- }
}
/**
diff --git a/src/main/java/com/google/devtools/build/lib/syntax/SkylarkNestedSet.java b/src/main/java/com/google/devtools/build/lib/syntax/SkylarkNestedSet.java
index 8637597c29..55daeba25c 100644
--- a/src/main/java/com/google/devtools/build/lib/syntax/SkylarkNestedSet.java
+++ b/src/main/java/com/google/devtools/build/lib/syntax/SkylarkNestedSet.java
@@ -212,7 +212,7 @@ public final class SkylarkNestedSet implements Iterable<Object> {
@Override
public String toString() {
- return EvalUtils.prettyPrintValue(this);
+ return Printer.repr(this);
}
public Order getOrder() {
diff --git a/src/main/java/com/google/devtools/build/lib/syntax/SkylarkSignatureProcessor.java b/src/main/java/com/google/devtools/build/lib/syntax/SkylarkSignatureProcessor.java
index 42946eefb1..d114dc7992 100644
--- a/src/main/java/com/google/devtools/build/lib/syntax/SkylarkSignatureProcessor.java
+++ b/src/main/java/com/google/devtools/build/lib/syntax/SkylarkSignatureProcessor.java
@@ -193,7 +193,7 @@ public class SkylarkSignatureProcessor {
} else if (defaultValue != null && enforcedType != null) {
Preconditions.checkArgument(enforcedType.contains(defaultValue),
"In function '%s', parameter '%s' has default value %s that isn't of enforced type %s",
- name, param.name(), EvalUtils.prettyPrintValue(defaultValue), enforcedType);
+ name, param.name(), Printer.repr(defaultValue), enforcedType);
}
return new Parameter.Optional<>(param.name(), officialType, defaultValue);
}
diff --git a/src/test/java/com/google/devtools/build/lib/packages/RuleClassTest.java b/src/test/java/com/google/devtools/build/lib/packages/RuleClassTest.java
index 7ecf86bb7b..0eee09b298 100644
--- a/src/test/java/com/google/devtools/build/lib/packages/RuleClassTest.java
+++ b/src/test/java/com/google/devtools/build/lib/packages/RuleClassTest.java
@@ -279,7 +279,7 @@ public class RuleClassTest extends PackageLoadingTestCase {
// TODO(blaze-team): (2009) refactor to use assertContainsEvent
Iterator<String> expectedMessages = Arrays.asList(
"expected value of type 'list(label)' for attribute 'my-labellist-attr' "
- + "in 'ruleA' rule, but got 'foobar' (string)",
+ + "in 'ruleA' rule, but got \"foobar\" (string)",
"no such attribute 'bogus-attr' in 'ruleA' rule",
"missing value for mandatory "
+ "attribute 'my-string-attr' in 'ruleA' rule",
diff --git a/src/test/java/com/google/devtools/build/lib/packages/TypeTest.java b/src/test/java/com/google/devtools/build/lib/packages/TypeTest.java
index 39c5dbeefa..6604e6286f 100644
--- a/src/test/java/com/google/devtools/build/lib/packages/TypeTest.java
+++ b/src/test/java/com/google/devtools/build/lib/packages/TypeTest.java
@@ -71,7 +71,7 @@ public class TypeTest {
// This does not use assertMessageContainsWordsWithQuotes because at least
// one test should test exact wording (but they all shouldn't to make
// changing/improving the messages easy).
- assertThat(e).hasMessage("expected value of type 'int', but got 'foo' (string)");
+ assertThat(e).hasMessage("expected value of type 'int', but got \"foo\" (string)");
}
}
@@ -83,7 +83,7 @@ public class TypeTest {
fail();
} catch (Type.ConversionException e) {
assertThat(e).hasMessage("expected value of type 'list(string)' for myexpr, "
- + "but got '[(1,2), 3, 4]' (string)");
+ + "but got \"[(1,2), 3, 4]\" (string)");
}
}
@@ -100,7 +100,7 @@ public class TypeTest {
Type.STRING.convert(3, null);
fail();
} catch (Type.ConversionException e) {
- MoreAsserts.assertContainsWordsWithQuotes(e.getMessage(), "3", "string");
+ assertThat(e).hasMessage("expected value of type 'string', but got 3 (int)");
}
}
@@ -123,7 +123,8 @@ public class TypeTest {
Type.BOOLEAN.convert("unexpected", null);
fail();
} catch (Type.ConversionException e) {
- MoreAsserts.assertContainsWordsWithQuotes(e.getMessage(), "unexpected");
+ assertThat(e).hasMessage(
+ "expected value of type 'int', but got \"unexpected\" (string)");
}
// Integers other than [0, 1] should fail.
try {
@@ -264,7 +265,7 @@ public class TypeTest {
Type.LABEL.convert(3, null);
fail();
} catch (Type.ConversionException e) {
- MoreAsserts.assertContainsWordsWithQuotes(e.getMessage(), "3", "string");
+ assertThat(e).hasMessage("expected value of type 'string', but got 3 (int)");
}
}
@@ -296,18 +297,18 @@ public class TypeTest {
Type.STRING_DICT.convert(input, null);
fail();
} catch (Type.ConversionException e) {
- assertThat(e).hasMessage("expected value of type 'string' for dict value element, but got "
- + "'[\"bar\", \"baz\"]' (List)");
+ assertThat(e).hasMessage("expected value of type 'string' for dict value element, "
+ + "but got [\"bar\", \"baz\"] (List)");
}
}
@Test
public void testNonStringList() throws Exception {
try {
- Type.STRING_LIST.convert(3, null);
+ Type.STRING_LIST.convert(3, "blah");
fail();
} catch (Type.ConversionException e) {
- MoreAsserts.assertContainsWordsWithQuotes(e.getMessage(), "3", "list(string)");
+ assertThat(e).hasMessage("expected value of type 'list(string)' for blah, but got 3 (int)");
}
}
@@ -315,10 +316,11 @@ public class TypeTest {
public void testStringListBadElements() throws Exception {
Object input = Arrays.<Object>asList("foo", "bar", 1);
try {
- Type.STRING_LIST.convert(input, null);
+ Type.STRING_LIST.convert(input, "argument quux");
fail();
} catch (Type.ConversionException e) {
- MoreAsserts.assertContainsWordsWithQuotes(e.getMessage(), "1", "string");
+ assertThat(e).hasMessage(
+ "expected value of type 'string' for element 2 of argument quux, but got 1 (int)");
}
}
@@ -338,10 +340,10 @@ public class TypeTest {
@Test
public void testNonLabelList() throws Exception {
try {
- Type.LABEL_LIST.convert(3, null, currentRule);
+ Type.LABEL_LIST.convert(3, "foo", currentRule);
fail();
} catch (Type.ConversionException e) {
- MoreAsserts.assertContainsWordsWithQuotes(e.getMessage(), "3", "list(label)");
+ assertThat(e).hasMessage("expected value of type 'list(label)' for foo, but got 3 (int)");
}
}
@@ -352,7 +354,8 @@ public class TypeTest {
Type.LABEL_LIST.convert(list, null, currentRule);
fail();
} catch (Type.ConversionException e) {
- MoreAsserts.assertContainsWordsWithQuotes(e.getMessage(), "2", "string");
+ assertThat(e).hasMessage(
+ "expected value of type 'string' for element 1 of null, but got 2 (int)");
}
}
@@ -392,8 +395,8 @@ public class TypeTest {
Type.LABEL_LIST_DICT.convert(input, null, currentRule);
fail();
} catch (Type.ConversionException e) {
- assertThat(e).hasMessage("expected value of type 'string' for dict key element,"
- + " but got '2' (int)");
+ assertThat(e).hasMessage(
+ "expected value of type 'string' for dict key element, but got 2 (int)");
}
}
@@ -405,7 +408,9 @@ public class TypeTest {
Type.LABEL_LIST_DICT.convert(input, null, currentRule);
fail();
} catch (Type.ConversionException e) {
- MoreAsserts.assertContainsWordsWithQuotes(e.getMessage(), "//foo:bar", "list(label)");
+ assertThat(e).hasMessage(
+ "expected value of type 'list(label)' for dict value element, "
+ + "but got \"//foo:bar\" (string)");
}
}
@@ -419,7 +424,7 @@ public class TypeTest {
fail();
} catch (Type.ConversionException e) {
assertThat(e).hasMessage("expected value of type 'list(label)' for dict value element, "
- + "but got 'bar' (string)");
+ + "but got \"bar\" (string)");
}
}
@@ -460,7 +465,7 @@ public class TypeTest {
fail();
} catch (Type.ConversionException e) {
assertThat(e).hasMessage(
- "expected value of type 'string' for dict key element, but got '2' (int)");
+ "expected value of type 'string' for dict key element, but got 2 (int)");
}
}
@@ -472,7 +477,9 @@ public class TypeTest {
Type.STRING_LIST_DICT.convert(input, null, currentRule);
fail();
} catch (Type.ConversionException e) {
- MoreAsserts.assertContainsWordsWithQuotes(e.getMessage(), "bar", "list(string)");
+ assertThat(e).hasMessage(
+ "expected value of type 'list(string)' for dict value element, "
+ + "but got \"bar\" (string)");
}
}
@@ -485,7 +492,7 @@ public class TypeTest {
fail();
} catch (Type.ConversionException e) {
assertThat(e).hasMessage("expected value of type 'string' for dict key element, but got "
- + "'[\"foo\"]' (List)");
+ + "[\"foo\"] (List)");
}
}
@@ -512,7 +519,7 @@ public class TypeTest {
fail();
} catch (Type.ConversionException e) {
assertThat(e).hasMessage("expected value of type 'string' for dict key element, but got "
- + "'2' (int)");
+ + "2 (int)");
}
}
@@ -525,7 +532,7 @@ public class TypeTest {
fail();
} catch (Type.ConversionException e) {
assertThat(e).hasMessage("expected value of type 'string' for dict value element, but got "
- + "'[\"bang\"]' (List)");
+ + "[\"bang\"] (List)");
}
}
@@ -539,7 +546,7 @@ public class TypeTest {
fail();
} catch (Type.ConversionException e) {
assertThat(e).hasMessage("expected value of type 'string' for dict key element, but got "
- + "'[\"foo\", \"bar\"]' (List)");
+ + "[\"foo\", \"bar\"] (List)");
}
}
diff --git a/src/test/java/com/google/devtools/build/lib/syntax/BaseFunctionTest.java b/src/test/java/com/google/devtools/build/lib/syntax/BaseFunctionTest.java
index acd9fb03e4..da709a002b 100644
--- a/src/test/java/com/google/devtools/build/lib/syntax/BaseFunctionTest.java
+++ b/src/test/java/com/google/devtools/build/lib/syntax/BaseFunctionTest.java
@@ -142,15 +142,15 @@ public class BaseFunctionTest extends EvaluationTestCase {
+ "b1 = bar(name='foo', type='jpg', version=42)\n"
+ "b2 = bar()\n");
- assertThat(EvalUtils.prettyPrintValue(lookup("v1")))
+ assertThat(Printer.repr(lookup("v1")))
.isEqualTo("(1, 2, 3, 4, 5, 6, 7, 8, (), {})");
- assertThat(EvalUtils.prettyPrintValue(lookup("v2")))
+ assertThat(Printer.repr(lookup("v2")))
.isEqualTo("(1, \"x\", \"y\", \"z\", 5, 6, 7, 9, (\"t\",), {\"i\": 0})");
// NB: the conversion to a TreeMap below ensures the keys are sorted.
- assertThat(EvalUtils.prettyPrintValue(
+ assertThat(Printer.repr(
new TreeMap<String, Object>((Map<String, Object>) lookup("b1"))))
.isEqualTo("{\"name\": \"foo\", \"type\": \"jpg\", \"version\": 42}");
- assertThat(EvalUtils.prettyPrintValue(lookup("b2"))).isEqualTo("{}");
+ assertThat(Printer.repr(lookup("b2"))).isEqualTo("{}");
}
}
diff --git a/src/test/java/com/google/devtools/build/lib/syntax/EvalUtilsTest.java b/src/test/java/com/google/devtools/build/lib/syntax/EvalUtilsTest.java
index 13c0987364..fa98c70e3d 100644
--- a/src/test/java/com/google/devtools/build/lib/syntax/EvalUtilsTest.java
+++ b/src/test/java/com/google/devtools/build/lib/syntax/EvalUtilsTest.java
@@ -14,11 +14,9 @@
package com.google.devtools.build.lib.syntax;
-import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
import com.google.common.collect.Lists;
@@ -27,7 +25,6 @@ import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
import java.util.Arrays;
-import java.util.IllegalFormatException;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
@@ -39,15 +36,18 @@ import java.util.Map;
@RunWith(JUnit4.class)
public class EvalUtilsTest {
- private static List<?> makeList(Object ...args) {
+ private static List<?> makeList(Object... args) {
return EvalUtils.makeSequence(Arrays.<Object>asList(args), false);
}
- private static List<?> makeTuple(Object ...args) {
+
+ private static List<?> makeTuple(Object... args) {
return EvalUtils.makeSequence(Arrays.<Object>asList(args), true);
}
+
private static Map<Object, Object> makeDict() {
return new LinkedHashMap<>();
}
+
private static FilesetEntry makeFilesetEntry() {
try {
return new FilesetEntry(Label.parseAbsolute("//foo:bar"),
@@ -78,164 +78,4 @@ public class EvalUtilsTest {
assertFalse(EvalUtils.isImmutable(makeDict()));
assertFalse(EvalUtils.isImmutable(makeFilesetEntry()));
}
-
- @Test
- public void testPrintValue() throws Exception {
- // Note that prettyPrintValue and printValue only differ on behaviour of
- // labels and strings at toplevel.
- assertEquals("foo\nbar", EvalUtils.printValue("foo\nbar"));
- assertEquals("\"foo\\nbar\"", EvalUtils.prettyPrintValue("foo\nbar"));
- assertEquals("foo\nbar", EvalUtils.printValue("foo\nbar"));
- assertEquals("'", EvalUtils.printValue("'"));
- assertEquals("\"'\"", EvalUtils.prettyPrintValue("'"));
- assertEquals("\"", EvalUtils.printValue("\""));
- assertEquals("\"\\\"\"", EvalUtils.prettyPrintValue("\""));
- assertEquals("a\\b", EvalUtils.printValue("a\\b"));
- assertEquals("\"a\\\\b\"", EvalUtils.prettyPrintValue("a\\b"));
- assertEquals("3", EvalUtils.printValue(3));
- assertEquals("3", EvalUtils.prettyPrintValue(3));
- assertEquals("None", EvalUtils.printValue(Environment.NONE));
- assertEquals("None", EvalUtils.prettyPrintValue(Environment.NONE));
-
- assertEquals("//x:x", EvalUtils.printValue(Label.parseAbsolute("//x")));
- assertEquals("\"//x:x\"", EvalUtils.prettyPrintValue(Label.parseAbsolute("//x")));
-
- List<?> list = makeList("foo", "bar");
- List<?> tuple = makeTuple("foo", "bar");
-
- assertEquals("(1, [\"foo\", \"bar\"], 3)",
- EvalUtils.printValue(makeTuple(1, list, 3)));
- assertEquals("(1, [\"foo\", \"bar\"], 3)",
- EvalUtils.prettyPrintValue(makeTuple(1, list, 3)));
- assertEquals("[1, (\"foo\", \"bar\"), 3]",
- EvalUtils.printValue(makeList(1, tuple, 3)));
- assertEquals("[1, (\"foo\", \"bar\"), 3]",
- EvalUtils.prettyPrintValue(makeList(1, tuple, 3)));
-
- Map<Object, Object> dict = makeDict();
- dict.put(1, tuple);
- dict.put(2, list);
- dict.put("foo", makeList());
- assertEquals("{1: (\"foo\", \"bar\"), 2: [\"foo\", \"bar\"], \"foo\": []}",
- EvalUtils.printValue(dict));
- assertEquals("{1: (\"foo\", \"bar\"), 2: [\"foo\", \"bar\"], \"foo\": []}",
- EvalUtils.prettyPrintValue(dict));
- assertEquals("FilesetEntry(srcdir = \"//foo:bar\", files = [], "
- + "excludes = [\"xyz\"], destdir = \"\", "
- + "strip_prefix = \".\", symlinks = \"copy\")",
- EvalUtils.prettyPrintValue(makeFilesetEntry()));
- }
-
- private void checkFormatPositionalFails(String format, List<?> tuple,
- String errorMessage) {
- try {
- EvalUtils.formatString(format, tuple);
- fail();
- } catch (IllegalFormatException e) {
- assertThat(e).hasMessage(errorMessage);
- }
- }
-
- @Test
- public void testFormatPositional() throws Exception {
- assertEquals("foo 3", EvalUtils.formatString("%s %d", makeTuple("foo", 3)));
-
- // Note: formatString doesn't perform scalar x -> (x) conversion;
- // The %-operator is responsible for that.
- assertThat(EvalUtils.formatString("", makeTuple())).isEmpty();
- assertEquals("foo", EvalUtils.formatString("%s", makeTuple("foo")));
- assertEquals("3.14159", EvalUtils.formatString("%s", makeTuple(3.14159)));
- checkFormatPositionalFails("%s", makeTuple(1, 2, 3),
- "not all arguments converted during string formatting");
- assertEquals("%foo", EvalUtils.formatString("%%%s", makeTuple("foo")));
- checkFormatPositionalFails("%%s", makeTuple("foo"),
- "not all arguments converted during string formatting");
- checkFormatPositionalFails("% %s", makeTuple("foo"),
- "invalid arguments for format string");
- assertEquals("[1, 2, 3]", EvalUtils.formatString("%s", makeTuple(makeList(1, 2, 3))));
- assertEquals("(1, 2, 3)", EvalUtils.formatString("%s", makeTuple(makeTuple(1, 2, 3))));
- assertEquals("[]", EvalUtils.formatString("%s", makeTuple(makeList())));
- assertEquals("()", EvalUtils.formatString("%s", makeTuple(makeTuple())));
-
- checkFormatPositionalFails("%.3g", makeTuple(), "invalid arguments for format string");
- checkFormatPositionalFails("%.3g", makeTuple(1, 2), "invalid arguments for format string");
- checkFormatPositionalFails("%.s", makeTuple(), "invalid arguments for format string");
- }
-
- private String createExpectedFilesetEntryString(FilesetEntry.SymlinkBehavior symlinkBehavior) {
- return "FilesetEntry(srcdir = \"//x:x\","
- + " files = [\"//x:x\"],"
- + " excludes = [],"
- + " destdir = \"\","
- + " strip_prefix = \".\","
- + " symlinks = \"" + symlinkBehavior.toString().toLowerCase() + "\")";
- }
-
- private FilesetEntry createTestFilesetEntry(FilesetEntry.SymlinkBehavior symlinkBehavior)
- throws Exception {
- Label label = Label.parseAbsolute("//x");
- return new FilesetEntry(label,
- Arrays.asList(label),
- Arrays.<String>asList(),
- "",
- symlinkBehavior,
- ".");
- }
-
- @Test
- public void testFilesetEntrySymlinkAttr() throws Exception {
- FilesetEntry entryDereference =
- createTestFilesetEntry(FilesetEntry.SymlinkBehavior.DEREFERENCE);
-
- assertEquals(createExpectedFilesetEntryString(FilesetEntry.SymlinkBehavior.DEREFERENCE),
- EvalUtils.prettyPrintValue(entryDereference));
- }
-
- private FilesetEntry createStripPrefixFilesetEntry(String stripPrefix) throws Exception {
- Label label = Label.parseAbsolute("//x");
- return new FilesetEntry(
- label,
- Arrays.asList(label),
- Arrays.<String>asList(),
- "",
- FilesetEntry.SymlinkBehavior.DEREFERENCE,
- stripPrefix);
- }
-
- @Test
- public void testFilesetEntryStripPrefixAttr() throws Exception {
- FilesetEntry withoutStripPrefix = createStripPrefixFilesetEntry(".");
- FilesetEntry withStripPrefix = createStripPrefixFilesetEntry("orange");
-
- String prettyWithout = EvalUtils.prettyPrintValue(withoutStripPrefix);
- String prettyWith = EvalUtils.prettyPrintValue(withStripPrefix);
-
- assertThat(prettyWithout).contains("strip_prefix = \".\"");
- assertThat(prettyWith).contains("strip_prefix = \"orange\"");
- }
-
- @Test
- public void testRegressionCrashInPrettyPrintValue() throws Exception {
- // Would cause crash in code such as this:
- // Fileset(name='x', entries=[], out=[FilesetEntry(files=['a'])])
- // While formatting the "expected x, got y" message for the 'out'
- // attribute, prettyPrintValue(FilesetEntry) would be recursively called
- // with a List<Label> even though this isn't a valid datatype in the
- // interpreter.
- // Fileset isn't part of bazel, even though FilesetEntry is.
- Label label = Label.parseAbsolute("//x");
- assertEquals("FilesetEntry(srcdir = \"//x:x\","
- + " files = [\"//x:x\"],"
- + " excludes = [],"
- + " destdir = \"\","
- + " strip_prefix = \".\","
- + " symlinks = \"copy\")",
- EvalUtils.prettyPrintValue(
- new FilesetEntry(label,
- Arrays.asList(label),
- Arrays.<String>asList(),
- "",
- FilesetEntry.SymlinkBehavior.COPY,
- ".")));
- }
}
diff --git a/src/test/java/com/google/devtools/build/lib/syntax/EvaluationTest.java b/src/test/java/com/google/devtools/build/lib/syntax/EvaluationTest.java
index b3e5bcff48..7177c7f1d0 100644
--- a/src/test/java/com/google/devtools/build/lib/syntax/EvaluationTest.java
+++ b/src/test/java/com/google/devtools/build/lib/syntax/EvaluationTest.java
@@ -542,7 +542,7 @@ public class EvaluationTest extends EvaluationTestCase {
@Test
public void testListComprehensionOnDictionaryCompositeExpression() throws Exception {
eval("d = {1:'a',2:'b'}\n" + "l = [d[x] for x in d]");
- assertEquals("[\"a\", \"b\"]", EvalUtils.prettyPrintValue(lookup("l")));
+ assertEquals("[\"a\", \"b\"]", Printer.repr(lookup("l")));
}
@Test
diff --git a/src/test/java/com/google/devtools/build/lib/syntax/PrinterTest.java b/src/test/java/com/google/devtools/build/lib/syntax/PrinterTest.java
new file mode 100644
index 0000000000..d69c76f681
--- /dev/null
+++ b/src/test/java/com/google/devtools/build/lib/syntax/PrinterTest.java
@@ -0,0 +1,214 @@
+// Copyright 2006-2015 Google Inc. All Rights Reserved.
+//
+// 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.devtools.build.lib.syntax;
+
+import static com.google.common.truth.Truth.assertThat;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Lists;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+import java.util.Arrays;
+import java.util.IllegalFormatException;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Test properties of the evaluator's datatypes and utility functions
+ * without actually creating any parse trees.
+ */
+@RunWith(JUnit4.class)
+public class PrinterTest {
+
+ private static List<?> makeList(Object... args) {
+ return EvalUtils.makeSequence(Arrays.<Object>asList(args), false);
+ }
+
+ private static List<?> makeTuple(Object... args) {
+ return EvalUtils.makeSequence(Arrays.<Object>asList(args), true);
+ }
+
+ private static FilesetEntry makeFilesetEntry() {
+ try {
+ return new FilesetEntry(Label.parseAbsolute("//foo:bar"),
+ Lists.<Label>newArrayList(), Lists.newArrayList("xyz"), "",
+ FilesetEntry.SymlinkBehavior.COPY, ".");
+ } catch (Label.SyntaxException e) {
+ throw new RuntimeException("Bad label: ", e);
+ }
+ }
+
+ @Test
+ public void testPrinter() throws Exception {
+ // Note that prettyPrintValue and printValue only differ on behaviour of
+ // labels and strings at toplevel.
+ assertEquals("foo\nbar", Printer.str("foo\nbar"));
+ assertEquals("\"foo\\nbar\"", Printer.repr("foo\nbar"));
+ assertEquals("'", Printer.str("'"));
+ assertEquals("\"'\"", Printer.repr("'"));
+ assertEquals("\"", Printer.str("\""));
+ assertEquals("\"\\\"\"", Printer.repr("\""));
+ assertEquals("3", Printer.str(3));
+ assertEquals("3", Printer.repr(3));
+ assertEquals("None", Printer.repr(Environment.NONE));
+
+ assertEquals("//x:x", Printer.str(Label.parseAbsolute("//x")));
+ assertEquals("\"//x:x\"", Printer.repr(Label.parseAbsolute("//x")));
+
+ List<?> list = makeList("foo", "bar");
+ List<?> tuple = makeTuple("foo", "bar");
+
+ assertEquals("(1, [\"foo\", \"bar\"], 3)",
+ Printer.str(makeTuple(1, list, 3)));
+ assertEquals("(1, [\"foo\", \"bar\"], 3)",
+ Printer.repr(makeTuple(1, list, 3)));
+ assertEquals("[1, (\"foo\", \"bar\"), 3]",
+ Printer.str(makeList(1, tuple, 3)));
+ assertEquals("[1, (\"foo\", \"bar\"), 3]",
+ Printer.repr(makeList(1, tuple, 3)));
+
+ Map<Object, Object> dict = ImmutableMap.of(
+ 1, tuple,
+ 2, list,
+ "foo", makeList());
+ assertEquals("{1: (\"foo\", \"bar\"), 2: [\"foo\", \"bar\"], \"foo\": []}",
+ Printer.str(dict));
+ assertEquals("{1: (\"foo\", \"bar\"), 2: [\"foo\", \"bar\"], \"foo\": []}",
+ Printer.repr(dict));
+ assertEquals("FilesetEntry(srcdir = \"//foo:bar\", files = [], "
+ + "excludes = [\"xyz\"], destdir = \"\", "
+ + "strip_prefix = \".\", symlinks = \"copy\")",
+ Printer.repr(makeFilesetEntry()));
+ }
+
+ private void checkFormatPositionalFails(String format, List<?> tuple, String errorMessage) {
+ try {
+ Printer.format(format, tuple);
+ fail();
+ } catch (IllegalFormatException e) {
+ assertThat(e).hasMessage(errorMessage);
+ }
+ }
+
+ @Test
+ public void testFormatPositional() throws Exception {
+ assertEquals("foo 3", Printer.format("%s %d", makeTuple("foo", 3)));
+
+ // Note: formatString doesn't perform scalar x -> (x) conversion;
+ // The %-operator is responsible for that.
+ assertThat(Printer.format("", makeTuple())).isEmpty();
+ assertEquals("foo", Printer.format("%s", makeTuple("foo")));
+ assertEquals("3.14159", Printer.format("%s", makeTuple(3.14159)));
+ checkFormatPositionalFails("%s", makeTuple(1, 2, 3),
+ "not all arguments converted during string formatting");
+ assertEquals("%foo", Printer.format("%%%s", makeTuple("foo")));
+ checkFormatPositionalFails("%%s", makeTuple("foo"),
+ "not all arguments converted during string formatting");
+ checkFormatPositionalFails("% %s", makeTuple("foo"),
+ "invalid arguments for format string");
+ assertEquals("[1, 2, 3]", Printer.format("%s", makeTuple(makeList(1, 2, 3))));
+ assertEquals("(1, 2, 3)", Printer.format("%s", makeTuple(makeTuple(1, 2, 3))));
+ assertEquals("[]", Printer.format("%s", makeTuple(makeList())));
+ assertEquals("()", Printer.format("%s", makeTuple(makeTuple())));
+
+ checkFormatPositionalFails("%.3g", makeTuple(), "invalid arguments for format string");
+ checkFormatPositionalFails("%.3g", makeTuple(1, 2), "invalid arguments for format string");
+ checkFormatPositionalFails("%.s", makeTuple(), "invalid arguments for format string");
+ }
+
+ private String createExpectedFilesetEntryString(FilesetEntry.SymlinkBehavior symlinkBehavior) {
+ return "FilesetEntry(srcdir = \"//x:x\","
+ + " files = [\"//x:x\"],"
+ + " excludes = [],"
+ + " destdir = \"\","
+ + " strip_prefix = \".\","
+ + " symlinks = \"" + symlinkBehavior.toString().toLowerCase() + "\")";
+ }
+
+ private FilesetEntry createTestFilesetEntry(FilesetEntry.SymlinkBehavior symlinkBehavior)
+ throws Exception {
+ Label label = Label.parseAbsolute("//x");
+ return new FilesetEntry(
+ label,
+ Arrays.asList(label),
+ Arrays.<String>asList(),
+ "",
+ symlinkBehavior,
+ ".");
+ }
+
+ @Test
+ public void testFilesetEntrySymlinkAttr() throws Exception {
+ FilesetEntry entryDereference =
+ createTestFilesetEntry(FilesetEntry.SymlinkBehavior.DEREFERENCE);
+
+ assertEquals(createExpectedFilesetEntryString(FilesetEntry.SymlinkBehavior.DEREFERENCE),
+ Printer.repr(entryDereference));
+ }
+
+ private FilesetEntry createStripPrefixFilesetEntry(String stripPrefix) throws Exception {
+ Label label = Label.parseAbsolute("//x");
+ return new FilesetEntry(
+ label,
+ Arrays.asList(label),
+ Arrays.<String>asList(),
+ "",
+ FilesetEntry.SymlinkBehavior.DEREFERENCE,
+ stripPrefix);
+ }
+
+ @Test
+ public void testFilesetEntryStripPrefixAttr() throws Exception {
+ FilesetEntry withoutStripPrefix = createStripPrefixFilesetEntry(".");
+ FilesetEntry withStripPrefix = createStripPrefixFilesetEntry("orange");
+
+ String prettyWithout = Printer.repr(withoutStripPrefix);
+ String prettyWith = Printer.repr(withStripPrefix);
+
+ assertThat(prettyWithout).contains("strip_prefix = \".\"");
+ assertThat(prettyWith).contains("strip_prefix = \"orange\"");
+ }
+
+ @Test
+ public void testRegressionCrashInPrettyPrintValue() throws Exception {
+ // Would cause crash in code such as this:
+ // Fileset(name='x', entries=[], out=[FilesetEntry(files=['a'])])
+ // While formatting the "expected x, got y" message for the 'out'
+ // attribute, prettyPrintValue(FilesetEntry) would be recursively called
+ // with a List<Label> even though this isn't a valid datatype in the
+ // interpreter.
+ // Fileset isn't part of bazel, even though FilesetEntry is.
+ Label label = Label.parseAbsolute("//x");
+ assertEquals("FilesetEntry(srcdir = \"//x:x\","
+ + " files = [\"//x:x\"],"
+ + " excludes = [],"
+ + " destdir = \"\","
+ + " strip_prefix = \".\","
+ + " symlinks = \"copy\")",
+ Printer.repr(
+ new FilesetEntry(
+ label,
+ Arrays.asList(label),
+ Arrays.<String>asList(),
+ "",
+ FilesetEntry.SymlinkBehavior.COPY,
+ ".")));
+ }
+}
diff --git a/src/test/java/com/google/devtools/build/lib/syntax/SkylarkShell.java b/src/test/java/com/google/devtools/build/lib/syntax/SkylarkShell.java
index f00d8f5ab7..c4bb7d84a9 100644
--- a/src/test/java/com/google/devtools/build/lib/syntax/SkylarkShell.java
+++ b/src/test/java/com/google/devtools/build/lib/syntax/SkylarkShell.java
@@ -72,7 +72,7 @@ class SkylarkShell {
try {
Object result = ev.eval(input);
if (result != null) {
- System.out.println(EvalUtils.prettyPrintValue(result));
+ System.out.println(Printer.repr(result));
}
} catch (Exception e) {
e.printStackTrace();