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