diff --git a/clang/include/clang/Tooling/Syntax/Mutations.h b/clang/include/clang/Tooling/Syntax/Mutations.h --- a/clang/include/clang/Tooling/Syntax/Mutations.h +++ b/clang/include/clang/Tooling/Syntax/Mutations.h @@ -31,6 +31,7 @@ /// EXPECTS: S->canModify() == true void removeStatement(syntax::Arena &A, syntax::Statement *S); +void parenthesizeRHS(syntax::Arena &A, syntax::BinaryOperatorExpression *BOE); } // namespace syntax } // namespace clang diff --git a/clang/lib/Tooling/Syntax/Mutations.cpp b/clang/lib/Tooling/Syntax/Mutations.cpp --- a/clang/lib/Tooling/Syntax/Mutations.cpp +++ b/clang/lib/Tooling/Syntax/Mutations.cpp @@ -8,6 +8,7 @@ #include "clang/Tooling/Syntax/Mutations.h" #include "clang/Basic/LLVM.h" #include "clang/Basic/SourceLocation.h" +#include "clang/Basic/TokenKinds.h" #include "clang/Lex/Token.h" #include "clang/Tooling/Core/Replacement.h" #include "clang/Tooling/Syntax/BuildTree.h" @@ -102,3 +103,22 @@ MutationsImpl::replace(S, createEmptyStatement(A)); } + +void syntax::parenthesizeRHS(syntax::Arena &A, + syntax::BinaryOperatorExpression *BOE) { + assert(BOE); + assert(BOE->canModify()); + + auto *RHS = BOE->getRhs(); + auto *BeforeRHS = findPrevious(RHS); + MutationsImpl::remove(RHS); + + auto *NewRHS = + createTree(A, + {{createLeaf(A, tok::l_paren), NodeRole::OpenParen}, + {RHS, NodeRole::SubExpression}, + {createLeaf(A, tok::r_paren), NodeRole::CloseParen}}, + NodeKind::ParenExpression); + + MutationsImpl::addAfter(BeforeRHS, NewRHS, NodeRole::RightHandSide); +} diff --git a/clang/unittests/Tooling/Syntax/MutationsTest.cpp b/clang/unittests/Tooling/Syntax/MutationsTest.cpp --- a/clang/unittests/Tooling/Syntax/MutationsTest.cpp +++ b/clang/unittests/Tooling/Syntax/MutationsTest.cpp @@ -52,6 +52,16 @@ EXPECT_FALSE(S->isOriginal()) << "node removed from tree cannot be marked as original"; }; + + Transformation ParenthesizeRHS = [this](const llvm::Annotations &Input, + TranslationUnit *Root) { + auto *BOE = cast( + nodeByRange(Input.range(), Root)); + ASSERT_TRUE(BOE->canModify()) << "cannot remove a statement"; + syntax::parenthesizeRHS(*Arena, BOE); + EXPECT_FALSE(BOE->isOriginal()) + << "modified node cannot be marked as original"; + }; }; INSTANTIATE_TEST_CASE_P(SyntaxTreeTests, MutationTest, @@ -71,4 +81,22 @@ CheckTransformation(RemoveStatement, "void test() { if (1) [[{}]] else {} }", "void test() { if (1) ; else {} }"); } + +TEST_P(MutationTest, Parenthesize_RHS) { + CheckTransformation(ParenthesizeRHS, R"cpp(void test() { [[1 + 2]]; })cpp", + "void test() { 1 + (2); }"); +} + +TEST_P(MutationTest, Parenthesize_RHS_MACRO) { + CheckTransformation(ParenthesizeRHS, R"cpp( +#define THENUMBER 42 +void test() { + [[1 + (THENUMBER)]]; +})cpp", + R"cpp( +#define THENUMBER 42 +void test() { + 1 + ((THENUMBER)); +})cpp"); +} } // namespace