diff --git a/clang/include/clang/Tooling/Syntax/BuildTree.h b/clang/include/clang/Tooling/Syntax/BuildTree.h --- a/clang/include/clang/Tooling/Syntax/BuildTree.h +++ b/clang/include/clang/Tooling/Syntax/BuildTree.h @@ -56,6 +56,16 @@ /// unmodifiable returns `nullptr`. syntax::Node *deepCopy(syntax::Arena &A, const syntax::Node *N); +/// Creates a completely independent copy of `N` with its macros expanded. +/// +/// Like `deepCopy` the copy is: +/// * Detached, i.e. `Parent == NextSibling == nullptr` and +/// `Role == Detached`. +/// * Synthesized, i.e. `Original == false`. +/// +/// However, `N` might have descendants coming from macros, in this case those +/// macros are expanded. +syntax::Node *deepCopyExpandingMacros(syntax::Arena &A, const syntax::Node *N); } // namespace syntax } // namespace clang #endif diff --git a/clang/lib/Tooling/Syntax/Synthesis.cpp b/clang/lib/Tooling/Syntax/Synthesis.cpp --- a/clang/lib/Tooling/Syntax/Synthesis.cpp +++ b/clang/lib/Tooling/Syntax/Synthesis.cpp @@ -218,8 +218,13 @@ return true; } -syntax::Node *deepCopyImpl(syntax::Arena &A, const syntax::Node *N) { +} // namespace + +syntax::Node *clang::syntax::deepCopyExpandingMacros(syntax::Arena &A, + const syntax::Node *N) { if (const auto *L = dyn_cast(N)) + // For original nodes `L->getToken()` gives us the expanded token, + // thus we implicitly expand any macros here. return createLeaf(A, L->getToken()->kind(), L->getToken()->text(A.getSourceManager())); @@ -227,17 +232,16 @@ std::vector> Children; for (const auto *Child = T->getFirstChild(); Child; Child = Child->getNextSibling()) - Children.push_back({deepCopyImpl(A, Child), Child->getRole()}); + Children.push_back({deepCopyExpandingMacros(A, Child), Child->getRole()}); return createTree(A, Children, N->getKind()); } -} // namespace syntax::Node *clang::syntax::deepCopy(syntax::Arena &A, const Node *N) { if (!canModifyAllDescendants(N)) return nullptr; - return deepCopyImpl(A, N); + return deepCopyExpandingMacros(A, N); } syntax::EmptyStatement *clang::syntax::createEmptyStatement(syntax::Arena &A) { diff --git a/clang/unittests/Tooling/Syntax/SynthesisTest.cpp b/clang/unittests/Tooling/Syntax/SynthesisTest.cpp --- a/clang/unittests/Tooling/Syntax/SynthesisTest.cpp +++ b/clang/unittests/Tooling/Syntax/SynthesisTest.cpp @@ -217,7 +217,53 @@ GetParam()); auto *Copy = deepCopy(*Arena, OriginalTree); - EXPECT_TRUE(Copy == nullptr); + EXPECT_EQ(Copy, nullptr); +} + +TEST_P(SynthesisTest, DeepCopy_MacroExpanding) { + auto *OriginalTree = buildTree(R"cpp( +#define HALF_IF if (1+ +#define HALF_IF_2 1) {} +void test() { + HALF_IF HALF_IF_2 else {} +})cpp", + GetParam()); + + auto *Copy = deepCopyExpandingMacros(*Arena, OriginalTree); + + // The syntax tree stores expanded Tokens already, as a result we can only see + // the macro expansion when computing replacements. The dump does show that + // nodes are `modifiable` now. + EXPECT_TRUE(treeDumpEqual(Copy, R"txt( +TranslationUnit Detached synthesized +`-SimpleDeclaration synthesized + |-'void' synthesized + |-SimpleDeclarator Declarator synthesized + | |-'test' synthesized + | `-ParametersAndQualifiers synthesized + | |-'(' OpenParen synthesized + | `-')' CloseParen synthesized + `-CompoundStatement synthesized + |-'{' OpenParen synthesized + |-IfStatement Statement synthesized + | |-'if' IntroducerKeyword synthesized + | |-'(' synthesized + | |-BinaryOperatorExpression synthesized + | | |-IntegerLiteralExpression LeftHandSide synthesized + | | | `-'1' LiteralToken synthesized + | | |-'+' OperatorToken synthesized + | | `-IntegerLiteralExpression RightHandSide synthesized + | | `-'1' LiteralToken synthesized + | |-')' synthesized + | |-CompoundStatement ThenStatement synthesized + | | |-'{' OpenParen synthesized + | | `-'}' CloseParen synthesized + | |-'else' ElseKeyword synthesized + | `-CompoundStatement ElseStatement synthesized + | |-'{' OpenParen synthesized + | `-'}' CloseParen synthesized + `-'}' CloseParen synthesized + )txt")); } TEST_P(SynthesisTest, Statement_EmptyStatement) {