932 lines
		
	
	
		
			19 KiB
		
	
	
	
		
			C++
		
	
	
	
			
		
		
	
	
			932 lines
		
	
	
		
			19 KiB
		
	
	
	
		
			C++
		
	
	
	
//===- 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 =
 | 
						|
        std::string(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
 |