1 //===-- ClangExpressionDeclMapTest.cpp ------------------------------------===// 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 #include "Plugins/ExpressionParser/Clang/ClangExpressionDeclMap.h" 10 #include "TestingSupport/Symbol/ClangTestUtils.h" 11 #include "lldb/Host/FileSystem.h" 12 #include "lldb/Host/HostInfo.h" 13 #include "lldb/Symbol/ClangASTContext.h" 14 #include "lldb/Symbol/ClangUtil.h" 15 #include "lldb/lldb-defines.h" 16 #include "gtest/gtest.h" 17 18 using namespace lldb_private; 19 using namespace lldb; 20 21 namespace { 22 struct FakeClangExpressionDeclMap : public ClangExpressionDeclMap { 23 FakeClangExpressionDeclMap(const ClangASTImporterSP &importer) 24 : ClangExpressionDeclMap(false, nullptr, lldb::TargetSP(), importer, 25 nullptr) { 26 m_scratch_context = clang_utils::createAST(); 27 } 28 std::unique_ptr<ClangASTContext> m_scratch_context; 29 /// Adds a persistent decl that can be found by the ClangExpressionDeclMap 30 /// via GetPersistentDecl. 31 void AddPersistentDeclForTest(clang::NamedDecl *d) { 32 // The declaration needs to have '$' prefix in its name like every 33 // persistent declaration and must be inside the scratch AST context. 34 assert(d); 35 assert(d->getName().startswith("$")); 36 assert(&d->getASTContext() == m_scratch_context->getASTContext()); 37 m_persistent_decls[d->getName()] = d; 38 } 39 40 protected: 41 // ClangExpressionDeclMap hooks. 42 43 clang::NamedDecl *GetPersistentDecl(ConstString name) override { 44 // ClangExpressionDeclMap wants to know if there is a persistent decl 45 // with the given name. Check the 46 return m_persistent_decls.lookup(name.GetStringRef()); 47 } 48 49 private: 50 /// The persistent decls in this test with their names as keys. 51 llvm::DenseMap<llvm::StringRef, clang::NamedDecl *> m_persistent_decls; 52 }; 53 } // namespace 54 55 namespace { 56 struct ClangExpressionDeclMapTest : public testing::Test { 57 static void SetUpTestCase() { 58 FileSystem::Initialize(); 59 HostInfo::Initialize(); 60 } 61 static void TearDownTestCase() { 62 HostInfo::Terminate(); 63 FileSystem::Terminate(); 64 } 65 66 /// The ClangASTImporter used during the test. 67 ClangASTImporterSP importer; 68 /// The ExpressionDeclMap for the current test case. 69 std::unique_ptr<FakeClangExpressionDeclMap> decl_map; 70 71 /// The target AST that lookup results should be imported to. 72 std::unique_ptr<ClangASTContext> target_ast; 73 74 void SetUp() override { 75 importer = std::make_shared<ClangASTImporter>(); 76 decl_map = std::make_unique<FakeClangExpressionDeclMap>(importer); 77 target_ast = clang_utils::createAST(); 78 decl_map->InstallASTContext(*target_ast); 79 } 80 81 void TearDown() override { 82 importer.reset(); 83 decl_map.reset(); 84 target_ast.reset(); 85 } 86 }; 87 } // namespace 88 89 TEST_F(ClangExpressionDeclMapTest, TestUnknownIdentifierLookup) { 90 // Tests looking up an identifier that can't be found anywhere. 91 92 // Setup a NameSearchContext for 'foo'. 93 llvm::SmallVector<clang::NamedDecl *, 16> decls; 94 clang::DeclarationName name = 95 clang_utils::getDeclarationName(*target_ast, "foo"); 96 const clang::DeclContext *dc = target_ast->GetTranslationUnitDecl(); 97 NameSearchContext search(*decl_map, decls, name, dc); 98 99 decl_map->FindExternalVisibleDecls(search); 100 101 // This shouldn't exist so we should get no lookups. 102 EXPECT_EQ(0U, decls.size()); 103 } 104 105 TEST_F(ClangExpressionDeclMapTest, TestPersistentDeclLookup) { 106 // Tests looking up a persistent decl from the scratch AST context. 107 108 // Create a '$persistent_class' record and add it as a persistent variable 109 // to the scratch AST context. 110 llvm::StringRef decl_name = "$persistent_class"; 111 CompilerType persistent_type = 112 clang_utils::createRecord(*decl_map->m_scratch_context, decl_name); 113 decl_map->AddPersistentDeclForTest(ClangUtil::GetAsTagDecl(persistent_type)); 114 115 // Setup a NameSearchContext for $persistent_class; 116 llvm::SmallVector<clang::NamedDecl *, 16> decls; 117 clang::DeclarationName name = 118 clang_utils::getDeclarationName(*target_ast, decl_name); 119 const clang::DeclContext *dc = target_ast->GetTranslationUnitDecl(); 120 NameSearchContext search(*decl_map, decls, name, dc); 121 122 // Search and check that we found $persistent_class. 123 decl_map->FindExternalVisibleDecls(search); 124 EXPECT_EQ(1U, decls.size()); 125 EXPECT_EQ(decl_name, decls.front()->getQualifiedNameAsString()); 126 auto *record = llvm::cast<clang::RecordDecl>(decls.front()); 127 // The class was minimally imported from the scratch AST context. 128 EXPECT_TRUE(record->hasExternalLexicalStorage()); 129 } 130