Starlark: make IfStatement immutable

Immutable objects are harder to construct, but safer/easier to work with.

Closes #11751.

PiperOrigin-RevId: 320958936
diff --git a/src/main/java/com/google/devtools/build/lib/syntax/IfStatement.java b/src/main/java/com/google/devtools/build/lib/syntax/IfStatement.java
index 931d12c..8cbffe8 100644
--- a/src/main/java/com/google/devtools/build/lib/syntax/IfStatement.java
+++ b/src/main/java/com/google/devtools/build/lib/syntax/IfStatement.java
@@ -25,19 +25,23 @@
   private final Expression condition;
   // These blocks may be non-null but empty after a misparse:
   private final ImmutableList<Statement> thenBlock; // non-empty
-  @Nullable ImmutableList<Statement> elseBlock; // non-empty if non-null; set after construction
+
+  @Nullable
+  final ImmutableList<Statement> elseBlock; // non-empty if non-null; set after construction
 
   IfStatement(
       FileLocations locs,
       TokenKind token,
       int ifOffset,
       Expression condition,
-      List<Statement> thenBlock) {
+      List<Statement> thenBlock,
+      @Nullable List<Statement> elseBlock) {
     super(locs);
     this.token = token;
     this.ifOffset = ifOffset;
     this.condition = condition;
     this.thenBlock = ImmutableList.copyOf(thenBlock);
+    this.elseBlock = elseBlock != null ? ImmutableList.copyOf(elseBlock) : null;
   }
 
   /**
@@ -63,10 +67,6 @@
     return elseBlock;
   }
 
-  void setElseBlock(List<Statement> elseBlock) {
-    this.elseBlock = ImmutableList.copyOf(elseBlock);
-  }
-
   @Override
   public int getStartOffset() {
     return ifOffset;
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 1627df3..5db54e7 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
@@ -1118,24 +1118,37 @@
     Expression cond = parseTest();
     expect(TokenKind.COLON);
     List<Statement> body = parseSuite();
-    IfStatement ifStmt = new IfStatement(locs, TokenKind.IF, ifOffset, cond, body);
-    IfStatement tail = ifStmt;
+
+    ArrayList<Integer> elifOffsets = new ArrayList<>();
+    ArrayList<Expression> elifConditions = new ArrayList<>();
+    ArrayList<List<Statement>> elifBodies = new ArrayList<>();
+
     while (token.kind == TokenKind.ELIF) {
-      int elifOffset = expect(TokenKind.ELIF);
-      cond = parseTest();
+      elifOffsets.add(expect(TokenKind.ELIF));
+      elifConditions.add(parseTest());
       expect(TokenKind.COLON);
-      body = parseSuite();
-      IfStatement elif = new IfStatement(locs, TokenKind.ELIF, elifOffset, cond, body);
-      tail.setElseBlock(ImmutableList.of(elif));
-      tail = elif;
+      elifBodies.add(parseSuite());
     }
+
+    List<Statement> tail;
     if (token.kind == TokenKind.ELSE) {
       expect(TokenKind.ELSE);
       expect(TokenKind.COLON);
-      body = parseSuite();
-      tail.setElseBlock(body);
+      tail = parseSuite();
+    } else {
+      tail = null;
     }
-    return ifStmt;
+
+    for (int i = elifOffsets.size() - 1; i >= 0; --i) {
+      int elifOffset = elifOffsets.get(i);
+      Expression elifCondition = elifConditions.get(i);
+      List<Statement> elifThenBlock = elifBodies.get(i);
+      IfStatement elif =
+          new IfStatement(locs, TokenKind.ELIF, elifOffset, elifCondition, elifThenBlock, tail);
+      tail = ImmutableList.of(elif);
+    }
+
+    return new IfStatement(locs, TokenKind.IF, ifOffset, cond, body, tail);
   }
 
   // for_stmt = FOR IDENTIFIER IN expr ':' suite