Allow return without expression, AST-equivalent to return None.

--
MOS_MIGRATED_REVID=100268427
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 21eb2a5..8f59047 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
@@ -84,7 +84,7 @@
   }
 
   private static final EnumSet<TokenKind> STATEMENT_TERMINATOR_SET =
-      EnumSet.of(TokenKind.EOF, TokenKind.NEWLINE);
+      EnumSet.of(TokenKind.EOF, TokenKind.NEWLINE, TokenKind.SEMI);
 
   private static final EnumSet<TokenKind> LIST_TERMINATOR_SET =
     EnumSet.of(TokenKind.EOF, TokenKind.RBRACKET, TokenKind.SEMI);
@@ -1483,11 +1483,20 @@
         token.right);
   }
 
-  // return_stmt ::= RETURN expr
+  // return_stmt ::= RETURN [expr]
   private ReturnStatement parseReturnStatement() {
     int start = token.left;
+    int end = token.right;
     expect(TokenKind.RETURN);
-    Expression expression = parseExpression();
+    
+    Expression expression;
+    if (STATEMENT_TERMINATOR_SET.contains(token.kind)) {
+        // this None makes the AST not correspond to the source exactly anymore
+        expression = new Identifier("None");
+        setLocation(expression, start, end);
+    } else {
+        expression = parseExpression();
+    }
     return setLocation(new ReturnStatement(expression), start, expression);
   }
 
diff --git a/src/test/java/com/google/devtools/build/lib/syntax/ParserTest.java b/src/test/java/com/google/devtools/build/lib/syntax/ParserTest.java
index 93e14a8..a9f32c0 100644
--- a/src/test/java/com/google/devtools/build/lib/syntax/ParserTest.java
+++ b/src/test/java/com/google/devtools/build/lib/syntax/ParserTest.java
@@ -967,6 +967,36 @@
     List<Statement> stmts3 = parseFile("[ i for (i, j, k) in [(1, 2, 3)] ]\n");
     assertThat(stmts3).hasSize(1);
   }
+  
+  @Test
+  public void testReturnNone() throws Exception {
+    List<Statement> defNone = parseFileForSkylark("def foo():", "  return None\n");
+    assertThat(defNone).hasSize(1);
+
+    List<Statement> bodyNone = ((FunctionDefStatement) defNone.get(0)).getStatements();
+    assertThat(bodyNone).hasSize(1);
+
+    ReturnStatement returnNone = (ReturnStatement) bodyNone.get(0);
+    assertEquals("None", ((Identifier) returnNone.getReturnExpression()).getName());
+
+    int i = 0;
+    for (String end : new String[]{";", "\n"}) {
+      List<Statement> defNoExpr = parseFileForSkylark("def bar" + i + "():", "  return" + end);
+      i++;
+      assertThat(defNoExpr).hasSize(1);
+  
+      List<Statement> bodyNoExpr = ((FunctionDefStatement) defNoExpr.get(0)).getStatements();
+      assertThat(bodyNoExpr).hasSize(1);
+  
+      ReturnStatement returnNoExpr = (ReturnStatement) bodyNoExpr.get(0);
+      Identifier none = (Identifier) returnNoExpr.getReturnExpression();
+      assertEquals("None", none.getName());
+      assertLocation(
+          returnNoExpr.getLocation().getStartOffset(),
+          returnNoExpr.getLocation().getEndOffset(),
+          none.getLocation());
+    }
+  }
 
   @Test
   public void testForLoopBadSyntax() throws Exception {