blob: e5e504bd67cfa7e009cb8173e08022c126617385 [file] [log] [blame]
// Copyright 2018 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "src/developer/debug/zxdb/symbols/dwarf_symbol_factory.h"
#include <initializer_list>
#include <gtest/gtest.h>
#include "src/developer/debug/zxdb/common/string_util.h"
#include "src/developer/debug/zxdb/symbols/array_type.h"
#include "src/developer/debug/zxdb/symbols/base_type.h"
#include "src/developer/debug/zxdb/symbols/collection.h"
#include "src/developer/debug/zxdb/symbols/data_member.h"
#include "src/developer/debug/zxdb/symbols/dwarf_test_util.h"
#include "src/developer/debug/zxdb/symbols/enumeration.h"
#include "src/developer/debug/zxdb/symbols/function.h"
#include "src/developer/debug/zxdb/symbols/function_type.h"
#include "src/developer/debug/zxdb/symbols/inherited_from.h"
#include "src/developer/debug/zxdb/symbols/input_location.h"
#include "src/developer/debug/zxdb/symbols/modified_type.h"
#include "src/developer/debug/zxdb/symbols/module_symbols_impl.h"
#include "src/developer/debug/zxdb/symbols/symbol.h"
#include "src/developer/debug/zxdb/symbols/template_parameter.h"
#include "src/developer/debug/zxdb/symbols/test_symbol_module.h"
#include "src/developer/debug/zxdb/symbols/variable.h"
namespace zxdb {
namespace {
const char kMyNsName[] = "my_ns";
const char kDoStructCallName[] = "DoStructCall";
const char kGetIntPtrName[] = "GetIntPtr";
const char kGetStructName[] = "GetStruct";
const char kGetStructMemberPtrName[] = "GetStructMemberPtr";
const char kPassRValueRefName[] = "PassRValueRef";
const char kCallInlineMemberName[] = "CallInlineMember";
const char kCallInlineName[] = "CallInline";
// Returns the function symbol with the given name. The name is assumed to exist as this function
// will EXPECT_* it to be valid.
//
// The name is passed as an initializer list representing the different identifier components, so
// for one function, use {"Foo"} and for one inside a namespace {"my_namespace", "Foo"}.
fxl::RefPtr<Function> GetFunctionWithName(ModuleSymbolsImpl* module_symbols,
std::initializer_list<std::string> name_comps) {
EXPECT_TRUE(name_comps.size() > 0);
Identifier name_identifier;
for (const auto& comp : name_comps)
name_identifier.AppendComponent(IdentifierComponent(comp));
std::vector<Location> locs = module_symbols->ResolveInputLocation(
SymbolContext::ForRelativeAddresses(), InputLocation(name_identifier));
EXPECT_EQ(1u, locs.size()) << "Expected exactly one function with this name: "
<< name_identifier.GetFullName();
if (locs.size() != 1)
return nullptr;
return RefPtrTo(locs[0].symbol().Get()->As<Function>());
}
} // namespace
TEST(DwarfSymbolFactory, Function) {
TestSymbolModule setup(TestSymbolModule::kBuilt);
ASSERT_TRUE(setup.Init("/build_dir").ok());
// Find the GetIntPtr function.
fxl::RefPtr<Function> function = GetFunctionWithName(setup.symbols(), {kGetIntPtrName});
ASSERT_TRUE(function);
// Unmangled name.
EXPECT_EQ(kGetIntPtrName, function->GetAssignedName());
// Mangled name. This tries not to depend on the exact name mangling rules while validating that
// it's reasonable. The mangled name shouldn't be exactly the same as the unmangled name, but
// should at least contain it.
EXPECT_NE(kGetIntPtrName, function->linkage_name());
EXPECT_NE(std::string::npos, function->linkage_name().find(kGetIntPtrName));
// Declaration location.
EXPECT_TRUE(function->decl_line().is_valid());
EXPECT_TRUE(StringEndsWith(function->decl_line().file(), "/type_test.cc"))
<< function->decl_line().file();
EXPECT_EQ(15, function->decl_line().line());
EXPECT_EQ("/build_dir", function->decl_line().comp_dir());
// Note: return type tested by ModifiedBaseType.
}
TEST(DwarfSymbolFactory, PtrToMemberFunction) {
TestSymbolModule setup(TestSymbolModule::kBuilt);
ASSERT_TRUE(setup.Init().ok());
// Find the GetIntPtr function.
fxl::RefPtr<const Function> get_function =
GetFunctionWithName(setup.symbols(), {kMyNsName, kGetStructMemberPtrName});
ASSERT_TRUE(get_function);
// Get the return type, this is a typedef (because functions can't return pointers to member
// functions).
auto return_typedef = get_function->return_type().Get()->As<ModifiedType>();
ASSERT_TRUE(return_typedef);
// The typedef references the member pointer. The type name encapsulates all return values and
// parameters so this tests everything at once.
const Symbol* member = return_typedef->modified().Get();
EXPECT_EQ("int (my_ns::Struct::*)(my_ns::Struct*, char)", member->GetFullName());
}
// FIXME(fxbug.dev/69724): A recent clang revision causes this test to fail due
// to the "this" parameter being omitted since it doesn't contain any meaningful
// attributes. To facilitate the Clang roll, we will temporarily disable this
// test, then re-enable it with a fix after the roll.
TEST(DwarfSymbolFactory, InlinedMemberFunction) {
TestSymbolModule setup(TestSymbolModule::kBuilt);
ASSERT_TRUE(setup.Init().ok());
// Find the CallInline function.
fxl::RefPtr<const Function> call_function =
GetFunctionWithName(setup.symbols(), {kCallInlineMemberName});
ASSERT_TRUE(call_function);
// It should have one inner block that's the inline function.
ASSERT_EQ(1u, call_function->inner_blocks().size());
const Function* inline_func = call_function->inner_blocks()[0].Get()->As<Function>();
ASSERT_TRUE(inline_func);
EXPECT_EQ(DwarfTag::kInlinedSubroutine, inline_func->tag());
EXPECT_EQ("ForInline::InlinedFunction", inline_func->GetFullName());
// The inline function should have two parameters, "this" and "param". ASSERT_EQ(2u,
// inline_func->parameters().size());
const Variable* param0 = inline_func->parameters()[0].Get()->As<Variable>();
ASSERT_TRUE(param0);
const Variable* param1 = inline_func->parameters()[1].Get()->As<Variable>();
ASSERT_TRUE(param1);
// They can appear in either order. Because it's an inlined function, the abtract origin can
// contribute to these parameters which can mean the iteration order is not clearly defined.
// Normally the parameters are in order.
const Variable* this_param = nullptr;
if (param0->GetAssignedName() == "this") {
this_param = param0;
EXPECT_EQ("param", param1->GetAssignedName());
} else {
EXPECT_EQ("param", param0->GetAssignedName());
EXPECT_EQ("this", param1->GetAssignedName());
this_param = param1;
}
// The object pointer on the function should refer to the "this" pointer retrieved above. This is
// tricky because normally the object pointer is on the "abstract origin" of the inlined routine,
// and will refer to a "this" parameter specified on the abstract origin. We need to correlate it
// to the one on the inlined instance to get the location correct.
EXPECT_EQ(this_param, inline_func->GetObjectPointerVariable());
}
TEST(DwarfSymbolFactory, InlinedFunction) {
TestSymbolModule setup(TestSymbolModule::kBuilt);
ASSERT_TRUE(setup.Init().ok());
// Find the CallInline function.
fxl::RefPtr<const Function> call_function =
GetFunctionWithName(setup.symbols(), {kCallInlineName});
ASSERT_TRUE(call_function);
// It should have one inner block that's the inline function.
ASSERT_EQ(1u, call_function->inner_blocks().size());
const Function* inline_func = call_function->inner_blocks()[0].Get()->As<Function>();
ASSERT_TRUE(inline_func);
EXPECT_EQ(DwarfTag::kInlinedSubroutine, inline_func->tag());
// Parameter decoding is tested by the InlinedMemberFunction test above. Here we care that the
// enclosing namespace is correct because of the different ways these inlined routines are
// declared.
EXPECT_EQ("my_ns::InlinedFunction", inline_func->GetFullName());
// Check the declaration location. This mostly verifies that the path name is relative to the
// build directory and that there aren't any unnecessary "." or "..".
EXPECT_EQ("../../src/developer/debug/zxdb/symbols/test_data/type_test.cc",
inline_func->decl_line().file());
// The containing block of the inline function should be the calling function. Note that the
// objects may not be the same.
ASSERT_TRUE(inline_func->containing_block());
auto containing_block = inline_func->containing_block().Get();
auto containing_func = containing_block->As<Function>();
ASSERT_TRUE(containing_func);
EXPECT_EQ(kCallInlineName, containing_func->GetFullName());
}
TEST(DwarfSymbolFactory, ModifiedBaseType) {
TestSymbolModule setup(TestSymbolModule::kBuilt);
ASSERT_TRUE(setup.Init().ok());
// Find the GetIntPtr function.
fxl::RefPtr<const Function> function = GetFunctionWithName(setup.symbols(), {kGetIntPtrName});
ASSERT_TRUE(function);
// Get the return type, this references a "pointer" modifier.
EXPECT_TRUE(function->return_type().is_valid());
const ModifiedType* ptr_mod = function->return_type().Get()->As<ModifiedType>();
ASSERT_TRUE(ptr_mod) << "Tag = " << static_cast<int>(function->return_type().Get()->tag());
EXPECT_EQ(DwarfTag::kPointerType, ptr_mod->tag());
EXPECT_EQ("const int*", ptr_mod->GetFullName());
// The modified type should be a "const" modifier.
const ModifiedType* const_mod = ptr_mod->modified().Get()->As<ModifiedType>();
ASSERT_TRUE(const_mod) << "Tag = " << static_cast<int>(function->return_type().Get()->tag());
EXPECT_EQ(DwarfTag::kConstType, const_mod->tag());
EXPECT_EQ("const int", const_mod->GetFullName());
// The modified type should be the int base type.
const BaseType* base = const_mod->modified().Get()->As<BaseType>();
ASSERT_TRUE(base);
EXPECT_EQ(DwarfTag::kBaseType, base->tag());
EXPECT_EQ("int", base->GetFullName());
// Validate the BaseType parameters.
EXPECT_EQ(BaseType::kBaseTypeSigned, base->base_type());
EXPECT_EQ("int", base->GetAssignedName());
// Try to be flexible about the size of ints on the platform.
EXPECT_TRUE(base->byte_size() == 4 || base->byte_size() == 8);
}
TEST(DwarfSymbolFactory, RValueRef) {
TestSymbolModule setup(TestSymbolModule::kBuilt);
ASSERT_TRUE(setup.Init().ok());
// Find the GetIntPtr function.
fxl::RefPtr<const Function> function =
GetFunctionWithName(setup.symbols(), {kMyNsName, kPassRValueRefName});
ASSERT_TRUE(function);
// Should have one parameter of rvalue ref type.
ASSERT_EQ(1u, function->parameters().size());
const Variable* var = function->parameters()[0].Get()->As<Variable>();
ASSERT_TRUE(var);
const ModifiedType* modified = var->type().Get()->As<ModifiedType>();
ASSERT_TRUE(modified);
EXPECT_EQ(DwarfTag::kRvalueReferenceType, modified->tag());
EXPECT_EQ("int&&", modified->GetFullName());
}
TEST(DwarfSymbolFactory, ArrayType) {
TestSymbolModule setup(TestSymbolModule::kBuilt);
ASSERT_TRUE(setup.Init().ok());
// Find the GetString function.
const char kGetString[] = "GetString";
fxl::RefPtr<const Function> function = GetFunctionWithName(setup.symbols(), {kGetString});
ASSERT_TRUE(function);
// Find the "str_array" variable in the function.
ASSERT_EQ(1u, function->variables().size());
const Variable* str_array = function->variables()[0].Get()->As<Variable>();
ASSERT_TRUE(str_array);
EXPECT_EQ("str_array", str_array->GetAssignedName());
// It should be an array type with length 14.
const ArrayType* array_type = str_array->type().Get()->As<ArrayType>();
ASSERT_TRUE(array_type);
EXPECT_EQ(14u, array_type->num_elts());
EXPECT_EQ("const char[14]", array_type->GetFullName());
// The inner type should be a "char".
const Type* elt_type = array_type->value_type();
ASSERT_TRUE(elt_type);
EXPECT_EQ("const char", elt_type->GetFullName());
}
TEST(DwarfSymbolFactory, Array2D) {
TestSymbolModule setup(TestSymbolModule::kBuilt);
ASSERT_TRUE(setup.Init().ok());
// Find the My2DArray function.
const char kMy2DArray[] = "My2DArray";
fxl::RefPtr<const Function> function = GetFunctionWithName(setup.symbols(), {kMy2DArray});
ASSERT_TRUE(function);
// Find the "array" variable in the function. It's declared as:
//
// volatile int array[3][4]
//
ASSERT_EQ(1u, function->variables().size());
const Variable* array = function->variables()[0].Get()->As<Variable>();
ASSERT_TRUE(array);
EXPECT_EQ("array", array->GetAssignedName());
// It should be an array type with length 3 (outer dimension).
const ArrayType* outer_array_type = array->type().Get()->As<ArrayType>();
ASSERT_TRUE(outer_array_type);
EXPECT_EQ(3u, outer_array_type->num_elts());
EXPECT_EQ("volatile int[3][4]", outer_array_type->GetFullName());
// The inner array type should be a int[4].
const Type* inner_type = outer_array_type->value_type();
ASSERT_TRUE(inner_type);
const ArrayType* inner_array_type = inner_type->As<ArrayType>();
ASSERT_TRUE(inner_array_type);
EXPECT_EQ(4u, inner_array_type->num_elts());
EXPECT_EQ("volatile int[4]", inner_array_type->GetFullName());
// The final contained type type should be a "volatile int".
const Type* elt_type = inner_array_type->value_type();
ASSERT_TRUE(elt_type);
EXPECT_EQ("volatile int", elt_type->GetFullName());
}
TEST(DwarfSymbolFactory, Collection) {
TestSymbolModule setup(TestSymbolModule::kBuilt);
ASSERT_TRUE(setup.Init().ok());
// Find the GetStruct function.
fxl::RefPtr<const Function> function =
GetFunctionWithName(setup.symbols(), {kMyNsName, kGetStructName});
ASSERT_TRUE(function);
// The return type should be the struct.
auto* struct_type = function->return_type().Get()->As<Collection>();
ASSERT_TRUE(struct_type);
EXPECT_EQ("my_ns::Struct", struct_type->GetFullName());
// The struct has five data members and two base classes.
ASSERT_EQ(5u, struct_type->data_members().size());
ASSERT_EQ(2u, struct_type->inherited_from().size());
// The first thing should be Base1 at offset 0.
auto* base1 = struct_type->inherited_from()[0].Get()->As<InheritedFrom>();
ASSERT_TRUE(base1);
auto* base1_type = base1->from().Get()->As<Type>();
EXPECT_EQ("my_ns::Base1", base1_type->GetFullName());
EXPECT_EQ(InheritedFrom::kConstant, base1->kind());
EXPECT_EQ(0u, base1->offset());
// It should be followed by Base2. To allow flexibility in packing without breaking this test, all
// offsets below check only that the offset is greater than the previous one and a multiple of 4.
auto* base2 = struct_type->inherited_from()[1].Get()->As<InheritedFrom>();
ASSERT_TRUE(base2);
auto* base2_type = base2->from().Get()->As<Type>();
EXPECT_EQ("my_ns::Base2", base2_type->GetFullName());
EXPECT_EQ(InheritedFrom::kConstant, base2->kind());
EXPECT_LT(0u, base2->offset());
EXPECT_TRUE(base2->offset() % 4 == 0);
// The base classes should be followed by the data members on the struct.
auto* member_a = struct_type->data_members()[0].Get()->As<DataMember>();
ASSERT_TRUE(member_a);
auto* member_a_type = member_a->type().Get()->As<Type>();
EXPECT_EQ("int", member_a_type->GetFullName());
EXPECT_LT(base2->offset(), member_a->member_location());
EXPECT_TRUE(member_a->member_location() % 4 == 0);
// The second data member should be "Struct* member_b".
auto* member_b = struct_type->data_members()[1].Get()->As<DataMember>();
ASSERT_TRUE(member_b);
auto* member_b_type = member_b->type().Get()->As<Type>();
EXPECT_EQ("my_ns::Struct*", member_b_type->GetFullName());
EXPECT_LT(member_a->member_location(), member_b->member_location());
EXPECT_TRUE(member_b->member_location() % 4 == 0);
// The third data member is "const void* v". Void is weird because it will be represented as a
// modified pointer type of nothing.
auto* member_v = struct_type->data_members()[2].Get()->As<DataMember>();
ASSERT_TRUE(member_v);
auto* member_v_type = member_v->type().Get()->As<Type>();
EXPECT_EQ("const void*", member_v_type->GetFullName());
EXPECT_LT(member_b->member_location(), member_v->member_location());
EXPECT_TRUE(member_v->member_location() % 4 == 0);
// The next data member should be kConstInt = -2. This is stored in a ConstValue as a
// little-endian 64-bit signed value.
//
// This assumes the compiler has encoded the constexpr as a DW_AT_const_value. It's theoretically
// possible for the constant value to be encoded as a DWARF expression but none of our compilers
// currently do that and we really want to test ConstValue here.
auto* member_ci = struct_type->data_members()[3].Get()->As<DataMember>();
ASSERT_TRUE(member_ci);
EXPECT_TRUE(member_ci->is_external());
EXPECT_EQ("const int", member_ci->type().Get()->As<Type>()->GetFullName());
EXPECT_TRUE(member_ci->const_value().has_value());
std::vector<uint8_t> expected_minus_two{0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
EXPECT_EQ(expected_minus_two, member_ci->const_value().GetConstValue(8));
// kConstLongDouble (see kConstInt above for notes).
auto* member_cd = struct_type->data_members()[4].Get()->As<DataMember>();
ASSERT_TRUE(member_cd);
EXPECT_TRUE(member_cd->is_external());
EXPECT_EQ("const long double", member_cd->type().Get()->As<Type>()->GetFullName());
EXPECT_TRUE(member_cd->const_value().has_value());
// This is a "long double" which can vary according to platform and compiler settings. Accept the
// standard encodings for 64, 80, and 128 bits.
std::vector<uint8_t> expected_64bit{0x1f, 0x85, 0xeb, 0x51, 0xb8, 0x1e, 0x09, 0x40};
std::vector<uint8_t> expected_80bit{0, 0xf8, 0x28, 0x5c, 0x8f, 0xc2, 0xf5, 0xc8, 0, 0x40};
std::vector<uint8_t> expected_128bit{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0,
0x51, 0xb8, 0x1e, 0x85, 0xeb, 0x91, 0x00, 0x40};
EXPECT_TRUE(expected_64bit == member_cd->const_value().GetConstValue(8) ||
expected_80bit == member_cd->const_value().GetConstValue(10) ||
expected_128bit == member_cd->const_value().GetConstValue(16));
}
// Covers cases of InheritedFrom not covered by the collection test above.
TEST(DwarfSymbolFactory, InheritedFrom) {
TestSymbolModule setup(TestSymbolModule::kBuilt);
ASSERT_TRUE(setup.Init().ok());
const char kGetVirtualDerived[] = "GetVirtualDerived";
fxl::RefPtr<const Function> function = GetFunctionWithName(setup.symbols(), {kGetVirtualDerived});
ASSERT_TRUE(function);
auto* derived_type = function->return_type().Get()->As<Collection>();
ASSERT_TRUE(derived_type);
EXPECT_EQ("VirtualDerived", derived_type->GetFullName());
ASSERT_EQ(1u, derived_type->inherited_from().size());
const InheritedFrom* inherited = derived_type->inherited_from()[0].Get()->As<InheritedFrom>();
ASSERT_TRUE(inherited);
// Validate that it has a nonempty expression. This test doesn't require that the expression
// be a specific thing.
EXPECT_EQ(InheritedFrom::kExpression, inherited->kind());
EXPECT_FALSE(inherited->location_expression().data().empty());
}
TEST(DwarfSymbolFactory, Enum) {
TestSymbolModule setup(TestSymbolModule::kBuilt);
ASSERT_TRUE(setup.Init().ok());
// Find the GetStruct function.
const char kGetStruct[] = "GetStructWithEnums";
fxl::RefPtr<const Function> function = GetFunctionWithName(setup.symbols(), {kGetStruct});
ASSERT_TRUE(function);
// The return type should be the struct.
auto* struct_type = function->return_type().Get()->As<Collection>();
ASSERT_TRUE(struct_type);
EXPECT_EQ("StructWithEnums", struct_type->GetFullName());
// There are three enum members defined in the struct.
ASSERT_EQ(3u, struct_type->data_members().size());
// First is a regular enum with no values.
auto regular_enum =
struct_type->data_members()[0].Get()->As<DataMember>()->type().Get()->As<Enumeration>();
ASSERT_TRUE(regular_enum);
EXPECT_EQ("StructWithEnums::RegularEnum", regular_enum->GetFullName());
EXPECT_TRUE(regular_enum->values().empty());
// Second is an anonymous signed enum with two values. We don't bother to test the enumerator
// values on this one since some aspects will be compiler-dependent.
auto anon_enum =
struct_type->data_members()[1].Get()->As<DataMember>()->type().Get()->As<Enumeration>();
ASSERT_TRUE(anon_enum);
EXPECT_EQ("StructWithEnums::(anon enum)", anon_enum->GetFullName());
EXPECT_TRUE(anon_enum->is_signed());
EXPECT_EQ(2u, anon_enum->values().size());
// Third is a type enum with two values.
auto typed_enum =
struct_type->data_members()[2].Get()->As<DataMember>()->type().Get()->As<Enumeration>();
ASSERT_TRUE(typed_enum);
EXPECT_EQ("StructWithEnums::TypedEnum", typed_enum->GetFullName());
EXPECT_TRUE(typed_enum->is_signed());
ASSERT_EQ(2u, typed_enum->values().size());
EXPECT_EQ(1u, typed_enum->byte_size());
// Since this is typed, the values should be known. The map contains the signed values casted to
// an unsigned int.
auto first_value = *typed_enum->values().begin();
auto second_value = *(++typed_enum->values().begin());
EXPECT_EQ(1u, first_value.first);
EXPECT_EQ("TYPED_B", first_value.second);
EXPECT_EQ(static_cast<uint64_t>(-1), second_value.first);
EXPECT_EQ("TYPED_A", second_value.second);
}
// Tests nested code blocks, variables, and parameters.
TEST(DwarfSymbolFactory, CodeBlocks) {
TestSymbolModule setup(TestSymbolModule::kBuilt);
ASSERT_TRUE(setup.Init().ok());
// Find the DoStructCall function.
fxl::RefPtr<const Function> function =
GetFunctionWithName(setup.symbols(), {kMyNsName, kDoStructCallName});
ASSERT_TRUE(function);
// It should have two parameters, arg1 and arg2.
const Variable* struct_arg = nullptr;
const Variable* int_arg = nullptr;
ASSERT_EQ(2u, function->parameters().size());
for (const auto& param : function->parameters()) {
const Variable* cur_var = param.Get()->As<Variable>();
ASSERT_TRUE(cur_var); // Each parameter should decode to a variable.
if (cur_var->GetAssignedName() == "arg1")
struct_arg = cur_var;
else if (cur_var->GetAssignedName() == "arg2")
int_arg = cur_var;
}
// Both args should have valid locations with non-empty expressions. This doesn't test the actual
// programs because that could vary by build.
EXPECT_FALSE(struct_arg->location().is_null());
EXPECT_FALSE(int_arg->location().is_null());
// Validate the arg1 type (const Struct&).
ASSERT_TRUE(struct_arg);
const Type* struct_arg_type = struct_arg->type().Get()->As<Type>();
ASSERT_TRUE(struct_arg_type);
EXPECT_EQ("const my_ns::Struct&", struct_arg_type->GetFullName());
// Validate the arg2 type (int).
ASSERT_TRUE(int_arg);
const Type* int_arg_type = int_arg->type().Get()->As<Type>();
ASSERT_TRUE(int_arg_type);
EXPECT_EQ("int", int_arg_type->GetFullName());
// The function block should have one variable (var1).
ASSERT_EQ(1u, function->variables().size());
const Variable* var1 = function->variables()[0].Get()->As<Variable>();
ASSERT_TRUE(var1);
const Type* var1_type = var1->type().Get()->As<Type>();
ASSERT_TRUE(var1_type);
EXPECT_EQ("volatile int", var1_type->GetFullName());
// There should be one child lexical scope.
ASSERT_EQ(1u, function->inner_blocks().size());
const CodeBlock* inner = function->inner_blocks()[0].Get()->As<CodeBlock>();
// The lexical scope should have one child variable.
ASSERT_EQ(1u, inner->variables().size());
const Variable* var2 = inner->variables()[0].Get()->As<Variable>();
ASSERT_TRUE(var2);
const Type* var2_type = var2->type().Get()->As<Type>();
ASSERT_TRUE(var2_type);
EXPECT_EQ("volatile my_ns::Struct", var2_type->GetFullName());
// The lexical scope should have no other children.
EXPECT_TRUE(inner->inner_blocks().empty());
}
// Tests both nullptr_t and typedef decoding (which is how it's encoded).
TEST(DwarfSymbolFactory, NullPtrTTypedef) {
TestSymbolModule setup(TestSymbolModule::kBuilt);
ASSERT_TRUE(setup.Init().ok());
// Find the GetNullPtrT function.
const char kGetNullPtrT[] = "GetNullPtrT";
fxl::RefPtr<const Function> function = GetFunctionWithName(setup.symbols(), {kGetNullPtrT});
ASSERT_TRUE(function);
// The return type should be nullptr_t.
auto* nullptr_t_type = function->return_type().Get()->As<Type>();
ASSERT_TRUE(nullptr_t_type);
EXPECT_EQ("nullptr_t", nullptr_t_type->GetFullName());
// The standard defined nullptr_t as "typedef decltype(nullptr) nullptr_t"
auto* typedef_type = nullptr_t_type->As<ModifiedType>();
ASSERT_TRUE(typedef_type);
EXPECT_EQ(DwarfTag::kTypedef, typedef_type->tag());
// Check the type underlying the typedef.
auto* underlying = typedef_type->modified().Get()->As<Type>();
ASSERT_TRUE(underlying);
EXPECT_EQ("decltype(nullptr)", underlying->GetFullName());
// Currently Clang defines this as an "unspecified" type. Since this isn't specified, it's
// possible this may change in the future, but if it does we need to check to make sure everything
// works properly.
EXPECT_EQ(DwarfTag::kUnspecifiedType, underlying->tag());
// The decoder should have forced the size to be the size of a pointer.
EXPECT_EQ(8u, underlying->byte_size());
}
TEST(DwarfSymbolFactory, TemplateParams) {
TestSymbolModule setup(TestSymbolModule::kBuilt);
ASSERT_TRUE(setup.Init().ok());
// Find the GetTemplate() function.
const char kGetTemplate[] = "GetTemplate";
fxl::RefPtr<const Function> function = GetFunctionWithName(setup.symbols(), {kGetTemplate});
ASSERT_TRUE(function);
// The return type should be our collection
auto* my_template = function->return_type().Get()->As<Collection>();
ASSERT_TRUE(my_template);
EXPECT_EQ("MyTemplate<my_ns::Struct, 42>", my_template->GetFullName());
// There should be two template parameters.
ASSERT_EQ(2u, my_template->template_params().size());
// The first one is "T = my_ns::Struct".
auto first_param = my_template->template_params()[0].Get()->As<TemplateParameter>();
ASSERT_TRUE(first_param);
EXPECT_EQ("T", first_param->GetAssignedName());
EXPECT_EQ("T", first_param->GetFullName());
auto first_type = first_param->type().Get()->As<Type>();
ASSERT_TRUE(first_type);
EXPECT_EQ("my_ns::Struct", first_type->GetFullName());
// The second one is "i = int(42)".
auto second_param = my_template->template_params()[1].Get()->As<TemplateParameter>();
ASSERT_TRUE(second_param);
EXPECT_EQ("i", second_param->GetAssignedName());
EXPECT_EQ("i", second_param->GetFullName());
auto second_type = second_param->type().Get()->As<Type>();
ASSERT_TRUE(second_type);
EXPECT_EQ("int", second_type->GetFullName());
}
// TODO(brettw) test using statements. See GetUsing() in type_test.cc
} // namespace zxdb