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