teemperor created this revision.
teemperor added reviewers: martong, balazske, a.sidorin, shafik.
Herald added subscribers: cfe-commits, rnkovacs.
Herald added a project: clang.

We are currently implementing support in LLDB that reconstructs the STL 
templates from
the target program in the expression evaluator. This reconstruction happens 
during the
import process from our debug info AST into the expression evaluation AST[1], 
which means
we need a way to intercept the ASTImporter import process in some controlled 
fashion.

This patch adds a shim to the ASTImporter that allows us to intercept any 
Import calls an
ASTImporter may make (either externally or while importing internally). We then 
provide
a shim in LLDB that does do our template reconstruction.

The shim is on purpose not reusing some ASTImporter functionalities like the 
map of already
imported decls because we want to keep it flexible. If a Shim wants to use this 
functionality
it needs to update/check the map itself.

[1] The reason for this is that we can't reliable import templates with the 
ASTImporter, so
actually reconstructing the template in the debug info AST and then importing 
it doesn't work.
It would also be much slower.


Repository:
  rC Clang

https://reviews.llvm.org/D59485

Files:
  clang/include/clang/AST/ASTImporter.h
  clang/lib/AST/ASTImporter.cpp
  clang/unittests/AST/ASTImporterTest.cpp

Index: clang/unittests/AST/ASTImporterTest.cpp
===================================================================
--- clang/unittests/AST/ASTImporterTest.cpp
+++ clang/unittests/AST/ASTImporterTest.cpp
@@ -316,11 +316,13 @@
     std::unique_ptr<ASTUnit> Unit;
     TranslationUnitDecl *TUDecl = nullptr;
     std::unique_ptr<ASTImporter> Importer;
-    TU(StringRef Code, StringRef FileName, ArgVector Args)
+    ImportShim *Shim;
+    TU(StringRef Code, StringRef FileName, ArgVector Args,
+       ImportShim *Shim = nullptr)
         : Code(Code), FileName(FileName),
           Unit(tooling::buildASTFromCodeWithArgs(this->Code, Args,
                                                  this->FileName)),
-          TUDecl(Unit->getASTContext().getTranslationUnitDecl()) {
+          TUDecl(Unit->getASTContext().getTranslationUnitDecl()), Shim(Shim) {
       Unit->enableSourceFileDiagnostics();
     }
 
@@ -331,6 +333,7 @@
             new ASTImporter(ToAST->getASTContext(), ToAST->getFileManager(),
                             Unit->getASTContext(), Unit->getFileManager(),
                             false, &LookupTable));
+        Importer->setShim(Shim);
       }
       assert(&ToAST->getASTContext() == &Importer->getToContext());
       createVirtualFileIfNeeded(ToAST, FileName, Code);
@@ -401,11 +404,12 @@
   // Must not be called more than once within the same test.
   std::tuple<Decl *, Decl *>
   getImportedDecl(StringRef FromSrcCode, Language FromLang, StringRef ToSrcCode,
-                  Language ToLang, StringRef Identifier = DeclToImportID) {
+                  Language ToLang, StringRef Identifier = DeclToImportID,
+                  ImportShim *Shim = nullptr) {
     ArgVector FromArgs = getArgVectorForLanguage(FromLang),
               ToArgs = getArgVectorForLanguage(ToLang);
 
-    FromTUs.emplace_back(FromSrcCode, InputFileName, FromArgs);
+    FromTUs.emplace_back(FromSrcCode, InputFileName, FromArgs, Shim);
     TU &FromTU = FromTUs.back();
 
     assert(!ToAST);
@@ -455,6 +459,12 @@
     return ToAST->getASTContext().getTranslationUnitDecl();
   }
 
+  ASTImporter &getImporter(Decl *From, Language ToLang) {
+    lazyInitToAST(ToLang, "", OutputFileName);
+    TU *FromTU = findFromTU(From);
+    return *FromTU->Importer;
+  }
+
   // Import the given Decl into the ToCtx.
   // May be called several times in a given test.
   // The different instances of the param From may have different ASTContext.
@@ -544,6 +554,81 @@
   EXPECT_THAT(RedeclsD1, ::testing::ContainerEq(RedeclsD2));
 }
 
