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