Edit

kc3-lang/angle/src/tests/preprocessor_tests/if_test.cpp

Branch :

  • Show log

    Commit

  • Author : Olli Etuaho
    Date : 2016-11-22 17:36:49
    Hash : f1cf5e63
    Message : Prevent stack overflow in macro expansion Add a configurable limit for how many nested MacroExpander objects can be created in the preprocessor, so that stack overflow can be prevented in case of malicious shaders. By default the limit is set to 1000. In unit tests the limit is set lower to make the test run faster. Includes refactoring of most of the preprocessor tests so that they use utility functions provided by the test class instead of repeating the same code for initializing the preprocessor. BUG=angleproject:1600 TEST=angle_unittests Change-Id: I23b5140d9f2dc52df96111650db63150f7238494 Reviewed-on: https://chromium-review.googlesource.com/413986 Commit-Queue: Olli Etuaho <oetuaho@nvidia.com> Reviewed-by: Corentin Wallez <cwallez@chromium.org>

  • src/tests/preprocessor_tests/if_test.cpp
  • //
    // Copyright (c) 2012 The ANGLE Project Authors. All rights reserved.
    // Use of this source code is governed by a BSD-style license that can be
    // found in the LICENSE file.
    //
    
    #include "PreprocessorTest.h"
    #include "compiler/preprocessor/Token.h"
    
    class IfTest : public SimplePreprocessorTest
    {
    };
    
    TEST_F(IfTest, If_0)
    {
        const char* str = "pass_1\n"
                          "#if 0\n"
                          "fail\n"
                          "#endif\n"
                          "pass_2\n";
        const char* expected = "pass_1\n"
                               "\n"
                               "\n"
                               "\n"
                               "pass_2\n";
    
        preprocess(str, expected);
    }
    
    TEST_F(IfTest, If_1)
    {
        const char* str = "pass_1\n"
                          "#if 1\n"
                          "pass_2\n"
                          "#endif\n"
                          "pass_3\n";
        const char* expected = "pass_1\n"
                               "\n"
                               "pass_2\n"
                               "\n"
                               "pass_3\n";
    
        preprocess(str, expected);
    }
    
    TEST_F(IfTest, If_0_Else)
    {
        const char* str = "pass_1\n"
                          "#if 0\n"
                          "fail\n"
                          "#else\n"
                          "pass_2\n"
                          "#endif\n"
                          "pass_3\n";
        const char* expected = "pass_1\n"
                               "\n"
                               "\n"
                               "\n"
                               "pass_2\n"
                               "\n"
                               "pass_3\n";
    
        preprocess(str, expected);
    }
    
    TEST_F(IfTest, If_1_Else)
    {
        const char* str = "pass_1\n"
                          "#if 1\n"
                          "pass_2\n"
                          "#else\n"
                          "fail\n"
                          "#endif\n"
                          "pass_3\n";
        const char* expected = "pass_1\n"
                               "\n"
                               "pass_2\n"
                               "\n"
                               "\n"
                               "\n"
                               "pass_3\n";
    
        preprocess(str, expected);
    }
    
    TEST_F(IfTest, If_0_Elif)
    {
        const char* str = "pass_1\n"
                          "#if 0\n"
                          "fail_1\n"
                          "#elif 0\n"
                          "fail_2\n"
                          "#elif 1\n"
                          "pass_2\n"
                          "#elif 1\n"
                          "fail_3\n"
                          "#else\n"
                          "fail_4\n"
                          "#endif\n"
                          "pass_3\n";
        const char* expected = "pass_1\n"
                               "\n"
                               "\n"
                               "\n"
                               "\n"
                               "\n"
                               "pass_2\n"
                               "\n"
                               "\n"
                               "\n"
                               "\n"
                               "\n"
                               "pass_3\n";
    
        preprocess(str, expected);
    }
    
    TEST_F(IfTest, If_1_Elif)
    {
        const char* str = "pass_1\n"
                          "#if 1\n"
                          "pass_2\n"
                          "#elif 0\n"
                          "fail_1\n"
                          "#elif 1\n"
                          "fail_2\n"
                          "#else\n"
                          "fail_4\n"
                          "#endif\n"
                          "pass_3\n";
        const char* expected = "pass_1\n"
                               "\n"
                               "pass_2\n"
                               "\n"
                               "\n"
                               "\n"
                               "\n"
                               "\n"
                               "\n"
                               "\n"
                               "pass_3\n";
    
        preprocess(str, expected);
    }
    
    TEST_F(IfTest, If_Elif_Else)
    {
        const char* str = "pass_1\n"
                          "#if 0\n"
                          "fail_1\n"
                          "#elif 0\n"
                          "fail_2\n"
                          "#elif 0\n"
                          "fail_3\n"
                          "#else\n"
                          "pass_2\n"
                          "#endif\n"
                          "pass_3\n";
        const char* expected = "pass_1\n"
                               "\n"
                               "\n"
                               "\n"
                               "\n"
                               "\n"
                               "\n"
                               "\n"
                               "pass_2\n"
                               "\n"
                               "pass_3\n";
    
        preprocess(str, expected);
    }
    
    TEST_F(IfTest, If_0_Nested)
    {
        const char* str = "pass_1\n"
                          "#if 0\n"
                          "fail_1\n"
                          "#if 1\n"
                          "fail_2\n"
                          "#else\n"
                          "fail_3\n"
                          "#endif\n"
                          "#else\n"
                          "pass_2\n"
                          "#endif\n"
                          "pass_3\n";
        const char* expected = "pass_1\n"
                               "\n"
                               "\n"
                               "\n"
                               "\n"
                               "\n"
                               "\n"
                               "\n"
                               "\n"
                               "pass_2\n"
                               "\n"
                               "pass_3\n";
    
        preprocess(str, expected);
    }
    
    TEST_F(IfTest, If_1_Nested)
    {
        const char* str = "pass_1\n"
                          "#if 1\n"
                          "pass_2\n"
                          "#if 1\n"
                          "pass_3\n"
                          "#else\n"
                          "fail_1\n"
                          "#endif\n"
                          "#else\n"
                          "fail_2\n"
                          "#endif\n"
                          "pass_4\n";
        const char* expected = "pass_1\n"
                               "\n"
                               "pass_2\n"
                               "\n"
                               "pass_3\n"
                               "\n"
                               "\n"
                               "\n"
                               "\n"
                               "\n"
                               "\n"
                               "pass_4\n";
    
        preprocess(str, expected);
    }
    
    TEST_F(IfTest, OperatorPrecedence)
    {
        const char* str = "#if 1 + 2 * 3 + - (26 % 17 - + 4 / 2)\n"
                          "fail_1\n"
                          "#else\n"
                          "pass_1\n"
                          "#endif\n";
        const char* expected = "\n"
                               "\n"
                               "\n"
                               "pass_1\n"
                               "\n";
    
        preprocess(str, expected);
    }
    
    TEST_F(IfTest, OperatorDefined)
    {
        const char* str = "#if defined foo\n"
                          "fail_1\n"
                          "#else\n"
                          "pass_1\n"
                          "#endif\n"
                          "#define foo\n"
                          "#if defined(foo)\n"
                          "pass_2\n"
                          "#else\n"
                          "fail_2\n"
                          "#endif\n"
                          "#undef foo\n"
                          "#if defined ( foo ) \n"
                          "fail_3\n"
                          "#else\n"
                          "pass_3\n"
                          "#endif\n";
        const char* expected = "\n"
                               "\n"
                               "\n"
                               "pass_1\n"
                               "\n"
                               "\n"
                               "\n"
                               "pass_2\n"
                               "\n"
                               "\n"
                               "\n"
                               "\n"
                               "\n"
                               "\n"
                               "\n"
                               "pass_3\n"
                               "\n";
    
        preprocess(str, expected);
    }
    
    TEST_F(IfTest, OperatorEQ)
    {
        const char* str = "#if 4 - 1 == 2 + 1\n"
                          "pass\n"
                          "#else\n"
                          "fail\n"
                          "#endif\n";
        const char* expected = "\n"
                               "pass\n"
                               "\n"
                               "\n"
                               "\n";
    
        preprocess(str, expected);
    }
    
    TEST_F(IfTest, OperatorNE)
    {
        const char* str = "#if 1 != 2\n"
                          "pass\n"
                          "#else\n"
                          "fail\n"
                          "#endif\n";
        const char* expected = "\n"
                               "pass\n"
                               "\n"
                               "\n"
                               "\n";
    
        preprocess(str, expected);
    }
    
    TEST_F(IfTest, OperatorLess)
    {
        const char* str = "#if 1 < 2\n"
                          "pass\n"
                          "#else\n"
                          "fail\n"
                          "#endif\n";
        const char* expected = "\n"
                               "pass\n"
                               "\n"
                               "\n"
                               "\n";
    
        preprocess(str, expected);
    }
    
    TEST_F(IfTest, OperatorGreater)
    {
        const char* str = "#if 2 > 1\n"
                          "pass\n"
                          "#else\n"
                          "fail\n"
                          "#endif\n";
        const char* expected = "\n"
                               "pass\n"
                               "\n"
                               "\n"
                               "\n";
    
        preprocess(str, expected);
    }
    
    TEST_F(IfTest, OperatorLE)
    {
        const char* str = "#if 1 <= 2\n"
                          "pass_1\n"
                          "#else\n"
                          "fail_1\n"
                          "#endif\n"
                          "#if 2 <= 2\n"
                          "pass_2\n"
                          "#else\n"
                          "fail_2\n"
                          "#endif\n";
        const char* expected = "\n"
                               "pass_1\n"
                               "\n"
                               "\n"
                               "\n"
                               "\n"
                               "pass_2\n"
                               "\n"
                               "\n"
                               "\n";
    
        preprocess(str, expected);
    }
    
    TEST_F(IfTest, OperatorGE)
    {
        const char* str = "#if 2 >= 1\n"
                          "pass_1\n"
                          "#else\n"
                          "fail_1\n"
                          "#endif\n"
                          "#if 2 >= 2\n"
                          "pass_2\n"
                          "#else\n"
                          "fail_2\n"
                          "#endif\n";
        const char* expected = "\n"
                               "pass_1\n"
                               "\n"
                               "\n"
                               "\n"
                               "\n"
                               "pass_2\n"
                               "\n"
                               "\n"
                               "\n";
    
        preprocess(str, expected);
    }
    
    TEST_F(IfTest, OperatorBitwiseOR)
    {
        const char* str = "#if (0xaaaaaaaa | 0x55555555) == 0xffffffff\n"
                          "pass\n"
                          "#else\n"
                          "fail\n"
                          "#endif\n";
        const char* expected = "\n"
                               "pass\n"
                               "\n"
                               "\n"
                               "\n";
    
        preprocess(str, expected);
    }
    
    TEST_F(IfTest, OperatorBitwiseAND)
    {
        const char* str = "#if (0xaaaaaaa & 0x5555555) == 0\n"
                          "pass\n"
                          "#else\n"
                          "fail\n"
                          "#endif\n";
        const char* expected = "\n"
                               "pass\n"
                               "\n"
                               "\n"
                               "\n";
    
        preprocess(str, expected);
    }
    
    TEST_F(IfTest, OperatorBitwiseXOR)
    {
        const char* str = "#if (0xaaaaaaa ^ 0x5555555) == 0xfffffff\n"
                          "pass\n"
                          "#else\n"
                          "fail\n"
                          "#endif\n";
        const char* expected = "\n"
                               "pass\n"
                               "\n"
                               "\n"
                               "\n";
    
        preprocess(str, expected);
    }
    
    TEST_F(IfTest, OperatorBitwiseComplement)
    {
        const char* str = "#if (~ 0xdeadbeef) == -3735928560\n"
                          "pass\n"
                          "#else\n"
                          "fail\n"
                          "#endif\n";
        const char* expected = "\n"
                               "pass\n"
                               "\n"
                               "\n"
                               "\n";
    
        preprocess(str, expected);
    }
    
    TEST_F(IfTest, OperatorLeft)
    {
        const char* str = "#if (1 << 12) == 4096\n"
                          "pass\n"
                          "#else\n"
                          "fail\n"
                          "#endif\n";
        const char* expected = "\n"
                               "pass\n"
                               "\n"
                               "\n"
                               "\n";
    
        preprocess(str, expected);
    }
    
    TEST_F(IfTest, OperatorRight)
    {
        const char* str = "#if (31762 >> 8) == 124\n"
                          "pass\n"
                          "#else\n"
                          "fail\n"
                          "#endif\n";
        const char* expected = "\n"
                               "pass\n"
                               "\n"
                               "\n"
                               "\n";
    
        preprocess(str, expected);
    }
    
    TEST_F(IfTest, ExpressionWithMacros)
    {
        const char* str = "#define one 1\n"
                          "#define two 2\n"
                          "#define three 3\n"
                          "#if one + two == three\n"
                          "pass\n"
                          "#else\n"
                          "fail\n"
                          "#endif\n";
        const char* expected = "\n"
                               "\n"
                               "\n"
                               "\n"
                               "pass\n"
                               "\n"
                               "\n"
                               "\n";
    
        preprocess(str, expected);
    }
    
    TEST_F(IfTest, JunkInsideExcludedBlockIgnored)
    {
        const char* str = "#if 0\n"
                          "foo !@#$%^&* .1bar\n"
                          "#foo\n"
                          "#if bar\n"
                          "fail\n"
                          "#endif\n"
                          "#else\n"
                          "pass\n"
                          "#endif\n";
        const char* expected = "\n"
                               "\n"
                               "\n"
                               "\n"
                               "\n"
                               "\n"
                               "\n"
                               "pass\n"
                               "\n";
    
        preprocess(str, expected);
    }
    
    TEST_F(IfTest, Ifdef)
    {
        const char* str = "#define foo\n"
                          "#ifdef foo\n"
                          "pass_1\n"
                          "#else\n"
                          "fail_1\n"
                          "#endif\n"
                          "#undef foo\n"
                          "#ifdef foo\n"
                          "fail_2\n"
                          "#else\n"
                          "pass_2\n"
                          "#endif\n";
        const char* expected = "\n"
                               "\n"
                               "pass_1\n"
                               "\n"
                               "\n"
                               "\n"
                               "\n"
                               "\n"
                               "\n"
                               "\n"
                               "pass_2\n"
                               "\n";
    
        preprocess(str, expected);
    }
    
    TEST_F(IfTest, Ifndef)
    {
        const char* str = "#define foo\n"
                          "#ifndef foo\n"
                          "fail_1\n"
                          "#else\n"
                          "pass_1\n"
                          "#endif\n"
                          "#undef foo\n"
                          "#ifndef foo\n"
                          "pass_2\n"
                          "#else\n"
                          "fail_2\n"
                          "#endif\n";
        const char* expected = "\n"
                               "\n"
                               "\n"
                               "\n"
                               "pass_1\n"
                               "\n"
                               "\n"
                               "\n"
                               "pass_2\n"
                               "\n"
                               "\n"
                               "\n";
    
        preprocess(str, expected);
    }
    
    TEST_F(IfTest, MissingExpression)
    {
        const char* str = "#if\n"
                          "#endif\n";
    
        EXPECT_CALL(mDiagnostics,
                    print(pp::Diagnostics::PP_INVALID_EXPRESSION,
                          pp::SourceLocation(0, 1),
                          "syntax error"));
    
        preprocess(str);
    }
    
    TEST_F(IfTest, DivisionByZero)
    {
        const char* str = "#if 1 / (3 - (1 + 2))\n"
                          "#endif\n";
    
        EXPECT_CALL(mDiagnostics,
                    print(pp::Diagnostics::PP_DIVISION_BY_ZERO,
                          pp::SourceLocation(0, 1), "1 / 0"));
    
        preprocess(str);
    }
    
    TEST_F(IfTest, ModuloByZero)
    {
        const char* str = "#if 1 % (3 - (1 + 2))\n"
                          "#endif\n";
    
        EXPECT_CALL(mDiagnostics,
                    print(pp::Diagnostics::PP_DIVISION_BY_ZERO,
                          pp::SourceLocation(0, 1), "1 % 0"));
    
        preprocess(str);
    }
    
    TEST_F(IfTest, DecIntegerOverflow)
    {
        const char* str = "#if 4294967296\n"
                          "#endif\n";
    
        EXPECT_CALL(mDiagnostics,
                    print(pp::Diagnostics::PP_INTEGER_OVERFLOW,
                          pp::SourceLocation(0, 1), "4294967296"));
    
        preprocess(str);
    }
    
    TEST_F(IfTest, OctIntegerOverflow)
    {
        const char* str = "#if 077777777777\n"
                          "#endif\n";
    
        EXPECT_CALL(mDiagnostics,
                    print(pp::Diagnostics::PP_INTEGER_OVERFLOW,
                          pp::SourceLocation(0, 1), "077777777777"));
    
        preprocess(str);
    }
    
    TEST_F(IfTest, HexIntegerOverflow)
    {
        const char* str = "#if 0xfffffffff\n"
                          "#endif\n";
    
        EXPECT_CALL(mDiagnostics,
                    print(pp::Diagnostics::PP_INTEGER_OVERFLOW,
                          pp::SourceLocation(0, 1), "0xfffffffff"));
    
        preprocess(str);
    }
    
    TEST_F(IfTest, UndefinedMacro)
    {
        const char* str = "#if UNDEFINED\n"
                          "#endif\n";
    
        EXPECT_CALL(mDiagnostics,
                    print(pp::Diagnostics::PP_CONDITIONAL_UNEXPECTED_TOKEN,
                          pp::SourceLocation(0, 1),
                          "UNDEFINED"));
    
        preprocess(str);
    }
    
    TEST_F(IfTest, InvalidExpressionIgnoredForExcludedElif)
    {
        const char* str = "#if 1\n"
                          "pass\n"
                          "#elif UNDEFINED\n"
                          "fail\n"
                          "#endif\n";
        const char* expected = "\n"
                               "pass\n"
                               "\n"
                               "\n"
                               "\n";
    
        // No error or warning.
        using testing::_;
        EXPECT_CALL(mDiagnostics, print(_, _, _)).Times(0);
    
        preprocess(str, expected);
    }
    
    TEST_F(IfTest, ElseWithoutIf)
    {
        const char* str = "#else\n";
    
        EXPECT_CALL(mDiagnostics,
                    print(pp::Diagnostics::PP_CONDITIONAL_ELSE_WITHOUT_IF,
                          pp::SourceLocation(0, 1),
                          "else"));
    
        preprocess(str);
    }
    
    TEST_F(IfTest, ElifWithoutIf)
    {
        const char* str = "#elif 1\n";
    
        EXPECT_CALL(mDiagnostics,
                    print(pp::Diagnostics::PP_CONDITIONAL_ELIF_WITHOUT_IF,
                          pp::SourceLocation(0, 1),
                          "elif"));
    
        preprocess(str);
    }
    
    TEST_F(IfTest, EndifWithoutIf)
    {
        const char* str = "#endif\n";
    
        EXPECT_CALL(mDiagnostics,
                    print(pp::Diagnostics::PP_CONDITIONAL_ENDIF_WITHOUT_IF,
                          pp::SourceLocation(0, 1),
                          "endif"));
    
        preprocess(str);
    }
    
    TEST_F(IfTest, ElseAfterElse)
    {
        const char* str = "#if 1\n"
                          "#else\n"
                          "#else\n"
                          "#endif\n";
    
        EXPECT_CALL(mDiagnostics,
                    print(pp::Diagnostics::PP_CONDITIONAL_ELSE_AFTER_ELSE,
                          pp::SourceLocation(0, 3),
                          "else"));
    
        preprocess(str);
    }
    
    TEST_F(IfTest, ElifAfterElse)
    {
        const char* str = "#if 1\n"
                          "#else\n"
                          "#elif 0\n"
                          "#endif\n";
    
        EXPECT_CALL(mDiagnostics,
                    print(pp::Diagnostics::PP_CONDITIONAL_ELIF_AFTER_ELSE,
                          pp::SourceLocation(0, 3),
                          "elif"));
    
        preprocess(str);
    }
    
    TEST_F(IfTest, UnterminatedIf)
    {
        const char* str = "#if 1\n";
    
        EXPECT_CALL(mDiagnostics,
                    print(pp::Diagnostics::PP_CONDITIONAL_UNTERMINATED,
                          pp::SourceLocation(0, 1),
                          "if"));
    
        preprocess(str);
    }
    
    TEST_F(IfTest, UnterminatedIfdef)
    {
        const char* str = "#ifdef foo\n";
    
        EXPECT_CALL(mDiagnostics,
                    print(pp::Diagnostics::PP_CONDITIONAL_UNTERMINATED,
                          pp::SourceLocation(0, 1),
                          "ifdef"));
    
        preprocess(str);
    }
    
    // The preprocessor only allows one expression to follow an #if directive.
    // Supplying two integer expressions should be an error.
    TEST_F(IfTest, ExtraIntExpression)
    {
        const char *str =
            "#if 1 1\n"
            "#endif\n";
    
        EXPECT_CALL(mDiagnostics, print(pp::Diagnostics::PP_CONDITIONAL_UNEXPECTED_TOKEN,
                                        pp::SourceLocation(0, 1), "1"));
    
        preprocess(str);
    }
    
    // The preprocessor only allows one expression to follow an #if directive.
    // Supplying two expressions where one uses a preprocessor define should be an error.
    TEST_F(IfTest, ExtraIdentifierExpression)
    {
        const char *str =
            "#define one 1\n"
            "#if 1 one\n"
            "#endif\n";
    
        EXPECT_CALL(mDiagnostics, print(pp::Diagnostics::PP_CONDITIONAL_UNEXPECTED_TOKEN,
                                        pp::SourceLocation(0, 2), "1"));
    
        preprocess(str);
    }
    
    // Divide by zero that's not evaluated because of short-circuiting should not cause an error.
    TEST_F(IfTest, ShortCircuitedDivideByZero)
    {
        const char *str =
            "#if 1 || (2 / 0)\n"
            "pass\n"
            "#endif\n";
        const char *expected =
            "\n"
            "pass\n"
            "\n";
    
        preprocess(str, expected);
    }
    
    // Undefined identifier that's not evaluated because of short-circuiting should not cause an error.
    TEST_F(IfTest, ShortCircuitedUndefined)
    {
        const char *str =
            "#if 1 || UNDEFINED\n"
            "pass\n"
            "#endif\n";
        const char *expected =
            "\n"
            "pass\n"
            "\n";
    
        preprocess(str, expected);
    }
    
    // Defined operator produced by macro expansion has undefined behavior according to C++ spec,
    // which the GLSL spec references (see C++14 draft spec section 16.1.4), but this behavior is
    // needed for passing dEQP tests, which enforce stricter compatibility between implementations.
    TEST_F(IfTest, DefinedOperatorValidAfterMacroExpansion)
    {
        const char *str =
            "#define foo defined\n"
            "#if !foo bar\n"
            "pass\n"
            "#endif\n";
    
        EXPECT_CALL(mDiagnostics, print(pp::Diagnostics::PP_CONDITIONAL_UNEXPECTED_TOKEN,
                                        pp::SourceLocation(0, 2), "defined"));
        EXPECT_CALL(mDiagnostics, print(pp::Diagnostics::PP_CONDITIONAL_UNEXPECTED_TOKEN,
                                        pp::SourceLocation(0, 2), "bar"));
    
        preprocess(str);
    }
    
    // Defined operator produced by macro expansion has undefined behavior according to C++ spec,
    // which the GLSL spec references (see C++14 draft spec section 16.1.4), but this behavior is
    // needed for passing dEQP tests, which enforce stricter compatibility between implementations.
    TEST_F(IfTest, UnterminatedDefinedInMacro)
    {
        const char *str =
            "#define foo defined(\n"
            "#if foo\n"
            "#endif\n";
    
        EXPECT_CALL(mDiagnostics, print(pp::Diagnostics::PP_CONDITIONAL_UNEXPECTED_TOKEN,
                                        pp::SourceLocation(0, 2), "defined"));
        EXPECT_CALL(mDiagnostics, print(pp::Diagnostics::PP_CONDITIONAL_UNEXPECTED_TOKEN,
                                        pp::SourceLocation(0, 2), "("));
    
        preprocess(str);
    }
    
    // Defined operator produced by macro expansion has undefined behavior according to C++ spec,
    // which the GLSL spec references (see C++14 draft spec section 16.1.4), but this behavior is
    // needed for passing dEQP tests, which enforce stricter compatibility between implementations.
    TEST_F(IfTest, UnterminatedDefinedInMacro2)
    {
        const char *str =
            "#define foo defined(bar\n"
            "#if foo\n"
            "#endif\n";
    
        EXPECT_CALL(mDiagnostics, print(pp::Diagnostics::PP_CONDITIONAL_UNEXPECTED_TOKEN,
                                        pp::SourceLocation(0, 2), "defined"));
        EXPECT_CALL(mDiagnostics, print(pp::Diagnostics::PP_CONDITIONAL_UNEXPECTED_TOKEN,
                                        pp::SourceLocation(0, 2), "("));
    
        preprocess(str);
    }
    
    // Undefined shift: negative shift offset.
    TEST_F(IfTest, BitShiftLeftOperatorNegativeOffset)
    {
        const char *str =
            "#if 2 << -1 == 1\n"
            "foo\n"
            "#endif\n";
    
        EXPECT_CALL(mDiagnostics,
                    print(pp::Diagnostics::PP_UNDEFINED_SHIFT, pp::SourceLocation(0, 1), "2 << -1"));
    
        preprocess(str);
    }
    
    // Undefined shift: shift offset is out of range.
    TEST_F(IfTest, BitShiftLeftOperatorOffset32)
    {
        const char *str =
            "#if 2 << 32 == 1\n"
            "foo\n"
            "#endif\n";
    
        EXPECT_CALL(mDiagnostics,
                    print(pp::Diagnostics::PP_UNDEFINED_SHIFT, pp::SourceLocation(0, 1), "2 << 32"));
    
        preprocess(str);
    }
    
    // Left hand side of shift is negative.
    TEST_F(IfTest, BitShiftLeftOperatorNegativeLHS)
    {
        const char *str =
            "#if (-2) << 1 == -4\n"
            "pass\n"
            "#endif\n";
        const char *expected =
            "\n"
            "pass\n"
            "\n";
    
        preprocess(str, expected);
    }
    
    // Undefined shift: shift offset is out of range.
    TEST_F(IfTest, BitShiftRightOperatorNegativeOffset)
    {
        const char *str =
            "#if 2 >> -1 == 4\n"
            "foo\n"
            "#endif\n";
    
        EXPECT_CALL(mDiagnostics,
                    print(pp::Diagnostics::PP_UNDEFINED_SHIFT, pp::SourceLocation(0, 1), "2 >> -1"));
    
        preprocess(str);
    }
    
    // Undefined shift: shift offset is out of range.
    TEST_F(IfTest, BitShiftRightOperatorOffset32)
    {
        const char *str =
            "#if 2 >> 32 == 0\n"
            "foo\n"
            "#endif\n";
    
        EXPECT_CALL(mDiagnostics,
                    print(pp::Diagnostics::PP_UNDEFINED_SHIFT, pp::SourceLocation(0, 1), "2 >> 32"));
    
        preprocess(str);
    }
    
    // Left hand side of shift is negative.
    TEST_F(IfTest, BitShiftRightOperatorNegativeLHS)
    {
        const char *str =
            "#if (-2) >> 1 == 0x7fffffff\n"
            "pass\n"
            "#endif\n";
        const char *expected =
            "\n"
            "pass\n"
            "\n";
    
        preprocess(str, expected);
    }