Parser: Useful error messages when a Python keyword is used.

--
MOS_MIGRATED_REVID=89307831
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 3ce949d..297aa13 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
@@ -299,6 +299,31 @@
     return previous;
   }
 
+  // Keywords that exist in Python and that we don't parse.
+  private static final EnumSet<TokenKind> FORBIDDEN_KEYWORDS =
+      EnumSet.of(TokenKind.AS, TokenKind.ASSERT, TokenKind.BREAK, TokenKind.CONTINUE,
+          TokenKind.DEL, TokenKind.EXCEPT, TokenKind.FINALLY, TokenKind.FROM, TokenKind.GLOBAL,
+          TokenKind.IMPORT, TokenKind.IS, TokenKind.LAMBDA, TokenKind.NONLOCAL, TokenKind.RAISE,
+          TokenKind.TRY, TokenKind.WITH, TokenKind.WHILE, TokenKind.YIELD);
+
+  private void checkForbiddenKeywords(Token token) {
+    if (parsePython || !FORBIDDEN_KEYWORDS.contains(token.kind)) {
+      return;
+    }
+    String error;
+    switch (token.kind) {
+      case ASSERT: error = "'assert' not supported, use 'fail' instead"; break;
+      case TRY: error = "'try' not supported, all exceptions are fatal"; break;
+      case IMPORT: error = "'import' not supported, use 'load' instead"; break;
+      case IS: error = "'is' not supported, use '==' instead"; break;
+      case LAMBDA: error = "'lambda' not supported, declare a function instead"; break;
+      case RAISE: error = "'raise' not supported, use 'fail' instead"; break;
+      case WHILE: error = "'while' not supported, use 'for' instead"; break;
+      default: error = "keyword '" + token.kind.getPrettyName() + "' not supported"; break;
+    }
+    reportError(lexer.createLocation(token.left, token.right), error);
+  }
+
   private void nextToken() {
     if (pushedToken != null) {
       token = pushedToken;
@@ -313,6 +338,7 @@
         }
       }
     }
+    checkForbiddenKeywords(token);
     if (DEBUGGING) {
       System.err.print(token);
     }
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 afca4b3..683f2c7 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
@@ -316,8 +316,16 @@
   public void testAssignKeyword() {
     syntaxEvents.setFailFast(false);
     parseExpr("with = 4");
+    syntaxEvents.assertContainsEvent("keyword 'with' not supported");
     syntaxEvents.assertContainsEvent("syntax error at 'with': expected expression");
-    syntaxEvents.collector().clear();
+  }
+
+  @Test
+  public void testTry() {
+    syntaxEvents.setFailFast(false);
+    parseExpr("try: 1 + 1");
+    syntaxEvents.assertContainsEvent("'try' not supported, all exceptions are fatal");
+    syntaxEvents.assertContainsEvent("syntax error at 'try': expected expression");
   }
 
   @Test
@@ -754,6 +762,21 @@
   }
 
   @Test
+  public void testFunctionDefinitionIgnoredEvenWithUnsupportedKeyword() throws Exception {
+    // Parser skips over entire function definitions without reporting error,
+    // when parsePython is set to true.
+    List<Statement> stmts = parseFile(
+        "x = 1;\n"
+        + "def foo(x, y, **z):\n"
+        + "  try:\n"
+        + "    x = 2\n"
+        + "  with: pass\n"
+        + "  return 2\n"
+        + "x = 3", true /* parsePython */);
+    assertThat(stmts).hasSize(2);
+  }
+
+  @Test
   public void testFunctionDefinitionIgnored() throws Exception {
     // Parser skips over entire function definitions without reporting error,
     // when parsePython is set to true.