Add static name resolution

This implements a part of the name resolution document: https://github.com/bazelbuild/proposals/blob/master/docs/2018-06-18-name-resolution.md
and it is enabled with the flag --incompatible_static_name_resolution

There are two visible changes:

1. Local variables can be used before their definition point.
2. Local variables will shadow global variables, even if they are not initialiazed yet.

https://github.com/bazelbuild/bazel/issues/5637

RELNOTES: None.
PiperOrigin-RevId: 210562752
diff --git a/src/main/java/com/google/devtools/build/lib/syntax/Environment.java b/src/main/java/com/google/devtools/build/lib/syntax/Environment.java
index 387f6ef..f0f695e 100644
--- a/src/main/java/com/google/devtools/build/lib/syntax/Environment.java
+++ b/src/main/java/com/google/devtools/build/lib/syntax/Environment.java
@@ -81,15 +81,17 @@
  */
 public final class Environment implements Freezable, Debuggable {
 
-  /**
-   * A phase for enabling or disabling certain builtin functions
-   */
-  public enum Phase { WORKSPACE, LOADING, ANALYSIS }
+  /** A phase for enabling or disabling certain builtin functions */
+  public enum Phase {
+    WORKSPACE,
+    LOADING,
+    ANALYSIS
+  }
 
   /**
-   * A mapping of bindings, either mutable or immutable according to an associated
-   * {@link Mutability}. The order of the bindings within a single {@link Frame} is deterministic
-   * but unspecified.
+   * A mapping of bindings, either mutable or immutable according to an associated {@link
+   * Mutability}. The order of the bindings within a single {@link Frame} is deterministic but
+   * unspecified.
    *
    * <p>Any non-frozen {@link Frame} must have the same {@link Mutability} as the current {@link
    * Environment}, to avoid interference from other evaluation contexts. For example, a {@link
@@ -127,14 +129,14 @@
     void put(Environment env, String varname, Object value) throws MutabilityException;
 
     /**
-     * TODO(laurentlb): Remove this method when possible. It should probably not
-     * be part of the public interface.
+     * TODO(laurentlb): Remove this method when possible. It should probably not be part of the
+     * public interface.
      */
     void remove(Environment env, String varname) throws MutabilityException;
 
     /**
-     * Returns a map containing all bindings of this {@link Frame} and of its transitive
-     * parents, taking into account shadowing precedence.
+     * Returns a map containing all bindings of this {@link Frame} and of its transitive parents,
+     * taking into account shadowing precedence.
      *
      * <p>The bindings are returned in a deterministic order (for a given sequence of initial values
      * and updates).
@@ -149,7 +151,7 @@
           : new MutableLexicalFrame(mutability);
     }
 
-    static LexicalFrame createForUserDefinedFunctionCall(Mutability mutability, int numArgs) {
+    static LexicalFrame create(Mutability mutability, int numArgs) {
       Preconditions.checkState(!mutability.isFrozen());
       return new MutableLexicalFrame(mutability, /*initialCapacity=*/ numArgs);
     }
@@ -248,22 +250,19 @@
    * {@link GlobalFrame}s can represent different builtin scopes with a linear precedence ordering.
    *
    * <p>A {@link GlobalFrame} can also be constructed in a two-phase process. To do this, call the
