From ef0b054301cb6f9a8540dd0ef577a1fc22c73624 Mon Sep 17 00:00:00 2001
From: Joao Matos <joao@tritao.eu>
Date: Tue, 6 Nov 2018 16:09:19 +0000
Subject: [PATCH] Split Parser::ParseHeader into helper methods.

Use clang::SemaConsumer interface callback to walk over the parsed AST.

This deals with a nasty bug that occurs when processing late-parsed template declarations.

The clang::Parser object life is tied to clang::ParseAST function, so we need to process the AST in the callback.

Fixes https://github.com/mono/CppSharp/issues/1123 and (hopefully) https://github.com/mono/CppSharp/issues/1124.
---
 src/CppParser/Parser.cpp | 133 ++++++++++++++++++++++-----------------
 src/CppParser/Parser.h   |  19 +++++-
 tests/Common/Common.h    |  24 +++++++
 3 files changed, 117 insertions(+), 59 deletions(-)

diff --git a/src/CppParser/Parser.cpp b/src/CppParser/Parser.cpp
index 679fce62..cc2b8a14 100644
--- a/src/CppParser/Parser.cpp
+++ b/src/CppParser/Parser.cpp
@@ -44,8 +44,7 @@
 #include <clang/Driver/ToolChain.h>
 #include <clang/Driver/Util.h>
 #include <clang/Index/USRGeneration.h>
-#include <CodeGen/CodeGenModule.h>
-#include <CodeGen/CodeGenTypes.h>
+
 #include <CodeGen/TargetInfo.h>
 #include <CodeGen/CGCall.h>
 #include <CodeGen/CGCXXABI.h>
@@ -3278,9 +3277,8 @@ bool Parser::IsValidDeclaration(const clang::SourceLocation& Loc)
 
 //-----------------------------------//
 
