[LLD][COFF] Emit tail merge pdata for delay load thunks on ARM64EC (#116810)
[llvm-project.git] / clang / unittests / CrossTU / CrossTranslationUnitTest.cpp
blob28f71d61e2c57f14bf54ffabe287fa932f586b27
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 //===----------------------------------------------------------------------===//
9 #include "clang/CrossTU/CrossTranslationUnit.h"
10 #include "clang/AST/ASTConsumer.h"
11 #include "clang/AST/ParentMapContext.h"
12 #include "clang/Frontend/CompilerInstance.h"
13 #include "clang/Frontend/FrontendAction.h"
14 #include "clang/Tooling/Tooling.h"
15 #include "llvm/Support/FileSystem.h"
16 #include "llvm/Support/Path.h"
17 #include "llvm/Support/ToolOutputFile.h"
18 #include "gtest/gtest.h"
19 #include <cassert>
21 namespace clang {
22 namespace cross_tu {
24 namespace {
26 class CTUASTConsumer : public clang::ASTConsumer {
27 public:
28 explicit CTUASTConsumer(clang::CompilerInstance &CI, bool *Success)
29 : CTU(CI), Success(Success) {}
31 void HandleTranslationUnit(ASTContext &Ctx) override {
32 auto FindFInTU = [](const TranslationUnitDecl *TU) {
33 const FunctionDecl *FD = nullptr;
34 for (const Decl *D : TU->decls()) {
35 FD = dyn_cast<FunctionDecl>(D);
36 if (FD && FD->getName() == "f")
37 break;
39 return FD;
42 const TranslationUnitDecl *TU = Ctx.getTranslationUnitDecl();
43 const FunctionDecl *FD = FindFInTU(TU);
44 assert(FD && FD->getName() == "f");
45 bool OrigFDHasBody = FD->hasBody();
47 const DynTypedNodeList ParentsBeforeImport =
48 Ctx.getParentMapContext().getParents<Decl>(*FD);
49 ASSERT_FALSE(ParentsBeforeImport.empty());
51 // Prepare the index file and the AST file.
52 int ASTFD;
53 llvm::SmallString<256> ASTFileName;
54 ASSERT_FALSE(
55 llvm::sys::fs::createTemporaryFile("f_ast", "ast", ASTFD, ASTFileName));
56 llvm::ToolOutputFile ASTFile(ASTFileName, ASTFD);
58 int IndexFD;
59 llvm::SmallString<256> IndexFileName;
60 ASSERT_FALSE(llvm::sys::fs::createTemporaryFile("index", "txt", IndexFD,
61 IndexFileName));
62 llvm::ToolOutputFile IndexFile(IndexFileName, IndexFD);
63 IndexFile.os() << "9:c:@F@f#I# " << ASTFileName << "\n";
64 IndexFile.os().flush();
65 EXPECT_TRUE(llvm::sys::fs::exists(IndexFileName));
67 StringRef SourceText = "int f(int) { return 0; }\n";
68 // This file must exist since the saved ASTFile will reference it.
69 int SourceFD;
70 llvm::SmallString<256> SourceFileName;
71 ASSERT_FALSE(llvm::sys::fs::createTemporaryFile("input", "cpp", SourceFD,
72 SourceFileName));
73 llvm::ToolOutputFile SourceFile(SourceFileName, SourceFD);
74 SourceFile.os() << SourceText;
75 SourceFile.os().flush();
76 EXPECT_TRUE(llvm::sys::fs::exists(SourceFileName));
78 std::unique_ptr<ASTUnit> ASTWithDefinition =
79 tooling::buildASTFromCode(SourceText, SourceFileName);
80 ASTWithDefinition->Save(ASTFileName.str());
81 EXPECT_TRUE(llvm::sys::fs::exists(ASTFileName));
83 // Load the definition from the AST file.
84 llvm::Expected<const FunctionDecl *> NewFDorError = handleExpected(
85 CTU.getCrossTUDefinition(FD, "", IndexFileName, false),
86 []() { return nullptr; }, [](IndexError &) {});
88 if (NewFDorError) {
89 const FunctionDecl *NewFD = *NewFDorError;
90 *Success = NewFD && NewFD->hasBody() && !OrigFDHasBody;
92 if (NewFD) {
93 // Check parent map.
94 const DynTypedNodeList ParentsAfterImport =
95 Ctx.getParentMapContext().getParents<Decl>(*FD);
96 const DynTypedNodeList ParentsOfImported =
97 Ctx.getParentMapContext().getParents<Decl>(*NewFD);
98 EXPECT_TRUE(
99 checkParentListsEq(ParentsBeforeImport, ParentsAfterImport));
100 EXPECT_FALSE(ParentsOfImported.empty());
105 static bool checkParentListsEq(const DynTypedNodeList &L1,
106 const DynTypedNodeList &L2) {
107 if (L1.size() != L2.size())
108 return false;
109 for (unsigned int I = 0; I < L1.size(); ++I)
110 if (L1[I] != L2[I])
111 return false;
112 return true;
115 private:
116 CrossTranslationUnitContext CTU;
117 bool *Success;
120 class CTUAction : public clang::ASTFrontendAction {
121 public:
122 CTUAction(bool *Success, unsigned OverrideLimit)
123 : Success(Success), OverrideLimit(OverrideLimit) {}
125 protected:
126 std::unique_ptr<clang::ASTConsumer>
127 CreateASTConsumer(clang::CompilerInstance &CI, StringRef) override {
128 CI.getAnalyzerOpts().CTUImportThreshold = OverrideLimit;
129 CI.getAnalyzerOpts().CTUImportCppThreshold = OverrideLimit;
130 return std::make_unique<CTUASTConsumer>(CI, Success);
133 private:
134 bool *Success;
135 const unsigned OverrideLimit;
138 } // end namespace
140 TEST(CrossTranslationUnit, CanLoadFunctionDefinition) {
141 bool Success = false;
142 EXPECT_TRUE(tooling::runToolOnCode(std::make_unique<CTUAction>(&Success, 1u),
143 "int f(int);"));
144 EXPECT_TRUE(Success);
147 TEST(CrossTranslationUnit, RespectsLoadThreshold) {
148 bool Success = false;
149 EXPECT_TRUE(tooling::runToolOnCode(std::make_unique<CTUAction>(&Success, 0u),
150 "int f(int);"));
151 EXPECT_FALSE(Success);
154 TEST(CrossTranslationUnit, IndexFormatCanBeParsed) {
155 llvm::StringMap<std::string> Index;
156 Index["a"] = "/b/f1";
157 Index["c"] = "/d/f2";
158 Index["e"] = "/f/f3";
159 std::string IndexText = createCrossTUIndexString(Index);
161 int IndexFD;
162 llvm::SmallString<256> IndexFileName;
163 ASSERT_FALSE(llvm::sys::fs::createTemporaryFile("index", "txt", IndexFD,
164 IndexFileName));
165 llvm::ToolOutputFile IndexFile(IndexFileName, IndexFD);
166 IndexFile.os() << IndexText;
167 IndexFile.os().flush();
168 EXPECT_TRUE(llvm::sys::fs::exists(IndexFileName));
169 llvm::Expected<llvm::StringMap<std::string>> IndexOrErr =
170 parseCrossTUIndex(IndexFileName);
171 EXPECT_TRUE((bool)IndexOrErr);
172 llvm::StringMap<std::string> ParsedIndex = IndexOrErr.get();
173 for (const auto &E : Index) {
174 EXPECT_TRUE(ParsedIndex.count(E.getKey()));
175 EXPECT_EQ(ParsedIndex[E.getKey()], E.getValue());
177 for (const auto &E : ParsedIndex)
178 EXPECT_TRUE(Index.count(E.getKey()));
181 TEST(CrossTranslationUnit, EmptyInvocationListIsNotValid) {
182 auto Input = "";
184 llvm::Expected<InvocationListTy> Result = parseInvocationList(Input);
185 EXPECT_FALSE(static_cast<bool>(Result));
186 bool IsWrongFromatError = false;
187 llvm::handleAllErrors(Result.takeError(), [&](IndexError &Err) {
188 IsWrongFromatError =
189 Err.getCode() == index_error_code::invocation_list_wrong_format;
191 EXPECT_TRUE(IsWrongFromatError);
194 TEST(CrossTranslationUnit, AmbiguousInvocationListIsDetected) {
195 // The same source file occurs twice (for two different architecture) in
196 // this test case. The disambiguation is the responsibility of the user.
197 auto Input = R"(
198 /tmp/main.cpp:
199 - clang++
200 - -c
201 - -m32
202 - -o
203 - main32.o
204 - /tmp/main.cpp
205 /tmp/main.cpp:
206 - clang++
207 - -c
208 - -m64
209 - -o
210 - main64.o
211 - /tmp/main.cpp
214 llvm::Expected<InvocationListTy> Result = parseInvocationList(Input);
215 EXPECT_FALSE(static_cast<bool>(Result));
216 bool IsAmbiguousError = false;
217 llvm::handleAllErrors(Result.takeError(), [&](IndexError &Err) {
218 IsAmbiguousError =
219 Err.getCode() == index_error_code::invocation_list_ambiguous;
221 EXPECT_TRUE(IsAmbiguousError);
224 TEST(CrossTranslationUnit, SingleInvocationCanBeParsed) {
225 auto Input = R"(
226 /tmp/main.cpp:
227 - clang++
228 - /tmp/main.cpp
230 llvm::Expected<InvocationListTy> Result = parseInvocationList(Input);
231 EXPECT_TRUE(static_cast<bool>(Result));
233 EXPECT_EQ(Result->size(), 1u);
235 auto It = Result->find("/tmp/main.cpp");
236 EXPECT_TRUE(It != Result->end());
237 EXPECT_EQ(It->getValue()[0], "clang++");
238 EXPECT_EQ(It->getValue()[1], "/tmp/main.cpp");
241 TEST(CrossTranslationUnit, MultipleInvocationsCanBeParsed) {
242 auto Input = R"(
243 /tmp/main.cpp:
244 - clang++
245 - /tmp/other.o
246 - /tmp/main.cpp
247 /tmp/other.cpp:
248 - g++
249 - -c
250 - -o
251 - /tmp/other.o
252 - /tmp/other.cpp
254 llvm::Expected<InvocationListTy> Result = parseInvocationList(Input);
255 EXPECT_TRUE(static_cast<bool>(Result));
257 EXPECT_EQ(Result->size(), 2u);
259 auto It = Result->find("/tmp/main.cpp");
260 EXPECT_TRUE(It != Result->end());
261 EXPECT_EQ(It->getKey(), "/tmp/main.cpp");
262 EXPECT_EQ(It->getValue()[0], "clang++");
263 EXPECT_EQ(It->getValue()[1], "/tmp/other.o");
264 EXPECT_EQ(It->getValue()[2], "/tmp/main.cpp");
266 It = Result->find("/tmp/other.cpp");
267 EXPECT_TRUE(It != Result->end());
268 EXPECT_EQ(It->getValue()[0], "g++");
269 EXPECT_EQ(It->getValue()[1], "-c");
270 EXPECT_EQ(It->getValue()[2], "-o");
271 EXPECT_EQ(It->getValue()[3], "/tmp/other.o");
272 EXPECT_EQ(It->getValue()[4], "/tmp/other.cpp");
275 } // end namespace cross_tu
276 } // end namespace clang