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