preprocessor: Fix negative shift with bad ids.

Fix this by producing an error on undefined or negative shifts.

BUG=629518

Change-Id: Idfca5ed3fc8e557f6178408f3426a5ef2ce7cf14
Reviewed-on: https://chromium-review.googlesource.com/362020
Reviewed-by: Antoine Labour <piman@chromium.org>
Reviewed-by: Corentin Wallez <cwallez@chromium.org>
Commit-Queue: Jamie Madill <jmadill@chromium.org>
diff --git a/src/compiler/preprocessor/DiagnosticsBase.cpp b/src/compiler/preprocessor/DiagnosticsBase.cpp
index 68c6e9c..9f62a9e 100644
--- a/src/compiler/preprocessor/DiagnosticsBase.cpp
+++ b/src/compiler/preprocessor/DiagnosticsBase.cpp
@@ -115,6 +115,8 @@
         return "invalid line directive";
       case PP_NON_PP_TOKEN_BEFORE_EXTENSION_ESSL3:
         return "extension directive must occur before any non-preprocessor tokens in ESSL3";
+      case PP_UNDEFINED_SHIFT:
+          return "shift exponent is negative or undefined";
       // Errors end.
       // Warnings begin.
       case PP_EOF_IN_DIRECTIVE:
diff --git a/src/compiler/preprocessor/DiagnosticsBase.h b/src/compiler/preprocessor/DiagnosticsBase.h
index d26c174..a089920 100644
--- a/src/compiler/preprocessor/DiagnosticsBase.h
+++ b/src/compiler/preprocessor/DiagnosticsBase.h
@@ -65,6 +65,7 @@
         PP_INVALID_FILE_NUMBER,
         PP_INVALID_LINE_DIRECTIVE,
         PP_NON_PP_TOKEN_BEFORE_EXTENSION_ESSL3,
+        PP_UNDEFINED_SHIFT,
         PP_ERROR_END,
 
         PP_WARNING_BEGIN,
diff --git a/src/compiler/preprocessor/ExpressionParser.cpp b/src/compiler/preprocessor/ExpressionParser.cpp
index 193b150..b765c64 100644
--- a/src/compiler/preprocessor/ExpressionParser.cpp
+++ b/src/compiler/preprocessor/ExpressionParser.cpp
@@ -498,12 +498,9 @@
 
 #if YYDEBUG
   /* YYRLINE[YYN] -- Source line where rule number YYN was defined.  */
-static const yytype_uint16 yyrline[] =
-{
-       0,   110,   110,   117,   118,   129,   129,   150,   150,   171,
-     174,   177,   180,   183,   186,   189,   192,   195,   198,   201,
-     204,   207,   210,   230,   250,   253,   256,   259,   262,   265
-};
+static const yytype_uint16 yyrline[] = {0,   110, 110, 117, 118, 129, 129, 150, 150, 171,
+                                        174, 177, 180, 183, 186, 189, 192, 195, 198, 218,
+                                        238, 241, 244, 264, 284, 287, 290, 293, 296, 299};
 #endif
 
 #if YYDEBUG || YYERROR_VERBOSE || 0
@@ -1495,7 +1492,23 @@
   case 18:
 
     {
-        (yyval) = (yyvsp[-2]) >> (yyvsp[0]);
+        if ((yyvsp[0]) < 0)
+        {
+            if (!context->isIgnoringErrors())
+            {
+                std::ostringstream stream;
+                stream << (yyvsp[-2]) << " >> " << (yyvsp[0]);
+                std::string text = stream.str();
+                context->diagnostics->report(pp::Diagnostics::PP_UNDEFINED_SHIFT,
+                                             context->token->location, text.c_str());
+                *(context->valid) = false;
+            }
+            (yyval) = static_cast<YYSTYPE>(0);
+        }
+        else
+        {
+            (yyval) = (yyvsp[-2]) >> (yyvsp[0]);
+        }
     }
 
     break;
@@ -1503,7 +1516,23 @@
   case 19:
 
     {
-        (yyval) = (yyvsp[-2]) << (yyvsp[0]);
+        if ((yyvsp[0]) < 0)
+        {
+            if (!context->isIgnoringErrors())
+            {
+                std::ostringstream stream;
+                stream << (yyvsp[-2]) << " << " << (yyvsp[0]);
+                std::string text = stream.str();
+                context->diagnostics->report(pp::Diagnostics::PP_UNDEFINED_SHIFT,
+                                             context->token->location, text.c_str());
+                *(context->valid) = false;
+            }
+            (yyval) = static_cast<YYSTYPE>(0);
+        }
+        else
+        {
+            (yyval) = (yyvsp[-2]) << (yyvsp[0]);
+        }
     }
 
     break;
