blob: 2b5c8f6e140482cf841f515a7b50679b12a18892 [file]
//===-- TestTypeSystemClang.cpp -------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#include "Plugins/ExpressionParser/Clang/ClangUtil.h"
#include "Plugins/TypeSystem/Clang/TypeSystemClang.h"
#include "TestingSupport/SubsystemRAII.h"
#include "TestingSupport/Symbol/ClangTestUtils.h"
#include "lldb/Core/Declaration.h"
#include "lldb/Host/FileSystem.h"
#include "lldb/Host/HostInfo.h"
#include "lldb/lldb-enumerations.h"
#include "clang/AST/DeclCXX.h"
#include "clang/AST/DeclObjC.h"
#include "clang/AST/ExprCXX.h"
#include "llvm/IR/GlobalValue.h"
#include "gtest/gtest.h"
using namespace clang;
using namespace lldb;
using namespace lldb_private;
class TestTypeSystemClang : public testing::Test {
public:
SubsystemRAII<FileSystem, HostInfo> subsystems;
void SetUp() override {
m_holder =
std::make_unique<clang_utils::TypeSystemClangHolder>("test ASTContext");
m_ast = m_holder->GetAST();
}
void TearDown() override {
m_ast = nullptr;
m_holder.reset();
}
protected:
TypeSystemClang *m_ast = nullptr;
std::unique_ptr<clang_utils::TypeSystemClangHolder> m_holder;
QualType GetBasicQualType(BasicType type) const {
return ClangUtil::GetQualType(m_ast->GetBasicTypeFromAST(type));
}
QualType GetBasicQualType(const char *name) const {
return ClangUtil::GetQualType(
m_ast->GetBuiltinTypeByName(ConstString(name)));
}
CompilerType GetBuiltinTypeForDWARFEncodingAndBitSize(
llvm::StringRef type_name, uint32_t encoding, uint32_t bit_size) const {
return m_ast->GetBuiltinTypeForDWARFEncodingAndBitSize(type_name, encoding,
bit_size);
}
};
TEST_F(TestTypeSystemClang, TestGetBasicTypeFromEnum) {
clang::ASTContext &context = m_ast->getASTContext();
EXPECT_TRUE(
context.hasSameType(GetBasicQualType(eBasicTypeBool), context.BoolTy));
EXPECT_TRUE(
context.hasSameType(GetBasicQualType(eBasicTypeChar), context.CharTy));
EXPECT_TRUE(context.hasSameType(GetBasicQualType(eBasicTypeChar8),
context.Char8Ty));
EXPECT_TRUE(context.hasSameType(GetBasicQualType(eBasicTypeChar16),
context.Char16Ty));
EXPECT_TRUE(context.hasSameType(GetBasicQualType(eBasicTypeChar32),
context.Char32Ty));
EXPECT_TRUE(context.hasSameType(GetBasicQualType(eBasicTypeDouble),
context.DoubleTy));
EXPECT_TRUE(context.hasSameType(GetBasicQualType(eBasicTypeDoubleComplex),
context.getComplexType(context.DoubleTy)));
EXPECT_TRUE(
context.hasSameType(GetBasicQualType(eBasicTypeFloat), context.FloatTy));
EXPECT_TRUE(context.hasSameType(GetBasicQualType(eBasicTypeFloatComplex),
context.getComplexType(context.FloatTy)));
EXPECT_TRUE(
context.hasSameType(GetBasicQualType(eBasicTypeHalf), context.HalfTy));
EXPECT_TRUE(context.hasSameType(GetBasicQualType(eBasicTypeFloat128),
context.Float128Ty));
EXPECT_TRUE(
context.hasSameType(GetBasicQualType(eBasicTypeInt), context.IntTy));
EXPECT_TRUE(context.hasSameType(GetBasicQualType(eBasicTypeInt128),
context.Int128Ty));
EXPECT_TRUE(
context.hasSameType(GetBasicQualType(eBasicTypeLong), context.LongTy));
EXPECT_TRUE(context.hasSameType(GetBasicQualType(eBasicTypeLongDouble),
context.LongDoubleTy));
EXPECT_TRUE(
context.hasSameType(GetBasicQualType(eBasicTypeLongDoubleComplex),
context.getComplexType(context.LongDoubleTy)));
EXPECT_TRUE(context.hasSameType(GetBasicQualType(eBasicTypeLongLong),
context.LongLongTy));
EXPECT_TRUE(context.hasSameType(GetBasicQualType(eBasicTypeNullPtr),
context.NullPtrTy));
EXPECT_TRUE(context.hasSameType(GetBasicQualType(eBasicTypeObjCClass),
context.getObjCClassType()));
EXPECT_TRUE(context.hasSameType(GetBasicQualType(eBasicTypeObjCID),
context.getObjCIdType()));
EXPECT_TRUE(context.hasSameType(GetBasicQualType(eBasicTypeObjCSel),
context.getObjCSelType()));
EXPECT_TRUE(
context.hasSameType(GetBasicQualType(eBasicTypeShort), context.ShortTy));
EXPECT_TRUE(context.hasSameType(GetBasicQualType(eBasicTypeSignedChar),
context.SignedCharTy));
EXPECT_TRUE(context.hasSameType(GetBasicQualType(eBasicTypeUnsignedChar),
context.UnsignedCharTy));
EXPECT_TRUE(context.hasSameType(GetBasicQualType(eBasicTypeUnsignedInt),
context.UnsignedIntTy));
EXPECT_TRUE(context.hasSameType(GetBasicQualType(eBasicTypeUnsignedInt128),
context.UnsignedInt128Ty));
EXPECT_TRUE(context.hasSameType(GetBasicQualType(eBasicTypeUnsignedLong),
context.UnsignedLongTy));
EXPECT_TRUE(context.hasSameType(GetBasicQualType(eBasicTypeUnsignedLongLong),
context.UnsignedLongLongTy));
EXPECT_TRUE(context.hasSameType(GetBasicQualType(eBasicTypeUnsignedShort),
context.UnsignedShortTy));
EXPECT_TRUE(
context.hasSameType(GetBasicQualType(eBasicTypeVoid), context.VoidTy));
EXPECT_TRUE(
context.hasSameType(GetBasicQualType(eBasicTypeWChar), context.WCharTy));
}
TEST_F(TestTypeSystemClang, TestGetBasicTypeFromName) {
EXPECT_EQ(GetBasicQualType(eBasicTypeChar), GetBasicQualType("char"));
EXPECT_EQ(GetBasicQualType(eBasicTypeSignedChar),
GetBasicQualType("signed char"));
EXPECT_EQ(GetBasicQualType(eBasicTypeUnsignedChar),
GetBasicQualType("unsigned char"));
EXPECT_EQ(GetBasicQualType(eBasicTypeWChar), GetBasicQualType("wchar_t"));
EXPECT_EQ(GetBasicQualType(eBasicTypeSignedWChar),
GetBasicQualType("signed wchar_t"));
EXPECT_EQ(GetBasicQualType(eBasicTypeUnsignedWChar),
GetBasicQualType("unsigned wchar_t"));
EXPECT_EQ(GetBasicQualType(eBasicTypeShort), GetBasicQualType("short"));
EXPECT_EQ(GetBasicQualType(eBasicTypeShort), GetBasicQualType("short int"));
EXPECT_EQ(GetBasicQualType(eBasicTypeUnsignedShort),
GetBasicQualType("unsigned short"));
EXPECT_EQ(GetBasicQualType(eBasicTypeUnsignedShort),
GetBasicQualType("unsigned short int"));
EXPECT_EQ(GetBasicQualType(eBasicTypeInt), GetBasicQualType("int"));
EXPECT_EQ(GetBasicQualType(eBasicTypeInt), GetBasicQualType("signed int"));
EXPECT_EQ(GetBasicQualType(eBasicTypeUnsignedInt),
GetBasicQualType("unsigned int"));
EXPECT_EQ(GetBasicQualType(eBasicTypeUnsignedInt),
GetBasicQualType("unsigned"));
EXPECT_EQ(GetBasicQualType(eBasicTypeLong), GetBasicQualType("long"));
EXPECT_EQ(GetBasicQualType(eBasicTypeLong), GetBasicQualType("long int"));
EXPECT_EQ(GetBasicQualType(eBasicTypeUnsignedLong),
GetBasicQualType("unsigned long"));
EXPECT_EQ(GetBasicQualType(eBasicTypeUnsignedLong),
GetBasicQualType("unsigned long int"));
EXPECT_EQ(GetBasicQualType(eBasicTypeLongLong),
GetBasicQualType("long long"));
EXPECT_EQ(GetBasicQualType(eBasicTypeLongLong),
GetBasicQualType("long long int"));
EXPECT_EQ(GetBasicQualType(eBasicTypeUnsignedLongLong),
GetBasicQualType("unsigned long long"));
EXPECT_EQ(GetBasicQualType(eBasicTypeUnsignedLongLong),
GetBasicQualType("unsigned long long int"));
EXPECT_EQ(GetBasicQualType(eBasicTypeInt128), GetBasicQualType("__int128_t"));
EXPECT_EQ(GetBasicQualType(eBasicTypeUnsignedInt128),
GetBasicQualType("__uint128_t"));
EXPECT_EQ(GetBasicQualType(eBasicTypeInt128), GetBasicQualType("__int128"));
EXPECT_EQ(GetBasicQualType(eBasicTypeUnsignedInt128),
GetBasicQualType("unsigned __int128"));
EXPECT_EQ(GetBasicQualType(eBasicTypeVoid), GetBasicQualType("void"));
EXPECT_EQ(GetBasicQualType(eBasicTypeBool), GetBasicQualType("bool"));
EXPECT_EQ(GetBasicQualType(eBasicTypeFloat), GetBasicQualType("float"));
EXPECT_EQ(GetBasicQualType(eBasicTypeDouble), GetBasicQualType("double"));
EXPECT_EQ(GetBasicQualType(eBasicTypeLongDouble),
GetBasicQualType("long double"));
EXPECT_EQ(GetBasicQualType(eBasicTypeObjCID), GetBasicQualType("id"));
EXPECT_EQ(GetBasicQualType(eBasicTypeObjCSel), GetBasicQualType("SEL"));
EXPECT_EQ(GetBasicQualType(eBasicTypeNullPtr), GetBasicQualType("nullptr"));
}
void VerifyEncodingAndBitSize(TypeSystemClang &clang_context,
lldb::Encoding encoding, unsigned int bit_size) {
clang::ASTContext &context = clang_context.getASTContext();
CompilerType type =
clang_context.GetBuiltinTypeForEncodingAndBitSize(encoding, bit_size);
EXPECT_TRUE(type.IsValid());
QualType qtype = ClangUtil::GetQualType(type);
EXPECT_FALSE(qtype.isNull());
if (qtype.isNull())
return;
uint64_t actual_size = context.getTypeSize(qtype);
EXPECT_EQ(bit_size, actual_size);
const clang::Type *type_ptr = qtype.getTypePtr();
EXPECT_NE(nullptr, type_ptr);
if (!type_ptr)
return;
EXPECT_TRUE(type_ptr->isBuiltinType());
switch (encoding) {
case eEncodingSint:
EXPECT_TRUE(type_ptr->isSignedIntegerType());
break;
case eEncodingUint:
EXPECT_TRUE(type_ptr->isUnsignedIntegerType());
break;
case eEncodingIEEE754:
EXPECT_TRUE(type_ptr->isFloatingType());
break;
default:
FAIL() << "Unexpected encoding";
break;
}
}
TEST_F(TestTypeSystemClang, TestBuiltinTypeForEncodingAndBitSize) {
// Make sure we can get types of every possible size in every possible
// encoding.
// We can't make any guarantee about which specific type we get, because the
// standard
// isn't that specific. We only need to make sure the compiler hands us some
// type that
// is both a builtin type and matches the requested bit size.
VerifyEncodingAndBitSize(*m_ast, eEncodingSint, 8);
VerifyEncodingAndBitSize(*m_ast, eEncodingSint, 16);
VerifyEncodingAndBitSize(*m_ast, eEncodingSint, 32);
VerifyEncodingAndBitSize(*m_ast, eEncodingSint, 64);
VerifyEncodingAndBitSize(*m_ast, eEncodingSint, 128);
VerifyEncodingAndBitSize(*m_ast, eEncodingUint, 8);
VerifyEncodingAndBitSize(*m_ast, eEncodingUint, 16);
VerifyEncodingAndBitSize(*m_ast, eEncodingUint, 32);
VerifyEncodingAndBitSize(*m_ast, eEncodingUint, 64);
VerifyEncodingAndBitSize(*m_ast, eEncodingUint, 128);
VerifyEncodingAndBitSize(*m_ast, eEncodingIEEE754, 32);
VerifyEncodingAndBitSize(*m_ast, eEncodingIEEE754, 64);
}
TEST_F(TestTypeSystemClang, TestGetBuiltinTypeForDWARFEncodingAndBitSize) {
EXPECT_FALSE(GetBuiltinTypeForDWARFEncodingAndBitSize(
"_BitIn", llvm::dwarf::DW_ATE_signed, 2)
.IsValid());
EXPECT_FALSE(GetBuiltinTypeForDWARFEncodingAndBitSize(
"BitInt", llvm::dwarf::DW_ATE_signed, 2)
.IsValid());
EXPECT_FALSE(GetBuiltinTypeForDWARFEncodingAndBitSize(
"_BitInt(2)", llvm::dwarf::DW_ATE_signed_char, 2)
.IsValid());
EXPECT_FALSE(GetBuiltinTypeForDWARFEncodingAndBitSize(
"_BitInt", llvm::dwarf::DW_ATE_signed_char, 2)
.IsValid());
EXPECT_FALSE(GetBuiltinTypeForDWARFEncodingAndBitSize(
"_BitInt(2)", llvm::dwarf::DW_ATE_unsigned, 2)
.IsValid());
EXPECT_FALSE(GetBuiltinTypeForDWARFEncodingAndBitSize(
"_BitInt", llvm::dwarf::DW_ATE_unsigned, 2)
.IsValid());
EXPECT_EQ(GetBuiltinTypeForDWARFEncodingAndBitSize(
"_BitInt(2)", llvm::dwarf::DW_ATE_signed, 2)
.GetTypeName(),
"_BitInt(2)");
EXPECT_EQ(GetBuiltinTypeForDWARFEncodingAndBitSize(
"_BitInt", llvm::dwarf::DW_ATE_signed, 2)
.GetTypeName(),
"_BitInt(2)");
EXPECT_EQ(GetBuiltinTypeForDWARFEncodingAndBitSize(
"_BitInt(129)", llvm::dwarf::DW_ATE_signed, 129)
.GetTypeName(),
"_BitInt(129)");
EXPECT_EQ(GetBuiltinTypeForDWARFEncodingAndBitSize(
"_BitInt", llvm::dwarf::DW_ATE_signed, 129)
.GetTypeName(),
"_BitInt(129)");
EXPECT_FALSE(GetBuiltinTypeForDWARFEncodingAndBitSize(
"unsigned _BitIn", llvm::dwarf::DW_ATE_unsigned, 2)
.IsValid());
EXPECT_FALSE(GetBuiltinTypeForDWARFEncodingAndBitSize(
"unsigned BitInt", llvm::dwarf::DW_ATE_unsigned, 2)
.IsValid());
EXPECT_FALSE(GetBuiltinTypeForDWARFEncodingAndBitSize(
"unsigned _BitInt(2)", llvm::dwarf::DW_ATE_unsigned_char, 2)
.IsValid());
EXPECT_FALSE(GetBuiltinTypeForDWARFEncodingAndBitSize(
"unsigned _BitInt", llvm::dwarf::DW_ATE_unsigned_char, 2)
.IsValid());
EXPECT_FALSE(GetBuiltinTypeForDWARFEncodingAndBitSize(
"unsigned _BitInt(2)", llvm::dwarf::DW_ATE_signed, 2)
.IsValid());
EXPECT_FALSE(GetBuiltinTypeForDWARFEncodingAndBitSize(
"unsigned _BitInt", llvm::dwarf::DW_ATE_signed, 2)
.IsValid());
EXPECT_EQ(GetBuiltinTypeForDWARFEncodingAndBitSize(
"unsigned _BitInt(2)", llvm::dwarf::DW_ATE_unsigned, 2)
.GetTypeName(),
"unsigned _BitInt(2)");
EXPECT_EQ(GetBuiltinTypeForDWARFEncodingAndBitSize(
"unsigned _BitInt", llvm::dwarf::DW_ATE_unsigned, 2)
.GetTypeName(),
"unsigned _BitInt(2)");
EXPECT_EQ(GetBuiltinTypeForDWARFEncodingAndBitSize(
"unsigned _BitInt(129)", llvm::dwarf::DW_ATE_unsigned, 129)
.GetTypeName(),
"unsigned _BitInt(129)");
EXPECT_EQ(GetBuiltinTypeForDWARFEncodingAndBitSize(
"unsigned _BitInt", llvm::dwarf::DW_ATE_unsigned, 129)
.GetTypeName(),
"unsigned _BitInt(129)");
}
TEST_F(TestTypeSystemClang, TestBitIntTypeInfo) {
EXPECT_EQ(GetBuiltinTypeForDWARFEncodingAndBitSize(
"_BitInt", llvm::dwarf::DW_ATE_signed, 2)
.GetTypeInfo(),
eTypeIsSigned | eTypeIsScalar | eTypeHasValue | eTypeIsInteger);
EXPECT_EQ(GetBuiltinTypeForDWARFEncodingAndBitSize(
"unsigned _BitInt", llvm::dwarf::DW_ATE_unsigned, 2)
.GetTypeInfo(),
eTypeIsScalar | eTypeHasValue | eTypeIsInteger);
}
TEST_F(TestTypeSystemClang, TestBuiltinTypeForEmptyTriple) {
// Test that we can access type-info of builtin Clang AST
// types without crashing even when the target triple is
// empty.
TypeSystemClang ast("empty triple AST", llvm::Triple{});
// This test only makes sense if the builtin ASTContext types were
// not initialized.
ASSERT_TRUE(ast.getASTContext().VoidPtrTy.isNull());
EXPECT_FALSE(ast.GetBuiltinTypeByName(ConstString("int")).IsValid());
EXPECT_FALSE(ast.GetBuiltinTypeForDWARFEncodingAndBitSize(
"char", llvm::dwarf::DW_ATE_signed_char, 8)
.IsValid());
EXPECT_FALSE(ast.GetBuiltinTypeForEncodingAndBitSize(lldb::eEncodingUint, 8)
.IsValid());
EXPECT_FALSE(ast.GetPointerSizedIntType(/*is_signed=*/false));
EXPECT_FALSE(ast.GetIntTypeFromBitSize(8, /*is_signed=*/false));
EXPECT_FALSE(ast.GetPointerDiffType(/*is_signed=*/true));
EXPECT_FALSE(ast.GetPointerDiffType(/*is_signed=*/false));
CompilerType record_type =
ast.CreateRecordType(nullptr, OptionalClangModuleID(), "Record",
llvm::to_underlying(clang::TagTypeKind::Struct),
lldb::eLanguageTypeC_plus_plus, std::nullopt);
TypeSystemClang::StartTagDeclarationDefinition(record_type);
EXPECT_EQ(ast.AddFieldToRecordType(record_type, "field", record_type,
/*bitfield_bit_size=*/8),
nullptr);
TypeSystemClang::CompleteTagDeclarationDefinition(record_type);
}
TEST_F(TestTypeSystemClang, TestGetPointerDiffType) {
CompilerType ptrdiff_t = m_ast->GetPointerDiffType(/*is_signed=*/true);
EXPECT_EQ(ptrdiff_t.GetDisplayTypeName(), "__ptrdiff_t");
EXPECT_TRUE(ptrdiff_t.IsSigned());
EXPECT_EQ(
llvm::expectedToOptional(ptrdiff_t.GetByteSize(nullptr)).value_or(0),
m_ast->GetPointerByteSize());
CompilerType uptrdiff_t = m_ast->GetPointerDiffType(/*is_signed=*/false);
EXPECT_FALSE(uptrdiff_t.IsSigned());
EXPECT_EQ(
llvm::expectedToOptional(uptrdiff_t.GetByteSize(nullptr)).value_or(0),
m_ast->GetPointerByteSize());
}
TEST_F(TestTypeSystemClang, TestGetBuiltinTypeByName_BitInt) {
auto holder =
std::make_unique<clang_utils::TypeSystemClangHolder>("bitint_ast");
auto &ast = *holder->GetAST();
EXPECT_TRUE(ast.GetBuiltinTypeByName(ConstString("_BitInt(26)")).IsSigned());
EXPECT_FALSE(
ast.GetBuiltinTypeByName(ConstString("unsigned _BitInt(26)")).IsSigned());
}
TEST_F(TestTypeSystemClang, TestDisplayName) {
TypeSystemClang ast("some name", llvm::Triple());
EXPECT_EQ("some name", ast.getDisplayName());
}
TEST_F(TestTypeSystemClang, TestDisplayNameEmpty) {
TypeSystemClang ast("", llvm::Triple());
EXPECT_EQ("", ast.getDisplayName());
}
TEST_F(TestTypeSystemClang, TestGetEnumIntegerTypeInvalid) {
EXPECT_FALSE(m_ast->GetEnumerationIntegerType(CompilerType()).IsValid());
}
TEST_F(TestTypeSystemClang, TestGetEnumIntegerTypeUnexpectedType) {
CompilerType int_type = m_ast->GetBasicType(lldb::eBasicTypeInt);
CompilerType t = m_ast->GetEnumerationIntegerType(int_type);
EXPECT_FALSE(t.IsValid());
}
TEST_F(TestTypeSystemClang, TestGetEnumIntegerTypeBasicTypes) {
// All possible underlying integer types of enums.
const std::vector<lldb::BasicType> types_to_test = {
eBasicTypeInt, eBasicTypeUnsignedInt, eBasicTypeLong,
eBasicTypeUnsignedLong, eBasicTypeLongLong, eBasicTypeUnsignedLongLong,
};
for (bool scoped : {true, false}) {
SCOPED_TRACE("scoped: " + std::to_string(scoped));
for (lldb::BasicType basic_type : types_to_test) {
SCOPED_TRACE(std::to_string(basic_type));
auto holder =
std::make_unique<clang_utils::TypeSystemClangHolder>("enum_ast");
auto &ast = *holder->GetAST();
CompilerType basic_compiler_type = ast.GetBasicType(basic_type);
EXPECT_TRUE(basic_compiler_type.IsValid());
CompilerType enum_type = ast.CreateEnumerationType(
"my_enum", ast.GetTranslationUnitDecl(), OptionalClangModuleID(),
Declaration(), basic_compiler_type, scoped);
CompilerType t = ast.GetEnumerationIntegerType(enum_type);
// Check that the type we put in at the start is found again.
EXPECT_EQ(basic_compiler_type.GetTypeName(), t.GetTypeName());
}
}
}
TEST_F(TestTypeSystemClang, TestEnumerationValueSign) {
CompilerType enum_type = m_ast->CreateEnumerationType(
"my_enum_signed", m_ast->GetTranslationUnitDecl(),
OptionalClangModuleID(), Declaration(),
m_ast->GetBasicType(lldb::eBasicTypeSignedChar), false);
auto *enum_decl = m_ast->AddEnumerationValueToEnumerationType(
enum_type, Declaration(), "minus_one", -1, 8);
EXPECT_TRUE(enum_decl->getInitVal().isSigned());
}
TEST_F(TestTypeSystemClang, TestIsEnumerationType) {
auto holder =
std::make_unique<clang_utils::TypeSystemClangHolder>("enum_ast");
auto &ast = *holder->GetAST();
// Scoped signed enum
{
CompilerType enum_type = ast.CreateEnumerationType(
"scoped_signed_enum", ast.GetTranslationUnitDecl(),
OptionalClangModuleID(), Declaration(),
ast.GetBasicType(eBasicTypeLong), /*is_scoped=*/true);
ASSERT_TRUE(enum_type);
bool is_signed;
EXPECT_TRUE(enum_type.IsEnumerationType(is_signed));
EXPECT_TRUE(is_signed);
EXPECT_FALSE(enum_type.IsIntegerType(is_signed));
}
// Scoped unsigned enum
{
CompilerType enum_type = ast.CreateEnumerationType(
"scoped_unsigned_enum", ast.GetTranslationUnitDecl(),
OptionalClangModuleID(), Declaration(),
ast.GetBasicType(eBasicTypeUnsignedInt), /*is_scoped=*/true);
ASSERT_TRUE(enum_type);
bool is_signed;
EXPECT_TRUE(enum_type.IsEnumerationType(is_signed));
EXPECT_FALSE(is_signed);
EXPECT_FALSE(enum_type.IsIntegerType(is_signed));
}
// Unscoped signed enum
{
CompilerType enum_type = ast.CreateEnumerationType(
"unscoped_signed_enum", ast.GetTranslationUnitDecl(),
OptionalClangModuleID(), Declaration(),
ast.GetBasicType(eBasicTypeLong), /*is_scoped=*/false);
ASSERT_TRUE(enum_type);
bool is_signed;
EXPECT_TRUE(enum_type.IsEnumerationType(is_signed));
EXPECT_TRUE(is_signed);
EXPECT_FALSE(enum_type.IsIntegerType(is_signed));
}
// Unscoped unsigned enum
{
CompilerType enum_type = ast.CreateEnumerationType(
"unscoped_unsigned_enum", ast.GetTranslationUnitDecl(),
OptionalClangModuleID(), Declaration(),
ast.GetBasicType(eBasicTypeUnsignedInt), /*is_scoped=*/false);
ASSERT_TRUE(enum_type);
bool is_signed;
EXPECT_TRUE(enum_type.IsEnumerationType(is_signed));
EXPECT_FALSE(is_signed);
EXPECT_FALSE(enum_type.IsIntegerType(is_signed));
}
}
TEST_F(TestTypeSystemClang, TestIsIntegerType_BitInt) {
auto holder =
std::make_unique<clang_utils::TypeSystemClangHolder>("bitint_ast");
auto &ast = *holder->GetAST();
// Signed _BitInt
{
CompilerType bitint_type = ast.GetType(
ast.getASTContext().getBitIntType(/*Unsigned=*/false, /*NumBits=*/37));
ASSERT_TRUE(bitint_type);
EXPECT_TRUE(bitint_type.IsInteger());
EXPECT_TRUE(bitint_type.IsSigned());
bool is_signed;
EXPECT_TRUE(bitint_type.IsIntegerType(is_signed));
EXPECT_TRUE(is_signed);
EXPECT_TRUE(bitint_type.IsIntegerOrEnumerationType(is_signed));
EXPECT_TRUE(is_signed);
}
// Unsigned _BitInt
{
CompilerType bitint_type = ast.GetType(
ast.getASTContext().getBitIntType(/*Unsigned=*/true, /*NumBits=*/122));
ASSERT_TRUE(bitint_type);
EXPECT_TRUE(bitint_type.IsInteger());
EXPECT_FALSE(bitint_type.IsSigned());
bool is_signed;
EXPECT_TRUE(bitint_type.IsIntegerType(is_signed));
EXPECT_FALSE(is_signed);
EXPECT_TRUE(bitint_type.IsIntegerOrEnumerationType(is_signed));
EXPECT_FALSE(is_signed);
}
}
TEST_F(TestTypeSystemClang, TestOwningModule) {
auto holder =
std::make_unique<clang_utils::TypeSystemClangHolder>("module_ast");
auto &ast = *holder->GetAST();
CompilerType basic_compiler_type = ast.GetBasicType(BasicType::eBasicTypeInt);
CompilerType enum_type = ast.CreateEnumerationType(
"my_enum", ast.GetTranslationUnitDecl(), OptionalClangModuleID(100),
Declaration(), basic_compiler_type, false);
auto *ed = TypeSystemClang::GetAsEnumDecl(enum_type);
EXPECT_FALSE(!ed);
EXPECT_EQ(ed->getOwningModuleID(), 100u);
CompilerType record_type =
ast.CreateRecordType(nullptr, OptionalClangModuleID(200), "FooRecord",
llvm::to_underlying(clang::TagTypeKind::Struct),
lldb::eLanguageTypeC_plus_plus, std::nullopt);
auto *rd = TypeSystemClang::GetAsRecordDecl(record_type);
EXPECT_FALSE(!rd);
EXPECT_EQ(rd->getOwningModuleID(), 200u);
CompilerType class_type =
ast.CreateObjCClass("objc_class", ast.GetTranslationUnitDecl(),
OptionalClangModuleID(300), false);
auto *cd = TypeSystemClang::GetAsObjCInterfaceDecl(class_type);
EXPECT_FALSE(!cd);
EXPECT_EQ(cd->getOwningModuleID(), 300u);
}
TEST_F(TestTypeSystemClang, TestIsClangType) {
clang::ASTContext &context = m_ast->getASTContext();
lldb::opaque_compiler_type_t bool_ctype =
TypeSystemClang::GetOpaqueCompilerType(&context, lldb::eBasicTypeBool);
CompilerType bool_type(m_ast->weak_from_this(), bool_ctype);
CompilerType record_type =
m_ast->CreateRecordType(nullptr, OptionalClangModuleID(100), "FooRecord",
llvm::to_underlying(clang::TagTypeKind::Struct),
lldb::eLanguageTypeC_plus_plus, std::nullopt);
// Clang builtin type and record type should pass
EXPECT_TRUE(ClangUtil::IsClangType(bool_type));
EXPECT_TRUE(ClangUtil::IsClangType(record_type));
// Default constructed type should fail
EXPECT_FALSE(ClangUtil::IsClangType(CompilerType()));
}
TEST_F(TestTypeSystemClang, TestRemoveFastQualifiers) {
CompilerType record_type =
m_ast->CreateRecordType(nullptr, OptionalClangModuleID(), "FooRecord",
llvm::to_underlying(clang::TagTypeKind::Struct),
lldb::eLanguageTypeC_plus_plus, std::nullopt);
QualType qt;
qt = ClangUtil::GetQualType(record_type);
EXPECT_EQ(0u, qt.getLocalFastQualifiers());
record_type = record_type.AddConstModifier();
record_type = record_type.AddVolatileModifier();
record_type = record_type.AddRestrictModifier();
qt = ClangUtil::GetQualType(record_type);
EXPECT_NE(0u, qt.getLocalFastQualifiers());
record_type = ClangUtil::RemoveFastQualifiers(record_type);
qt = ClangUtil::GetQualType(record_type);
EXPECT_EQ(0u, qt.getLocalFastQualifiers());
}
TEST_F(TestTypeSystemClang, TestConvertAccessTypeToAccessSpecifier) {
EXPECT_EQ(AS_none,
TypeSystemClang::ConvertAccessTypeToAccessSpecifier(eAccessNone));
EXPECT_EQ(AS_none, TypeSystemClang::ConvertAccessTypeToAccessSpecifier(
eAccessPackage));
EXPECT_EQ(AS_public,
TypeSystemClang::ConvertAccessTypeToAccessSpecifier(eAccessPublic));
EXPECT_EQ(AS_private, TypeSystemClang::ConvertAccessTypeToAccessSpecifier(
eAccessPrivate));
EXPECT_EQ(AS_protected, TypeSystemClang::ConvertAccessTypeToAccessSpecifier(
eAccessProtected));
}
TEST_F(TestTypeSystemClang, TestRecordHasFields) {
CompilerType int_type = m_ast->GetBasicType(eBasicTypeInt);
// Test that a record with no fields returns false
CompilerType empty_base =
m_ast->CreateRecordType(nullptr, OptionalClangModuleID(), "EmptyBase",
llvm::to_underlying(clang::TagTypeKind::Struct),
lldb::eLanguageTypeC_plus_plus, std::nullopt);
TypeSystemClang::StartTagDeclarationDefinition(empty_base);
TypeSystemClang::CompleteTagDeclarationDefinition(empty_base);
RecordDecl *empty_base_decl = TypeSystemClang::GetAsRecordDecl(empty_base);
EXPECT_NE(nullptr, empty_base_decl);
EXPECT_FALSE(m_ast->RecordHasFields(empty_base_decl));
// Test that a record with direct fields returns true
CompilerType non_empty_base =
m_ast->CreateRecordType(nullptr, OptionalClangModuleID(), "NonEmptyBase",
llvm::to_underlying(clang::TagTypeKind::Struct),
lldb::eLanguageTypeC_plus_plus, std::nullopt);
TypeSystemClang::StartTagDeclarationDefinition(non_empty_base);
FieldDecl *non_empty_base_field_decl =
m_ast->AddFieldToRecordType(non_empty_base, "MyField", int_type, 0);
TypeSystemClang::CompleteTagDeclarationDefinition(non_empty_base);
RecordDecl *non_empty_base_decl =
TypeSystemClang::GetAsRecordDecl(non_empty_base);
EXPECT_NE(nullptr, non_empty_base_decl);
EXPECT_NE(nullptr, non_empty_base_field_decl);
EXPECT_TRUE(m_ast->RecordHasFields(non_empty_base_decl));
std::vector<std::unique_ptr<clang::CXXBaseSpecifier>> bases;
// Test that a record with no direct fields, but fields in a base returns true
CompilerType empty_derived =
m_ast->CreateRecordType(nullptr, OptionalClangModuleID(), "EmptyDerived",
llvm::to_underlying(clang::TagTypeKind::Struct),
lldb::eLanguageTypeC_plus_plus, std::nullopt);
TypeSystemClang::StartTagDeclarationDefinition(empty_derived);
std::unique_ptr<clang::CXXBaseSpecifier> non_empty_base_spec =
m_ast->CreateBaseClassSpecifier(non_empty_base.GetOpaqueQualType(),
lldb::eAccessPublic, false, false);
bases.push_back(std::move(non_empty_base_spec));
bool result = m_ast->TransferBaseClasses(empty_derived.GetOpaqueQualType(),
std::move(bases));
TypeSystemClang::CompleteTagDeclarationDefinition(empty_derived);
EXPECT_TRUE(result);
CXXRecordDecl *empty_derived_non_empty_base_cxx_decl =
m_ast->GetAsCXXRecordDecl(empty_derived.GetOpaqueQualType());
RecordDecl *empty_derived_non_empty_base_decl =
TypeSystemClang::GetAsRecordDecl(empty_derived);
EXPECT_EQ(1u, m_ast->GetNumBaseClasses(
empty_derived_non_empty_base_cxx_decl, false));
EXPECT_TRUE(m_ast->RecordHasFields(empty_derived_non_empty_base_decl));
// Test that a record with no direct fields, but fields in a virtual base
// returns true
CompilerType empty_derived2 =
m_ast->CreateRecordType(nullptr, OptionalClangModuleID(), "EmptyDerived2",
llvm::to_underlying(clang::TagTypeKind::Struct),
lldb::eLanguageTypeC_plus_plus, std::nullopt);
TypeSystemClang::StartTagDeclarationDefinition(empty_derived2);
std::unique_ptr<CXXBaseSpecifier> non_empty_vbase_spec =
m_ast->CreateBaseClassSpecifier(non_empty_base.GetOpaqueQualType(),
lldb::eAccessPublic, true, false);
bases.push_back(std::move(non_empty_vbase_spec));
result = m_ast->TransferBaseClasses(empty_derived2.GetOpaqueQualType(),
std::move(bases));
TypeSystemClang::CompleteTagDeclarationDefinition(empty_derived2);
EXPECT_TRUE(result);
CXXRecordDecl *empty_derived_non_empty_vbase_cxx_decl =
m_ast->GetAsCXXRecordDecl(empty_derived2.GetOpaqueQualType());
RecordDecl *empty_derived_non_empty_vbase_decl =
TypeSystemClang::GetAsRecordDecl(empty_derived2);
EXPECT_EQ(1u, m_ast->GetNumBaseClasses(
empty_derived_non_empty_vbase_cxx_decl, false));
EXPECT_TRUE(
m_ast->RecordHasFields(empty_derived_non_empty_vbase_decl));
}
TEST_F(TestTypeSystemClang, TemplateArguments) {
TypeSystemClang::TemplateParameterInfos infos;
infos.InsertArg("T", TemplateArgument(m_ast->getASTContext().IntTy));
llvm::APSInt arg(llvm::APInt(8, 47));
infos.InsertArg("I", TemplateArgument(m_ast->getASTContext(), arg,
m_ast->getASTContext().IntTy));
llvm::APFloat float_arg(5.5f);
infos.InsertArg("F", TemplateArgument(m_ast->getASTContext(),
m_ast->getASTContext().FloatTy,
clang::APValue(float_arg)));
llvm::APFloat double_arg(-15.2);
infos.InsertArg("D", TemplateArgument(m_ast->getASTContext(),
m_ast->getASTContext().DoubleTy,
clang::APValue(double_arg)));
// template<typename T, int I, float F, double D> struct foo;
ClassTemplateDecl *decl = m_ast->CreateClassTemplateDecl(
m_ast->GetTranslationUnitDecl(), OptionalClangModuleID(), "foo",
llvm::to_underlying(clang::TagTypeKind::Struct), infos);
ASSERT_NE(decl, nullptr);
// foo<int, 47>
ClassTemplateSpecializationDecl *spec_decl =
m_ast->CreateClassTemplateSpecializationDecl(
m_ast->GetTranslationUnitDecl(), OptionalClangModuleID(), decl,
llvm::to_underlying(clang::TagTypeKind::Struct), infos);
ASSERT_NE(spec_decl, nullptr);
CompilerType type = m_ast->CreateClassTemplateSpecializationType(spec_decl);
ASSERT_TRUE(type);
m_ast->StartTagDeclarationDefinition(type);
m_ast->CompleteTagDeclarationDefinition(type);
// typedef foo<int, 47> foo_def;
CompilerType typedef_type = type.CreateTypedef(
"foo_def", m_ast->CreateDeclContext(m_ast->GetTranslationUnitDecl()), 0);
CompilerType auto_type(
m_ast->weak_from_this(),
m_ast->getASTContext()
.getAutoType(clang::DeducedKind::Deduced,
ClangUtil::GetCanonicalQualType(typedef_type),
clang::AutoTypeKeyword::Auto)
.getAsOpaquePtr());
CompilerType int_type(m_ast->weak_from_this(),
m_ast->getASTContext().IntTy.getAsOpaquePtr());
CompilerType float_type(m_ast->weak_from_this(),
m_ast->getASTContext().FloatTy.getAsOpaquePtr());
CompilerType double_type(m_ast->weak_from_this(),
m_ast->getASTContext().DoubleTy.getAsOpaquePtr());
for (CompilerType t : {type, typedef_type, auto_type}) {
SCOPED_TRACE(t.GetTypeName().GetString());
const bool expand_pack = false;
EXPECT_EQ(
m_ast->GetTemplateArgumentKind(t.GetOpaqueQualType(), 0, expand_pack),
eTemplateArgumentKindType);
EXPECT_EQ(
m_ast->GetTypeTemplateArgument(t.GetOpaqueQualType(), 0, expand_pack),
int_type);
EXPECT_EQ(std::nullopt, m_ast->GetIntegralTemplateArgument(
t.GetOpaqueQualType(), 0, expand_pack));
EXPECT_EQ(
m_ast->GetTemplateArgumentKind(t.GetOpaqueQualType(), 1, expand_pack),
eTemplateArgumentKindIntegral);
EXPECT_EQ(
m_ast->GetTypeTemplateArgument(t.GetOpaqueQualType(), 1, expand_pack),
CompilerType());
auto result = m_ast->GetIntegralTemplateArgument(t.GetOpaqueQualType(), 1,
expand_pack);
ASSERT_NE(std::nullopt, result);
EXPECT_EQ(arg, result->value.GetAPSInt());
EXPECT_EQ(int_type, result->type);
EXPECT_EQ(
m_ast->GetTemplateArgumentKind(t.GetOpaqueQualType(), 2, expand_pack),
eTemplateArgumentKindStructuralValue);
EXPECT_EQ(
m_ast->GetTypeTemplateArgument(t.GetOpaqueQualType(), 2, expand_pack),
CompilerType());
auto float_result = m_ast->GetIntegralTemplateArgument(
t.GetOpaqueQualType(), 2, expand_pack);
ASSERT_NE(std::nullopt, float_result);
EXPECT_EQ(float_arg, float_result->value.GetAPFloat());
EXPECT_EQ(float_type, float_result->type);
EXPECT_EQ(
m_ast->GetTemplateArgumentKind(t.GetOpaqueQualType(), 3, expand_pack),
eTemplateArgumentKindStructuralValue);
EXPECT_EQ(
m_ast->GetTypeTemplateArgument(t.GetOpaqueQualType(), 3, expand_pack),
CompilerType());
auto double_result = m_ast->GetIntegralTemplateArgument(
t.GetOpaqueQualType(), 3, expand_pack);
ASSERT_NE(std::nullopt, double_result);
EXPECT_EQ(double_arg, double_result->value.GetAPFloat());
EXPECT_EQ(double_type, double_result->type);
}
}
class TestCreateClassTemplateDecl : public TestTypeSystemClang {
protected:
/// The class templates created so far by the Expect* functions below.
llvm::DenseSet<ClassTemplateDecl *> m_created_templates;
/// Utility function for creating a class template.
ClassTemplateDecl *
CreateClassTemplate(const TypeSystemClang::TemplateParameterInfos &infos) {
ClassTemplateDecl *decl = m_ast->CreateClassTemplateDecl(
m_ast->GetTranslationUnitDecl(), OptionalClangModuleID(), "foo",
llvm::to_underlying(clang::TagTypeKind::Struct), infos);
return decl;
}
/// Creates a new class template with the given template parameters.
/// Asserts that a new ClassTemplateDecl is created.
/// \param description The gtest scope string that should describe the input.
/// \param infos The template parameters that the class template should have.
/// \returns The created ClassTemplateDecl.
ClassTemplateDecl *
ExpectNewTemplate(std::string description,
const TypeSystemClang::TemplateParameterInfos &infos) {
SCOPED_TRACE(description);
ClassTemplateDecl *first_template = CreateClassTemplate(infos);
// A new template should have been created.
EXPECT_FALSE(m_created_templates.contains(first_template))
<< "Didn't create new class template but reused this existing decl:\n"
<< ClangUtil::DumpDecl(first_template);
m_created_templates.insert(first_template);
// Creating a new template with the same arguments should always return
// the template created above.
ClassTemplateDecl *second_template = CreateClassTemplate(infos);
EXPECT_EQ(first_template, second_template)
<< "Second attempt to create class template didn't reuse first decl:\n"
<< ClangUtil::DumpDecl(first_template) << "\nInstead created/reused:\n"
<< ClangUtil::DumpDecl(second_template);
return first_template;
}
/// Tries to create a new class template but asserts that an existing class
/// template in the current AST is reused (in contract so a new class
/// template being created).
/// \param description The gtest scope string that should describe the input.
/// \param infos The template parameters that the class template should have.
void
ExpectReusedTemplate(std::string description,
const TypeSystemClang::TemplateParameterInfos &infos,
ClassTemplateDecl *expected) {
SCOPED_TRACE(description);
ClassTemplateDecl *td = CreateClassTemplate(infos);
EXPECT_EQ(td, expected)
<< "Created/reused class template is:\n"
<< ClangUtil::DumpDecl(td) << "\nExpected to reuse:\n"
<< ClangUtil::DumpDecl(expected);
}
};
TEST_F(TestCreateClassTemplateDecl, FindExistingTemplates) {
// This tests the logic in TypeSystemClang::CreateClassTemplateDecl that
// decides whether an existing ClassTemplateDecl in the AST can be reused.
// The behaviour should follow the C++ rules for redeclaring templates
// (e.g., parameter names can be changed/omitted.)
// Test an empty template parameter list: <>
ExpectNewTemplate("<>", {{}, {}});
clang::TemplateArgument intArg(m_ast->getASTContext().IntTy);
clang::TemplateArgument int47Arg(m_ast->getASTContext(),
llvm::APSInt(llvm::APInt(32, 47)),
m_ast->getASTContext().IntTy);
clang::TemplateArgument floatArg(m_ast->getASTContext().FloatTy);
clang::TemplateArgument char47Arg(m_ast->getASTContext(),
llvm::APSInt(llvm::APInt(8, 47)),
m_ast->getASTContext().SignedCharTy);
clang::TemplateArgument char123Arg(m_ast->getASTContext(),
llvm::APSInt(llvm::APInt(8, 123)),
m_ast->getASTContext().SignedCharTy);
// Test that <typename T> with T = int creates a new template.
ClassTemplateDecl *single_type_arg =
ExpectNewTemplate("<typename T>", {{"T"}, {intArg}});
// Test that changing the parameter name doesn't create a new class template.
ExpectReusedTemplate("<typename A> (A = int)", {{"A"}, {intArg}},
single_type_arg);
// Test that changing the used type doesn't create a new class template.
ExpectReusedTemplate("<typename A> (A = float)", {{"A"}, {floatArg}},
single_type_arg);
// Test that <typename A, signed char I> creates a new template with A = int
// and I = 47;
ClassTemplateDecl *type_and_char_value =
ExpectNewTemplate("<typename A, signed char I> (I = 47)",
{{"A", "I"}, {floatArg, char47Arg}});
// Change the value of the I parameter to 123. The previously created
// class template should still be reused.
ExpectReusedTemplate("<typename A, signed char I> (I = 123)",
{{"A", "I"}, {floatArg, char123Arg}},
type_and_char_value);
// Change the type of the I parameter to int so we have <typename A, int I>.
// The class template from above can't be reused.
ExpectNewTemplate("<typename A, int I> (I = 123)",
{{"A", "I"}, {floatArg, int47Arg}});
// Test a second type parameter will also cause a new template to be created.
// We now have <typename A, int I, typename B>.
ClassTemplateDecl *type_and_char_value_and_type =
ExpectNewTemplate("<typename A, int I, typename B>",
{{"A", "I", "B"}, {floatArg, int47Arg, intArg}});
// Remove all the names from the parameters which shouldn't influence the
// way the templates get merged.
ExpectReusedTemplate("<typename, int, typename>",
{{"", "", ""}, {floatArg, int47Arg, intArg}},
type_and_char_value_and_type);
}
TEST_F(TestCreateClassTemplateDecl, FindExistingTemplatesWithParameterPack) {
// The same as FindExistingTemplates but for templates with parameter packs.
TypeSystemClang::TemplateParameterInfos infos;
clang::TemplateArgument intArg(m_ast->getASTContext().IntTy);
clang::TemplateArgument int1Arg(m_ast->getASTContext(),
llvm::APSInt(llvm::APInt(32, 1)),
m_ast->getASTContext().IntTy);
clang::TemplateArgument int123Arg(m_ast->getASTContext(),
llvm::APSInt(llvm::APInt(32, 123)),
m_ast->getASTContext().IntTy);
clang::TemplateArgument longArg(m_ast->getASTContext().LongTy);
clang::TemplateArgument long1Arg(m_ast->getASTContext(),
llvm::APSInt(llvm::APInt(64, 1)),
m_ast->getASTContext().LongTy);
infos.SetParameterPack(
std::make_unique<TypeSystemClang::TemplateParameterInfos>(
llvm::SmallVector<const char *>{"", ""},
llvm::SmallVector<TemplateArgument>{intArg, intArg}));
ClassTemplateDecl *type_pack =
ExpectNewTemplate("<typename ...> (int, int)", infos);
// Special case: An instantiation for a parameter pack with no values fits
// to whatever class template we find. There isn't enough information to
// do an actual comparison here.
infos.SetParameterPack(
std::make_unique<TypeSystemClang::TemplateParameterInfos>());
ExpectReusedTemplate("<...> (no values in pack)", infos, type_pack);
// Change the type content of pack type values.
infos.SetParameterPack(
std::make_unique<TypeSystemClang::TemplateParameterInfos>(
llvm::SmallVector<const char *>{"", ""},
llvm::SmallVector<TemplateArgument>{intArg, longArg}));
ExpectReusedTemplate("<typename ...> (int, long)", infos, type_pack);
// Change the number of pack values.
infos.SetParameterPack(
std::make_unique<TypeSystemClang::TemplateParameterInfos>(
llvm::SmallVector<const char *>{""},
llvm::SmallVector<TemplateArgument>{intArg}));
ExpectReusedTemplate("<typename ...> (int)", infos, type_pack);
// The names of the pack values shouldn't matter.
infos.SetParameterPack(
std::make_unique<TypeSystemClang::TemplateParameterInfos>(
llvm::SmallVector<const char *>{"A"},
llvm::SmallVector<TemplateArgument>{intArg}));
ExpectReusedTemplate("<typename ...> (int)", infos, type_pack);
// Changing the kind of template argument will create a new template.
infos.SetParameterPack(
std::make_unique<TypeSystemClang::TemplateParameterInfos>(
llvm::SmallVector<const char *>{"A"},
llvm::SmallVector<TemplateArgument>{int1Arg}));
ClassTemplateDecl *int_pack = ExpectNewTemplate("<int ...> (int = 1)", infos);
// Changing the value of integral parameters will not create a new template.
infos.SetParameterPack(
std::make_unique<TypeSystemClang::TemplateParameterInfos>(
llvm::SmallVector<const char *>{"A"},
llvm::SmallVector<TemplateArgument>{int123Arg}));
ExpectReusedTemplate("<int ...> (int = 123)", infos, int_pack);
// Changing the integral type will create a new template.
infos.SetParameterPack(
std::make_unique<TypeSystemClang::TemplateParameterInfos>(
llvm::SmallVector<const char *>{"A"},
llvm::SmallVector<TemplateArgument>{long1Arg}));
ExpectNewTemplate("<long ...> (long = 1)", infos);
// Prependinding a non-pack parameter will create a new template.
infos.InsertArg("T", intArg);
ExpectNewTemplate("<typename T, long...> (T = int, long = 1)", infos);
}
TEST_F(TestTypeSystemClang, OnlyPackName) {
TypeSystemClang::TemplateParameterInfos infos;
infos.SetPackName("A");
EXPECT_FALSE(infos.IsValid());
}
static QualType makeConstInt(clang::ASTContext &ctxt) {
QualType result(ctxt.IntTy);
result.addConst();
return result;
}
TEST_F(TestTypeSystemClang, TestGetTypeClassDeclType) {
clang::ASTContext &ctxt = m_ast->getASTContext();
auto *nullptr_expr = new (ctxt) CXXNullPtrLiteralExpr(ctxt.NullPtrTy, SourceLocation());
QualType t = ctxt.getDecltypeType(nullptr_expr, makeConstInt(ctxt));
EXPECT_EQ(lldb::eTypeClassBuiltin, m_ast->GetTypeClass(t.getAsOpaquePtr()));
}
TEST_F(TestTypeSystemClang, TestGetTypeClassTypeOf) {
clang::ASTContext &ctxt = m_ast->getASTContext();
QualType t = ctxt.getTypeOfType(makeConstInt(ctxt), TypeOfKind::Qualified);
EXPECT_EQ(lldb::eTypeClassBuiltin, m_ast->GetTypeClass(t.getAsOpaquePtr()));
}
TEST_F(TestTypeSystemClang, TestGetTypeClassTypeOfExpr) {
clang::ASTContext &ctxt = m_ast->getASTContext();
auto *nullptr_expr = new (ctxt) CXXNullPtrLiteralExpr(ctxt.NullPtrTy, SourceLocation());
QualType t = ctxt.getTypeOfExprType(nullptr_expr, TypeOfKind::Qualified);
EXPECT_EQ(lldb::eTypeClassBuiltin, m_ast->GetTypeClass(t.getAsOpaquePtr()));
}
TEST_F(TestTypeSystemClang, TestGetTypeClassNested) {
clang::ASTContext &ctxt = m_ast->getASTContext();
QualType t_base =
ctxt.getTypeOfType(makeConstInt(ctxt), TypeOfKind::Qualified);
QualType t = ctxt.getTypeOfType(t_base, TypeOfKind::Qualified);
EXPECT_EQ(lldb::eTypeClassBuiltin, m_ast->GetTypeClass(t.getAsOpaquePtr()));
}
TEST_F(TestTypeSystemClang, TestFunctionTemplateConstruction) {
// Tests creating a function template.
CompilerType int_type = m_ast->GetBasicType(lldb::eBasicTypeInt);
clang::TranslationUnitDecl *TU = m_ast->GetTranslationUnitDecl();
// Prepare the declarations/types we need for the template.
CompilerType clang_type = m_ast->CreateFunctionType(int_type, {}, false, 0U);
FunctionDecl *func = m_ast->CreateFunctionDeclaration(
TU, OptionalClangModuleID(), "foo", clang_type, StorageClass::SC_None,
false, /*asm_label=*/{});
TypeSystemClang::TemplateParameterInfos empty_params;
// Create the actual function template.
clang::FunctionTemplateDecl *func_template =
m_ast->CreateFunctionTemplateDecl(TU, OptionalClangModuleID(), func,
empty_params);
EXPECT_EQ(TU, func_template->getDeclContext());
EXPECT_EQ("foo", func_template->getName());
EXPECT_EQ(clang::AccessSpecifier::AS_public, func_template->getAccess());
}
TEST_F(TestTypeSystemClang, TestFunctionTemplateInRecordConstruction) {
// Tests creating a function template inside a record.
CompilerType int_type = m_ast->GetBasicType(lldb::eBasicTypeInt);
clang::TranslationUnitDecl *TU = m_ast->GetTranslationUnitDecl();
// Create a record we can put the function template int.
CompilerType record_type =
clang_utils::createRecordWithField(*m_ast, "record", int_type, "field");
clang::TagDecl *record = ClangUtil::GetAsTagDecl(record_type);
// Prepare the declarations/types we need for the template.
CompilerType clang_type = m_ast->CreateFunctionType(int_type, {}, false, 0U);
// We create the FunctionDecl for the template in the TU DeclContext because:
// 1. FunctionDecls can't be in a Record (only CXXMethodDecls can).
// 2. It is mirroring the behavior of DWARFASTParserClang::ParseSubroutine.
FunctionDecl *func = m_ast->CreateFunctionDeclaration(
TU, OptionalClangModuleID(), "foo", clang_type, StorageClass::SC_None,
false, /*asm_label=*/{});
TypeSystemClang::TemplateParameterInfos empty_params;
// Create the actual function template.
clang::FunctionTemplateDecl *func_template =
m_ast->CreateFunctionTemplateDecl(record, OptionalClangModuleID(), func,
empty_params);
EXPECT_EQ(record, func_template->getDeclContext());
EXPECT_EQ("foo", func_template->getName());
EXPECT_EQ(clang::AccessSpecifier::AS_public, func_template->getAccess());
}
TEST_F(TestTypeSystemClang, TestDeletingImplicitCopyCstrDueToMoveCStr) {
// We need to simulate this behavior in our AST that we construct as we don't
// have a Sema instance that can do this for us:
// C++11 [class.copy]p7, p18:
// If the class definition declares a move constructor or move assignment
// operator, an implicitly declared copy constructor or copy assignment
// operator is defined as deleted.
// Create a record and start defining it.
llvm::StringRef class_name = "S";
CompilerType t = clang_utils::createRecord(*m_ast, class_name);
m_ast->StartTagDeclarationDefinition(t);
// Create a move constructor that will delete the implicit copy constructor.
CompilerType return_type = m_ast->GetBasicType(lldb::eBasicTypeVoid);
std::array<CompilerType, 1> args{t.GetRValueReferenceType()};
CompilerType function_type = m_ast->CreateFunctionType(
return_type, args, /*variadic=*/false, /*quals*/ 0U);
bool is_virtual = false;
bool is_static = false;
bool is_inline = false;
bool is_explicit = true;
bool is_attr_used = false;
bool is_artificial = false;
m_ast->AddMethodToCXXRecordType(t.GetOpaqueQualType(), class_name,
/*asm_label=*/{}, function_type, is_virtual,
is_static, is_inline, is_explicit,
is_attr_used, is_artificial);
// Complete the definition and check the created record.
m_ast->CompleteTagDeclarationDefinition(t);
auto *record = llvm::cast<CXXRecordDecl>(ClangUtil::GetAsTagDecl(t));
// We can't call defaultedCopyConstructorIsDeleted() as this requires that
// the Decl passes through Sema which will actually compute this field.
// Instead we check that there is no copy constructor declared by the user
// which only leaves a non-deleted defaulted copy constructor as an option
// that our record will have no simple copy constructor.
EXPECT_FALSE(record->hasUserDeclaredCopyConstructor());
EXPECT_FALSE(record->hasSimpleCopyConstructor());
}
TEST_F(TestTypeSystemClang, TestNotDeletingUserCopyCstrDueToMoveCStr) {
// Tests that we don't delete the a user-defined copy constructor when
// a move constructor is provided.
// See also the TestDeletingImplicitCopyCstrDueToMoveCStr test.
llvm::StringRef class_name = "S";
CompilerType t = clang_utils::createRecord(*m_ast, class_name);
m_ast->StartTagDeclarationDefinition(t);
CompilerType return_type = m_ast->GetBasicType(lldb::eBasicTypeVoid);
bool is_virtual = false;
bool is_static = false;
bool is_inline = false;
bool is_explicit = true;
bool is_attr_used = false;
bool is_artificial = false;
// Create a move constructor.
{
std::array<CompilerType, 1> args{t.GetRValueReferenceType()};
CompilerType function_type = m_ast->CreateFunctionType(
return_type, args, /*variadic=*/false, /*quals*/ 0U);
m_ast->AddMethodToCXXRecordType(t.GetOpaqueQualType(), class_name,
/*asm_label=*/{}, function_type, is_virtual,
is_static, is_inline, is_explicit,
is_attr_used, is_artificial);
}
// Create a copy constructor.
{
std::array<CompilerType, 1> args{
t.GetLValueReferenceType().AddConstModifier()};
CompilerType function_type =
m_ast->CreateFunctionType(return_type, args,
/*variadic=*/false, /*quals*/ 0U);
m_ast->AddMethodToCXXRecordType(t.GetOpaqueQualType(), class_name,
/*asm_label=*/{}, function_type, is_virtual,
is_static, is_inline, is_explicit,
is_attr_used, is_artificial);
}
// Complete the definition and check the created record.
m_ast->CompleteTagDeclarationDefinition(t);
auto *record = llvm::cast<CXXRecordDecl>(ClangUtil::GetAsTagDecl(t));
EXPECT_TRUE(record->hasUserDeclaredCopyConstructor());
}
TEST_F(TestTypeSystemClang, AddMethodToObjCObjectType) {
// Create an interface decl and mark it as having external storage.
CompilerType c = m_ast->CreateObjCClass("A", m_ast->GetTranslationUnitDecl(),
OptionalClangModuleID(),
/*IsInternal*/ false);
ObjCInterfaceDecl *interface = m_ast->GetAsObjCInterfaceDecl(c);
m_ast->SetHasExternalStorage(c.GetOpaqueQualType(), true);
EXPECT_TRUE(interface->hasExternalLexicalStorage());
// Add a method to the interface.
std::vector<CompilerType> args;
CompilerType func_type = m_ast->CreateFunctionType(
m_ast->GetBasicType(lldb::eBasicTypeInt), args, /*variadic*/ false,
/*quals*/ 0, clang::CallingConv::CC_C);
bool variadic = false;
bool artificial = false;
bool objc_direct = false;
clang::ObjCMethodDecl *method = TypeSystemClang::AddMethodToObjCObjectType(
c, "-[A foo]", func_type, artificial, variadic, objc_direct);
ASSERT_NE(method, nullptr);
// The interface decl should still have external lexical storage.
EXPECT_TRUE(interface->hasExternalLexicalStorage());
// Test some properties of the created ObjCMethodDecl.
EXPECT_FALSE(method->isVariadic());
EXPECT_TRUE(method->isImplicit());
EXPECT_FALSE(method->isDirectMethod());
EXPECT_EQ(method->getDeclName().getObjCSelector().getAsString(), "foo");
}
TEST_F(TestTypeSystemClang, GetFullyUnqualifiedType) {
CompilerType bool_ = m_ast->GetBasicType(eBasicTypeBool);
CompilerType cv_bool = bool_.AddConstModifier().AddVolatileModifier();
// const volatile bool -> bool
EXPECT_EQ(bool_, cv_bool.GetFullyUnqualifiedType());
// const volatile bool[47] -> bool[47]
EXPECT_EQ(bool_.GetArrayType(47),
cv_bool.GetArrayType(47).GetFullyUnqualifiedType());
// const volatile bool[47][42] -> bool[47][42]
EXPECT_EQ(
bool_.GetArrayType(42).GetArrayType(47),
cv_bool.GetArrayType(42).GetArrayType(47).GetFullyUnqualifiedType());
// const volatile bool * -> bool *
EXPECT_EQ(bool_.GetPointerType(),
cv_bool.GetPointerType().GetFullyUnqualifiedType());
// const volatile bool *[47] -> bool *[47]
EXPECT_EQ(
bool_.GetPointerType().GetArrayType(47),
cv_bool.GetPointerType().GetArrayType(47).GetFullyUnqualifiedType());
}
TEST(TestScratchTypeSystemClang, InferSubASTFromLangOpts) {
LangOptions lang_opts;
EXPECT_EQ(
ScratchTypeSystemClang::DefaultAST,
ScratchTypeSystemClang::InferIsolatedASTKindFromLangOpts(lang_opts));
lang_opts.Modules = true;
EXPECT_EQ(
ScratchTypeSystemClang::IsolatedASTKind::CppModules,
ScratchTypeSystemClang::InferIsolatedASTKindFromLangOpts(lang_opts));
}
TEST_F(TestTypeSystemClang, GetDeclContextByNameWhenMissingSymbolFile) {
// Test that a type system without a symbol file is handled gracefully.
std::vector<CompilerDecl> decls =
m_ast->DeclContextFindDeclByName(nullptr, ConstString("SomeName"), true);
EXPECT_TRUE(decls.empty());
}
TEST_F(TestTypeSystemClang, AddMethodToCXXRecordType_ParmVarDecls) {
// Tests that AddMethodToCXXRecordType creates ParmVarDecl's with
// a correct clang::DeclContext.
llvm::StringRef class_name = "S";
CompilerType t = clang_utils::createRecord(*m_ast, class_name);
m_ast->StartTagDeclarationDefinition(t);
CompilerType return_type = m_ast->GetBasicType(lldb::eBasicTypeVoid);
const bool is_virtual = false;
const bool is_static = false;
const bool is_inline = false;
const bool is_explicit = true;
const bool is_attr_used = false;
const bool is_artificial = false;
llvm::SmallVector<CompilerType> param_types{
m_ast->GetBasicType(lldb::eBasicTypeInt),
m_ast->GetBasicType(lldb::eBasicTypeShort)};
CompilerType function_type =
m_ast->CreateFunctionType(return_type, param_types,
/*variadic=*/false, /*quals*/ 0U);
m_ast->AddMethodToCXXRecordType(t.GetOpaqueQualType(), "myFunc",
/*asm_label=*/{}, function_type, is_virtual,
is_static, is_inline, is_explicit,
is_attr_used, is_artificial);
// Complete the definition and check the created record.
m_ast->CompleteTagDeclarationDefinition(t);
auto *record = llvm::cast<CXXRecordDecl>(ClangUtil::GetAsTagDecl(t));
auto method_it = record->method_begin();
ASSERT_NE(method_it, record->method_end());
EXPECT_EQ(method_it->getNumParams(), param_types.size());
// DeclContext of each parameter should be the CXXMethodDecl itself.
EXPECT_EQ(method_it->getParamDecl(0)->getDeclContext(), *method_it);
EXPECT_EQ(method_it->getParamDecl(1)->getDeclContext(), *method_it);
}
TEST_F(TestTypeSystemClang, TestGetTypeInfo) {
// Tests TypeSystemClang::GetTypeInfo
const ASTContext &ast = m_ast->getASTContext();
CompilerType complex_int = m_ast->GetType(ast.getComplexType(ast.IntTy));
EXPECT_EQ(complex_int.GetTypeInfo(),
(eTypeIsInteger | eTypeIsComplex | eTypeIsBuiltIn | eTypeHasValue));
CompilerType complex_float = m_ast->GetType(ast.getComplexType(ast.FloatTy));
EXPECT_EQ(complex_float.GetTypeInfo(),
(eTypeIsFloat | eTypeIsComplex | eTypeIsBuiltIn | eTypeHasValue));
CompilerType vector_of_int =
m_ast->GetType(ast.getVectorType(ast.IntTy, 1, VectorKind::Generic));
EXPECT_EQ(vector_of_int.GetTypeInfo(),
(eTypeIsInteger | eTypeIsVector | eTypeHasChildren));
CompilerType vector_of_float =
m_ast->GetType(ast.getVectorType(ast.FloatTy, 1, VectorKind::Generic));
EXPECT_EQ(vector_of_float.GetTypeInfo(),
(eTypeIsFloat | eTypeIsVector | eTypeHasChildren));
}
TEST_F(TestTypeSystemClang, TestIsRealFloatingPointType) {
// Tests CompilerType::IsRealFloatingPointType
const ASTContext &ast = m_ast->getASTContext();
EXPECT_FALSE(m_ast->GetType(ast.getComplexType(ast.FloatTy))
.IsRealFloatingPointType());
EXPECT_FALSE(
m_ast->GetType(ast.getVectorType(ast.FloatTy, 1, VectorKind::Generic))
.IsRealFloatingPointType());
EXPECT_TRUE(m_ast->GetType(ast.HalfTy).IsRealFloatingPointType());
EXPECT_TRUE(m_ast->GetType(ast.FloatTy).IsRealFloatingPointType());
EXPECT_TRUE(m_ast->GetType(ast.DoubleTy).IsRealFloatingPointType());
EXPECT_TRUE(m_ast->GetType(ast.LongDoubleTy).IsRealFloatingPointType());
EXPECT_TRUE(m_ast->GetType(ast.Float128Ty).IsRealFloatingPointType());
EXPECT_TRUE(m_ast->GetType(ast.BFloat16Ty).IsRealFloatingPointType());
EXPECT_TRUE(m_ast->GetType(ast.Ibm128Ty).IsRealFloatingPointType());
}
TEST_F(TestTypeSystemClang, TestIsFloatingPointType) {
// Tests CompilerType::IsFloatingPointType
const ASTContext &ast = m_ast->getASTContext();
// Vectors of floats
EXPECT_FALSE(
m_ast->GetType(ast.getVectorType(ast.FloatTy, 1, VectorKind::Generic))
.IsFloatingPointType());
EXPECT_FALSE(
m_ast->GetType(ast.getVectorType(ast.DoubleTy, 1, VectorKind::Generic))
.IsFloatingPointType());
// Complex floats
EXPECT_TRUE(
m_ast->GetType(ast.getComplexType(ast.FloatTy)).IsFloatingPointType());
EXPECT_TRUE(
m_ast->GetType(ast.getComplexType(ast.DoubleTy)).IsFloatingPointType());
EXPECT_FALSE(
m_ast->GetType(ast.getComplexType(ast.IntTy)).IsFloatingPointType());
// Builtin floats
EXPECT_TRUE(m_ast->GetType(ast.HalfTy).IsFloatingPointType());
EXPECT_TRUE(m_ast->GetType(ast.FloatTy).IsFloatingPointType());
EXPECT_TRUE(m_ast->GetType(ast.DoubleTy).IsFloatingPointType());
EXPECT_TRUE(m_ast->GetType(ast.LongDoubleTy).IsFloatingPointType());
EXPECT_TRUE(m_ast->GetType(ast.Float128Ty).IsFloatingPointType());
EXPECT_TRUE(m_ast->GetType(ast.BFloat16Ty).IsFloatingPointType());
EXPECT_TRUE(m_ast->GetType(ast.Ibm128Ty).IsFloatingPointType());
}
TEST_F(TestTypeSystemClang, TestGetIsComplexType) {
// Tests CompilerType::IsComplexType
const ASTContext &ast = m_ast->getASTContext();
EXPECT_TRUE(m_ast->GetType(ast.getComplexType(ast.IntTy)).IsComplexType());
EXPECT_TRUE(m_ast->GetType(ast.getComplexType(ast.FloatTy)).IsComplexType());
EXPECT_TRUE(m_ast->GetType(ast.getComplexType(ast.VoidTy)).IsComplexType());
EXPECT_FALSE(m_ast
->GetType(ast.getIncompleteArrayType(
ast.getComplexType(ast.FloatTy), /*ASM=*/{},
/*IndexTypeQuals=*/{}))
.IsComplexType());
}
TEST_F(TestTypeSystemClang, AsmLabel_CtorDtor) {
// Tests TypeSystemClang::DeclGetMangledName for constructors/destructors
// with and without AsmLabels.
llvm::StringRef class_name = "S";
CompilerType t = clang_utils::createRecord(*m_ast, class_name);
m_ast->StartTagDeclarationDefinition(t);
CompilerType return_type = m_ast->GetBasicType(lldb::eBasicTypeVoid);
const bool is_virtual = false;
const bool is_static = false;
const bool is_inline = false;
const bool is_explicit = true;
const bool is_attr_used = false;
const bool is_artificial = false;
CompilerType function_type =
m_ast->CreateFunctionType(return_type, {},
/*variadic=*/false, /*quals*/ 0U);
auto *ctor_nolabel = m_ast->AddMethodToCXXRecordType(
t.GetOpaqueQualType(), "S", /*asm_label=*/{}, function_type, is_virtual,
is_static, is_inline, is_explicit, is_attr_used, is_artificial);
auto *dtor_nolabel = m_ast->AddMethodToCXXRecordType(
t.GetOpaqueQualType(), "~S", /*asm_label=*/{}, function_type, is_virtual,
is_static, is_inline, is_explicit, is_attr_used, is_artificial);
auto *ctor = m_ast->AddMethodToCXXRecordType(
t.GetOpaqueQualType(), "S", /*asm_label=*/"$__lldb_func::0x0:0x0:S",
function_type, is_virtual, is_static, is_inline, is_explicit,
is_attr_used, is_artificial);
auto *dtor = m_ast->AddMethodToCXXRecordType(
t.GetOpaqueQualType(), "~S", /*asm_label=*/"$__lldb_func::0x0:0x0:~S",
function_type, is_virtual, is_static, is_inline, is_explicit,
is_attr_used, is_artificial);
m_ast->CompleteTagDeclarationDefinition(t);
ASSERT_TRUE(ctor_nolabel);
ASSERT_TRUE(dtor_nolabel);
ASSERT_TRUE(ctor);
ASSERT_TRUE(dtor);
#ifdef _WIN32
EXPECT_STREQ(m_ast->DeclGetMangledName(ctor_nolabel).GetCString(),
"??0S@@QEAA@XZ");
EXPECT_STREQ(m_ast->DeclGetMangledName(dtor_nolabel).GetCString(),
"??_DS@@QEAAXXZ");
#else
EXPECT_STREQ(m_ast->DeclGetMangledName(ctor_nolabel).GetCString(),
"_ZN1SC1Ev");
EXPECT_STREQ(m_ast->DeclGetMangledName(dtor_nolabel).GetCString(),
"_ZN1SD1Ev");
#endif
EXPECT_STREQ(llvm::GlobalValue::dropLLVMManglingEscape(
m_ast->DeclGetMangledName(ctor).GetStringRef())
.data(),
"$__lldb_func:C0:0x0:0x0:S");
EXPECT_STREQ(llvm::GlobalValue::dropLLVMManglingEscape(
m_ast->DeclGetMangledName(dtor).GetStringRef())
.data(),
"$__lldb_func:D1:0x0:0x0:~S");
}
struct AsmLabelTestCase {
llvm::StringRef mangled;
llvm::StringRef expected;
};
class TestTypeSystemClangAsmLabel
: public testing::TestWithParam<AsmLabelTestCase> {
public:
SubsystemRAII<FileSystem, HostInfo> subsystems;
void SetUp() override {
m_holder =
std::make_unique<clang_utils::TypeSystemClangHolder>("test ASTContext");
m_ast = m_holder->GetAST();
}
void TearDown() override {
m_ast = nullptr;
m_holder.reset();
}
protected:
TypeSystemClang *m_ast = nullptr;
std::unique_ptr<clang_utils::TypeSystemClangHolder> m_holder;
};
static AsmLabelTestCase g_asm_label_test_cases[] = {
{/*mangled=*/"$__lldb_func::0x0:0x0:_Z3foov",
/*expected=*/"_Z3foov"},
{/*mangled=*/"$__lldb_func::0x0:0x0:foo",
/*expected=*/"$__lldb_func::0x0:0x0:foo"},
{/*mangled=*/"foo",
/*expected=*/"foo"},
{/*mangled=*/"_Z3foov",
/*expected=*/"_Z3foov"},
{/*mangled=*/"$__lldb_func:",
/*expected=*/"$__lldb_func:"},
};
TEST_P(TestTypeSystemClangAsmLabel, DeclGetMangledName) {
const auto &[mangled, expected] = GetParam();
CompilerType int_type = m_ast->GetBasicType(lldb::eBasicTypeInt);
clang::TranslationUnitDecl *TU = m_ast->GetTranslationUnitDecl();
// Prepare the declarations/types we need for the template.
CompilerType clang_type = m_ast->CreateFunctionType(int_type, {}, false, 0U);
FunctionDecl *func = m_ast->CreateFunctionDeclaration(
TU, OptionalClangModuleID(), "foo", clang_type, StorageClass::SC_None,
false, /*asm_label=*/mangled);
ASSERT_EQ(llvm::GlobalValue::dropLLVMManglingEscape(
m_ast->DeclGetMangledName(func).GetStringRef()),
expected);
}
INSTANTIATE_TEST_SUITE_P(AsmLabelTests, TestTypeSystemClangAsmLabel,
testing::ValuesIn(g_asm_label_test_cases));
TEST_F(TestTypeSystemClang, TestIsMemberDataPointerType) {
// Create struct S { int x; void foo(); };
CompilerType int_type = m_ast->GetBasicType(lldb::eBasicTypeInt);
CompilerType record_type = clang_utils::createRecord(*m_ast, "S");
// int S::* — member data pointer
CompilerType member_data_ptr =
TypeSystemClang::CreateMemberPointerType(record_type, int_type);
EXPECT_TRUE(member_data_ptr.IsMemberDataPointerType());
EXPECT_FALSE(member_data_ptr.IsMemberFunctionPointerType());
// void (S::*)() — member function pointer
CompilerType void_type = m_ast->GetBasicType(lldb::eBasicTypeVoid);
CompilerType func_type = m_ast->CreateFunctionType(void_type, {}, false, 0U);
CompilerType member_func_ptr =
TypeSystemClang::CreateMemberPointerType(record_type, func_type);
EXPECT_FALSE(member_func_ptr.IsMemberDataPointerType());
EXPECT_TRUE(member_func_ptr.IsMemberFunctionPointerType());
// int* — regular pointer, neither member data nor member function
CompilerType regular_ptr = int_type.GetPointerType();
EXPECT_FALSE(regular_ptr.IsMemberDataPointerType());
EXPECT_FALSE(regular_ptr.IsMemberFunctionPointerType());
// int — not a pointer at all
EXPECT_FALSE(int_type.IsMemberDataPointerType());
EXPECT_FALSE(int_type.IsMemberFunctionPointerType());
}