diff --git a/clang/lib/Lex/DependencyDirectivesSourceMinimizer.cpp b/clang/lib/Lex/DependencyDirectivesSourceMinimizer.cpp --- a/clang/lib/Lex/DependencyDirectivesSourceMinimizer.cpp +++ b/clang/lib/Lex/DependencyDirectivesSourceMinimizer.cpp @@ -763,12 +763,11 @@ if (top() == pp_else) popToken(); - // Strip out "#elif" if they're empty. - while (top() == pp_elif) - popToken(); - - // If "#if" is empty, strip it and skip the "#endif". - if (top() == pp_if || top() == pp_ifdef || top() == pp_ifndef) { + // If "#ifdef" is empty, strip it and skip the "#endif". + // Avoid stripping "#if" / "#elif" as they could contain a "__has_include" + // (either directly, or through a macro expansion) without an "#include" + // inside the "#if/elif", which could affect the dependency output. + if (top() == pp_ifdef || top() == pp_ifndef) { popToken(); skipLine(First, End); return false; diff --git a/clang/test/ClangScanDeps/Inputs/has_include_if_elif.json b/clang/test/ClangScanDeps/Inputs/has_include_if_elif.json new file mode 100644 --- /dev/null +++ b/clang/test/ClangScanDeps/Inputs/has_include_if_elif.json @@ -0,0 +1,7 @@ +[ +{ + "directory": "DIR", + "command": "clang -E DIR/has_include_if_elif2.cpp -IInputs", + "file": "DIR/has_include_if_elif2.cpp" +} +] diff --git a/clang/test/ClangScanDeps/has_include_if_elif.cpp b/clang/test/ClangScanDeps/has_include_if_elif.cpp new file mode 100644 --- /dev/null +++ b/clang/test/ClangScanDeps/has_include_if_elif.cpp @@ -0,0 +1,38 @@ +// RUN: rm -rf %t.dir +// RUN: rm -rf %t.cdb +// RUN: mkdir -p %t.dir +// RUN: cp %s %t.dir/has_include_if_elif2.cpp +// RUN: mkdir %t.dir/Inputs +// RUN: cp %S/Inputs/header.h %t.dir/Inputs/header.h +// RUN: cp %S/Inputs/header.h %t.dir/Inputs/header2.h +// RUN: cp %S/Inputs/header.h %t.dir/Inputs/header3.h +// RUN: cp %S/Inputs/header.h %t.dir/Inputs/header4.h +// RUN: sed -e "s|DIR|%/t.dir|g" %S/Inputs/has_include_if_elif.json > %t.cdb +// +// RUN: clang-scan-deps -compilation-database %t.cdb -j 1 -mode preprocess-minimized-sources | \ +// RUN: FileCheck %s +// RUN: clang-scan-deps -compilation-database %t.cdb -j 1 -mode preprocess | \ +// RUN: FileCheck %s + +#if __has_include("header.h") +#endif + +#if 0 +#elif __has_include("header2.h") +#endif + +#define H3 __has_include("header3.h") +#if H3 +#endif + +#define H4 __has_include("header4.h") + +#if 0 +#elif H4 +#endif + +// CHECK: has_include_if_elif2.cpp +// CHECK-NEXT: Inputs{{/|\\}}header.h +// CHECK-NEXT: Inputs{{/|\\}}header2.h +// CHECK-NEXT: Inputs{{/|\\}}header3.h +// CHECK-NEXT: Inputs{{/|\\}}header4.h diff --git a/clang/unittests/Lex/DependencyDirectivesSourceMinimizerTest.cpp b/clang/unittests/Lex/DependencyDirectivesSourceMinimizerTest.cpp --- a/clang/unittests/Lex/DependencyDirectivesSourceMinimizerTest.cpp +++ b/clang/unittests/Lex/DependencyDirectivesSourceMinimizerTest.cpp @@ -328,12 +328,17 @@ SmallVector Out; ASSERT_FALSE(minimizeSourceToDependencyDirectives("#ifdef A\n" + "void skip();\n" "#elif B\n" "#elif C\n" "#else D\n" "#endif\n", Out)); - EXPECT_STREQ("", Out.data()); + EXPECT_STREQ("#ifdef A\n" + "#elif B\n" + "#elif C\n" + "#endif\n", + Out.data()); } TEST(MinimizeSourceToDependencyDirectivesTest, Pragma) { @@ -507,6 +512,12 @@ for (auto Source : { "#warning \\\n#include \n", "#error \\\n#include \n", + }) { + ASSERT_FALSE(minimizeSourceToDependencyDirectives(Source, Out)); + EXPECT_STREQ("", Out.data()); + } + + for (auto Source : { "#if MACRO\n#warning '\n#endif\n", "#if MACRO\n#warning \"\n#endif\n", "#if MACRO\n#warning /*\n#endif\n", @@ -515,7 +526,7 @@ "#if MACRO\n#error /*\n#endif\n", }) { ASSERT_FALSE(minimizeSourceToDependencyDirectives(Source, Out)); - EXPECT_STREQ("", Out.data()); + EXPECT_STREQ("#if MACRO\n#endif\n", Out.data()); } } @@ -543,7 +554,7 @@ #include )"; ASSERT_FALSE(minimizeSourceToDependencyDirectives(Source, Out)); - EXPECT_STREQ("#include \n", Out.data()); + EXPECT_STREQ("#if DEBUG\n#endif\n#include \n", Out.data()); } TEST(MinimizeSourceToDependencyDirectivesTest, CharacterLiteralPrefixU) {