150
|
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/Frontend/CompilerInstance.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:
|
|
27 explicit CTUASTConsumer(clang::CompilerInstance &CI, bool *Success)
|
|
28 : CTU(CI), Success(Success) {}
|
|
29
|
|
30 void HandleTranslationUnit(ASTContext &Ctx) {
|
|
31 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
|
|
41 const TranslationUnitDecl *TU = Ctx.getTranslationUnitDecl();
|
|
42 const FunctionDecl *FD = FindFInTU(TU);
|
|
43 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));
|
|
51 llvm::ToolOutputFile ASTFile(ASTFileName, ASTFD);
|
|
52
|
|
53 int IndexFD;
|
|
54 llvm::SmallString<256> IndexFileName;
|
|
55 ASSERT_FALSE(llvm::sys::fs::createTemporaryFile("index", "txt", IndexFD,
|
|
56 IndexFileName));
|
|
57 llvm::ToolOutputFile IndexFile(IndexFileName, IndexFD);
|
|
58 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));
|
|
68 llvm::ToolOutputFile SourceFile(SourceFileName, SourceFD);
|
|
69 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.
|
|
79 llvm::Expected<const FunctionDecl *> NewFDorError = handleExpected(
|
|
80 CTU.getCrossTUDefinition(FD, "", IndexFileName, false),
|
|
81 []() { return nullptr; }, [](IndexError &) {});
|
|
82
|
|
83 if (NewFDorError) {
|
|
84 const FunctionDecl *NewFD = *NewFDorError;
|
|
85 *Success = NewFD && NewFD->hasBody() && !OrigFDHasBody;
|
|
86
|
|
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 }
|
|
108 }
|
|
109 }
|
|
110
|
|
111 private:
|
|
112 CrossTranslationUnitContext CTU;
|
|
113 bool *Success;
|
|
114 };
|
|
115
|
|
116 class CTUAction : public clang::ASTFrontendAction {
|
|
117 public:
|
|
118 CTUAction(bool *Success, unsigned OverrideLimit)
|
|
119 : Success(Success), OverrideLimit(OverrideLimit) {}
|
|
120
|
|
121 protected:
|
|
122 std::unique_ptr<clang::ASTConsumer>
|
|
123 CreateASTConsumer(clang::CompilerInstance &CI, StringRef) override {
|
|
124 CI.getAnalyzerOpts()->CTUImportThreshold = OverrideLimit;
|
|
125 return std::make_unique<CTUASTConsumer>(CI, Success);
|
|
126 }
|
|
127
|
|
128 private:
|
|
129 bool *Success;
|
|
130 const unsigned OverrideLimit;
|
|
131 };
|
|
132
|
|
133 } // end namespace
|
|
134
|
|
135 TEST(CrossTranslationUnit, CanLoadFunctionDefinition) {
|
|
136 bool Success = false;
|
|
137 EXPECT_TRUE(tooling::runToolOnCode(std::make_unique<CTUAction>(&Success, 1u),
|
|
138 "int f(int);"));
|
|
139 EXPECT_TRUE(Success);
|
|
140 }
|
|
141
|
|
142 TEST(CrossTranslationUnit, RespectsLoadThreshold) {
|
|
143 bool Success = false;
|
|
144 EXPECT_TRUE(tooling::runToolOnCode(std::make_unique<CTUAction>(&Success, 0u),
|
|
145 "int f(int);"));
|
|
146 EXPECT_FALSE(Success);
|
|
147 }
|
|
148
|
|
149 TEST(CrossTranslationUnit, IndexFormatCanBeParsed) {
|
|
150 llvm::StringMap<std::string> Index;
|
|
151 Index["a"] = "/b/f1";
|
|
152 Index["c"] = "/d/f2";
|
|
153 Index["e"] = "/f/f3";
|
|
154 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));
|
|
160 llvm::ToolOutputFile IndexFile(IndexFileName, IndexFD);
|
|
161 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
|
|
176 TEST(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
|
|
196 } // end namespace cross_tu
|
|
197 } // end namespace clang
|