-void Parser::WalkAST()
+void Parser::WalkAST(clang::TranslationUnitDecl* TU)
 {
-    auto TU = c->getASTContext().getTranslationUnitDecl();
     for (auto D : TU->decls())
     {
         if (D->getBeginLoc().isValid() &&
@@ -4049,35 +4047,32 @@ void Parser::HandleDiagnostics(ParserResult* res)
     }
 }
 
-ParserResult* Parser::ParseHeader(const std::vector<std::string>& SourceFiles)
+void Parser::SetupLLVMCodegen()
 {
-    assert(opts->ASTContext && "Expected a valid ASTContext");
-
-    auto res = new ParserResult();
-
-    if (SourceFiles.empty())
-    {
-        res->kind = ParserResultKind::FileNotFound;
-        return res;
-    }
+    // Initialize enough Clang codegen machinery so we can get at ABI details.
+    LLVMModule.reset(new llvm::Module("", LLVMCtx));
 
-    Setup();
+    LLVMModule->setTargetTriple(c->getTarget().getTriple().getTriple());
+    LLVMModule->setDataLayout(c->getTarget().getDataLayout());
 
-    std::unique_ptr<clang::SemaConsumer> SC(new clang::SemaConsumer());
-    c->setASTConsumer(std::move(SC));
+    CGM.reset(new clang::CodeGen::CodeGenModule(c->getASTContext(),
+        c->getHeaderSearchOpts(), c->getPreprocessorOpts(),
+        c->getCodeGenOpts(), *LLVMModule, c->getDiagnostics()));
 
-    c->createSema(clang::TU_Complete, 0);
+    CGT.reset(new clang::CodeGen::CodeGenTypes(*CGM.get()));
 
-    auto DiagClient = new DiagnosticConsumer();
-    c->getDiagnostics().setClient(DiagClient);
+    codeGenTypes = CGT.get();
+}
 
+bool Parser::SetupSourceFiles(const std::vector<std::string>& SourceFiles,
+    std::vector<const clang::FileEntry*>& FileEntries)
+{
     // Check that the file is reachable.
     const clang::DirectoryLookup *Dir;
     llvm::SmallVector<
         std::pair<const clang::FileEntry *, const clang::DirectoryEntry *>,
         0> Includers;
 
-    std::vector<const clang::FileEntry*> FileEntries;
     for (const auto& SourceFile : SourceFiles)
     {
         auto FileEntry = c->getPreprocessor().getHeaderSearchInfo().LookupFile(SourceFile,
@@ -4085,71 +4080,97 @@ ParserResult* Parser::ParseHeader(const std::vector<std::string>& SourceFiles)
             nullptr, Dir, Includers, nullptr, nullptr, nullptr, nullptr, nullptr);
 
         if (!FileEntry)
-        {
-            res->kind = ParserResultKind::FileNotFound;
-            return res;
-        }
+            return false;
+
         FileEntries.push_back(FileEntry);
     }
 
     // Create a virtual file that includes the header. This gets rid of some
     // Clang warnings about parsing an header file as the main file.
 
-    std::string str;
+    std::string source;
     for (const auto& SourceFile : SourceFiles)
     {
-        str += "#include \"" + SourceFile + "\"" + "\n";
+        source += "#include \"" + SourceFile + "\"" + "\n";
     }
-    str += "\0";
+    source += "\0";
 
-    auto buffer = llvm::MemoryBuffer::getMemBuffer(str);
+    auto buffer = llvm::MemoryBuffer::getMemBufferCopy(source);
     auto& SM = c->getSourceManager();
     SM.setMainFileID(SM.createFileID(std::move(buffer)));
 
-    clang::DiagnosticConsumer* client = c->getDiagnostics().getClient();
-    client->BeginSourceFile(c->getLangOpts(), &c->getPreprocessor());
+    return true;
+}
 
-    ParseAST(c->getSema());
+class SemaConsumer : public clang::SemaConsumer {
+    CppSharp::CppParser::Parser& Parser;
+    std::vector<const clang::FileEntry*>& FileEntries;
+public:
+    SemaConsumer(CppSharp::CppParser::Parser& parser,
+        std::vector<const clang::FileEntry*>& entries)
+        : Parser(parser), FileEntries(entries) {}
+    virtual void HandleTranslationUnit(clang::ASTContext& Ctx) override;
+};
 
-    client->EndSourceFile();
+void SemaConsumer::HandleTranslationUnit(clang::ASTContext& Ctx)
+{
+    auto FileEntry = FileEntries[0];
+    auto FileName = FileEntry->getName();
+    auto Unit = Parser.opts->ASTContext->FindOrCreateModule(FileName);
 
-    HandleDiagnostics(res);
+    auto TU = Ctx.getTranslationUnitDecl();
+    Parser.HandleDeclaration(TU, Unit);
 
-    if(client->getNumErrors() != 0)
+    if (Unit->originalPtr == nullptr)
+        Unit->originalPtr = (void*)FileEntry;
+
+    Parser.WalkAST(TU);
+}
+
+ParserResult* Parser::ParseHeader(const std::vector<std::string>& SourceFiles)
+{
+    assert(opts->ASTContext && "Expected a valid ASTContext");
+
+    auto res = new ParserResult();
+
+    if (SourceFiles.empty())
     {
-        res->kind = ParserResultKind::Error;
+        res->kind = ParserResultKind::FileNotFound;
         return res;
     }
 
-    auto& AST = c->getASTContext();
+    Setup();
+    SetupLLVMCodegen();
 
-    auto FileEntry = FileEntries[0];
-    auto FileName = FileEntry->getName();
-    auto Unit = opts->ASTContext->FindOrCreateModule(FileName);
+    std::vector<const clang::FileEntry*> FileEntries;
+    if (!SetupSourceFiles(SourceFiles, FileEntries))
+    {
+        res->kind = ParserResultKind::FileNotFound;
+        return res;
+    }
 
-    auto TU = AST.getTranslationUnitDecl();
-    HandleDeclaration(TU, Unit);
+    std::unique_ptr<SemaConsumer> SC(new SemaConsumer(*this, FileEntries));
+    c->setASTConsumer(std::move(SC));
 
-    if (Unit->originalPtr == nullptr)
-        Unit->originalPtr = (void*)FileEntry;
+    c->createSema(clang::TU_Complete, 0);
 
-    // Initialize enough Clang codegen machinery so we can get at ABI details.
-    llvm::LLVMContext Ctx;
-    std::unique_ptr<llvm::Module> M(new llvm::Module("", Ctx));
+    auto DiagClient = new DiagnosticConsumer();
+    c->getDiagnostics().setClient(DiagClient);
 
-    M->setTargetTriple(c->getTarget().getTriple().getTriple());
-    M->setDataLayout(c->getTarget().getDataLayout());
+    clang::DiagnosticConsumer* client = c->getDiagnostics().getClient();
+    client->BeginSourceFile(c->getLangOpts(), &c->getPreprocessor());
 
-    std::unique_ptr<clang::CodeGen::CodeGenModule> CGM(
-        new clang::CodeGen::CodeGenModule(c->getASTContext(), c->getHeaderSearchOpts(),
-        c->getPreprocessorOpts(), c->getCodeGenOpts(), *M, c->getDiagnostics()));
+    ParseAST(c->getSema());
 
-    std::unique_ptr<clang::CodeGen::CodeGenTypes> CGT(
-        new clang::CodeGen::CodeGenTypes(*CGM.get()));
+    client->EndSourceFile();
 
-    codeGenTypes = CGT.get();
+    HandleDiagnostics(res);
 
-    WalkAST();
+    if(client->getNumErrors() != 0)
+    {
+        res->kind = ParserResultKind::Error;
+        return res;
+    }
 
     res->targetInfo = GetTargetInfo();
 
diff --git a/src/CppParser/Parser.h b/src/CppParser/Parser.h
index 998102c4..2c77c081 100644
--- a/src/CppParser/Parser.h
+++ b/src/CppParser/Parser.h
@@ -18,6 +18,9 @@
 #include <clang/Frontend/CompilerInstance.h>
 #include <clang/Sema/Scope.h>
 
+#include <CodeGen/CodeGenModule.h>
+#include <CodeGen/CodeGenTypes.h>
+
 #include "CXXABI.h"
 #include "CppParser.h"
 
@@ -53,11 +56,19 @@ public:
     ParserResult* ParseHeader(const std::vector<std::string>& SourceFiles);
     ParserResult* ParseLibrary(const std::string& File);
 
+    void WalkAST(clang::TranslationUnitDecl* TU);
+    void HandleDeclaration(const clang::Decl* D, Declaration* Decl);
+    CppParserOptions* opts;
+
 private:
+
+    void SetupLLVMCodegen();
+    bool SetupSourceFiles(const std::vector<std::string>& SourceFiles,
+        std::vector<const clang::FileEntry*>& FileEntries);
+
     bool IsSupported(const clang::NamedDecl* ND);
     bool IsSupported(const clang::CXXMethodDecl* MD);
     // AST traversers
-    void WalkAST();
     Declaration* WalkDeclaration(const clang::Decl* D);
     Declaration* WalkDeclarationDef(clang::Decl* D);
     Enumeration* WalkEnum(const clang::EnumDecl* ED);
@@ -137,7 +148,6 @@ private:
     DeclarationContext* GetNamespace(const clang::Decl* D, const clang::DeclContext* Ctx);
     DeclarationContext* GetNamespace(const clang::Decl* D);
 
-    void HandleDeclaration(const clang::Decl* D, Declaration* Decl);
     void HandleOriginalText(const clang::Decl* D, Declaration* Decl);
     void HandleComments(const clang::Decl* D, Declaration* Decl);
     void HandleDiagnostics(ParserResult* res);
@@ -154,9 +164,12 @@ private:
     ParserTargetInfo* GetTargetInfo();
 
     int index;
-    CppParserOptions* opts;
     std::unique_ptr<clang::CompilerInstance> c;
     clang::TargetCXXABI::Kind targetABI;
+    llvm::LLVMContext LLVMCtx;
+    std::unique_ptr<llvm::Module> LLVMModule;
+    std::unique_ptr<clang::CodeGen::CodeGenModule> CGM;
+    std::unique_ptr<clang::CodeGen::CodeGenTypes> CGT;
     clang::CodeGen::CodeGenTypes* codeGenTypes;
     std::unordered_map<const clang::TemplateTypeParmDecl*, TypeTemplateParameter*> walkedTypeTemplateParameters;
     std::unordered_map<const clang::TemplateTemplateParmDecl*, TemplateTemplateParameter*> walkedTemplateTemplateParameters;
diff --git a/tests/Common/Common.h b/tests/Common/Common.h
index 3e8cd3ee..8e32acd0 100644
--- a/tests/Common/Common.h
+++ b/tests/Common/Common.h
@@ -1433,3 +1433,27 @@ enum ItemsDifferByCase
     Case_a,
     Case_A
 };
+
+template <typename T> struct MyListBase
+{
+protected:
+    ~MyListBase() {}
+};
+
+template <typename T>
+class MyList : public MyListBase<T>
+{
+public:
+    inline MyList() { }
+};
+
+template <> struct MyListBase<int>
+{
+};
+
+class MyIntList : public MyList<int>
+{
+    inline MyIntList(MyList<int> &&l) { }
+};
+
+void MyFunc(MyList<void *> *list);