EvalException can now show a url for additional information

Add doc for the "read only" error message.

--
MOS_MIGRATED_REVID=138194709
diff --git a/site/versions/master/docs/skylark/errors/read-only-variable.md b/site/versions/master/docs/skylark/errors/read-only-variable.md
new file mode 100644
index 0000000..4b1ecce
--- /dev/null
+++ b/site/versions/master/docs/skylark/errors/read-only-variable.md
@@ -0,0 +1,26 @@
+# Variable x is read only
+
+A global variable cannot be reassigned. It will always point to the same object.
+However, its content might change, if the value is mutable (for example, the
+content of a list). Local variables don't have this restriction.
+
+```python
+a = [1, 2]
+
+a[1] = 3
+
+b = 3
+
+b = 4  # forbidden
+```
+
+`ERROR: /path/ext.bzl:7:1: Variable b is read only`
+
+You will get a similar error if you try to redefine a function (function
+overloading is not supported), for example:
+
+```python
+def foo(x): return x + 1
+
+def foo(x, y): return x + y  # forbidden
+```
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)