xref: /llvm-project/clang/unittests/CrossTU/CrossTranslationUnitTest.cpp (revision 8e0c9bb91f484b7d2fa86c01919d96a41a7071d7)
1e350b0a1SGabor Horvath //===- unittest/Tooling/CrossTranslationUnitTest.cpp - Tooling unit tests -===//
2e350b0a1SGabor Horvath //
32946cd70SChandler Carruth // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
42946cd70SChandler Carruth // See https://llvm.org/LICENSE.txt for license information.
52946cd70SChandler Carruth // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6e350b0a1SGabor Horvath //
7e350b0a1SGabor Horvath //===----------------------------------------------------------------------===//
8e350b0a1SGabor Horvath 
9e350b0a1SGabor Horvath #include "clang/CrossTU/CrossTranslationUnit.h"
10435b458aSEndre Fülöp #include "clang/AST/ASTConsumer.h"
11f3b34466SBalázs Kéri #include "clang/AST/ParentMapContext.h"
125cc18516SEndre Fülöp #include "clang/Frontend/CompilerInstance.h"
13e350b0a1SGabor Horvath #include "clang/Frontend/FrontendAction.h"
14e350b0a1SGabor Horvath #include "clang/Tooling/Tooling.h"
15e350b0a1SGabor Horvath #include "llvm/Support/FileSystem.h"
16e350b0a1SGabor Horvath #include "llvm/Support/Path.h"
17e350b0a1SGabor Horvath #include "llvm/Support/ToolOutputFile.h"
18e350b0a1SGabor Horvath #include "gtest/gtest.h"
19e350b0a1SGabor Horvath #include <cassert>
20e350b0a1SGabor Horvath 
21e350b0a1SGabor Horvath namespace clang {
22e350b0a1SGabor Horvath namespace cross_tu {
23e350b0a1SGabor Horvath 
24e350b0a1SGabor Horvath namespace {
25e350b0a1SGabor Horvath 
26e350b0a1SGabor Horvath class CTUASTConsumer : public clang::ASTConsumer {
27e350b0a1SGabor Horvath public:
CTUASTConsumer(clang::CompilerInstance & CI,bool * Success)28e350b0a1SGabor Horvath   explicit CTUASTConsumer(clang::CompilerInstance &CI, bool *Success)
29e350b0a1SGabor Horvath       : CTU(CI), Success(Success) {}
30e350b0a1SGabor Horvath 
HandleTranslationUnit(ASTContext & Ctx)3110505604SLogan Smith   void HandleTranslationUnit(ASTContext &Ctx) override {
32d22f8773SBalazs Keri     auto FindFInTU = [](const TranslationUnitDecl *TU) {
33e350b0a1SGabor Horvath       const FunctionDecl *FD = nullptr;
34e350b0a1SGabor Horvath       for (const Decl *D : TU->decls()) {
35e350b0a1SGabor Horvath         FD = dyn_cast<FunctionDecl>(D);
36e350b0a1SGabor Horvath         if (FD && FD->getName() == "f")
37e350b0a1SGabor Horvath           break;
38e350b0a1SGabor Horvath       }
39d22f8773SBalazs Keri       return FD;
40d22f8773SBalazs Keri     };
41d22f8773SBalazs Keri 
42d22f8773SBalazs Keri     const TranslationUnitDecl *TU = Ctx.getTranslationUnitDecl();
43d22f8773SBalazs Keri     const FunctionDecl *FD = FindFInTU(TU);
44e350b0a1SGabor Horvath     assert(FD && FD->getName() == "f");
45e350b0a1SGabor Horvath     bool OrigFDHasBody = FD->hasBody();
46e350b0a1SGabor Horvath 
47f3b34466SBalázs Kéri     const DynTypedNodeList ParentsBeforeImport =
48f3b34466SBalázs Kéri         Ctx.getParentMapContext().getParents<Decl>(*FD);
49f3b34466SBalázs Kéri     ASSERT_FALSE(ParentsBeforeImport.empty());
50f3b34466SBalázs Kéri 
51e350b0a1SGabor Horvath     // Prepare the index file and the AST file.
52e350b0a1SGabor Horvath     int ASTFD;
53e350b0a1SGabor Horvath     llvm::SmallString<256> ASTFileName;
54e350b0a1SGabor Horvath     ASSERT_FALSE(
55e350b0a1SGabor Horvath         llvm::sys::fs::createTemporaryFile("f_ast", "ast", ASTFD, ASTFileName));
562590edf6SReid Kleckner     llvm::ToolOutputFile ASTFile(ASTFileName, ASTFD);
57e350b0a1SGabor Horvath 
58e350b0a1SGabor Horvath     int IndexFD;
59e350b0a1SGabor Horvath     llvm::SmallString<256> IndexFileName;
60e350b0a1SGabor Horvath     ASSERT_FALSE(llvm::sys::fs::createTemporaryFile("index", "txt", IndexFD,
61e350b0a1SGabor Horvath                                                     IndexFileName));
622590edf6SReid Kleckner     llvm::ToolOutputFile IndexFile(IndexFileName, IndexFD);
639f902542SElla Ma     IndexFile.os() << "9:c:@F@f#I# " << ASTFileName << "\n";
64e350b0a1SGabor Horvath     IndexFile.os().flush();
65e350b0a1SGabor Horvath     EXPECT_TRUE(llvm::sys::fs::exists(IndexFileName));
66e350b0a1SGabor Horvath 
67e350b0a1SGabor Horvath     StringRef SourceText = "int f(int) { return 0; }\n";
68e350b0a1SGabor Horvath     // This file must exist since the saved ASTFile will reference it.
69e350b0a1SGabor Horvath     int SourceFD;
70e350b0a1SGabor Horvath     llvm::SmallString<256> SourceFileName;
71e350b0a1SGabor Horvath     ASSERT_FALSE(llvm::sys::fs::createTemporaryFile("input", "cpp", SourceFD,
72e350b0a1SGabor Horvath                                                     SourceFileName));
732590edf6SReid Kleckner     llvm::ToolOutputFile SourceFile(SourceFileName, SourceFD);
74e350b0a1SGabor Horvath     SourceFile.os() << SourceText;
75e350b0a1SGabor Horvath     SourceFile.os().flush();
76e350b0a1SGabor Horvath     EXPECT_TRUE(llvm::sys::fs::exists(SourceFileName));
77e350b0a1SGabor Horvath 
78e350b0a1SGabor Horvath     std::unique_ptr<ASTUnit> ASTWithDefinition =
79e350b0a1SGabor Horvath         tooling::buildASTFromCode(SourceText, SourceFileName);
80e350b0a1SGabor Horvath     ASTWithDefinition->Save(ASTFileName.str());
81e350b0a1SGabor Horvath     EXPECT_TRUE(llvm::sys::fs::exists(ASTFileName));
82e350b0a1SGabor Horvath 
83e350b0a1SGabor Horvath     // Load the definition from the AST file.
840752d12cSEndre Fulop     llvm::Expected<const FunctionDecl *> NewFDorError = handleExpected(
850752d12cSEndre Fulop         CTU.getCrossTUDefinition(FD, "", IndexFileName, false),
860752d12cSEndre Fulop         []() { return nullptr; }, [](IndexError &) {});
87e350b0a1SGabor Horvath 
880752d12cSEndre Fulop     if (NewFDorError) {
890752d12cSEndre Fulop       const FunctionDecl *NewFD = *NewFDorError;
90e350b0a1SGabor Horvath       *Success = NewFD && NewFD->hasBody() && !OrigFDHasBody;
91d22f8773SBalazs Keri 
92d22f8773SBalazs Keri       if (NewFD) {
93f3b34466SBalázs Kéri         // Check parent map.
94f3b34466SBalázs Kéri         const DynTypedNodeList ParentsAfterImport =
95f3b34466SBalázs Kéri             Ctx.getParentMapContext().getParents<Decl>(*FD);
96f3b34466SBalázs Kéri         const DynTypedNodeList ParentsOfImported =
97f3b34466SBalázs Kéri             Ctx.getParentMapContext().getParents<Decl>(*NewFD);
98f3b34466SBalázs Kéri         EXPECT_TRUE(
99f3b34466SBalázs Kéri             checkParentListsEq(ParentsBeforeImport, ParentsAfterImport));
100f3b34466SBalázs Kéri         EXPECT_FALSE(ParentsOfImported.empty());
101d22f8773SBalazs Keri       }
102e350b0a1SGabor Horvath     }
1030752d12cSEndre Fulop   }
104e350b0a1SGabor Horvath 
checkParentListsEq(const DynTypedNodeList & L1,const DynTypedNodeList & L2)105f3b34466SBalázs Kéri   static bool checkParentListsEq(const DynTypedNodeList &L1,
106f3b34466SBalázs Kéri                                  const DynTypedNodeList &L2) {
107f3b34466SBalázs Kéri     if (L1.size() != L2.size())
108f3b34466SBalázs Kéri       return false;
109f3b34466SBalázs Kéri     for (unsigned int I = 0; I < L1.size(); ++I)
110f3b34466SBalázs Kéri       if (L1[I] != L2[I])
111f3b34466SBalázs Kéri         return false;
112f3b34466SBalázs Kéri     return true;
113f3b34466SBalázs Kéri   }
114f3b34466SBalázs Kéri 
115e350b0a1SGabor Horvath private:
116e350b0a1SGabor Horvath   CrossTranslationUnitContext CTU;
117e350b0a1SGabor Horvath   bool *Success;
118e350b0a1SGabor Horvath };
119e350b0a1SGabor Horvath 
120e350b0a1SGabor Horvath class CTUAction : public clang::ASTFrontendAction {
121e350b0a1SGabor Horvath public:
CTUAction(bool * Success,unsigned OverrideLimit)1220752d12cSEndre Fulop   CTUAction(bool *Success, unsigned OverrideLimit)
1230752d12cSEndre Fulop       : Success(Success), OverrideLimit(OverrideLimit) {}
124e350b0a1SGabor Horvath 
125e350b0a1SGabor Horvath protected:
126e350b0a1SGabor Horvath   std::unique_ptr<clang::ASTConsumer>
CreateASTConsumer(clang::CompilerInstance & CI,StringRef)127e350b0a1SGabor Horvath   CreateASTConsumer(clang::CompilerInstance &CI, StringRef) override {
128*8e0c9bb9SJan Svoboda     CI.getAnalyzerOpts().CTUImportThreshold = OverrideLimit;
129*8e0c9bb9SJan Svoboda     CI.getAnalyzerOpts().CTUImportCppThreshold = OverrideLimit;
1302b3d49b6SJonas Devlieghere     return std::make_unique<CTUASTConsumer>(CI, Success);
131e350b0a1SGabor Horvath   }
132e350b0a1SGabor Horvath 
133e350b0a1SGabor Horvath private:
134e350b0a1SGabor Horvath   bool *Success;
1350752d12cSEndre Fulop   const unsigned OverrideLimit;
136e350b0a1SGabor Horvath };
137e350b0a1SGabor Horvath 
138e350b0a1SGabor Horvath } // end namespace
139e350b0a1SGabor Horvath 
TEST(CrossTranslationUnit,CanLoadFunctionDefinition)140e350b0a1SGabor Horvath TEST(CrossTranslationUnit, CanLoadFunctionDefinition) {
141e350b0a1SGabor Horvath   bool Success = false;
142b22804b3SDmitri Gribenko   EXPECT_TRUE(tooling::runToolOnCode(std::make_unique<CTUAction>(&Success, 1u),
143b22804b3SDmitri Gribenko                                      "int f(int);"));
144e350b0a1SGabor Horvath   EXPECT_TRUE(Success);
145e350b0a1SGabor Horvath }
146e350b0a1SGabor Horvath 
TEST(CrossTranslationUnit,RespectsLoadThreshold)1470752d12cSEndre Fulop TEST(CrossTranslationUnit, RespectsLoadThreshold) {
1480752d12cSEndre Fulop   bool Success = false;
149b22804b3SDmitri Gribenko   EXPECT_TRUE(tooling::runToolOnCode(std::make_unique<CTUAction>(&Success, 0u),
150b22804b3SDmitri Gribenko                                      "int f(int);"));
1510752d12cSEndre Fulop   EXPECT_FALSE(Success);
1520752d12cSEndre Fulop }
1530752d12cSEndre Fulop 
TEST(CrossTranslationUnit,IndexFormatCanBeParsed)154e350b0a1SGabor Horvath TEST(CrossTranslationUnit, IndexFormatCanBeParsed) {
155e350b0a1SGabor Horvath   llvm::StringMap<std::string> Index;
156724beacaSGabor Horvath   Index["a"] = "/b/f1";
157724beacaSGabor Horvath   Index["c"] = "/d/f2";
158724beacaSGabor Horvath   Index["e"] = "/f/f3";
159e350b0a1SGabor Horvath   std::string IndexText = createCrossTUIndexString(Index);
160e350b0a1SGabor Horvath 
161e350b0a1SGabor Horvath   int IndexFD;
162e350b0a1SGabor Horvath   llvm::SmallString<256> IndexFileName;
163e350b0a1SGabor Horvath   ASSERT_FALSE(llvm::sys::fs::createTemporaryFile("index", "txt", IndexFD,
164e350b0a1SGabor Horvath                                                   IndexFileName));
1652590edf6SReid Kleckner   llvm::ToolOutputFile IndexFile(IndexFileName, IndexFD);
166e350b0a1SGabor Horvath   IndexFile.os() << IndexText;
167e350b0a1SGabor Horvath   IndexFile.os().flush();
168e350b0a1SGabor Horvath   EXPECT_TRUE(llvm::sys::fs::exists(IndexFileName));
169e350b0a1SGabor Horvath   llvm::Expected<llvm::StringMap<std::string>> IndexOrErr =
1705cc18516SEndre Fülöp       parseCrossTUIndex(IndexFileName);
171e350b0a1SGabor Horvath   EXPECT_TRUE((bool)IndexOrErr);
172e350b0a1SGabor Horvath   llvm::StringMap<std::string> ParsedIndex = IndexOrErr.get();
173e350b0a1SGabor Horvath   for (const auto &E : Index) {
174e350b0a1SGabor Horvath     EXPECT_TRUE(ParsedIndex.count(E.getKey()));
175e350b0a1SGabor Horvath     EXPECT_EQ(ParsedIndex[E.getKey()], E.getValue());
176e350b0a1SGabor Horvath   }
177e350b0a1SGabor Horvath   for (const auto &E : ParsedIndex)
178e350b0a1SGabor Horvath     EXPECT_TRUE(Index.count(E.getKey()));
179e350b0a1SGabor Horvath }
180e350b0a1SGabor Horvath 
TEST(CrossTranslationUnit,EmptyInvocationListIsNotValid)1815cc18516SEndre Fülöp TEST(CrossTranslationUnit, EmptyInvocationListIsNotValid) {
1825cc18516SEndre Fülöp   auto Input = "";
18396717125SDmitri Gribenko 
1845cc18516SEndre Fülöp   llvm::Expected<InvocationListTy> Result = parseInvocationList(Input);
1855cc18516SEndre Fülöp   EXPECT_FALSE(static_cast<bool>(Result));
1865cc18516SEndre Fülöp   bool IsWrongFromatError = false;
1875cc18516SEndre Fülöp   llvm::handleAllErrors(Result.takeError(), [&](IndexError &Err) {
1885cc18516SEndre Fülöp     IsWrongFromatError =
1895cc18516SEndre Fülöp         Err.getCode() == index_error_code::invocation_list_wrong_format;
1905cc18516SEndre Fülöp   });
1915cc18516SEndre Fülöp   EXPECT_TRUE(IsWrongFromatError);
1925cc18516SEndre Fülöp }
1935cc18516SEndre Fülöp 
TEST(CrossTranslationUnit,AmbiguousInvocationListIsDetected)1945cc18516SEndre Fülöp TEST(CrossTranslationUnit, AmbiguousInvocationListIsDetected) {
1955cc18516SEndre Fülöp   // The same source file occurs twice (for two different architecture) in
1965cc18516SEndre Fülöp   // this test case. The disambiguation is the responsibility of the user.
1975cc18516SEndre Fülöp   auto Input = R"(
1985cc18516SEndre Fülöp   /tmp/main.cpp:
1995cc18516SEndre Fülöp     - clang++
2005cc18516SEndre Fülöp     - -c
2015cc18516SEndre Fülöp     - -m32
2025cc18516SEndre Fülöp     - -o
2035cc18516SEndre Fülöp     - main32.o
2045cc18516SEndre Fülöp     - /tmp/main.cpp
2055cc18516SEndre Fülöp   /tmp/main.cpp:
2065cc18516SEndre Fülöp     - clang++
2075cc18516SEndre Fülöp     - -c
2085cc18516SEndre Fülöp     - -m64
2095cc18516SEndre Fülöp     - -o
2105cc18516SEndre Fülöp     - main64.o
2115cc18516SEndre Fülöp     - /tmp/main.cpp
2125cc18516SEndre Fülöp   )";
2135cc18516SEndre Fülöp 
2145cc18516SEndre Fülöp   llvm::Expected<InvocationListTy> Result = parseInvocationList(Input);
2155cc18516SEndre Fülöp   EXPECT_FALSE(static_cast<bool>(Result));
2165cc18516SEndre Fülöp   bool IsAmbiguousError = false;
2175cc18516SEndre Fülöp   llvm::handleAllErrors(Result.takeError(), [&](IndexError &Err) {
2185cc18516SEndre Fülöp     IsAmbiguousError =
2195cc18516SEndre Fülöp         Err.getCode() == index_error_code::invocation_list_ambiguous;
2205cc18516SEndre Fülöp   });
2215cc18516SEndre Fülöp   EXPECT_TRUE(IsAmbiguousError);
2225cc18516SEndre Fülöp }
2235cc18516SEndre Fülöp 
TEST(CrossTranslationUnit,SingleInvocationCanBeParsed)2245cc18516SEndre Fülöp TEST(CrossTranslationUnit, SingleInvocationCanBeParsed) {
2255cc18516SEndre Fülöp   auto Input = R"(
2265cc18516SEndre Fülöp   /tmp/main.cpp:
2275cc18516SEndre Fülöp     - clang++
2285cc18516SEndre Fülöp     - /tmp/main.cpp
2295cc18516SEndre Fülöp   )";
2305cc18516SEndre Fülöp   llvm::Expected<InvocationListTy> Result = parseInvocationList(Input);
2315cc18516SEndre Fülöp   EXPECT_TRUE(static_cast<bool>(Result));
2325cc18516SEndre Fülöp 
2335cc18516SEndre Fülöp   EXPECT_EQ(Result->size(), 1u);
2345cc18516SEndre Fülöp 
2355cc18516SEndre Fülöp   auto It = Result->find("/tmp/main.cpp");
2365cc18516SEndre Fülöp   EXPECT_TRUE(It != Result->end());
2375cc18516SEndre Fülöp   EXPECT_EQ(It->getValue()[0], "clang++");
2385cc18516SEndre Fülöp   EXPECT_EQ(It->getValue()[1], "/tmp/main.cpp");
2395cc18516SEndre Fülöp }
2405cc18516SEndre Fülöp 
TEST(CrossTranslationUnit,MultipleInvocationsCanBeParsed)2415cc18516SEndre Fülöp TEST(CrossTranslationUnit, MultipleInvocationsCanBeParsed) {
2425cc18516SEndre Fülöp   auto Input = R"(
2435cc18516SEndre Fülöp   /tmp/main.cpp:
2445cc18516SEndre Fülöp     - clang++
2455cc18516SEndre Fülöp     - /tmp/other.o
2465cc18516SEndre Fülöp     - /tmp/main.cpp
2475cc18516SEndre Fülöp   /tmp/other.cpp:
2485cc18516SEndre Fülöp     - g++
2495cc18516SEndre Fülöp     - -c
2505cc18516SEndre Fülöp     - -o
2515cc18516SEndre Fülöp     - /tmp/other.o
2525cc18516SEndre Fülöp     - /tmp/other.cpp
2535cc18516SEndre Fülöp   )";
2545cc18516SEndre Fülöp   llvm::Expected<InvocationListTy> Result = parseInvocationList(Input);
2555cc18516SEndre Fülöp   EXPECT_TRUE(static_cast<bool>(Result));
2565cc18516SEndre Fülöp 
2575cc18516SEndre Fülöp   EXPECT_EQ(Result->size(), 2u);
2585cc18516SEndre Fülöp 
2595cc18516SEndre Fülöp   auto It = Result->find("/tmp/main.cpp");
2605cc18516SEndre Fülöp   EXPECT_TRUE(It != Result->end());
2615cc18516SEndre Fülöp   EXPECT_EQ(It->getKey(), "/tmp/main.cpp");
2625cc18516SEndre Fülöp   EXPECT_EQ(It->getValue()[0], "clang++");
2635cc18516SEndre Fülöp   EXPECT_EQ(It->getValue()[1], "/tmp/other.o");
2645cc18516SEndre Fülöp   EXPECT_EQ(It->getValue()[2], "/tmp/main.cpp");
2655cc18516SEndre Fülöp 
2665cc18516SEndre Fülöp   It = Result->find("/tmp/other.cpp");
2675cc18516SEndre Fülöp   EXPECT_TRUE(It != Result->end());
2685cc18516SEndre Fülöp   EXPECT_EQ(It->getValue()[0], "g++");
2695cc18516SEndre Fülöp   EXPECT_EQ(It->getValue()[1], "-c");
2705cc18516SEndre Fülöp   EXPECT_EQ(It->getValue()[2], "-o");
2715cc18516SEndre Fülöp   EXPECT_EQ(It->getValue()[3], "/tmp/other.o");
2725cc18516SEndre Fülöp   EXPECT_EQ(It->getValue()[4], "/tmp/other.cpp");
27396717125SDmitri Gribenko }
27496717125SDmitri Gribenko 
275e350b0a1SGabor Horvath } // end namespace cross_tu
276e350b0a1SGabor Horvath } // end namespace clang
277