+namespace {
+class RedirectShim : public ImportShim {
+  llvm::Optional<Decl *> Import(ASTImporter &Importer, Decl *FromD) override {
+    auto *ND = dyn_cast<NamedDecl>(FromD);
+    if (!ND)
+      return {};
+    if (ND->getName() != "shouldNotBeImported")
+      return {};
+    for (Decl *D : Importer.getToContext().getTranslationUnitDecl()->decls())
+      if (auto ND = dyn_cast<NamedDecl>(D))
+        if (ND->getName() == "realDecl")
+          return ND;
+    return {};
+  }
+};
+} // namespace
+
+struct ShimTestCase : ASTImporterOptionSpecificTestBase {};
+
+// Test that the shim can intercept an import call.
+TEST_P(ShimTestCase, InterceptImport) {
+  RedirectShim Shim;
+  Decl *From, *To;
+  std::tie(From, To) = getImportedDecl("class shouldNotBeImported {};",
+                                       Lang_CXX, "class realDecl {};", Lang_CXX,
+                                       "shouldNotBeImported", &Shim);
+  auto *Imported = cast<CXXRecordDecl>(To);
+  EXPECT_EQ(Imported->getQualifiedNameAsString(), "realDecl");
+
+  // Make sure the Shim prevented the importing of the decl.
+  auto *ToTU = Imported->getTranslationUnitDecl();
+  auto Pattern = functionDecl(hasName("shouldNotBeImported"));
+  unsigned count =
+      DeclCounterWithPredicate<CXXRecordDecl>().match(ToTU, Pattern);
+  EXPECT_EQ(0U, count);
+}
+
+// Test that when we indirectly import a declaration the shim still works. Also
+// tests that the shim allows the import process to continue when we try to
+// import a declaration that we ignore in the shim.
+TEST_P(ShimTestCase, InterceptIndirectImport) {
+  RedirectShim Shim;
+  Decl *From, *To;
+  std::tie(From, To) =
+      getImportedDecl("class shouldNotBeImported {};"
+                      "class F { shouldNotBeImported f; };",
+                      Lang_CXX, "class realDecl {};", Lang_CXX, "F", &Shim);
+
+  // Make sure the Shim prevented the importing of the decl.
+  auto *ToTU = To->getTranslationUnitDecl();
+  auto Pattern = functionDecl(hasName("shouldNotBeImported"));
+  unsigned count =
+      DeclCounterWithPredicate<CXXRecordDecl>().match(ToTU, Pattern);
+  EXPECT_EQ(0U, count);
+}
+
+namespace {
+class NoOpShim : public ImportShim {
+  llvm::Optional<Decl *> Import(ASTImporter &Importer, Decl *FromD) override {
+    return {};
+  }
+};
+} // namespace
+
+// Test a shim that just does nothing.
+TEST_P(ShimTestCase, NoOpShim) {
+  NoOpShim Shim;
+  Decl *From, *To;
+  std::tie(From, To) =
+      getImportedDecl("class declToImport {};", Lang_CXX, "class realDecl {};",
+                      Lang_CXX, DeclToImportID, &Shim);
+  auto *Imported = cast<CXXRecordDecl>(To);
+  EXPECT_EQ(Imported->getQualifiedNameAsString(), "declToImport");
+}
+
 TEST_P(ImportExpr, ImportStringLiteral) {
   MatchVerifier<Decl> Verifier;
   testImport(
@@ -5512,6 +5597,9 @@
 INSTANTIATE_TEST_CASE_P(ParameterizedTests, ASTImporterOptionSpecificTestBase,
                         DefaultTestValuesForRunOptions, );
 
+INSTANTIATE_TEST_CASE_P(ParameterizedTests, ShimTestCase,
+                        DefaultTestValuesForRunOptions, );
+
 INSTANTIATE_TEST_CASE_P(ParameterizedTests, ImportFunctions,
                         DefaultTestValuesForRunOptions, );
 
Index: clang/lib/AST/ASTImporter.cpp
===================================================================
--- clang/lib/AST/ASTImporter.cpp
+++ clang/lib/AST/ASTImporter.cpp
@@ -7827,6 +7827,14 @@
   if (!FromD)
     return nullptr;
 
+  // First check if the Shim wants to rewrite this import call.
+  if (Shim) {
+    llvm::Optional<Decl *> NewD = Shim->Import(*this, FromD);
+    // Shim has given us a decl and we pretend we successfully imported.
+    if (NewD)
+      return *NewD;
+  }
+
   ASTNodeImporter Importer(*this);
 
   // Check whether we've already imported this declaration.
Index: clang/include/clang/AST/ASTImporter.h
===================================================================
--- clang/include/clang/AST/ASTImporter.h
+++ clang/include/clang/AST/ASTImporter.h
@@ -78,6 +78,20 @@
   // the different entries in a given redecl chain.
   llvm::SmallVector<Decl*, 2> getCanonicalForwardRedeclChain(Decl* D);
 
+  class ASTImporter;
+  /// Shim that allows intercepting the import process of the ASTImporter.
+  struct ImportShim {
+    virtual ~ImportShim() = default;
+    /// Called from the ASTImporter before the given Decl is imported.
+    ///
+    /// If this method returns a Decl, the ASTImporter will abort the current
+    /// import step and treat the returned decl as if it was just imported.
+    /// If this method returns nothing, the ASTImporter continues to import the
+    /// given Decl on its own.
+    virtual llvm::Optional<Decl *> Import(ASTImporter &Importer,
+                                          Decl *FromD) = 0;
+  };
+
   /// Imports selected nodes from one AST context into another context,
   /// merging AST nodes where appropriate.
   class ASTImporter {
@@ -137,6 +151,10 @@
     /// (which we have already complained about).
     NonEquivalentDeclSet NonEquivalentDecls;
 
+    /// The Shim that should rewrite the import calls of this ASTImporter, or
+    /// a nullptr of this ASTImporter has no shim.
+    ImportShim *Shim = nullptr;
+
     using FoundDeclsTy = SmallVector<NamedDecl *, 2>;
     FoundDeclsTy findDeclsInToCtx(DeclContext *DC, DeclarationName Name);
 
@@ -170,6 +188,10 @@
     /// to-be-completed forward declarations when possible.
     bool isMinimalImport() const { return Minimal; }
 
+    ImportShim *getShim() { return Shim; }
+
+    void setShim(ImportShim *S) { Shim = S; }
+
     /// \brief Import the given object, returns the result.
     ///
     /// \param To Import the object into this variable.
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to