EvalException can now show a url for additional information

Add doc for the "read only" error message.

--
MOS_MIGRATED_REVID=138194709
diff --git a/src/main/java/com/google/devtools/build/lib/syntax/EvalException.java b/src/main/java/com/google/devtools/build/lib/syntax/EvalException.java
index 97d695f..ed91459 100644
--- a/src/main/java/com/google/devtools/build/lib/syntax/EvalException.java
+++ b/src/main/java/com/google/devtools/build/lib/syntax/EvalException.java
@@ -51,6 +51,16 @@
     this.dueToIncompleteAST = false;
   }
 
+  public EvalException(Location location, String message, String url) {
+    this.location = location;
+    this.dueToIncompleteAST = false;
+    this.message =
+        Preconditions.checkNotNull(message)
+            + "\n"
+            + "Need help? See "
+            + Preconditions.checkNotNull(url);
+  }
+
   /**
    * @param location the location where evaluation/execution failed.
    * @param message the error message.
diff --git a/src/main/java/com/google/devtools/build/lib/syntax/ValidationEnvironment.java b/src/main/java/com/google/devtools/build/lib/syntax/ValidationEnvironment.java
index c1a94c5..6aa5a7d 100644
--- a/src/main/java/com/google/devtools/build/lib/syntax/ValidationEnvironment.java
+++ b/src/main/java/com/google/devtools/build/lib/syntax/ValidationEnvironment.java
@@ -90,7 +90,10 @@
 
   private void checkReadonly(String varname, Location location) throws EvalException {
     if (readOnlyVariables.contains(varname)) {
-      throw new EvalException(location, String.format("Variable %s is read only", varname));
+      throw new EvalException(
+          location,
+          String.format("Variable %s is read only", varname),
+          "https://bazel.build/versions/master/docs/skylark/errors/read-only-variable.html");
     }
   }
 
diff --git a/src/test/java/com/google/devtools/build/lib/syntax/EnvironmentTest.java b/src/test/java/com/google/devtools/build/lib/syntax/EnvironmentTest.java
index 6c8cbd7..f303082 100644
--- a/src/test/java/com/google/devtools/build/lib/syntax/EnvironmentTest.java
+++ b/src/test/java/com/google/devtools/build/lib/syntax/EnvironmentTest.java
@@ -301,7 +301,7 @@
       env.eval("special_var = 41");
       throw new AssertionError("failed to fail");
     } catch (IllegalArgumentException e) {
-      assertThat(e).hasMessage("ERROR 1:1: Variable special_var is read only");
+      assertThat(e.getMessage()).contains("ERROR 1:1: Variable special_var is read only");
     }
 
     try {
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 bd029849..c383ae91 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
@@ -463,7 +463,7 @@
 
   @Test
   public void testListComprehensionModifiesGlobalEnv() throws Exception {
-    new SkylarkTest().update("x", 42).testIfExactError("ERROR 1:1: Variable x is read only",
+    new SkylarkTest().update("x", 42).testIfErrorContains("ERROR 1:1: Variable x is read only",
         "[x + 1 for x in [1,2,3]]");
     new BuildTest().update("x", 42).setUp("y =[x + 1 for x in [1,2,3]]")
         .testExactOrder("y", 2, 3, 4).testLookup("x", 3); // (x is global)