In https://github.com/llvm/llvm-project/pull/182956 we stopped using the access specifiers and unconditionally set all access to `AS_public`. This patch is a follow-up cleanup to remove all the `AccessType` parameters of the `TypeSystemClang` APIs (which since https://github.com/llvm/llvm-project/pull/182956 are never used)
166 lines
5.7 KiB
C++
166 lines
5.7 KiB
C++
//===----------------------------------------------------------------------===//
|
|
//
|
|
// 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/Language/CPlusPlus/LibStdcpp.h"
|
|
#include "Plugins/TypeSystem/Clang/TypeSystemClang.h"
|
|
#include "TestingSupport/SubsystemRAII.h"
|
|
#include "TestingSupport/Symbol/ClangTestUtils.h"
|
|
#include "lldb/DataFormatters/TypeSynthetic.h"
|
|
#include "lldb/ValueObject/ValueObject.h"
|
|
|
|
#include "gtest/gtest.h"
|
|
|
|
using namespace lldb;
|
|
using namespace lldb_private;
|
|
|
|
namespace {
|
|
|
|
/// A minimal ValueObject mock that returns null from GetChildAtIndex for a
|
|
/// specified index. This simulates the crash scenario where GetChildAtIndex()
|
|
/// returns null due to incomplete debug info or type system errors.
|
|
class ValueObjectWithNullChild : public ValueObject {
|
|
public:
|
|
static lldb::ValueObjectSP Create(CompilerType type, ConstString name,
|
|
size_t null_child_idx) {
|
|
auto manager = ValueObjectManager::Create();
|
|
auto *obj =
|
|
new ValueObjectWithNullChild(*manager, type, name, null_child_idx);
|
|
return obj->GetSP();
|
|
}
|
|
|
|
~ValueObjectWithNullChild() override = default;
|
|
|
|
llvm::Expected<uint64_t> GetByteSize() override { return 4; }
|
|
|
|
lldb::ValueType GetValueType() const override {
|
|
return lldb::eValueTypeConstResult;
|
|
}
|
|
|
|
llvm::Expected<uint32_t> CalculateNumChildren(uint32_t max) override {
|
|
auto num_or_err = m_type.GetNumChildren(true, nullptr);
|
|
if (!num_or_err)
|
|
return num_or_err.takeError();
|
|
return *num_or_err;
|
|
}
|
|
|
|
ConstString GetTypeName() override { return m_type.GetTypeName(); }
|
|
|
|
ConstString GetDisplayTypeName() override { return GetTypeName(); }
|
|
|
|
bool IsInScope() override { return true; }
|
|
|
|
protected:
|
|
bool UpdateValue() override {
|
|
m_error.Clear();
|
|
return true;
|
|
}
|
|
|
|
CompilerType GetCompilerTypeImpl() override { return m_type; }
|
|
|
|
/// This is the key method - return null for the specified child index
|
|
/// to simulate the crash scenario.
|
|
ValueObject *CreateChildAtIndex(size_t idx) override {
|
|
if (idx == m_null_child_idx) {
|
|
return nullptr; // Return null to trigger the crash scenario!
|
|
}
|
|
return ValueObject::CreateChildAtIndex(idx);
|
|
}
|
|
|
|
private:
|
|
ValueObjectWithNullChild(ValueObjectManager &manager, CompilerType type,
|
|
ConstString name, size_t null_child_idx)
|
|
: ValueObject(nullptr, manager), m_type(type),
|
|
m_null_child_idx(null_child_idx) {
|
|
SetName(name);
|
|
}
|
|
|
|
CompilerType m_type;
|
|
size_t m_null_child_idx;
|
|
};
|
|
|
|
class LibStdcppTupleTest : public ::testing::Test {
|
|
public:
|
|
SubsystemRAII<FileSystem, HostInfo> m_subsystems;
|
|
|
|
void SetUp() override {
|
|
m_holder = std::make_unique<clang_utils::TypeSystemClangHolder>("test");
|
|
m_type_system = m_holder->GetAST();
|
|
}
|
|
|
|
/// Create a struct type with a child named "std::_Tuple_impl<0, int>"
|
|
/// to trigger the tuple synthetic frontend's child iteration.
|
|
CompilerType CreateTypeWithTupleImplChild() {
|
|
// Create outer type
|
|
CompilerType outer_type = m_type_system->CreateRecordType(
|
|
m_type_system->getASTContext().getTranslationUnitDecl(),
|
|
OptionalClangModuleID(), "std::tuple<int>", 0,
|
|
lldb::LanguageType::eLanguageTypeC_plus_plus);
|
|
|
|
// Create inner _Tuple_impl type
|
|
CompilerType inner_type = m_type_system->CreateRecordType(
|
|
m_type_system->getASTContext().getTranslationUnitDecl(),
|
|
OptionalClangModuleID(), "std::_Tuple_impl<0, int>", 0,
|
|
lldb::LanguageType::eLanguageTypeC_plus_plus);
|
|
|
|
TypeSystemClang::StartTagDeclarationDefinition(inner_type);
|
|
TypeSystemClang::CompleteTagDeclarationDefinition(inner_type);
|
|
|
|
// Add the inner type as a field of the outer type
|
|
TypeSystemClang::StartTagDeclarationDefinition(outer_type);
|
|
m_type_system->AddFieldToRecordType(outer_type, "std::_Tuple_impl<0, int>",
|
|
inner_type, 0);
|
|
TypeSystemClang::CompleteTagDeclarationDefinition(outer_type);
|
|
|
|
return outer_type;
|
|
}
|
|
|
|
TypeSystemClang *m_type_system;
|
|
|
|
private:
|
|
std::unique_ptr<clang_utils::TypeSystemClangHolder> m_holder;
|
|
};
|
|
|
|
} // anonymous namespace
|
|
|
|
TEST_F(LibStdcppTupleTest, CreatorHandlesNullValueObject) {
|
|
auto *frontend = formatters::LibStdcppTupleSyntheticFrontEndCreator(
|
|
nullptr, lldb::ValueObjectSP());
|
|
EXPECT_EQ(frontend, nullptr);
|
|
}
|
|
|
|
/// This test verifies the null child handling fix.
|
|
/// It creates a ValueObject that returns null from GetChildAtIndex(0),
|
|
/// simulating the crash scenario from incomplete debug info.
|
|
/// WITHOUT the fix (null check), this test will crash with SIGSEGV.
|
|
/// WITH the fix, this test passes.
|
|
TEST_F(LibStdcppTupleTest, UpdateHandlesNullChild) {
|
|
CompilerType type = CreateTypeWithTupleImplChild();
|
|
|
|
// Create a ValueObject that returns null for child at index 0
|
|
auto valobj_sp = ValueObjectWithNullChild::Create(
|
|
type, ConstString("test_tuple"), 0 /* null_child_idx */);
|
|
ASSERT_TRUE(valobj_sp);
|
|
|
|
// Verify our mock returns null for child 0
|
|
ASSERT_FALSE(valobj_sp->GetChildAtIndex(0));
|
|
|
|
// Create the frontend - this calls Update() which iterates through children.
|
|
// WITHOUT the null check fix, this crashes with SIGSEGV when trying to call
|
|
// GetName() on a null child_sp.
|
|
// WITH the fix, this succeeds because null children are skipped.
|
|
auto *frontend =
|
|
formatters::LibStdcppTupleSyntheticFrontEndCreator(nullptr, valobj_sp);
|
|
ASSERT_NE(frontend, nullptr);
|
|
|
|
// If we get here, the null check worked.
|
|
auto num_children = frontend->CalculateNumChildren();
|
|
ASSERT_TRUE(static_cast<bool>(num_children));
|
|
|
|
delete frontend;
|
|
}
|