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