Clang Project

clang_source_code/unittests/CrossTU/CrossTranslationUnitTest.cpp
1//===- unittest/Tooling/CrossTranslationUnitTest.cpp - Tooling unit tests -===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8
9#include "clang/CrossTU/CrossTranslationUnit.h"
10#include "clang/AST/ASTConsumer.h"
11#include "clang/Frontend/FrontendAction.h"
12#include "clang/Tooling/Tooling.h"
13#include "llvm/Support/FileSystem.h"
14#include "llvm/Support/Path.h"
15#include "llvm/Support/ToolOutputFile.h"
16#include "gtest/gtest.h"
17#include <cassert>
18
19namespace clang {
20namespace cross_tu {
21
22namespace {
23
24class CTUASTConsumer : public clang::ASTConsumer {
25public:
26  explicit CTUASTConsumer(clang::CompilerInstance &CIbool *Success)
27      : CTU(CI), Success(Success) {}
28
29  void HandleTranslationUnit(ASTContext &Ctx) {
30    const TranslationUnitDecl *TU = Ctx.getTranslationUnitDecl();
31    const FunctionDecl *FD = nullptr;
32    for (const Decl *D : TU->decls()) {
33      FD = dyn_cast<FunctionDecl>(D);
34      if (FD && FD->getName() == "f")
35        break;
36    }
37     (0) . __assert_fail ("FD && FD->getName() == \"f\"", "/home/seafit/code_projects/clang_source/clang/unittests/CrossTU/CrossTranslationUnitTest.cpp", 37, __PRETTY_FUNCTION__))" file_link="../../../include/assert.h.html#88" macro="true">assert(FD && FD->getName() == "f");
38    bool OrigFDHasBody = FD->hasBody();
39
40    // Prepare the index file and the AST file.
41    int ASTFD;
42    llvm::SmallString<256ASTFileName;
43    ASSERT_FALSE(
44        llvm::sys::fs::createTemporaryFile("f_ast""ast", ASTFD, ASTFileName));
45    llvm::ToolOutputFile ASTFile(ASTFileName, ASTFD);
46
47    int IndexFD;
48    llvm::SmallString<256IndexFileName;
49    ASSERT_FALSE(llvm::sys::fs::createTemporaryFile("index""txt", IndexFD,
50                                                    IndexFileName));
51    llvm::ToolOutputFile IndexFile(IndexFileName, IndexFD);
52    IndexFile.os() << "c:@F@f#I# " << ASTFileName << "\n";
53    IndexFile.os().flush();
54    EXPECT_TRUE(llvm::sys::fs::exists(IndexFileName));
55
56    StringRef SourceText = "int f(int) { return 0; }\n";
57    // This file must exist since the saved ASTFile will reference it.
58    int SourceFD;
59    llvm::SmallString<256SourceFileName;
60    ASSERT_FALSE(llvm::sys::fs::createTemporaryFile("input""cpp", SourceFD,
61                                                    SourceFileName));
62    llvm::ToolOutputFile SourceFile(SourceFileName, SourceFD);
63    SourceFile.os() << SourceText;
64    SourceFile.os().flush();
65    EXPECT_TRUE(llvm::sys::fs::exists(SourceFileName));
66
67    std::unique_ptr<ASTUnitASTWithDefinition =
68        tooling::buildASTFromCode(SourceText, SourceFileName);
69    ASTWithDefinition->Save(ASTFileName.str());
70    EXPECT_TRUE(llvm::sys::fs::exists(ASTFileName));
71
72    // Load the definition from the AST file.
73    llvm::Expected<const FunctionDecl *> NewFDorError =
74        CTU.getCrossTUDefinition(FD, "", IndexFileName);
75    EXPECT_TRUE((bool)NewFDorError);
76    const FunctionDecl *NewFD = *NewFDorError;
77
78    *Success = NewFD && NewFD->hasBody() && !OrigFDHasBody;
79  }
80
81private:
82  CrossTranslationUnitContext CTU;
83  bool *Success;
84};
85
86class CTUAction : public clang::ASTFrontendAction {
87public:
88  CTUAction(bool *Success) : Success(Success) {}
89
90protected:
91  std::unique_ptr<clang::ASTConsumer>
92  CreateASTConsumer(clang::CompilerInstance &CIStringRef) override {
93    return llvm::make_unique<CTUASTConsumer>(CI, Success);
94  }
95
96private:
97  bool *Success;
98};
99
100// end namespace
101
102TEST(CrossTranslationUnit, CanLoadFunctionDefinition) {
103  bool Success = false;
104  EXPECT_TRUE(tooling::runToolOnCode(new CTUAction(&Success), "int f(int);"));
105  EXPECT_TRUE(Success);
106}
107
108TEST(CrossTranslationUnit, IndexFormatCanBeParsed) {
109  llvm::StringMap<std::string> Index;
110  Index["a"] = "/b/f1";
111  Index["c"] = "/d/f2";
112  Index["e"] = "/f/f3";
113  std::string IndexText = createCrossTUIndexString(Index);
114
115  int IndexFD;
116  llvm::SmallString<256IndexFileName;
117  ASSERT_FALSE(llvm::sys::fs::createTemporaryFile("index""txt", IndexFD,
118                                                  IndexFileName));
119  llvm::ToolOutputFile IndexFile(IndexFileName, IndexFD);
120  IndexFile.os() << IndexText;
121  IndexFile.os().flush();
122  EXPECT_TRUE(llvm::sys::fs::exists(IndexFileName));
123  llvm::Expected<llvm::StringMap<std::string>> IndexOrErr =
124      parseCrossTUIndex(IndexFileName, "");
125  EXPECT_TRUE((bool)IndexOrErr);
126  llvm::StringMap<std::string> ParsedIndex = IndexOrErr.get();
127  for (const auto &E : Index) {
128    EXPECT_TRUE(ParsedIndex.count(E.getKey()));
129    EXPECT_EQ(ParsedIndex[E.getKey()], E.getValue());
130  }
131  for (const auto &E : ParsedIndex)
132    EXPECT_TRUE(Index.count(E.getKey()));
133}
134
135TEST(CrossTranslationUnit, CTUDirIsHandledCorrectly) {
136  llvm::StringMap<std::string> Index;
137  Index["a"] = "/b/c/d";
138  std::string IndexText = createCrossTUIndexString(Index);
139
140  int IndexFD;
141  llvm::SmallString<256IndexFileName;
142  ASSERT_FALSE(llvm::sys::fs::createTemporaryFile("index""txt", IndexFD,
143                                                  IndexFileName));
144  llvm::ToolOutputFile IndexFile(IndexFileName, IndexFD);
145  IndexFile.os() << IndexText;
146  IndexFile.os().flush();
147  EXPECT_TRUE(llvm::sys::fs::exists(IndexFileName));
148  llvm::Expected<llvm::StringMap<std::string>> IndexOrErr =
149      parseCrossTUIndex(IndexFileName, "/ctudir");
150  EXPECT_TRUE((bool)IndexOrErr);
151  llvm::StringMap<std::string> ParsedIndex = IndexOrErr.get();
152  EXPECT_EQ(ParsedIndex["a"], "/ctudir/b/c/d");
153}
154
155// end namespace cross_tu
156// end namespace clang
157