summaryrefslogtreecommitdiffstats
path: root/gnu/llvm/clang/unittests/Tooling/Syntax/TreeTest.cpp
diff options
context:
space:
mode:
authorpatrick <patrick@openbsd.org>2020-08-03 14:31:31 +0000
committerpatrick <patrick@openbsd.org>2020-08-03 14:31:31 +0000
commite5dd70708596ae51455a0ffa086a00c5b29f8583 (patch)
tree5d676f27b570bacf71e786c3b5cff3e6f6679b59 /gnu/llvm/clang/unittests/Tooling/Syntax/TreeTest.cpp
parentImport LLVM 10.0.0 release including clang, lld and lldb. (diff)
downloadwireguard-openbsd-e5dd70708596ae51455a0ffa086a00c5b29f8583.tar.xz
wireguard-openbsd-e5dd70708596ae51455a0ffa086a00c5b29f8583.zip
Import LLVM 10.0.0 release including clang, lld and lldb.
ok hackroom tested by plenty
Diffstat (limited to 'gnu/llvm/clang/unittests/Tooling/Syntax/TreeTest.cpp')
-rw-r--r--gnu/llvm/clang/unittests/Tooling/Syntax/TreeTest.cpp930
1 files changed, 930 insertions, 0 deletions
diff --git a/gnu/llvm/clang/unittests/Tooling/Syntax/TreeTest.cpp b/gnu/llvm/clang/unittests/Tooling/Syntax/TreeTest.cpp
new file mode 100644
index 00000000000..42d77d89353
--- /dev/null
+++ b/gnu/llvm/clang/unittests/Tooling/Syntax/TreeTest.cpp
@@ -0,0 +1,930 @@
+//===- TreeTest.cpp -------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/Tooling/Syntax/Tree.h"
+#include "clang/AST/ASTConsumer.h"
+#include "clang/AST/Decl.h"
+#include "clang/AST/Stmt.h"
+#include "clang/Basic/LLVM.h"
+#include "clang/Basic/TokenKinds.h"
+#include "clang/Frontend/CompilerInstance.h"
+#include "clang/Frontend/CompilerInvocation.h"
+#include "clang/Frontend/FrontendAction.h"
+#include "clang/Lex/PreprocessorOptions.h"
+#include "clang/Tooling/Core/Replacement.h"
+#include "clang/Tooling/Syntax/BuildTree.h"
+#include "clang/Tooling/Syntax/Mutations.h"
+#include "clang/Tooling/Syntax/Nodes.h"
+#include "clang/Tooling/Syntax/Tokens.h"
+#include "clang/Tooling/Tooling.h"
+#include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Support/Casting.h"
+#include "llvm/Support/Error.h"
+#include "llvm/Testing/Support/Annotations.h"
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+#include <cstdlib>
+
+using namespace clang;
+
+namespace {
+static llvm::ArrayRef<syntax::Token> tokens(syntax::Node *N) {
+ assert(N->isOriginal() && "tokens of modified nodes are not well-defined");
+ if (auto *L = dyn_cast<syntax::Leaf>(N))
+ return llvm::makeArrayRef(L->token(), 1);
+ auto *T = cast<syntax::Tree>(N);
+ return llvm::makeArrayRef(T->firstLeaf()->token(),
+ T->lastLeaf()->token() + 1);
+}
+
+class SyntaxTreeTest : public ::testing::Test {
+protected:
+ // Build a syntax tree for the code.
+ syntax::TranslationUnit *buildTree(llvm::StringRef Code) {
+ // FIXME: this code is almost the identical to the one in TokensTest. Share
+ // it.
+ class BuildSyntaxTree : public ASTConsumer {
+ public:
+ BuildSyntaxTree(syntax::TranslationUnit *&Root,
+ std::unique_ptr<syntax::Arena> &Arena,
+ std::unique_ptr<syntax::TokenCollector> Tokens)
+ : Root(Root), Arena(Arena), Tokens(std::move(Tokens)) {
+ assert(this->Tokens);
+ }
+
+ void HandleTranslationUnit(ASTContext &Ctx) override {
+ Arena = std::make_unique<syntax::Arena>(Ctx.getSourceManager(),
+ Ctx.getLangOpts(),
+ std::move(*Tokens).consume());
+ Tokens = nullptr; // make sure we fail if this gets called twice.
+ Root = syntax::buildSyntaxTree(*Arena, *Ctx.getTranslationUnitDecl());
+ }
+
+ private:
+ syntax::TranslationUnit *&Root;
+ std::unique_ptr<syntax::Arena> &Arena;
+ std::unique_ptr<syntax::TokenCollector> Tokens;
+ };
+
+ class BuildSyntaxTreeAction : public ASTFrontendAction {
+ public:
+ BuildSyntaxTreeAction(syntax::TranslationUnit *&Root,
+ std::unique_ptr<syntax::Arena> &Arena)
+ : Root(Root), Arena(Arena) {}
+
+ std::unique_ptr<ASTConsumer>
+ CreateASTConsumer(CompilerInstance &CI, StringRef InFile) override {
+ // We start recording the tokens, ast consumer will take on the result.
+ auto Tokens =
+ std::make_unique<syntax::TokenCollector>(CI.getPreprocessor());
+ return std::make_unique<BuildSyntaxTree>(Root, Arena,
+ std::move(Tokens));
+ }
+
+ private:
+ syntax::TranslationUnit *&Root;
+ std::unique_ptr<syntax::Arena> &Arena;
+ };
+
+ constexpr const char *FileName = "./input.cpp";
+ FS->addFile(FileName, time_t(), llvm::MemoryBuffer::getMemBufferCopy(""));
+ if (!Diags->getClient())
+ Diags->setClient(new IgnoringDiagConsumer);
+ // Prepare to run a compiler.
+ std::vector<const char *> Args = {"syntax-test", "-std=c++11",
+ "-fsyntax-only", FileName};
+ Invocation = createInvocationFromCommandLine(Args, Diags, FS);
+ assert(Invocation);
+ Invocation->getFrontendOpts().DisableFree = false;
+ Invocation->getPreprocessorOpts().addRemappedFile(
+ FileName, llvm::MemoryBuffer::getMemBufferCopy(Code).release());
+ CompilerInstance Compiler;
+ Compiler.setInvocation(Invocation);
+ Compiler.setDiagnostics(Diags.get());
+ Compiler.setFileManager(FileMgr.get());
+ Compiler.setSourceManager(SourceMgr.get());
+
+ syntax::TranslationUnit *Root = nullptr;
+ BuildSyntaxTreeAction Recorder(Root, this->Arena);
+ if (!Compiler.ExecuteAction(Recorder)) {
+ ADD_FAILURE() << "failed to run the frontend";
+ std::abort();
+ }
+ return Root;
+ }
+
+ // Adds a file to the test VFS.
+ void addFile(llvm::StringRef Path, llvm::StringRef Contents) {
+ if (!FS->addFile(Path, time_t(),
+ llvm::MemoryBuffer::getMemBufferCopy(Contents))) {
+ ADD_FAILURE() << "could not add a file to VFS: " << Path;
+ }
+ }
+
+ /// Finds the deepest node in the tree that covers exactly \p R.
+ /// FIXME: implement this efficiently and move to public syntax tree API.
+ syntax::Node *nodeByRange(llvm::Annotations::Range R, syntax::Node *Root) {
+ llvm::ArrayRef<syntax::Token> Toks = tokens(Root);
+
+ if (Toks.front().location().isFileID() &&
+ Toks.back().location().isFileID() &&
+ syntax::Token::range(*SourceMgr, Toks.front(), Toks.back()) ==
+ syntax::FileRange(SourceMgr->getMainFileID(), R.Begin, R.End))
+ return Root;
+
+ auto *T = dyn_cast<syntax::Tree>(Root);
+ if (!T)
+ return nullptr;
+ for (auto *C = T->firstChild(); C != nullptr; C = C->nextSibling()) {
+ if (auto *Result = nodeByRange(R, C))
+ return Result;
+ }
+ return nullptr;
+ }
+
+ // Data fields.
+ llvm::IntrusiveRefCntPtr<DiagnosticsEngine> Diags =
+ new DiagnosticsEngine(new DiagnosticIDs, new DiagnosticOptions);
+ IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> FS =
+ new llvm::vfs::InMemoryFileSystem;
+ llvm::IntrusiveRefCntPtr<FileManager> FileMgr =
+ new FileManager(FileSystemOptions(), FS);
+ llvm::IntrusiveRefCntPtr<SourceManager> SourceMgr =
+ new SourceManager(*Diags, *FileMgr);
+ std::shared_ptr<CompilerInvocation> Invocation;
+ // Set after calling buildTree().
+ std::unique_ptr<syntax::Arena> Arena;
+};
+
+TEST_F(SyntaxTreeTest, Basic) {
+ std::pair</*Input*/ std::string, /*Expected*/ std::string> Cases[] = {
+ {
+ R"cpp(
+int main() {}
+void foo() {}
+ )cpp",
+ R"txt(
+*: TranslationUnit
+|-SimpleDeclaration
+| |-int
+| |-main
+| |-(
+| |-)
+| `-CompoundStatement
+| |-{
+| `-}
+`-SimpleDeclaration
+ |-void
+ |-foo
+ |-(
+ |-)
+ `-CompoundStatement
+ |-{
+ `-}
+)txt"},
+ // if.
+ {
+ R"cpp(
+int main() {
+ if (true) {}
+ if (true) {} else if (false) {}
+}
+ )cpp",
+ R"txt(
+*: TranslationUnit
+`-SimpleDeclaration
+ |-int
+ |-main
+ |-(
+ |-)
+ `-CompoundStatement
+ |-{
+ |-IfStatement
+ | |-if
+ | |-(
+ | |-UnknownExpression
+ | | `-true
+ | |-)
+ | `-CompoundStatement
+ | |-{
+ | `-}
+ |-IfStatement
+ | |-if
+ | |-(
+ | |-UnknownExpression
+ | | `-true
+ | |-)
+ | |-CompoundStatement
+ | | |-{
+ | | `-}
+ | |-else
+ | `-IfStatement
+ | |-if
+ | |-(
+ | |-UnknownExpression
+ | | `-false
+ | |-)
+ | `-CompoundStatement
+ | |-{
+ | `-}
+ `-}
+ )txt"},
+ // for.
+ {R"cpp(
+void test() {
+ for (;;) {}
+}
+)cpp",
+ R"txt(
+*: TranslationUnit
+`-SimpleDeclaration
+ |-void
+ |-test
+ |-(
+ |-)
+ `-CompoundStatement
+ |-{
+ |-ForStatement
+ | |-for
+ | |-(
+ | |-;
+ | |-;
+ | |-)
+ | `-CompoundStatement
+ | |-{
+ | `-}
+ `-}
+ )txt"},
+ // declaration statement.
+ {"void test() { int a = 10; }",
+ R"txt(
+*: TranslationUnit
+`-SimpleDeclaration
+ |-void
+ |-test
+ |-(
+ |-)
+ `-CompoundStatement
+ |-{
+ |-DeclarationStatement
+ | |-SimpleDeclaration
+ | | |-int
+ | | |-a
+ | | |-=
+ | | `-UnknownExpression
+ | | `-10
+ | `-;
+ `-}
+)txt"},
+ {"void test() { ; }", R"txt(
+*: TranslationUnit
+`-SimpleDeclaration
+ |-void
+ |-test
+ |-(
+ |-)
+ `-CompoundStatement
+ |-{
+ |-EmptyStatement
+ | `-;
+ `-}
+)txt"},
+ // switch, case and default.
+ {R"cpp(
+void test() {
+ switch (true) {
+ case 0:
+ default:;
+ }
+}
+)cpp",
+ R"txt(
+*: TranslationUnit
+`-SimpleDeclaration
+ |-void
+ |-test
+ |-(
+ |-)
+ `-CompoundStatement
+ |-{
+ |-SwitchStatement
+ | |-switch
+ | |-(
+ | |-UnknownExpression
+ | | `-true
+ | |-)
+ | `-CompoundStatement
+ | |-{
+ | |-CaseStatement
+ | | |-case
+ | | |-UnknownExpression
+ | | | `-0
+ | | |-:
+ | | `-DefaultStatement
+ | | |-default
+ | | |-:
+ | | `-EmptyStatement
+ | | `-;
+ | `-}
+ `-}
+)txt"},
+ // while.
+ {R"cpp(
+void test() {
+ while (true) { continue; break; }
+}
+)cpp",
+ R"txt(
+*: TranslationUnit
+`-SimpleDeclaration
+ |-void
+ |-test
+ |-(
+ |-)
+ `-CompoundStatement
+ |-{
+ |-WhileStatement
+ | |-while
+ | |-(
+ | |-UnknownExpression
+ | | `-true
+ | |-)
+ | `-CompoundStatement
+ | |-{
+ | |-ContinueStatement
+ | | |-continue
+ | | `-;
+ | |-BreakStatement
+ | | |-break
+ | | `-;
+ | `-}
+ `-}
+)txt"},
+ // return.
+ {R"cpp(
+int test() { return 1; }
+ )cpp",
+ R"txt(
+*: TranslationUnit
+`-SimpleDeclaration
+ |-int
+ |-test
+ |-(
+ |-)
+ `-CompoundStatement
+ |-{
+ |-ReturnStatement
+ | |-return
+ | |-UnknownExpression
+ | | `-1
+ | `-;
+ `-}
+)txt"},
+ // Range-based for.
+ {R"cpp(
+void test() {
+ int a[3];
+ for (int x : a) ;
+}
+ )cpp",
+ R"txt(
+*: TranslationUnit
+`-SimpleDeclaration
+ |-void
+ |-test
+ |-(
+ |-)
+ `-CompoundStatement
+ |-{
+ |-DeclarationStatement
+ | |-SimpleDeclaration
+ | | |-int
+ | | |-a
+ | | |-[
+ | | |-UnknownExpression
+ | | | `-3
+ | | `-]
+ | `-;
+ |-RangeBasedForStatement
+ | |-for
+ | |-(
+ | |-SimpleDeclaration
+ | | |-int
+ | | |-x
+ | | `-:
+ | |-UnknownExpression
+ | | `-a
+ | |-)
+ | `-EmptyStatement
+ | `-;
+ `-}
+ )txt"},
+ // Unhandled statements should end up as 'unknown statement'.
+ // This example uses a 'label statement', which does not yet have a syntax
+ // counterpart.
+ {"void main() { foo: return 100; }", R"txt(
+*: TranslationUnit
+`-SimpleDeclaration
+ |-void
+ |-main
+ |-(
+ |-)
+ `-CompoundStatement
+ |-{
+ |-UnknownStatement
+ | |-foo
+ | |-:
+ | `-ReturnStatement
+ | |-return
+ | |-UnknownExpression
+ | | `-100
+ | `-;
+ `-}
+)txt"},
+ // expressions should be wrapped in 'ExpressionStatement' when they appear
+ // in a statement position.
+ {R"cpp(
+void test() {
+ test();
+ if (true) test(); else test();
+}
+ )cpp",
+ R"txt(
+*: TranslationUnit
+`-SimpleDeclaration
+ |-void
+ |-test
+ |-(
+ |-)
+ `-CompoundStatement
+ |-{
+ |-ExpressionStatement
+ | |-UnknownExpression
+ | | |-test
+ | | |-(
+ | | `-)
+ | `-;
+ |-IfStatement
+ | |-if
+ | |-(
+ | |-UnknownExpression
+ | | `-true
+ | |-)
+ | |-ExpressionStatement
+ | | |-UnknownExpression
+ | | | |-test
+ | | | |-(
+ | | | `-)
+ | | `-;
+ | |-else
+ | `-ExpressionStatement
+ | |-UnknownExpression
+ | | |-test
+ | | |-(
+ | | `-)
+ | `-;
+ `-}
+)txt"},
+ // Multiple declarators group into a single SimpleDeclaration.
+ {R"cpp(
+ int *a, b;
+ )cpp",
+ R"txt(
+*: TranslationUnit
+`-SimpleDeclaration
+ |-int
+ |-*
+ |-a
+ |-,
+ |-b
+ `-;
+ )txt"},
+ {R"cpp(
+ typedef int *a, b;
+ )cpp",
+ R"txt(
+*: TranslationUnit
+`-SimpleDeclaration
+ |-typedef
+ |-int
+ |-*
+ |-a
+ |-,
+ |-b
+ `-;
+ )txt"},
+ // Multiple declarators inside a statement.
+ {R"cpp(
+void foo() {
+ int *a, b;
+ typedef int *ta, tb;
+}
+ )cpp",
+ R"txt(
+*: TranslationUnit
+`-SimpleDeclaration
+ |-void
+ |-foo
+ |-(
+ |-)
+ `-CompoundStatement
+ |-{
+ |-DeclarationStatement
+ | |-SimpleDeclaration
+ | | |-int
+ | | |-*
+ | | |-a
+ | | |-,
+ | | `-b
+ | `-;
+ |-DeclarationStatement
+ | |-SimpleDeclaration
+ | | |-typedef
+ | | |-int
+ | | |-*
+ | | |-ta
+ | | |-,
+ | | `-tb
+ | `-;
+ `-}
+ )txt"},
+ {R"cpp(
+namespace a { namespace b {} }
+namespace a::b {}
+namespace {}
+
+namespace foo = a;
+ )cpp",
+ R"txt(
+*: TranslationUnit
+|-NamespaceDefinition
+| |-namespace
+| |-a
+| |-{
+| |-NamespaceDefinition
+| | |-namespace
+| | |-b
+| | |-{
+| | `-}
+| `-}
+|-NamespaceDefinition
+| |-namespace
+| |-a
+| |-::
+| |-b
+| |-{
+| `-}
+|-NamespaceDefinition
+| |-namespace
+| |-{
+| `-}
+`-NamespaceAliasDefinition
+ |-namespace
+ |-foo
+ |-=
+ |-a
+ `-;
+)txt"},
+ // Free-standing classes, must live inside a SimpleDeclaration.
+ {R"cpp(
+sturct X;
+struct X {};
+
+struct Y *y1;
+struct Y {} *y2;
+
+struct {} *a1;
+ )cpp",
+ R"txt(
+*: TranslationUnit
+|-SimpleDeclaration
+| |-sturct
+| |-X
+| `-;
+|-SimpleDeclaration
+| |-struct
+| |-X
+| |-{
+| |-}
+| `-;
+|-SimpleDeclaration
+| |-struct
+| |-Y
+| |-*
+| |-y1
+| `-;
+|-SimpleDeclaration
+| |-struct
+| |-Y
+| |-{
+| |-}
+| |-*
+| |-y2
+| `-;
+`-SimpleDeclaration
+ |-struct
+ |-{
+ |-}
+ |-*
+ |-a1
+ `-;
+)txt"},
+ {R"cpp(
+namespace ns {}
+using namespace ::ns;
+ )cpp",
+ R"txt(
+*: TranslationUnit
+|-NamespaceDefinition
+| |-namespace
+| |-ns
+| |-{
+| `-}
+`-UsingNamespaceDirective
+ |-using
+ |-namespace
+ |-::
+ |-ns
+ `-;
+ )txt"},
+ {R"cpp(
+namespace ns { int a; }
+using ns::a;
+ )cpp",
+ R"txt(
+*: TranslationUnit
+|-NamespaceDefinition
+| |-namespace
+| |-ns
+| |-{
+| |-SimpleDeclaration
+| | |-int
+| | |-a
+| | `-;
+| `-}
+`-UsingDeclaration
+ |-using
+ |-ns
+ |-::
+ |-a
+ `-;
+ )txt"},
+ {R"cpp(
+template <class T> struct X {
+ using T::foo;
+ using typename T::bar;
+};
+ )cpp",
+ R"txt(
+*: TranslationUnit
+`-UnknownDeclaration
+ |-template
+ |-<
+ |-UnknownDeclaration
+ | |-class
+ | `-T
+ |->
+ `-SimpleDeclaration
+ |-struct
+ |-X
+ |-{
+ |-UsingDeclaration
+ | |-using
+ | |-T
+ | |-::
+ | |-foo
+ | `-;
+ |-UsingDeclaration
+ | |-using
+ | |-typename
+ | |-T
+ | |-::
+ | |-bar
+ | `-;
+ |-}
+ `-;
+ )txt"},
+ {R"cpp(
+using type = int;
+ )cpp",
+ R"txt(
+*: TranslationUnit
+`-TypeAliasDeclaration
+ |-using
+ |-type
+ |-=
+ |-int
+ `-;
+ )txt"},
+ {R"cpp(
+;
+ )cpp",
+ R"txt(
+*: TranslationUnit
+`-EmptyDeclaration
+ `-;
+ )txt"},
+ {R"cpp(
+static_assert(true, "message");
+static_assert(true);
+ )cpp",
+ R"txt(
+*: TranslationUnit
+|-StaticAssertDeclaration
+| |-static_assert
+| |-(
+| |-UnknownExpression
+| | `-true
+| |-,
+| |-UnknownExpression
+| | `-"message"
+| |-)
+| `-;
+`-StaticAssertDeclaration
+ |-static_assert
+ |-(
+ |-UnknownExpression
+ | `-true
+ |-)
+ `-;
+ )txt"},
+ {R"cpp(
+extern "C" int a;
+extern "C" { int b; int c; }
+ )cpp",
+ R"txt(
+*: TranslationUnit
+|-LinkageSpecificationDeclaration
+| |-extern
+| |-"C"
+| `-SimpleDeclaration
+| |-int
+| |-a
+| `-;
+`-LinkageSpecificationDeclaration
+ |-extern
+ |-"C"
+ |-{
+ |-SimpleDeclaration
+ | |-int
+ | |-b
+ | `-;
+ |-SimpleDeclaration
+ | |-int
+ | |-c
+ | `-;
+ `-}
+ )txt"},
+ // Some nodes are non-modifiable, they are marked with 'I:'.
+ {R"cpp(
+#define HALF_IF if (1+
+#define HALF_IF_2 1) {}
+void test() {
+ HALF_IF HALF_IF_2 else {}
+})cpp",
+ R"txt(
+*: TranslationUnit
+`-SimpleDeclaration
+ |-void
+ |-test
+ |-(
+ |-)
+ `-CompoundStatement
+ |-{
+ |-IfStatement
+ | |-I: if
+ | |-I: (
+ | |-I: UnknownExpression
+ | | |-I: 1
+ | | |-I: +
+ | | `-I: 1
+ | |-I: )
+ | |-I: CompoundStatement
+ | | |-I: {
+ | | `-I: }
+ | |-else
+ | `-CompoundStatement
+ | |-{
+ | `-}
+ `-}
+ )txt"},
+ // All nodes can be mutated.
+ {R"cpp(
+#define OPEN {
+#define CLOSE }
+
+void test() {
+ OPEN
+ 1;
+ CLOSE
+
+ OPEN
+ 2;
+ }
+}
+)cpp",
+ R"txt(
+*: TranslationUnit
+`-SimpleDeclaration
+ |-void
+ |-test
+ |-(
+ |-)
+ `-CompoundStatement
+ |-{
+ |-CompoundStatement
+ | |-{
+ | |-ExpressionStatement
+ | | |-UnknownExpression
+ | | | `-1
+ | | `-;
+ | `-}
+ |-CompoundStatement
+ | |-{
+ | |-ExpressionStatement
+ | | |-UnknownExpression
+ | | | `-2
+ | | `-;
+ | `-}
+ `-}
+ )txt"},
+ };
+
+ for (const auto &T : Cases) {
+ SCOPED_TRACE(T.first);
+
+ auto *Root = buildTree(T.first);
+ std::string Expected = llvm::StringRef(T.second).trim().str();
+ std::string Actual = llvm::StringRef(Root->dump(*Arena)).trim();
+ EXPECT_EQ(Expected, Actual) << "the resulting dump is:\n" << Actual;
+ }
+}
+
+TEST_F(SyntaxTreeTest, Mutations) {
+ using Transformation = std::function<void(
+ const llvm::Annotations & /*Input*/, syntax::TranslationUnit * /*Root*/)>;
+ auto CheckTransformation = [this](std::string Input, std::string Expected,
+ Transformation Transform) -> void {
+ llvm::Annotations Source(Input);
+ auto *Root = buildTree(Source.code());
+
+ Transform(Source, Root);
+
+ auto Replacements = syntax::computeReplacements(*Arena, *Root);
+ auto Output = tooling::applyAllReplacements(Source.code(), Replacements);
+ if (!Output) {
+ ADD_FAILURE() << "could not apply replacements: "
+ << llvm::toString(Output.takeError());
+ return;
+ }
+
+ EXPECT_EQ(Expected, *Output) << "input is:\n" << Input;
+ };
+
+ // Removes the selected statement. Input should have exactly one selected
+ // range and it should correspond to a single statement.
+ auto RemoveStatement = [this](const llvm::Annotations &Input,
+ syntax::TranslationUnit *TU) {
+ auto *S = cast<syntax::Statement>(nodeByRange(Input.range(), TU));
+ ASSERT_TRUE(S->canModify()) << "cannot remove a statement";
+ syntax::removeStatement(*Arena, S);
+ EXPECT_TRUE(S->isDetached());
+ EXPECT_FALSE(S->isOriginal())
+ << "node removed from tree cannot be marked as original";
+ };
+
+ std::vector<std::pair<std::string /*Input*/, std::string /*Expected*/>>
+ Cases = {
+ {"void test() { [[100+100;]] test(); }", "void test() { test(); }"},
+ {"void test() { if (true) [[{}]] else {} }",
+ "void test() { if (true) ; else {} }"},
+ {"void test() { [[;]] }", "void test() { }"}};
+ for (const auto &C : Cases)
+ CheckTransformation(C.first, C.second, RemoveStatement);
+}
+
+TEST_F(SyntaxTreeTest, SynthesizedNodes) {
+ buildTree("");
+
+ auto *C = syntax::createPunctuation(*Arena, tok::comma);
+ ASSERT_NE(C, nullptr);
+ EXPECT_EQ(C->token()->kind(), tok::comma);
+ EXPECT_TRUE(C->canModify());
+ EXPECT_FALSE(C->isOriginal());
+ EXPECT_TRUE(C->isDetached());
+
+ auto *S = syntax::createEmptyStatement(*Arena);
+ ASSERT_NE(S, nullptr);
+ EXPECT_TRUE(S->canModify());
+ EXPECT_FALSE(S->isOriginal());
+ EXPECT_TRUE(S->isDetached());
+}
+
+} // namespace