Skylark: Functions don't need to be declared in order.

--
MOS_MIGRATED_REVID=93515487
diff --git a/src/main/java/com/google/devtools/build/lib/syntax/FunctionDefStatement.java b/src/main/java/com/google/devtools/build/lib/syntax/FunctionDefStatement.java
index 256abb8..dce9e9c 100644
--- a/src/main/java/com/google/devtools/build/lib/syntax/FunctionDefStatement.java
+++ b/src/main/java/com/google/devtools/build/lib/syntax/FunctionDefStatement.java
@@ -106,6 +106,5 @@
     for (Statement stmts : statements) {
       stmts.validate(localEnv);
     }
-    env.declare(ident.getName(), getLocation());
   }
 }
diff --git a/src/main/java/com/google/devtools/build/lib/syntax/Parser.java b/src/main/java/com/google/devtools/build/lib/syntax/Parser.java
index 5c363a6..6381c15 100644
--- a/src/main/java/com/google/devtools/build/lib/syntax/Parser.java
+++ b/src/main/java/com/google/devtools/build/lib/syntax/Parser.java
@@ -194,9 +194,7 @@
     List<Statement> statements = parser.parseFileInput();
     boolean hasSemanticalErrors = false;
     try {
-      for (Statement statement : statements) {
-        statement.validate(validationEnvironment);
-      }
+      validationEnvironment.validateAst(statements);
     } catch (EvalException e) {
       // Do not report errors caused by a previous parsing error, as it has already been reported.
       if (!e.isDueToIncompleteAST()) {
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 5d38ccf..a615d67 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
@@ -20,6 +20,7 @@
 
 import java.util.HashMap;
 import java.util.HashSet;
+import java.util.List;
 import java.util.Map;
 import java.util.Set;
 import java.util.Stack;
@@ -154,4 +155,23 @@
     readOnlyVariables.removeAll(futureReadOnlyVariables.peek());
     clonable = false;
   }
+
+  /**
+   * Validates the AST and runs static checks.
+   */
+  public void validateAst(List<Statement> statements) throws EvalException {
+    // Add every function in the environment before validating. This is
+    // necessary because functions may call other functions defined
+    // later in the file.
+    for (Statement statement : statements) {
+      if (statement instanceof FunctionDefStatement) {
+        FunctionDefStatement fct = (FunctionDefStatement) statement;
+        declare(fct.getIdent().getName(), fct.getLocation());
+      }
+    }
+
+    for (Statement statement : statements) {
+      statement.validate(this);
+    }
+  }
 }
diff --git a/src/test/java/com/google/devtools/build/lib/syntax/SkylarkEvaluationTest.java b/src/test/java/com/google/devtools/build/lib/syntax/SkylarkEvaluationTest.java
index 579268f..43e5ac8 100644
--- a/src/test/java/com/google/devtools/build/lib/syntax/SkylarkEvaluationTest.java
+++ b/src/test/java/com/google/devtools/build/lib/syntax/SkylarkEvaluationTest.java
@@ -664,6 +664,22 @@
   }
 
   @Test
+  public void testFunctionCallOrdering() throws Exception {
+    eval("def func(): return foo() * 2",
+         "def foo(): return 2",
+         "x = func()");
+    assertThat(lookup("x")).isEqualTo(4);
+  }
+
+  @Test
+  public void testFunctionCallBadOrdering() throws Exception {
+    checkEvalError("name 'foo' is not defined",
+         "def func(): return foo() * 2",
+         "x = func()",
+         "def foo(): return 2");
+  }
+
+  @Test
   public void testNoneTrueFalseInSkylark() throws Exception {
     eval("a = None",
       "b = True",
diff --git a/src/test/java/com/google/devtools/build/lib/syntax/ValidationTests.java b/src/test/java/com/google/devtools/build/lib/syntax/ValidationTests.java
index edbf550..fbbdbcc 100644
--- a/src/test/java/com/google/devtools/build/lib/syntax/ValidationTests.java
+++ b/src/test/java/com/google/devtools/build/lib/syntax/ValidationTests.java
@@ -107,14 +107,14 @@
 
   @Test
   public void testFunctionDefRecursion() throws Exception {
-    checkError("function 'func' does not exist",
+    parse(
         "def func():",
         "  func()\n");
   }
 
   @Test
   public void testMutualRecursion() throws Exception {
-    checkError("function 'bar' does not exist",
+    parse(
         "def foo(i):",
         "  bar(i)",
         "def bar(i):",
@@ -123,13 +123,19 @@
   }
 
   @Test
-  public void testFunctionDoesNotExistInFunctionDef() {
-    checkError("function 'foo' does not exist",
+  public void testFunctionDefinedBelow() {
+    parse(
         "def bar(): a = foo() + 'a'",
         "def foo(): return 1\n");
   }
 
   @Test
+  public void testFunctionDoesNotExist() {
+    checkError("function 'foo' does not exist",
+        "def bar(): a = foo() + 'a'");
+  }
+
+  @Test
   public void testStructMembersAreImmutable() {
     checkError("can only assign to variables and tuples, not to 's.x'",
         "s = struct(x = 'a')",