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 @@ -45,17 +45,13 @@ // Synthesis of Syntax Nodes syntax::EmptyStatement *createEmptyStatement(syntax::Arena &A); -/// Creates a completely independent copy of `N` (a deep copy). +/// Creates a completely independent copy of `N` with its macros expanded. /// /// The copy is: /// * Detached, i.e. `Parent == NextSibling == nullptr` and /// `Role == Detached`. /// * Synthesized, i.e. `Original == false`. -/// -/// `N` might be backed by source code but if any descendants of `N` are -/// unmodifiable returns `nullptr`. -syntax::Node *deepCopy(syntax::Arena &A, const syntax::Node *N); - +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 @@ -201,25 +201,11 @@ return T; } -namespace { -bool canModifyAllDescendants(const syntax::Node *N) { - if (const auto *L = dyn_cast(N)) - return L->canModify(); - - const auto *T = cast(N); - - if (!T->canModify()) - return false; - for (const auto *Child = T->getFirstChild(); Child; - Child = Child->getNextSibling()) - if (!canModifyAllDescendants(Child)) - return false; - - return true; -} - -syntax::Node *deepCopyImpl(syntax::Arena &A, const syntax::Node *N) { +syntax::Node *clang::syntax::deepCopyExpandingMacros(syntax::Arena &A, + const syntax::Node *N) { if (const auto *L = dyn_cast(N)) + // `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,18 +213,10 @@ 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); -} syntax::EmptyStatement *clang::syntax::createEmptyStatement(syntax::Arena &A) { return cast( 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) {