1 //===- unittest/AST/ASTImporterFixtures.h - AST unit test support ---------===//
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 /// \file
10 /// Fixture classes for testing the ASTImporter.
11 //
12 //===----------------------------------------------------------------------===//
13 
14 #ifndef LLVM_CLANG_UNITTESTS_AST_IMPORTER_FIXTURES_H
15 #define LLVM_CLANG_UNITTESTS_AST_IMPORTER_FIXTURES_H
16 
17 #include "gmock/gmock.h"
18 
19 #include "clang/AST/ASTImporter.h"
20 #include "clang/AST/ASTImporterSharedState.h"
21 #include "clang/Frontend/ASTUnit.h"
22 #include "llvm/Support/Error.h"
23 #include "llvm/Support/ErrorHandling.h"
24 
25 #include "DeclMatcher.h"
26 #include "Language.h"
27 
28 #include <sstream>
29 
30 namespace clang {
31 
32 class ASTImporter;
33 class ASTImporterSharedState;
34 class ASTUnit;
35 
36 namespace ast_matchers {
37 
38 const StringRef DeclToImportID = "declToImport";
39 const StringRef DeclToVerifyID = "declToVerify";
40 
41 // Creates a virtual file and assigns that to the context of given AST. If the
42 // file already exists then the file will not be created again as a duplicate.
43 void createVirtualFileIfNeeded(ASTUnit *ToAST, StringRef FileName,
44                                std::unique_ptr<llvm::MemoryBuffer> &&Buffer);
45 
46 void createVirtualFileIfNeeded(ASTUnit *ToAST, StringRef FileName,
47                                StringRef Code);
48 
49 // Common base for the different families of ASTImporter tests that are
50 // parameterized on the compiler options which may result a different AST. E.g.
51 // -fms-compatibility or -fdelayed-template-parsing.
52 class CompilerOptionSpecificTest : public ::testing::Test {
53 protected:
54   // Return the extra arguments appended to runtime options at compilation.
getExtraArgs()55   virtual ArgVector getExtraArgs() const { return ArgVector(); }
56 
57   // Returns the argument vector used for a specific language option, this set
58   // can be tweaked by the test parameters.
getArgVectorForLanguage(Language Lang)59   ArgVector getArgVectorForLanguage(Language Lang) const {
60     ArgVector Args = getBasicRunOptionsForLanguage(Lang);
61     ArgVector ExtraArgs = getExtraArgs();
62     for (const auto &Arg : ExtraArgs) {
63       Args.push_back(Arg);
64     }
65     return Args;
66   }
67 };
68 
69 const auto DefaultTestValuesForRunOptions = ::testing::Values(
70     ArgVector(), ArgVector{"-fdelayed-template-parsing"},
71     ArgVector{"-fms-compatibility"},
72     ArgVector{"-fdelayed-template-parsing", "-fms-compatibility"});
73 
74 // This class provides generic methods to write tests which can check internal
75 // attributes of AST nodes like getPreviousDecl(), isVirtual(), etc. Also,
76 // this fixture makes it possible to import from several "From" contexts.
77 class ASTImporterTestBase : public CompilerOptionSpecificTest {
78 
79   const char *const InputFileName = "input.cc";
80   const char *const OutputFileName = "output.cc";
81 
82 public:
83   /// Allocates an ASTImporter (or one of its subclasses).
84   typedef std::function<ASTImporter *(
85       ASTContext &, FileManager &, ASTContext &, FileManager &, bool,
86       const std::shared_ptr<ASTImporterSharedState> &SharedState)>
87       ImporterConstructor;
88 
89   // ODR handling type for the AST importer.
90   ASTImporter::ODRHandlingType ODRHandling;
91 
92   // The lambda that constructs the ASTImporter we use in this test.
93   ImporterConstructor Creator;
94 
95 private:
96   // Buffer for the To context, must live in the test scope.
97   std::string ToCode;
98 
99   // Represents a "From" translation unit and holds an importer object which we
100   // use to import from this translation unit.
101   struct TU {
102     // Buffer for the context, must live in the test scope.
103     std::string Code;
104     std::string FileName;
105     std::unique_ptr<ASTUnit> Unit;
106     TranslationUnitDecl *TUDecl = nullptr;
107     std::unique_ptr<ASTImporter> Importer;
108     ImporterConstructor Creator;
109     ASTImporter::ODRHandlingType ODRHandling;
110 
111     TU(StringRef Code, StringRef FileName, ArgVector Args,
112        ImporterConstructor C = ImporterConstructor(),
113        ASTImporter::ODRHandlingType ODRHandling =
114            ASTImporter::ODRHandlingType::Conservative);
115     ~TU();
116 
117     void
118     lazyInitImporter(const std::shared_ptr<ASTImporterSharedState> &SharedState,
119                      ASTUnit *ToAST);
120     Decl *import(const std::shared_ptr<ASTImporterSharedState> &SharedState,
121                  ASTUnit *ToAST, Decl *FromDecl);
122     llvm::Expected<Decl *>
123     importOrError(const std::shared_ptr<ASTImporterSharedState> &SharedState,
124                   ASTUnit *ToAST, Decl *FromDecl);
125     QualType import(const std::shared_ptr<ASTImporterSharedState> &SharedState,
126                     ASTUnit *ToAST, QualType FromType);
127   };
128 
129   // We may have several From contexts and related translation units. In each
130   // AST, the buffers for the source are handled via references and are set
131   // during the creation of the AST. These references must point to a valid
132   // buffer until the AST is alive. Thus, we must use a list in order to avoid
133   // moving of the stored objects because that would mean breaking the
134   // references in the AST. By using a vector a move could happen when the
135   // vector is expanding, with the list we won't have these issues.
136   std::list<TU> FromTUs;
137 
138   // Initialize the shared state if not initialized already.
139   void lazyInitSharedState(TranslationUnitDecl *ToTU);
140 
141   void lazyInitToAST(Language ToLang, StringRef ToSrcCode, StringRef FileName);
142 
143 protected:
144   std::shared_ptr<ASTImporterSharedState> SharedStatePtr;
145 
146 public:
147   // We may have several From context but only one To context.
148   std::unique_ptr<ASTUnit> ToAST;
149 
150   // Returns with the TU associated with the given Decl.
151   TU *findFromTU(Decl *From);
152 
153   // Creates an AST both for the From and To source code and imports the Decl
154   // of the identifier into the To context.
155   // Must not be called more than once within the same test.
156   std::tuple<Decl *, Decl *>
157   getImportedDecl(StringRef FromSrcCode, Language FromLang, StringRef ToSrcCode,
158                   Language ToLang, StringRef Identifier = DeclToImportID);
159 
160   // Creates a TU decl for the given source code which can be used as a From
161   // context.  May be called several times in a given test (with different file
162   // name).
163   TranslationUnitDecl *getTuDecl(StringRef SrcCode, Language Lang,
164                                  StringRef FileName = "input.cc");
165 
166   // Creates the To context with the given source code and returns the TU decl.
167   TranslationUnitDecl *getToTuDecl(StringRef ToSrcCode, Language ToLang);
168 
169   // Import the given Decl into the ToCtx.
170   // May be called several times in a given test.
171   // The different instances of the param From may have different ASTContext.
172   Decl *Import(Decl *From, Language ToLang);
173 
Import(DeclT * From,Language Lang)174   template <class DeclT> DeclT *Import(DeclT *From, Language Lang) {
175     return cast_or_null<DeclT>(Import(cast<Decl>(From), Lang));
176   }
177 
178   // Import the given Decl into the ToCtx.
179   // Same as Import but returns the result of the import which can be an error.
180   llvm::Expected<Decl *> importOrError(Decl *From, Language ToLang);
181 
182   QualType ImportType(QualType FromType, Decl *TUDecl, Language ToLang);
183 
ASTImporterTestBase()184   ASTImporterTestBase()
185       : ODRHandling(ASTImporter::ODRHandlingType::Conservative) {}
186   ~ASTImporterTestBase();
187 };
188 
189 class ASTImporterOptionSpecificTestBase
190     : public ASTImporterTestBase,
191       public ::testing::WithParamInterface<ArgVector> {
192 protected:
getExtraArgs()193   ArgVector getExtraArgs() const override { return GetParam(); }
194 };
195 
196 template <class T>
isSuccess(llvm::Expected<T> & ValOrErr)197 ::testing::AssertionResult isSuccess(llvm::Expected<T> &ValOrErr) {
198   if (ValOrErr)
199     return ::testing::AssertionSuccess() << "Expected<> contains no error.";
200   else
201     return ::testing::AssertionFailure()
202            << "Expected<> contains error: " << toString(ValOrErr.takeError());
203 }
204 
205 template <class T>
isImportError(llvm::Expected<T> & ValOrErr,ImportError::ErrorKind Kind)206 ::testing::AssertionResult isImportError(llvm::Expected<T> &ValOrErr,
207                                          ImportError::ErrorKind Kind) {
208   if (ValOrErr) {
209     return ::testing::AssertionFailure() << "Expected<> is expected to contain "
210                                             "error but does contain value \""
211                                          << (*ValOrErr) << "\"";
212   } else {
213     std::ostringstream OS;
214     bool Result = false;
215     auto Err = llvm::handleErrors(
216         ValOrErr.takeError(), [&OS, &Result, Kind](clang::ImportError &IE) {
217           if (IE.Error == Kind) {
218             Result = true;
219             OS << "Expected<> contains an ImportError " << IE.toString();
220           } else {
221             OS << "Expected<> contains an ImportError " << IE.toString()
222                << " instead of kind " << Kind;
223           }
224         });
225     if (Err) {
226       OS << "Expected<> contains unexpected error: "
227          << toString(std::move(Err));
228     }
229     if (Result)
230       return ::testing::AssertionSuccess() << OS.str();
231     else
232       return ::testing::AssertionFailure() << OS.str();
233   }
234 }
235 
236 } // end namespace ast_matchers
237 } // end namespace clang
238 
239 #endif
240