blob: 86ede5e319cb485020ec0b9feaba65a83c93e8d5 [file] [log] [blame]
Gabor Horvath6254bf42017-09-22 11:11:01 +00001//===- unittest/Tooling/CrossTranslationUnitTest.cpp - Tooling unit tests -===//
2//
Chandler Carruth324f9182019-01-19 08:50:56 +00003// 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
Gabor Horvath6254bf42017-09-22 11:11:01 +00006//
7//===----------------------------------------------------------------------===//
8
9#include "clang/CrossTU/CrossTranslationUnit.h"
Endre Fulopfbc4d7b2019-07-08 12:37:10 +000010#include "clang/Frontend/CompilerInstance.h"
Gabor Horvath6254bf42017-09-22 11:11:01 +000011#include "clang/AST/ASTConsumer.h"
12#include "clang/Frontend/FrontendAction.h"
13#include "clang/Tooling/Tooling.h"
Gabor Horvath6254bf42017-09-22 11:11:01 +000014#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
20namespace clang {
21namespace cross_tu {
22
23namespace {
24
25class CTUASTConsumer : public clang::ASTConsumer {
26public:
27 explicit CTUASTConsumer(clang::CompilerInstance &CI, bool *Success)
28 : CTU(CI), Success(Success) {}
29
30 void HandleTranslationUnit(ASTContext &Ctx) {
Balazs Kerib1b90622019-07-24 10:16:37 +000031 auto FindFInTU = [](const TranslationUnitDecl *TU) {
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 return FD;
39 };
40
Gabor Horvath6254bf42017-09-22 11:11:01 +000041 const TranslationUnitDecl *TU = Ctx.getTranslationUnitDecl();
Balazs Kerib1b90622019-07-24 10:16:37 +000042 const FunctionDecl *FD = FindFInTU(TU);
Gabor Horvath6254bf42017-09-22 11:11:01 +000043 assert(FD && FD->getName() == "f");
44 bool OrigFDHasBody = FD->hasBody();
45
46 // Prepare the index file and the AST file.
47 int ASTFD;
48 llvm::SmallString<256> ASTFileName;
49 ASSERT_FALSE(
50 llvm::sys::fs::createTemporaryFile("f_ast", "ast", ASTFD, ASTFileName));
Reid Kleckner38808932017-09-23 01:04:42 +000051 llvm::ToolOutputFile ASTFile(ASTFileName, ASTFD);
Gabor Horvath6254bf42017-09-22 11:11:01 +000052
53 int IndexFD;
54 llvm::SmallString<256> IndexFileName;
55 ASSERT_FALSE(llvm::sys::fs::createTemporaryFile("index", "txt", IndexFD,
56 IndexFileName));
Reid Kleckner38808932017-09-23 01:04:42 +000057 llvm::ToolOutputFile IndexFile(IndexFileName, IndexFD);
Gabor Horvath6254bf42017-09-22 11:11:01 +000058 IndexFile.os() << "c:@F@f#I# " << ASTFileName << "\n";
59 IndexFile.os().flush();
60 EXPECT_TRUE(llvm::sys::fs::exists(IndexFileName));
61
62 StringRef SourceText = "int f(int) { return 0; }\n";
63 // This file must exist since the saved ASTFile will reference it.
64 int SourceFD;
65 llvm::SmallString<256> SourceFileName;
66 ASSERT_FALSE(llvm::sys::fs::createTemporaryFile("input", "cpp", SourceFD,
67 SourceFileName));
Reid Kleckner38808932017-09-23 01:04:42 +000068 llvm::ToolOutputFile SourceFile(SourceFileName, SourceFD);
Gabor Horvath6254bf42017-09-22 11:11:01 +000069 SourceFile.os() << SourceText;
70 SourceFile.os().flush();
71 EXPECT_TRUE(llvm::sys::fs::exists(SourceFileName));
72
73 std::unique_ptr<ASTUnit> ASTWithDefinition =
74 tooling::buildASTFromCode(SourceText, SourceFileName);
75 ASTWithDefinition->Save(ASTFileName.str());
76 EXPECT_TRUE(llvm::sys::fs::exists(ASTFileName));
77
78 // Load the definition from the AST file.
Endre Fulopfbc4d7b2019-07-08 12:37:10 +000079 llvm::Expected<const FunctionDecl *> NewFDorError = handleExpected(
80 CTU.getCrossTUDefinition(FD, "", IndexFileName, false),
81 []() { return nullptr; }, [](IndexError &) {});
Gabor Horvath6254bf42017-09-22 11:11:01 +000082
Endre Fulopfbc4d7b2019-07-08 12:37:10 +000083 if (NewFDorError) {
84 const FunctionDecl *NewFD = *NewFDorError;
85 *Success = NewFD && NewFD->hasBody() && !OrigFDHasBody;
Balazs Kerib1b90622019-07-24 10:16:37 +000086
87 if (NewFD) {
88 // Check GetImportedFromSourceLocation.
89 llvm::Optional<std::pair<SourceLocation, ASTUnit *>> SLocResult =
90 CTU.getImportedFromSourceLocation(NewFD->getLocation());
91 EXPECT_TRUE(SLocResult);
92 if (SLocResult) {
93 SourceLocation OrigSLoc = (*SLocResult).first;
94 ASTUnit *OrigUnit = (*SLocResult).second;
95 // OrigUnit is created internally by CTU (is not the
96 // ASTWithDefinition).
97 TranslationUnitDecl *OrigTU =
98 OrigUnit->getASTContext().getTranslationUnitDecl();
99 const FunctionDecl *FDWithDefinition = FindFInTU(OrigTU);
100 EXPECT_TRUE(FDWithDefinition);
101 if (FDWithDefinition) {
102 EXPECT_EQ(FDWithDefinition->getName(), "f");
103 EXPECT_TRUE(FDWithDefinition->isThisDeclarationADefinition());
104 EXPECT_EQ(OrigSLoc, FDWithDefinition->getLocation());
105 }
106 }
107 }
Endre Fulopfbc4d7b2019-07-08 12:37:10 +0000108 }
Gabor Horvath6254bf42017-09-22 11:11:01 +0000109 }
110
111private:
112 CrossTranslationUnitContext CTU;
113 bool *Success;
114};
115
116class CTUAction : public clang::ASTFrontendAction {
117public:
Endre Fulopfbc4d7b2019-07-08 12:37:10 +0000118 CTUAction(bool *Success, unsigned OverrideLimit)
119 : Success(Success), OverrideLimit(OverrideLimit) {}
Gabor Horvath6254bf42017-09-22 11:11:01 +0000120
121protected:
122 std::unique_ptr<clang::ASTConsumer>
123 CreateASTConsumer(clang::CompilerInstance &CI, StringRef) override {
Endre Fulopfbc4d7b2019-07-08 12:37:10 +0000124 CI.getAnalyzerOpts()->CTUImportThreshold = OverrideLimit;
Jonas Devlieghere49a6b092019-08-14 23:04:18 +0000125 return std::make_unique<CTUASTConsumer>(CI, Success);
Gabor Horvath6254bf42017-09-22 11:11:01 +0000126 }
127
128private:
129 bool *Success;
Endre Fulopfbc4d7b2019-07-08 12:37:10 +0000130 const unsigned OverrideLimit;
Gabor Horvath6254bf42017-09-22 11:11:01 +0000131};
132
133} // end namespace
134
135TEST(CrossTranslationUnit, CanLoadFunctionDefinition) {
136 bool Success = false;
Dmitri Gribenkob6cbdf12019-08-30 09:29:34 +0000137 EXPECT_TRUE(tooling::runToolOnCode(std::make_unique<CTUAction>(&Success, 1u),
138 "int f(int);"));
Gabor Horvath6254bf42017-09-22 11:11:01 +0000139 EXPECT_TRUE(Success);
140}
141
Endre Fulopfbc4d7b2019-07-08 12:37:10 +0000142TEST(CrossTranslationUnit, RespectsLoadThreshold) {
143 bool Success = false;
Dmitri Gribenkob6cbdf12019-08-30 09:29:34 +0000144 EXPECT_TRUE(tooling::runToolOnCode(std::make_unique<CTUAction>(&Success, 0u),
145 "int f(int);"));
Endre Fulopfbc4d7b2019-07-08 12:37:10 +0000146 EXPECT_FALSE(Success);
147}
148
Gabor Horvath6254bf42017-09-22 11:11:01 +0000149TEST(CrossTranslationUnit, IndexFormatCanBeParsed) {
150 llvm::StringMap<std::string> Index;
Gabor Horvath224b1ed2017-10-27 12:53:37 +0000151 Index["a"] = "/b/f1";
152 Index["c"] = "/d/f2";
153 Index["e"] = "/f/f3";
Gabor Horvath6254bf42017-09-22 11:11:01 +0000154 std::string IndexText = createCrossTUIndexString(Index);
155
156 int IndexFD;
157 llvm::SmallString<256> IndexFileName;
158 ASSERT_FALSE(llvm::sys::fs::createTemporaryFile("index", "txt", IndexFD,
159 IndexFileName));
Reid Kleckner38808932017-09-23 01:04:42 +0000160 llvm::ToolOutputFile IndexFile(IndexFileName, IndexFD);
Gabor Horvath6254bf42017-09-22 11:11:01 +0000161 IndexFile.os() << IndexText;
162 IndexFile.os().flush();
163 EXPECT_TRUE(llvm::sys::fs::exists(IndexFileName));
164 llvm::Expected<llvm::StringMap<std::string>> IndexOrErr =
165 parseCrossTUIndex(IndexFileName, "");
166 EXPECT_TRUE((bool)IndexOrErr);
167 llvm::StringMap<std::string> ParsedIndex = IndexOrErr.get();
168 for (const auto &E : Index) {
169 EXPECT_TRUE(ParsedIndex.count(E.getKey()));
170 EXPECT_EQ(ParsedIndex[E.getKey()], E.getValue());
171 }
172 for (const auto &E : ParsedIndex)
173 EXPECT_TRUE(Index.count(E.getKey()));
174}
175
Gabor Horvath224b1ed2017-10-27 12:53:37 +0000176TEST(CrossTranslationUnit, CTUDirIsHandledCorrectly) {
177 llvm::StringMap<std::string> Index;
178 Index["a"] = "/b/c/d";
179 std::string IndexText = createCrossTUIndexString(Index);
180
181 int IndexFD;
182 llvm::SmallString<256> IndexFileName;
183 ASSERT_FALSE(llvm::sys::fs::createTemporaryFile("index", "txt", IndexFD,
184 IndexFileName));
185 llvm::ToolOutputFile IndexFile(IndexFileName, IndexFD);
186 IndexFile.os() << IndexText;
187 IndexFile.os().flush();
188 EXPECT_TRUE(llvm::sys::fs::exists(IndexFileName));
189 llvm::Expected<llvm::StringMap<std::string>> IndexOrErr =
190 parseCrossTUIndex(IndexFileName, "/ctudir");
191 EXPECT_TRUE((bool)IndexOrErr);
192 llvm::StringMap<std::string> ParsedIndex = IndexOrErr.get();
193 EXPECT_EQ(ParsedIndex["a"], "/ctudir/b/c/d");
194}
195
Gabor Horvath6254bf42017-09-22 11:11:01 +0000196} // end namespace cross_tu
197} // end namespace clang