diff --git a/clang/unittests/Tooling/Syntax/CMakeLists.txt b/clang/unittests/Tooling/Syntax/CMakeLists.txt index 5de7d24e26750..62d1ecc81cadf 100644 --- a/clang/unittests/Tooling/Syntax/CMakeLists.txt +++ b/clang/unittests/Tooling/Syntax/CMakeLists.txt @@ -14,6 +14,7 @@ clang_target_link_libraries(SyntaxTests clangFrontend clangLex clangSerialization + clangTesting clangTooling clangToolingCore clangToolingSyntax diff --git a/clang/unittests/Tooling/Syntax/TreeTest.cpp b/clang/unittests/Tooling/Syntax/TreeTest.cpp index 04786257c434f..705cb872c6a4b 100644 --- a/clang/unittests/Tooling/Syntax/TreeTest.cpp +++ b/clang/unittests/Tooling/Syntax/TreeTest.cpp @@ -17,6 +17,7 @@ #include "clang/Frontend/FrontendAction.h" #include "clang/Frontend/TextDiagnosticPrinter.h" #include "clang/Lex/PreprocessorOptions.h" +#include "clang/Testing/CommandLineArgs.h" #include "clang/Tooling/Core/Replacement.h" #include "clang/Tooling/Syntax/BuildTree.h" #include "clang/Tooling/Syntax/Mutations.h" @@ -25,6 +26,7 @@ #include "clang/Tooling/Tooling.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/StringExtras.h" #include "llvm/ADT/StringRef.h" #include "llvm/Support/Casting.h" #include "llvm/Support/Error.h" @@ -45,12 +47,71 @@ static llvm::ArrayRef tokens(syntax::Node *N) { T->lastLeaf()->token() + 1); } -class SyntaxTreeTest : public ::testing::Test { +struct TestClangConfig { + TestLanguage Language; + std::string Target; + + bool isCXX() const { + return Language == Lang_CXX || Language == Lang_CXX11 || + Language == Lang_CXX14 || Language == Lang_CXX17 || + Language == Lang_CXX2a; + } + + bool isCXX11OrLater() const { + return Language == Lang_CXX11 || Language == Lang_CXX14 || + Language == Lang_CXX17 || Language == Lang_CXX2a; + } + + bool hasDelayedTemplateParsing() const { + return Target == "x86_64-pc-win32-msvc"; + } + + std::vector getCommandLineArgs() const { + std::vector Result = getCommandLineArgsForTesting(Language); + Result.push_back("-target"); + Result.push_back(Target); + return Result; + } + + std::string toString() const { + std::string Result; + llvm::raw_string_ostream OS(Result); + OS << "{ Language=" << Language << ", Target=" << Target << " }"; + return OS.str(); + } + + friend std::ostream &operator<<(std::ostream &OS, + const TestClangConfig &ClangConfig) { + return OS << ClangConfig.toString(); + } + + static std::vector &allConfigs() { + static std::vector all_configs = []() { + std::vector all_configs; + for (TestLanguage lang : {Lang_C, Lang_C89, Lang_CXX, Lang_CXX11, + Lang_CXX14, Lang_CXX17, Lang_CXX2a}) { + TestClangConfig config; + config.Language = lang; + config.Target = "x86_64-pc-linux-gnu"; + all_configs.push_back(config); + + // Windows target is interesting to test because it enables + // `-fdelayed-template-parsing`. + config.Target = "x86_64-pc-win32-msvc"; + all_configs.push_back(config); + } + return all_configs; + }(); + return all_configs; + } +}; + +class SyntaxTreeTest : public ::testing::Test, + public ::testing::WithParamInterface { protected: // Build a syntax tree for the code. - syntax::TranslationUnit * - buildTree(llvm::StringRef Code, - const std::string &Target = "x86_64-pc-linux-gnu") { + syntax::TranslationUnit *buildTree(llvm::StringRef Code, + const TestClangConfig &ClangConfig) { // FIXME: this code is almost the identical to the one in TokensTest. Share // it. class BuildSyntaxTree : public ASTConsumer { @@ -105,11 +166,19 @@ class SyntaxTreeTest : public ::testing::Test { diag::Severity::Ignored, SourceLocation()); // Prepare to run a compiler. - std::vector Args = { - "syntax-test", "-target", Target.c_str(), - FileName, "-fsyntax-only", "-std=c++17", + std::vector Args = { + "syntax-test", + "-fsyntax-only", }; - Invocation = createInvocationFromCommandLine(Args, Diags, FS); + llvm::copy(ClangConfig.getCommandLineArgs(), std::back_inserter(Args)); + Args.push_back(FileName); + + std::vector ArgsCStr; + for (const std::string &arg : Args) { + ArgsCStr.push_back(arg.c_str()); + } + + Invocation = createInvocationFromCommandLine(ArgsCStr, Diags, FS); assert(Invocation); Invocation->getFrontendOpts().DisableFree = false; Invocation->getPreprocessorOpts().addRemappedFile( @@ -133,32 +202,15 @@ class SyntaxTreeTest : public ::testing::Test { return Root; } - void expectTreeDumpEqual(StringRef Code, StringRef Tree, - bool RunWithDelayedTemplateParsing = true) { + void expectTreeDumpEqual(StringRef Code, StringRef Tree) { + SCOPED_TRACE(llvm::join(GetParam().getCommandLineArgs(), " ")); SCOPED_TRACE(Code); - std::string Expected = Tree.trim().str(); - - // We want to run the test with -fdelayed-template-parsing enabled and - // disabled, therefore we use these representative targets that differ in - // the default value. - // We are not passing -fdelayed-template-parsing directly but we are using - // the `-target` to improve coverage and discover differences in behavior - // early. - for (const std::string Target : - {"x86_64-pc-linux-gnu", "x86_64-pc-win32-msvc"}) { - if (!RunWithDelayedTemplateParsing && - Target == "x86_64-pc-win32-msvc") { - continue; - } - auto *Root = buildTree(Code, Target); - EXPECT_EQ(Diags->getClient()->getNumErrors(), 0u) - << "Source file has syntax errors, they were printed to the test log"; - std::string Actual = std::string(StringRef(Root->dump(*Arena)).trim()); - EXPECT_EQ(Expected, Actual) - << "for target " << Target << " the resulting dump is:\n" - << Actual; - } + auto *Root = buildTree(Code, GetParam()); + EXPECT_EQ(Diags->getClient()->getNumErrors(), 0u) + << "Source file has syntax errors, they were printed to the test log"; + std::string Actual = std::string(StringRef(Root->dump(*Arena)).trim()); + EXPECT_EQ(Tree.trim().str(), Actual); } // Adds a file to the test VFS. @@ -206,7 +258,7 @@ class SyntaxTreeTest : public ::testing::Test { std::unique_ptr Arena; }; -TEST_F(SyntaxTreeTest, Simple) { +TEST_P(SyntaxTreeTest, Simple) { expectTreeDumpEqual( R"cpp( int main() {} @@ -237,7 +289,7 @@ void foo() {} )txt"); } -TEST_F(SyntaxTreeTest, SimpleVariable) { +TEST_P(SyntaxTreeTest, SimpleVariable) { expectTreeDumpEqual( R"cpp( int a; @@ -261,7 +313,7 @@ int b = 42; )txt"); } -TEST_F(SyntaxTreeTest, SimpleFunction) { +TEST_P(SyntaxTreeTest, SimpleFunction) { expectTreeDumpEqual( R"cpp( void foo(int a, int b) {} @@ -290,12 +342,12 @@ void foo(int a, int b) {} )txt"); } -TEST_F(SyntaxTreeTest, If) { +TEST_P(SyntaxTreeTest, If) { expectTreeDumpEqual( R"cpp( int main() { - if (true) {} - if (true) {} else if (false) {} + if (1) {} + if (1) {} else if (0) {} } )cpp", R"txt( @@ -313,7 +365,7 @@ int main() { | |-if | |-( | |-UnknownExpression - | | `-true + | | `-1 | |-) | `-CompoundStatement | |-{ @@ -322,7 +374,7 @@ int main() { | |-if | |-( | |-UnknownExpression - | | `-true + | | `-1 | |-) | |-CompoundStatement | | |-{ @@ -332,7 +384,7 @@ int main() { | |-if | |-( | |-UnknownExpression - | | `-false + | | `-0 | |-) | `-CompoundStatement | |-{ @@ -341,7 +393,7 @@ int main() { )txt"); } -TEST_F(SyntaxTreeTest, For) { +TEST_P(SyntaxTreeTest, For) { expectTreeDumpEqual( R"cpp( void test() { @@ -372,12 +424,16 @@ void test() { )txt"); } -TEST_F(SyntaxTreeTest, RangeBasedFor) { +TEST_P(SyntaxTreeTest, RangeBasedFor) { + if (!GetParam().isCXX11OrLater()) { + return; + } expectTreeDumpEqual( R"cpp( void test() { int a[3]; - for (int x : a) ; + for (int x : a) + ; } )cpp", R"txt( @@ -419,7 +475,7 @@ void test() { )txt"); } -TEST_F(SyntaxTreeTest, DeclarationStatement) { +TEST_P(SyntaxTreeTest, DeclarationStatement) { expectTreeDumpEqual("void test() { int a = 10; }", R"txt( *: TranslationUnit @@ -445,11 +501,11 @@ TEST_F(SyntaxTreeTest, DeclarationStatement) { )txt"); } -TEST_F(SyntaxTreeTest, Switch) { +TEST_P(SyntaxTreeTest, Switch) { expectTreeDumpEqual( R"cpp( void test() { - switch (true) { + switch (1) { case 0: default:; } @@ -470,7 +526,7 @@ void test() { | |-switch | |-( | |-UnknownExpression - | | `-true + | | `-1 | |-) | `-CompoundStatement | |-{ @@ -489,11 +545,11 @@ void test() { )txt"); } -TEST_F(SyntaxTreeTest, While) { +TEST_P(SyntaxTreeTest, While) { expectTreeDumpEqual( R"cpp( void test() { - while (true) { continue; break; } + while (1) { continue; break; } } )cpp", R"txt( @@ -511,7 +567,7 @@ void test() { | |-while | |-( | |-UnknownExpression - | | `-true + | | `-1 | |-) | `-CompoundStatement | |-{ @@ -526,7 +582,7 @@ void test() { )txt"); } -TEST_F(SyntaxTreeTest, UnhandledStatement) { +TEST_P(SyntaxTreeTest, UnhandledStatement) { // Unhandled statements should end up as 'unknown statement'. // This example uses a 'label statement', which does not yet have a syntax // counterpart. @@ -554,14 +610,14 @@ TEST_F(SyntaxTreeTest, UnhandledStatement) { )txt"); } -TEST_F(SyntaxTreeTest, Expressions) { +TEST_P(SyntaxTreeTest, Expressions) { // expressions should be wrapped in 'ExpressionStatement' when they appear // in a statement position. expectTreeDumpEqual( R"cpp( void test() { test(); - if (true) test(); else test(); + if (1) test(); else test(); } )cpp", R"txt( @@ -586,7 +642,7 @@ void test() { | |-if | |-( | |-UnknownExpression - | | `-true + | | `-1 | |-) | |-ExpressionStatement | | |-UnknownExpression @@ -607,7 +663,7 @@ void test() { )txt"); } -TEST_F(SyntaxTreeTest, PostfixUnaryOperator) { +TEST_P(SyntaxTreeTest, PostfixUnaryOperator) { expectTreeDumpEqual( R"cpp( void test(int a) { @@ -646,7 +702,11 @@ void test(int a) { )txt"); } -TEST_F(SyntaxTreeTest, PrefixUnaryOperator) { +TEST_P(SyntaxTreeTest, PrefixUnaryOperator) { + if (!GetParam().isCXX()) { + // TODO: Split parts that depend on C++ into a separate test. + return; + } expectTreeDumpEqual( R"cpp( void test(int a, int *ap, bool b) { @@ -762,7 +822,11 @@ void test(int a, int *ap, bool b) { )txt"); } -TEST_F(SyntaxTreeTest, BinaryOperator) { +TEST_P(SyntaxTreeTest, BinaryOperator) { + if (!GetParam().isCXX()) { + // TODO: Split parts that depend on C++ into a separate test. + return; + } expectTreeDumpEqual( R"cpp( void test(int a) { @@ -880,7 +944,7 @@ void test(int a) { )txt"); } -TEST_F(SyntaxTreeTest, NestedBinaryOperator) { +TEST_P(SyntaxTreeTest, NestedBinaryOperator) { expectTreeDumpEqual( R"cpp( void test(int a, int b) { @@ -993,7 +1057,10 @@ void test(int a, int b) { )txt"); } -TEST_F(SyntaxTreeTest, UserDefinedBinaryOperator) { +TEST_P(SyntaxTreeTest, UserDefinedBinaryOperator) { + if (!GetParam().isCXX()) { + return; + } expectTreeDumpEqual( R"cpp( struct X { @@ -1121,7 +1188,7 @@ void test(X x, X y) { )txt"); } -TEST_F(SyntaxTreeTest, MultipleDeclaratorsGrouping) { +TEST_P(SyntaxTreeTest, MultipleDeclaratorsGrouping) { expectTreeDumpEqual( R"cpp( int *a, b; int *c, d; @@ -1149,7 +1216,7 @@ TEST_F(SyntaxTreeTest, MultipleDeclaratorsGrouping) { )txt"); } -TEST_F(SyntaxTreeTest, MultipleDeclaratorsGroupingTypedef) { +TEST_P(SyntaxTreeTest, MultipleDeclaratorsGroupingTypedef) { expectTreeDumpEqual( R"cpp( typedef int *a, b; @@ -1169,7 +1236,7 @@ TEST_F(SyntaxTreeTest, MultipleDeclaratorsGroupingTypedef) { )txt"); } -TEST_F(SyntaxTreeTest, MultipleDeclaratorsInsideStatement) { +TEST_P(SyntaxTreeTest, MultipleDeclaratorsInsideStatement) { expectTreeDumpEqual( R"cpp( void foo() { @@ -1213,7 +1280,10 @@ void foo() { )txt"); } -TEST_F(SyntaxTreeTest, Namespaces) { +TEST_P(SyntaxTreeTest, Namespaces) { + if (!GetParam().isCXX()) { + return; + } expectTreeDumpEqual( R"cpp( namespace a { namespace b {} } @@ -1254,7 +1324,10 @@ namespace foo = a; )txt"); } -TEST_F(SyntaxTreeTest, UsingDirective) { +TEST_P(SyntaxTreeTest, UsingDirective) { + if (!GetParam().isCXX()) { + return; + } expectTreeDumpEqual( R"cpp( namespace ns {} @@ -1276,7 +1349,10 @@ using namespace ::ns; )txt"); } -TEST_F(SyntaxTreeTest, UsingDeclaration) { +TEST_P(SyntaxTreeTest, UsingDeclaration) { + if (!GetParam().isCXX()) { + return; + } expectTreeDumpEqual( R"cpp( namespace ns { int a; } @@ -1303,7 +1379,7 @@ using ns::a; )txt"); } -TEST_F(SyntaxTreeTest, FreeStandingClasses) { +TEST_P(SyntaxTreeTest, FreeStandingClasses) { // Free-standing classes, must live inside a SimpleDeclaration. expectTreeDumpEqual( R"cpp( @@ -1354,7 +1430,15 @@ struct {} *a1; )txt"); } -TEST_F(SyntaxTreeTest, Templates) { +TEST_P(SyntaxTreeTest, Templates) { + if (!GetParam().isCXX()) { + return; + } + if (GetParam().hasDelayedTemplateParsing()) { + // FIXME: Make this test work on Windows by generating the expected syntax + // tree when `-fdelayed-template-parsing` is active. + return; + } expectTreeDumpEqual( R"cpp( template struct cls {}; @@ -1408,13 +1492,13 @@ template int fun() {} `-CompoundStatement |-{ `-} -)txt", - // FIXME: Make this test work on windows by generating the expected Syntax - // tree when -fdelayed-template-parsing is active. - /*RunWithDelayedTemplateParsing=*/false); +)txt"); } -TEST_F(SyntaxTreeTest, NestedTemplates) { +TEST_P(SyntaxTreeTest, NestedTemplates) { + if (!GetParam().isCXX()) { + return; + } expectTreeDumpEqual( R"cpp( template @@ -1456,7 +1540,10 @@ struct X { )txt"); } -TEST_F(SyntaxTreeTest, Templates2) { +TEST_P(SyntaxTreeTest, Templates2) { + if (!GetParam().isCXX()) { + return; + } expectTreeDumpEqual( R"cpp( template struct X { struct Y; }; @@ -1502,7 +1589,10 @@ template struct X::Y {}; )txt"); } -TEST_F(SyntaxTreeTest, TemplatesUsingUsing) { +TEST_P(SyntaxTreeTest, TemplatesUsingUsing) { + if (!GetParam().isCXX()) { + return; + } expectTreeDumpEqual( R"cpp( template struct X { @@ -1541,7 +1631,10 @@ template struct X { )txt"); } -TEST_F(SyntaxTreeTest, ExplicitTemplateInstantations) { +TEST_P(SyntaxTreeTest, ExplicitTemplateInstantations) { + if (!GetParam().isCXX()) { + return; + } expectTreeDumpEqual( R"cpp( template struct X {}; @@ -1618,7 +1711,10 @@ extern template struct X; )txt"); } -TEST_F(SyntaxTreeTest, UsingType) { +TEST_P(SyntaxTreeTest, UsingType) { + if (!GetParam().isCXX()) { + return; + } expectTreeDumpEqual( R"cpp( using type = int; @@ -1634,7 +1730,7 @@ using type = int; )txt"); } -TEST_F(SyntaxTreeTest, EmptyDeclaration) { +TEST_P(SyntaxTreeTest, EmptyDeclaration) { expectTreeDumpEqual( R"cpp( ; @@ -1646,7 +1742,10 @@ TEST_F(SyntaxTreeTest, EmptyDeclaration) { )txt"); } -TEST_F(SyntaxTreeTest, StaticAssert) { +TEST_P(SyntaxTreeTest, StaticAssert) { + if (!GetParam().isCXX11OrLater()) { + return; + } expectTreeDumpEqual( R"cpp( static_assert(true, "message"); @@ -1674,7 +1773,10 @@ static_assert(true); )txt"); } -TEST_F(SyntaxTreeTest, ExternC) { +TEST_P(SyntaxTreeTest, ExternC) { + if (!GetParam().isCXX()) { + return; + } expectTreeDumpEqual( R"cpp( extern "C" int a; @@ -1708,7 +1810,7 @@ extern "C" { int b; int c; } )txt"); } -TEST_F(SyntaxTreeTest, NonModifiableNodes) { +TEST_P(SyntaxTreeTest, NonModifiableNodes) { // Some nodes are non-modifiable, they are marked with 'I:'. expectTreeDumpEqual( R"cpp( @@ -1749,7 +1851,7 @@ void test() { )txt"); } -TEST_F(SyntaxTreeTest, ModifiableNodes) { +TEST_P(SyntaxTreeTest, ModifiableNodes) { // All nodes can be mutated. expectTreeDumpEqual( R"cpp( @@ -1795,7 +1897,7 @@ void test() { )txt"); } -TEST_F(SyntaxTreeTest, ArraySubscriptsInDeclarators) { +TEST_P(SyntaxTreeTest, ArraySubscriptsInDeclarators) { expectTreeDumpEqual( R"cpp( int a[10]; @@ -1858,7 +1960,11 @@ int c[] = {1,2,3}; `-; )txt"); } -TEST_F(SyntaxTreeTest, ParameterListsInDeclarators) { +TEST_P(SyntaxTreeTest, ParameterListsInDeclarators) { + if (!GetParam().isCXX()) { + // TODO: Split parts that depend on C++ into a separate test. + return; + } expectTreeDumpEqual( R"cpp( struct Test { @@ -1979,7 +2085,11 @@ struct Test { )txt"); } -TEST_F(SyntaxTreeTest, TrailingConst) { +TEST_P(SyntaxTreeTest, TrailingConst) { + if (!GetParam().isCXX()) { + // TODO: Split parts that depend on C++ into a separate test. + return; + } expectTreeDumpEqual( R"cpp( struct X { @@ -2006,7 +2116,10 @@ struct X { )txt"); } -TEST_F(SyntaxTreeTest, TrailingReturn) { +TEST_P(SyntaxTreeTest, TrailingReturn) { + if (!GetParam().isCXX11OrLater()) { + return; + } expectTreeDumpEqual( R"cpp( auto foo() -> int; @@ -2027,7 +2140,11 @@ auto foo() -> int; )txt"); } -TEST_F(SyntaxTreeTest, ExceptionSpecification) { +TEST_P(SyntaxTreeTest, ExceptionSpecification) { + if (!GetParam().isCXX11OrLater()) { + // TODO: Split parts that depend on C++11 into a separate test. + return; + } expectTreeDumpEqual( R"cpp( int a() noexcept; @@ -2072,7 +2189,7 @@ int c() throw(); )txt"); } -TEST_F(SyntaxTreeTest, DeclaratorsInParentheses) { +TEST_P(SyntaxTreeTest, DeclaratorsInParentheses) { expectTreeDumpEqual( R"cpp( int (a); @@ -2130,7 +2247,7 @@ int *(d)(int); )txt"); } -TEST_F(SyntaxTreeTest, ConstVolatileQualifiers) { +TEST_P(SyntaxTreeTest, ConstVolatileQualifiers) { expectTreeDumpEqual( R"cpp( const int west = -1; @@ -2184,7 +2301,10 @@ const int const *const *volatile b; )txt"); } -TEST_F(SyntaxTreeTest, RangesOfDeclaratorsWithTrailingReturnTypes) { +TEST_P(SyntaxTreeTest, RangesOfDeclaratorsWithTrailingReturnTypes) { + if (!GetParam().isCXX11OrLater()) { + return; + } expectTreeDumpEqual( R"cpp( auto foo() -> auto(*)(int) -> double*; @@ -2220,7 +2340,10 @@ auto foo() -> auto(*)(int) -> double*; )txt"); } -TEST_F(SyntaxTreeTest, MemberPointers) { +TEST_P(SyntaxTreeTest, MemberPointers) { + if (!GetParam().isCXX()) { + return; + } expectTreeDumpEqual( R"cpp( struct X {}; @@ -2257,7 +2380,7 @@ const int X::* b; )txt"); } -TEST_F(SyntaxTreeTest, ComplexDeclarator) { +TEST_P(SyntaxTreeTest, ComplexDeclarator) { expectTreeDumpEqual( R"cpp( void x(char a, short (*b)(int)); @@ -2293,7 +2416,7 @@ void x(char a, short (*b)(int)); )txt"); } -TEST_F(SyntaxTreeTest, ComplexDeclarator2) { +TEST_P(SyntaxTreeTest, ComplexDeclarator2) { expectTreeDumpEqual( R"cpp( void x(char a, short (*b)(int), long (**c)(long long)); @@ -2345,13 +2468,17 @@ void x(char a, short (*b)(int), long (**c)(long long)); )txt"); } -TEST_F(SyntaxTreeTest, Mutations) { +TEST_P(SyntaxTreeTest, Mutations) { + if (!GetParam().isCXX11OrLater()) { + return; + } + using Transformation = std::function; auto CheckTransformation = [this](std::string Input, std::string Expected, Transformation Transform) -> void { llvm::Annotations Source(Input); - auto *Root = buildTree(Source.code()); + auto *Root = buildTree(Source.code(), GetParam()); Transform(Source, Root); @@ -2388,8 +2515,8 @@ TEST_F(SyntaxTreeTest, Mutations) { CheckTransformation(C.first, C.second, RemoveStatement); } -TEST_F(SyntaxTreeTest, SynthesizedNodes) { - buildTree(""); +TEST_P(SyntaxTreeTest, SynthesizedNodes) { + buildTree("", GetParam()); auto *C = syntax::createPunctuation(*Arena, tok::comma); ASSERT_NE(C, nullptr); @@ -2405,4 +2532,7 @@ TEST_F(SyntaxTreeTest, SynthesizedNodes) { EXPECT_TRUE(S->isDetached()); } +INSTANTIATE_TEST_CASE_P(SyntaxTreeTests, SyntaxTreeTest, + testing::ValuesIn(TestClangConfig::allConfigs())); + } // namespace