diff --git a/src/compiler/preprocessor/ExpressionParser.y b/src/compiler/preprocessor/ExpressionParser.y
index 7b5d9e9..dc0080e 100644
--- a/src/compiler/preprocessor/ExpressionParser.y
+++ b/src/compiler/preprocessor/ExpressionParser.y
@@ -196,10 +196,44 @@
         $$ = $1 < $3;
     }
     | expression TOK_OP_RIGHT expression {
-        $$ = $1 >> $3;
+        if ($3 < 0)
+        {
+            if (!context->isIgnoringErrors())
+            {
+                std::ostringstream stream;
+                stream << $1 << " >> " << $3;
+                std::string text = stream.str();
+                context->diagnostics->report(pp::Diagnostics::PP_UNDEFINED_SHIFT,
+                                             context->token->location,
+                                             text.c_str());
+                *(context->valid) = false;
+            }
+            $$ = static_cast<YYSTYPE>(0);
+        }
+        else
+        {
+            $$ = $1 >> $3;
+        }
     }
     | expression TOK_OP_LEFT expression {
-        $$ = $1 << $3;
+        if ($3 < 0)
+        {
+            if (!context->isIgnoringErrors())
+            {
+                std::ostringstream stream;
+                stream << $1 << " << " << $3;
+                std::string text = stream.str();
+                context->diagnostics->report(pp::Diagnostics::PP_UNDEFINED_SHIFT,
+                                             context->token->location,
+                                             text.c_str());
+                *(context->valid) = false;
+            }
+            $$ = static_cast<YYSTYPE>(0);
+        }
+        else
+        {
+            $$ = $1 << $3;
+        }
     }
     | expression '-' expression {
         $$ = $1 - $3;
diff --git a/src/tests/compiler_tests/MalformedShader_test.cpp b/src/tests/compiler_tests/MalformedShader_test.cpp
index 96d0778..64980d7 100644
--- a/src/tests/compiler_tests/MalformedShader_test.cpp
+++ b/src/tests/compiler_tests/MalformedShader_test.cpp
@@ -1635,3 +1635,23 @@
         FAIL() << "Shader compilation succeeded, expecting failure " << mInfoLog;
     }
 }
+
+// Covers a bug in our parsing of malformed shift preprocessor expressions.
+TEST_F(MalformedShaderTest, LineDirectiveUndefinedShift)
+{
+    const std::string &shaderString = "#line x << y";
+    if (compile(shaderString))
+    {
+        FAIL() << "Shader compilation succeeded, expecting failure " << mInfoLog;
+    }
+}
+
+// Covers a bug in our parsing of malformed shift preprocessor expressions.
+TEST_F(MalformedShaderTest, LineDirectiveNegativeShift)
+{
+    const std::string &shaderString = "#line x << -1";
+    if (compile(shaderString))
+    {
+        FAIL() << "Shader compilation succeeded, expecting failure " << mInfoLog;
+    }
+}
diff --git a/src/tests/preprocessor_tests/define_test.cpp b/src/tests/preprocessor_tests/define_test.cpp
index c4529a0..b95a6b4 100644
--- a/src/tests/preprocessor_tests/define_test.cpp
+++ b/src/tests/preprocessor_tests/define_test.cpp
@@ -7,6 +7,8 @@
 #include "PreprocessorTest.h"
 #include "compiler/preprocessor/Token.h"
 
+using testing::_;
+
 class DefineTest : public PreprocessorTest
 {
 };
@@ -918,3 +920,22 @@
         "defined(bar)\n";
     preprocess(input, expected);
 }
+
+// Test that line directive expressions give errors on negative or undefined shifts.
+TEST_F(DefineTest, NegativeShiftInLineDirective)
+{
+    const char *input =
+        "#line 1 << -1\n"
+        "#line 1 >> -1\n"
+        "#line 1 << x\n"
+        "#line 1 >> x\n";
+    const char *expected =
+        "\n"
+        "\n"
+        "\n"
+        "\n";
+
+    EXPECT_CALL(mDiagnostics, print(pp::Diagnostics::PP_UNDEFINED_SHIFT, _, _)).Times(4);
+    EXPECT_CALL(mDiagnostics, print(pp::Diagnostics::PP_INVALID_LINE_NUMBER, _, _)).Times(2);
+    preprocess(input, expected);
+}
\ No newline at end of file