bpo-35029: Replace the SyntaxWarning exception with a SyntaxError. (GH-9999)

If SyntaxWarning was raised as an exception, it will be replaced
with a SyntaxError for better error reporting.
diff --git a/Lib/test/test_grammar.py b/Lib/test/test_grammar.py
index 9dd42b4..3d8b151 100644
--- a/Lib/test/test_grammar.py
+++ b/Lib/test/test_grammar.py
@@ -5,6 +5,7 @@
 import inspect
 import unittest
 import sys
+import warnings
 # testing import *
 from sys import *
 
@@ -1099,6 +1100,14 @@
         else:
             self.fail("AssertionError not raised by 'assert False'")
 
+        with self.assertWarnsRegex(SyntaxWarning, 'assertion is always true'):
+            compile('assert(x, "msg")', '<testcase>', 'exec')
+        with warnings.catch_warnings():
+            warnings.filterwarnings('error', category=SyntaxWarning)
+            with self.assertRaisesRegex(SyntaxError, 'assertion is always true'):
+                compile('assert(x, "msg")', '<testcase>', 'exec')
+            compile('assert x, "msg"', '<testcase>', 'exec')
+
 
     ### compound_stmt: if_stmt | while_stmt | for_stmt | try_stmt | funcdef | classdef
     # Tested below
diff --git a/Misc/NEWS.d/next/Core and Builtins/2018-10-20-10-26-15.bpo-35029.t4tZcQ.rst b/Misc/NEWS.d/next/Core and Builtins/2018-10-20-10-26-15.bpo-35029.t4tZcQ.rst
new file mode 100644
index 0000000..3644c44
--- /dev/null
+++ b/Misc/NEWS.d/next/Core and Builtins/2018-10-20-10-26-15.bpo-35029.t4tZcQ.rst
@@ -0,0 +1,2 @@
+:exc:`SyntaxWarning` raised as an exception at code generation time will be
+now replaced with a :exc:`SyntaxError` for better error reporting.
diff --git a/Python/compile.c b/Python/compile.c
index 78b7baf..11958d3 100644
--- a/Python/compile.c
+++ b/Python/compile.c
@@ -174,6 +174,7 @@
 static int compiler_addop_i(struct compiler *, int, Py_ssize_t);
 static int compiler_addop_j(struct compiler *, int, basicblock *, int);
 static int compiler_error(struct compiler *, const char *);
+static int compiler_warn(struct compiler *, const char *);
 static int compiler_nameop(struct compiler *, identifier, expr_context_ty);
 
 static PyCodeObject *compiler_mod(struct compiler *, mod_ty);
@@ -2971,7 +2972,6 @@
 {
     static PyObject *assertion_error = NULL;
     basicblock *end;
-    PyObject* msg;
 
     if (c->c_optimize)
         return 1;
@@ -2981,18 +2981,13 @@
             return 0;
     }
     if (s->v.Assert.test->kind == Tuple_kind &&
-        asdl_seq_LEN(s->v.Assert.test->v.Tuple.elts) > 0) {
-        msg = PyUnicode_FromString("assertion is always true, "
-                                   "perhaps remove parentheses?");
-        if (msg == NULL)
-            return 0;
-        if (PyErr_WarnExplicitObject(PyExc_SyntaxWarning, msg,
-                                     c->c_filename, c->u->u_lineno,
-                                     NULL, NULL) == -1) {
-            Py_DECREF(msg);
+        asdl_seq_LEN(s->v.Assert.test->v.Tuple.elts) > 0)
+    {
+        if (!compiler_warn(c, "assertion is always true, "
+                              "perhaps remove parentheses?"))
+        {
             return 0;
         }
-        Py_DECREF(msg);
     }
     end = compiler_new_block(c);
     if (end == NULL)
@@ -4793,6 +4788,31 @@
     return 0;
 }
 
+/* Emits a SyntaxWarning and returns 1 on success.
+   If a SyntaxWarning raised as error, replaces it with a SyntaxError
+   and returns 0.
+*/
+static int
+compiler_warn(struct compiler *c, const char *errstr)
+{
+    PyObject *msg = PyUnicode_FromString(errstr);
+    if (msg == NULL) {
+        return 0;
+    }
+    if (PyErr_WarnExplicitObject(PyExc_SyntaxWarning, msg, c->c_filename,
+                                 c->u->u_lineno, NULL, NULL) < 0)
+    {
+        Py_DECREF(msg);
+        if (PyErr_ExceptionMatches(PyExc_SyntaxWarning)) {
+            PyErr_Clear();
+            return compiler_error(c, errstr);
+        }
+        return 0;
+    }
+    Py_DECREF(msg);
+    return 1;
+}
+
 static int
 compiler_handle_subscr(struct compiler *c, const char *kind,
                        expr_context_ty ctx)