diff --git a/clang/lib/Format/UnwrappedLineParser.cpp b/clang/lib/Format/UnwrappedLineParser.cpp --- a/clang/lib/Format/UnwrappedLineParser.cpp +++ b/clang/lib/Format/UnwrappedLineParser.cpp @@ -1212,9 +1212,12 @@ // If a potential include guard has an #else, it's not an include guard. if (IncludeGuard == IG_Defined && PPBranchLevel == 0) IncludeGuard = IG_Rejected; + // Don't crash when there is an #else without an #if. + assert(PPBranchLevel >= -1); + if (PPBranchLevel <= -1) + conditionalCompilationStart(/*Unreachable=*/true); conditionalCompilationAlternative(); - if (PPBranchLevel > -1) - --PPBranchLevel; + --PPBranchLevel; parsePPUnknown(); ++PPBranchLevel; } diff --git a/clang/unittests/Format/FormatTest.cpp b/clang/unittests/Format/FormatTest.cpp --- a/clang/unittests/Format/FormatTest.cpp +++ b/clang/unittests/Format/FormatTest.cpp @@ -5193,6 +5193,47 @@ verifyNoCrash("a={0,1\n#if a\n#else\n;\n#endif\n}"); verifyNoCrash("#if a\na(\n#else\n#endif\n) a {a,b,c,d,f,g};"); verifyNoCrash("#ifdef A\n a(\n #else\n #endif\n) = []() { \n)}"); + verifyNoCrash("#else\n" + "a;\n" + "#if X\n" + "b;\n" + "#endif\n" + "#endif"); + verifyNoCrash("#elif X\n" + "a;\n" + "#endif\n" + "#ifdef X\n" + "b;\n" + "#endif"); + verifyNoCrash("#endif\n" + "a;\n" + "#else"); + verifyNoCrash("#else\n" + "a;\n" + "#elif\n" + "b;"); + verifyNoCrash("#endif\n" + "a;\n" + "#if X\n" + "a;\n" + "#elif\n" + "b;\n" + "#endif\n" + "#endif\n" + "#endif\n" + "#endif\n" + "#endif\n" + "#endif"); + std::function FormatBadBranches = + [&](const std::string &Prefix, unsigned MoreLines) { + const std::string Directives[] = {"#if X\n", "#else X\n", "#endif\n"}; + if (MoreLines == 0) + verifyNoCrash(Prefix); + else + for (const auto &Part : Directives) + FormatBadBranches(Prefix + Part, MoreLines - 1); + }; + FormatBadBranches("", 6); } TEST_F(FormatTest, MacrosWithoutTrailingSemicolon) {