-   * nullary constructor to create an uninitialized {@link GlobalFrame}, then call
-   * {@link #initialize}. It is illegal to use any other method in-between these two calls, or to
-   * call {@link #initialize} on an already initialized {@link GlobalFrame}.
+   * nullary constructor to create an uninitialized {@link GlobalFrame}, then call {@link
+   * #initialize}. It is illegal to use any other method in-between these two calls, or to call
+   * {@link #initialize} on an already initialized {@link GlobalFrame}.
    */
-
   public static final class GlobalFrame implements Frame {
     /**
      * Final, except that it may be initialized after instantiation. Null mutability indicates that
      * this Frame is uninitialized.
      */
-    @Nullable
-    private Mutability mutability;
+    @Nullable private Mutability mutability;
 
     /** Final, except that it may be initialized after instantiation. */
-    @Nullable
-    private GlobalFrame parent;
+    @Nullable private GlobalFrame parent;
 
     /**
      * If this frame is a global frame, the label for the corresponding target, e.g. {@code
@@ -271,8 +270,7 @@
      *
      * <p>Final, except that it may be initialized after instantiation.
      */
-    @Nullable
-    private Label label;
+    @Nullable private Label label;
 
     /** Bindings are maintained in order of creation. */
     private final LinkedHashMap<String, Object> bindings;
@@ -322,10 +320,12 @@
     }
 
     public void initialize(
-        Mutability mutability, @Nullable GlobalFrame parent,
-        @Nullable Label label, Map<String, Object> bindings) {
-      Preconditions.checkState(this.mutability == null,
-          "Attempted to initialize an already initialized Frame");
+        Mutability mutability,
+        @Nullable GlobalFrame parent,
+        @Nullable Label label,
+        Map<String, Object> bindings) {
+      Preconditions.checkState(
+          this.mutability == null, "Attempted to initialize an already initialized Frame");
       this.mutability = Preconditions.checkNotNull(mutability);
       this.parent = parent;
       this.label = label;
@@ -432,8 +432,7 @@
     }
 
     @Override
-    public void put(Environment env, String varname, Object value)
-        throws MutabilityException {
+    public void put(Environment env, String varname, Object value) throws MutabilityException {
       checkInitialized();
       Mutability.checkMutable(this, env.mutability());
       bindings.put(varname, value);
@@ -565,9 +564,10 @@
         return;
       }
       if (!(obj instanceof Extension)) {
-        throw new IllegalStateException(String.format(
-            "Expected an equal Extension, but got a %s instead of an Extension",
-            obj == null ? "null" : obj.getClass().getName()));
+        throw new IllegalStateException(
+            String.format(
+                "Expected an equal Extension, but got a %s instead of an Extension",
+                obj == null ? "null" : obj.getClass().getName()));
       }
       Extension other = (Extension) obj;
       ImmutableMap<String, Object> otherBindings = other.getBindings();
@@ -575,11 +575,12 @@
       Set<String> names = bindings.keySet();
       Set<String> otherNames = otherBindings.keySet();
       if (!names.equals(otherNames)) {
-        throw new IllegalStateException(String.format(
-            "Expected Extensions to be equal, but they don't define the same bindings: "
-                + "in this one but not given one: [%s]; in given one but not this one: [%s]",
-            Joiner.on(", ").join(Sets.difference(names, otherNames)),
-            Joiner.on(", ").join(Sets.difference(otherNames, names))));
+        throw new IllegalStateException(
+            String.format(
+                "Expected Extensions to be equal, but they don't define the same bindings: "
+                    + "in this one but not given one: [%s]; in given one but not this one: [%s]",
+                Joiner.on(", ").join(Sets.difference(names, otherNames)),
+                Joiner.on(", ").join(Sets.difference(otherNames, names))));
       }
 
       ArrayList<String> badEntries = new ArrayList<>();
@@ -638,10 +639,11 @@
       }
 
       if (!transitiveContentHashCode.equals(other.getTransitiveContentHashCode())) {
-        throw new IllegalStateException(String.format(
-            "Expected Extensions to be equal, but transitive content hashes don't match: %s != %s",
-            transitiveContentHashCode,
-            other.getTransitiveContentHashCode()));
+        throw new IllegalStateException(
+            String.format(
+                "Expected Extensions to be equal, but transitive content hashes don't match:"
+                    + " %s != %s",
+                transitiveContentHashCode, other.getTransitiveContentHashCode()));
       }
     }
 
@@ -652,8 +654,8 @@
   }
 
   /**
-   * Static Frame for lexical variables that are always looked up in the current Environment
-   * or for the definition Environment of the function currently being evaluated.
+   * Static Frame for lexical variables that are always looked up in the current Environment or for
+   * the definition Environment of the function currently being evaluated.
    */
   private Frame lexicalFrame;
 
@@ -666,20 +668,18 @@
   private GlobalFrame globalFrame;
 
   /**
-   * Dynamic Frame for variables that are always looked up in the runtime Environment,
-   * and never in the lexical or "global" Environment as it was at the time of function definition.
-   * For instance, PACKAGE_NAME.
+   * Dynamic Frame for variables that are always looked up in the runtime Environment, and never in
+   * the lexical or "global" Environment as it was at the time of function definition. For instance,
+   * PACKAGE_NAME.
    */
   private final Frame dynamicFrame;
 
-  /**
-   * The semantics options that affect how Skylark code is evaluated.
-   */
+  /** The semantics options that affect how Skylark code is evaluated. */
   private final SkylarkSemantics semantics;
 
   /**
-   * An EventHandler for errors and warnings. This is not used in the BUILD language,
-   * however it might be used in Skylark code called from the BUILD language, so shouldn't be null.
+   * An EventHandler for errors and warnings. This is not used in the BUILD language, however it
+   * might be used in Skylark code called from the BUILD language, so shouldn't be null.
    */
   private final EventHandler eventHandler;
 
@@ -690,30 +690,28 @@
 
   /**
    * Is this Environment being executed during the loading phase? Many builtin functions are only
-   * enabled during the loading phase, and check this flag.
-   * TODO(laurentlb): Remove from Environment
+   * enabled during the loading phase, and check this flag. TODO(laurentlb): Remove from Environment
    */
   private final Phase phase;
 
   /**
-   * When in a lexical (Skylark) Frame, this set contains the variable names that are global,
-   * as determined not by global declarations (not currently supported),
-   * but by previous lookups that ended being global or dynamic.
-   * This is necessary because if in a function definition something
+   * When in a lexical (Skylark) Frame, this set contains the variable names that are global, as
+   * determined not by global declarations (not currently supported), but by previous lookups that
+   * ended being global or dynamic. This is necessary because if in a function definition something
    * reads a global variable after which a local variable with the same name is assigned an
    * Exception needs to be thrown.
    */
   @Nullable private LinkedHashSet<String> knownGlobalVariables;
 
   /**
-   * When in a lexical (Skylark) frame, this lists the names of the functions in the call stack.
-   * We currently use it to artificially disable recursion.
+   * When in a lexical (Skylark) frame, this lists the names of the functions in the call stack. We
+   * currently use it to artificially disable recursion.
    */
   @Nullable private Continuation continuation;
 
   /**
-   * Gets the label of the BUILD file that is using this environment. For example, if a target
-   * //foo has a dependency on //bar which is a Skylark rule defined in //rules:my_rule.bzl being
+   * Gets the label of the BUILD file that is using this environment. For example, if a target //foo
+   * has a dependency on //bar which is a Skylark rule defined in //rules:my_rule.bzl being
    * evaluated in this environment, then this would return //foo.
    */
   @Nullable private final Label callerLabel;
@@ -739,9 +737,7 @@
     knownGlobalVariables = new LinkedHashSet<>();
   }
 
-  /**
-   * Exits a scope by restoring state from the current continuation
-   */
+  /** Exits a scope by restoring state from the current continuation */
   void exitScope() {
     Preconditions.checkNotNull(continuation);
     lexicalFrame = continuation.lexicalFrame;
@@ -753,8 +749,8 @@
   private final String transitiveHashCode;
 
   /**
-   * Checks that the current Environment is in the loading or the workspace phase.
-   * TODO(laurentlb): Move to SkylarkUtils
+   * Checks that the current Environment is in the loading or the workspace phase. TODO(laurentlb):
+   * Move to SkylarkUtils
    *
    * @param symbol name of the function being only authorized thus.
    */
@@ -765,8 +761,8 @@
   }
 
   /**
-   * Checks that the current Environment is in the loading phase.
-   * TODO(laurentlb): Move to SkylarkUtils
+   * Checks that the current Environment is in the loading phase. TODO(laurentlb): Move to
+   * SkylarkUtils
    *
    * @param symbol name of the function being only authorized thus.
    */
@@ -778,8 +774,9 @@
 
   /**
    * Is this a global Environment?
-   * @return true if the current code is being executed at the top-level,
-   * as opposed to inside the body of a function.
+   *
+   * @return true if the current code is being executed at the top-level, as opposed to inside the
+   *     body of a function.
    */
   boolean isGlobal() {
     return lexicalFrame instanceof GlobalFrame;
@@ -797,8 +794,9 @@
   }
 
   /**
-   * Returns an EventHandler for errors and warnings.
-   * The BUILD language doesn't use it directly, but can call Skylark code that does use it.
+   * Returns an EventHandler for errors and warnings. The BUILD language doesn't use it directly,
+   * but can call Skylark code that does use it.
+   *
    * @return an EventHandler
    */
   public EventHandler getEventHandler() {
@@ -824,9 +822,7 @@
     return continuation != null ? continuation.function : null;
   }
 
-  /**
-   * Returns the FuncallExpression and the BaseFunction for the top-level call being evaluated.
-   */
+  /** Returns the FuncallExpression and the BaseFunction for the top-level call being evaluated. */
   public Pair<FuncallExpression, BaseFunction> getTopCall() {
     Continuation continuation = this.continuation;
     if (continuation == null) {
@@ -924,7 +920,7 @@
     }
 
     /** Declares imported extensions for load() statements. */
-    public Builder setImportedExtensions (Map<String, Extension> importMap) {
+    public Builder setImportedExtensions(Map<String, Extension> importMap) {
       Preconditions.checkState(this.importedExtensions == null);
       this.importedExtensions = importMap;
       return this;
@@ -971,16 +967,15 @@
     return new Builder(mutability);
   }
 
-  /**
-   * Returns the caller's label.
-   */
+  /** Returns the caller's label. */
   public Label getCallerLabel() {
     return callerLabel;
   }
 
   /**
-   * Sets a binding for a special dynamic variable in this Environment.
-   * This is not for end-users, and will throw an AssertionError in case of conflict.
+   * Sets a binding for a special dynamic variable in this Environment. This is not for end-users,
+   * and will throw an AssertionError in case of conflict.
+   *
    * @param varname the name of the dynamic variable to be bound
    * @param value a value to bind to the variable
    * @return this Environment, in fluid style
@@ -988,8 +983,7 @@
   public Environment setupDynamic(String varname, Object value) {
     if (lookup(varname) != null) {
       throw new AssertionError(
-          String.format("Trying to bind dynamic variable '%s' but it is already bound",
-              varname));
+          String.format("Trying to bind dynamic variable '%s' but it is already bound", varname));
     }
     try {
       dynamicFrame.put(this, varname, value);
@@ -1014,10 +1008,11 @@
   }
 
   /**
-   * Modifies a binding in the current Frame of this Environment, as would an
-   * {@link AssignmentStatement}. Does not try to modify an inherited binding.
-   * This will shadow any inherited binding, which may be an error
-   * that you want to guard against before calling this function.
+   * Modifies a binding in the current Frame of this Environment, as would an {@link
+   * AssignmentStatement}. Does not try to modify an inherited binding. This will shadow any
+   * inherited binding, which may be an error that you want to guard against before calling this
+   * function.
+   *
    * @param varname the name of the variable to be bound
    * @param value the value to bind to the variable
    * @return this Environment, in fluid style
@@ -1042,8 +1037,7 @@
       // imported from a parent Environment by updating the current Environment, which will not
       // trigger a MutabilityException.
       throw new AssertionError(
-          Printer.format("Can't update %s to %r in frozen environment", varname, value),
-          e);
+          Printer.format("Can't update %s to %r in frozen environment", varname, value), e);
     }
     return this;
   }
@@ -1051,6 +1045,7 @@
   /**
    * Initializes a binding in this Environment. It is an error if the variable is already bound.
    * This is not for end-users, and will throw an AssertionError in case of conflict.
+   *
    * @param varname the name of the variable to be bound
    * @param value the value to bind to the variable
    * @return this Environment, in fluid style
@@ -1063,8 +1058,9 @@
   }
 
   /**
-   * Initializes a binding in this environment. Overrides any previous binding.
-   * This is not for end-users, and will throw an AssertionError in case of conflict.
+   * Initializes a binding in this environment. Overrides any previous binding. This is not for
+   * end-users, and will throw an AssertionError in case of conflict.
+   *
    * @param varname the name of the variable to be bound
    * @param value the value to bind to the variable
    * @return this Environment, in fluid style
@@ -1078,6 +1074,15 @@
   }
 
   /**
+   * Returns the value of a variable defined in the current lexical frame. Do not search in any
+   * parent scope. This function should be used once the AST has been analysed and we know which
+   * variables are local.
+   */
+  public Object localLookup(String varname) {
+    return lexicalFrame.get(varname);
+  }
+
+  /**
    * Returns the value from the environment whose name is "varname" if it exists, otherwise null.
    */
   public Object lookup(String varname) {
@@ -1247,14 +1252,16 @@
   }
 
   /**
-   * An Exception thrown when an attempt is made to import a symbol from a file
-   * that was not properly loaded.
+   * An Exception thrown when an attempt is made to import a symbol from a file that was not
+   * properly loaded.
    */
   static class LoadFailedException extends Exception {
     LoadFailedException(String importString) {
-      super(String.format("file '%s' was not correctly loaded. "
-              + "Make sure the 'load' statement appears in the global scope in your file",
-          importString));
+      super(
+          String.format(
+              "file '%s' was not correctly loaded. "
+                  + "Make sure the 'load' statement appears in the global scope in your file",
+              importString));
     }
 
     LoadFailedException(String importString, String symbolString, Iterable<String> allKeys) {
@@ -1308,8 +1315,8 @@
   }
 
   /**
-   * Returns a hash code calculated from the hash code of this Environment and the
-   * transitive closure of other Environments it loads.
+   * Returns a hash code calculated from the hash code of this Environment and the transitive
+   * closure of other Environments it loads.
    */
   public String getTransitiveContentHashCode() {
     return transitiveHashCode;
@@ -1351,16 +1358,17 @@
    * A handler that immediately throws {@link FailFastException} whenever an error or warning
    * occurs.
    *
-   * We do not reuse an existing unchecked exception type, because callers (e.g., test assertions)
-   * need to be able to distinguish between organically occurring exceptions and exceptions thrown
-   * by this handler.
+   * <p>We do not reuse an existing unchecked exception type, because callers (e.g., test
+   * assertions) need to be able to distinguish between organically occurring exceptions and
+   * exceptions thrown by this handler.
    */
-  public static final EventHandler FAIL_FAST_HANDLER = new EventHandler() {
-    @Override
-    public void handle(Event event) {
-      if (EventKind.ERRORS_AND_WARNINGS.contains(event.getKind())) {
-        throw new FailFastException(event.toString());
-      }
-    }
-  };
+  public static final EventHandler FAIL_FAST_HANDLER =
+      new EventHandler() {
+        @Override
+        public void handle(Event event) {
+          if (EventKind.ERRORS_AND_WARNINGS.contains(event.getKind())) {
+            throw new FailFastException(event.toString());
+          }
+        }
+      };
 }
diff --git a/src/main/java/com/google/devtools/build/lib/syntax/Identifier.java b/src/main/java/com/google/devtools/build/lib/syntax/Identifier.java
index e2bd0e3..8e06f69 100644
--- a/src/main/java/com/google/devtools/build/lib/syntax/Identifier.java
+++ b/src/main/java/com/google/devtools/build/lib/syntax/Identifier.java
@@ -14,6 +14,7 @@
 
 package com.google.devtools.build.lib.syntax;
 
+import com.google.common.base.Preconditions;
 import com.google.devtools.build.lib.util.SpellChecker;
 import java.io.IOException;
 import java.util.Set;
@@ -32,6 +33,9 @@
 public final class Identifier extends Expression {
 
   private final String name;
+  // The scope of the variable. The value is set when the AST has been analysed by
+  // ValidationEnvironment.
+  @Nullable private ValidationEnvironment.Scope scope;
 
   public Identifier(String name) {
     this.name = name;
@@ -55,6 +59,7 @@
 
   @Override
   public boolean equals(@Nullable Object object) {
+    // TODO(laurentlb): Remove this. AST nodes should probably not be comparable.
     if (object instanceof Identifier) {
       Identifier that = (Identifier) object;
       return this.name.equals(that.name);
@@ -64,12 +69,19 @@
 
   @Override
   public int hashCode() {
+    // TODO(laurentlb): Remove this.
     return name.hashCode();
   }
 
+  void setScope(ValidationEnvironment.Scope scope) {
+    Preconditions.checkState(this.scope == null);
+    this.scope = scope;
+  }
+
   @Override
   Object doEval(Environment env) throws EvalException {
-    Object value = env.lookup(name);
+    Object value =
+        scope == ValidationEnvironment.Scope.Local ? env.localLookup(name) : env.lookup(name);
     if (value == null) {
       throw createInvalidIdentifierException(env.getVariableNames());
     }
diff --git a/src/main/java/com/google/devtools/build/lib/syntax/UserDefinedFunction.java b/src/main/java/com/google/devtools/build/lib/syntax/UserDefinedFunction.java
index 8410dd7..5783d11 100644
--- a/src/main/java/com/google/devtools/build/lib/syntax/UserDefinedFunction.java
+++ b/src/main/java/com/google/devtools/build/lib/syntax/UserDefinedFunction.java
@@ -65,8 +65,7 @@
     }
 
     ImmutableList<String> names = signature.getSignature().getNames();
-    LexicalFrame lexicalFrame =
-        LexicalFrame.createForUserDefinedFunctionCall(env.mutability(), /*numArgs=*/ names.size());
+    LexicalFrame lexicalFrame = LexicalFrame.create(env.mutability(), /*numArgs=*/ names.size());
     try (SilentCloseable c = Profiler.instance().profile(ProfilerTask.SKYLARK_USER_FN, getName())) {
       env.enterScope(this, lexicalFrame, ast, definitionGlobals);
 
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 14d55ac..78e4da2 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
@@ -40,7 +40,7 @@
  */
 public final class ValidationEnvironment extends SyntaxTreeVisitor {
 
-  private enum Scope {
+  enum Scope {
     /** Symbols defined inside a function or a comprehension. */
     Local,
     /** Symbols defined at a module top-level, e.g. functions, loaded symbols. */
@@ -98,8 +98,9 @@
   }
 
   /**
-   * Add all definitions to the current block. This is done because symbols are sometimes used
-   * before their definition point (e.g. a functions are not necessarily declared in order).
+   * First pass: add all definitions to the current block. This is done because symbols are
+   * sometimes used before their definition point (e.g. a functions are not necessarily declared in
+   * order).
    *
    * <p>The old behavior (when incompatibleStaticNameResolution is false) doesn't have this first
    * pass.
@@ -167,9 +168,11 @@
 
   @Override
   public void visit(Identifier node) {
-    if (!hasSymbolInEnvironment(node.getName())) {
+    @Nullable Block b = blockThatDefines(node.getName());
+    if (b == null) {
       throw new ValidationException(node.createInvalidIdentifierException(getAllSymbols()));
     }
+    node.setScope(b.scope);
   }
 
   private void validateLValue(Location loc, Expression expr) {
@@ -304,14 +307,14 @@
     block.variables.add(varname);
   }
 
-  /** Returns true if the symbol exists in the validation environment (or a parent). */
-  private boolean hasSymbolInEnvironment(String varname) {
+  /** Returns the nearest Block that defines a symbol. */
+  private Block blockThatDefines(String varname) {
     for (Block b = block; b != null; b = b.parent) {
       if (b.variables.contains(varname)) {
-        return true;
+        return b;
       }
     }
-    return false;
+    return null;
   }
 
   /** Returns the set of all accessible symbols (both local and global) */
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 f11cfe7..1b4d0eb 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
@@ -26,9 +26,7 @@
 import org.junit.runner.RunWith;
 import org.junit.runners.JUnit4;
 
-/**
- * Tests of Environment.
- */
+/** Tests of Environment. */
 @RunWith(JUnit4.class)
 public class EnvironmentTest extends EvaluationTestCase {
 
@@ -95,7 +93,8 @@
     try (Mutability mut = Mutability.create("test")) {
       IllegalArgumentException expected =
           assertThrows(IllegalArgumentException.class, () -> Environment.builder(mut).build());
-      assertThat(expected).hasMessageThat()
+      assertThat(expected)
+          .hasMessageThat()
           .contains("must call either setSemantics or useDefaultSemantics");
     }
   }
@@ -114,12 +113,13 @@
               .update("wiz", 3);
     }
     try (Mutability mut = Mutability.create("inner")) {
-      innerEnv = Environment.builder(mut)
-          .useDefaultSemantics()
-          .setGlobals(outerEnv.getGlobals())
-          .build()
-          .update("foo", "bat")
-          .update("quux", 42);
+      innerEnv =
+          Environment.builder(mut)
+              .useDefaultSemantics()
+              .setGlobals(outerEnv.getGlobals())
+              .build()
+              .update("foo", "bat")
+              .update("quux", 42);
     }
 
     assertThat(outerEnv.getVariableNames())
@@ -256,7 +256,6 @@
   @Test
   public void testVariableIsReferencedBeforeAssignment() throws Exception {
     Environment env = newSkylarkEnvironment().update("global_var", 666);
-
     try {
       BuildFileAST.eval(env, "def foo(x): x += global_var; global_var = 36; return x", "foo(1)");
       throw new AssertionError("failed to fail");
@@ -272,29 +271,25 @@
   @Test
   public void testVarOrderDeterminism() throws Exception {
     Mutability parentMutability = Mutability.create("parent env");
-    Environment parentEnv = Environment.builder(parentMutability)
-        .useDefaultSemantics()
-        .build();
+    Environment parentEnv = Environment.builder(parentMutability).useDefaultSemantics().build();
     parentEnv.update("a", 1);
     parentEnv.update("c", 2);
     parentEnv.update("b", 3);
     Environment.GlobalFrame parentFrame = parentEnv.getGlobals();
     parentMutability.freeze();
     Mutability mutability = Mutability.create("testing");
-    Environment env = Environment.builder(mutability)
-        .useDefaultSemantics()
-        .setGlobals(parentFrame)
-        .build();
+    Environment env =
+        Environment.builder(mutability).useDefaultSemantics().setGlobals(parentFrame).build();
     env.update("x", 4);
     env.update("z", 5);
     env.update("y", 6);
     // The order just has to be deterministic, but for definiteness this test spells out the exact
     // order returned by the implementation: parent frame before current environment, and bindings
     // within a frame ordered by when they were added.
-    assertThat(env.getVariableNames())
-        .containsExactly("a", "c", "b", "x", "z", "y").inOrder();
+    assertThat(env.getVariableNames()).containsExactly("a", "c", "b", "x", "z", "y").inOrder();
     assertThat(env.getGlobals().getTransitiveBindings())
-        .containsExactly("a", 1, "c", 2, "b", 3, "x", 4, "z", 5, "y", 6).inOrder();
+        .containsExactly("a", 1, "c", 2, "b", 3, "x", 4, "z", 5, "y", 6)
+        .inOrder();
   }
 
   @Test
@@ -304,33 +299,30 @@
     Extension a = new Extension(ImmutableMap.of(), "a123");
     Extension b = new Extension(ImmutableMap.of(), "b456");
     Extension c = new Extension(ImmutableMap.of(), "c789");
-    Environment env1 = Environment.builder(Mutability.create("testing1"))
-        .useDefaultSemantics()
-        .setImportedExtensions(ImmutableMap.of("a", a, "b", b, "c", c))
-        .setFileContentHashCode("z")
-        .build();
-    Environment env2 = Environment.builder(Mutability.create("testing2"))
-        .useDefaultSemantics()
-        .setImportedExtensions(ImmutableMap.of("c", c, "b", b, "a", a))
-        .setFileContentHashCode("z")
-        .build();
+    Environment env1 =
+        Environment.builder(Mutability.create("testing1"))
+            .useDefaultSemantics()
+            .setImportedExtensions(ImmutableMap.of("a", a, "b", b, "c", c))
+            .setFileContentHashCode("z")
+            .build();
+    Environment env2 =
+        Environment.builder(Mutability.create("testing2"))
+            .useDefaultSemantics()
+            .setImportedExtensions(ImmutableMap.of("c", c, "b", b, "a", a))
+            .setFileContentHashCode("z")
+            .build();
     assertThat(env1.getTransitiveContentHashCode()).isEqualTo(env2.getTransitiveContentHashCode());
   }
 
   @Test
   public void testExtensionEqualityDebugging_RhsIsNull() {
-    assertCheckStateFailsWithMessage(
-        new Extension(ImmutableMap.of(), "abc"),
-        null,
-        "got a null");
+    assertCheckStateFailsWithMessage(new Extension(ImmutableMap.of(), "abc"), null, "got a null");
   }
 
   @Test
   public void testExtensionEqualityDebugging_RhsHasBadType() {
     assertCheckStateFailsWithMessage(
-        new Extension(ImmutableMap.of(), "abc"),
-        5,
-        "got a java.lang.Integer");
+        new Extension(ImmutableMap.of(), "abc"), 5, "got a java.lang.Integer");
   }
 
   @Test
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 6991363..64ebe3e 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
@@ -1796,6 +1796,32 @@
   }
 
   @Test
+  public void testLocalVariableDefinedBelow() throws Exception {
+    new SkylarkTest("--incompatible_static_name_resolution=true")
+        .setUp(
+            "def beforeEven(li):", // returns the value before the first even number
+            "    for i in li:",
+            "        if i % 2 == 0:",
+            "            return a",
+            "        else:",
+            "            a = i",
+            "res = beforeEven([1, 3, 4, 5])")
+        .testLookup("res", 3);
+  }
+
+  @Test
+  public void testShadowisNotInitialized() throws Exception {
+    new SkylarkTest("--incompatible_static_name_resolution=true")
+        .testIfErrorContains(
+            /* error message */ "name 'gl' is not defined",
+            "gl = 5",
+            "def foo():", // returns the value before the first even number
+            "    if False: gl = 2",
+            "    return gl",
+            "res = foo()");
+  }
+
+  @Test
   public void testFunctionCallRecursion() throws Exception {
     new SkylarkTest().testIfErrorContains("Recursion was detected when calling 'f' from 'g'",
         "def main():",