blob: c8e721761f0395ea2ba695ce6a7f361c666d48e1 [file] [log] [blame]
// Copyright 2019 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/console/format_name.h"
#include <gtest/gtest.h>
#include "src/developer/debug/zxdb/console/output_buffer.h"
#include "src/developer/debug/zxdb/expr/expr_parser.h"
#include "src/developer/debug/zxdb/symbols/base_type.h"
#include "src/developer/debug/zxdb/symbols/collection.h"
#include "src/developer/debug/zxdb/symbols/function.h"
#include "src/developer/debug/zxdb/symbols/namespace.h"
#include "src/developer/debug/zxdb/symbols/symbol_test_parent_setter.h"
#include "src/developer/debug/zxdb/symbols/type_test_support.h"
#include "src/developer/debug/zxdb/symbols/variable_test_support.h"
namespace zxdb {
TEST(FormatName, Regular) {
auto function = fxl::MakeRefCounted<Function>(DwarfTag::kSubprogram);
function->set_assigned_name("Function");
FormatFunctionNameOptions no_param_opts;
no_param_opts.name.show_global_qual = false;
no_param_opts.params = FormatFunctionNameOptions::kNoParams;
FormatFunctionNameOptions elide_param_opts;
elide_param_opts.name.show_global_qual = false;
elide_param_opts.params = FormatFunctionNameOptions::kElideParams;
FormatFunctionNameOptions type_param_opts;
type_param_opts.name.show_global_qual = false;
type_param_opts.params = FormatFunctionNameOptions::kParamTypes;
// Function with no parameters.
EXPECT_EQ("Function", FormatFunctionName(function.get(), no_param_opts).AsString());
EXPECT_EQ("Function()", FormatFunctionName(function.get(), elide_param_opts).AsString());
EXPECT_EQ("Function()", FormatFunctionName(function.get(), type_param_opts).AsString());
// Add two parameters.
auto int32_type = MakeInt32Type();
auto param_value = MakeVariableForTest("value", int32_type, 0x100, 0x200, DwarfExpr());
auto param_other = MakeVariableForTest("other_param", int32_type, 0x100, 0x200, DwarfExpr());
function->set_parameters({LazySymbol(param_value), LazySymbol(param_other)});
EXPECT_EQ("Function", FormatFunctionName(function.get(), no_param_opts).AsString());
EXPECT_EQ("Function(…)", FormatFunctionName(function.get(), elide_param_opts).AsString());
EXPECT_EQ("Function(int32_t, int32_t)",
FormatFunctionName(function.get(), type_param_opts).AsString());
// Put in a namespace and add some templates. This needs a new function because the name will be
// cached above.
function = fxl::MakeRefCounted<Function>(DwarfTag::kSubprogram);
function->set_assigned_name("Function<int>");
function->set_parameters({LazySymbol(param_value), LazySymbol(param_other)});
auto ns = fxl::MakeRefCounted<Namespace>();
ns->set_assigned_name("ns");
SymbolTestParentSetter function_parent(function, ns);
elide_param_opts.name.bold_last = true;
EXPECT_EQ(
"kNormal \"ns::\", "
"kHeading \"Function\", "
"kComment \"<int>(…)\"",
FormatFunctionName(function.get(), elide_param_opts).GetDebugString());
}
TEST(FormatName, ClangLambda) {
// Clang lambdas are anonymous classes with an "operator()" function.
auto closure = fxl::MakeRefCounted<Collection>(DwarfTag::kClassType);
auto function = fxl::MakeRefCounted<Function>(DwarfTag::kSubprogram);
function->set_assigned_name("operator()");
SymbolTestParentSetter function_parent(function, closure);
EXPECT_EQ("λ()", FormatFunctionName(function.get(), FormatFunctionNameOptions()).AsString());
}
TEST(FormatName, GCCLambda) {
// GCC lambdas are specially-named structs with an "operator()" function.
auto closure = fxl::MakeRefCounted<Collection>(DwarfTag::kStructureType);
closure->set_assigned_name("<lambda()>");
auto function = fxl::MakeRefCounted<Function>(DwarfTag::kSubprogram);
function->set_assigned_name("operator()");
SymbolTestParentSetter function_parent(function, closure);
EXPECT_EQ("λ()", FormatFunctionName(function.get(), FormatFunctionNameOptions()).AsString());
}
TEST(FormatName, RustClosure) {
// Rust closures are named like:
// "fuchsia_async::executor::{{impl}}::run_singlethreaded::{{closure}}<()>"
// The function "assigned name" will be "{{closure}}<()>".
// Make a function for the closure to be inside of.
auto enclosing = fxl::MakeRefCounted<Function>(DwarfTag::kSubprogram);
enclosing->set_assigned_name("EnclosingFunction");
auto closure = fxl::MakeRefCounted<Function>(DwarfTag::kSubprogram);
closure->set_assigned_name("{{closure}}<()>");
SymbolTestParentSetter closure_parent(closure, enclosing);
EXPECT_EQ("λ()", FormatFunctionName(closure.get(), FormatFunctionNameOptions()).AsString());
}
TEST(FormatName, FormatIdentifier) {
FormatIdentifierOptions global_opts;
global_opts.show_global_qual = true;
FormatIdentifierOptions no_global_opts;
no_global_opts.show_global_qual = false;
// Regular name with no global qualification.
OutputBuffer output = FormatIdentifier(Identifier("ThisIsAName"), global_opts);
EXPECT_EQ("kNormal \"ThisIsAName\"", output.GetDebugString());
output = FormatIdentifier(Identifier("ThisIsAName"), no_global_opts);
EXPECT_EQ("kNormal \"ThisIsAName\"", output.GetDebugString());
// Regular name with bolding.
FormatIdentifierOptions bold_global_opts;
bold_global_opts.show_global_qual = true;
bold_global_opts.bold_last = true;
output = FormatIdentifier(Identifier("ThisIsAName"), bold_global_opts);
EXPECT_EQ("kHeading \"ThisIsAName\"", output.GetDebugString());
// Hierarchical name.
ParsedIdentifier ident;
Err err = ExprParser::ParseIdentifier("::Foo<int, char*>::Bar<>", &ident);
ASSERT_FALSE(err.has_error());
EXPECT_EQ(
"kNormal \"::Foo\", "
"kComment \"<int, char*>\", "
"kNormal \"::\", "
"kHeading \"Bar\", "
"kComment \"<>\"",
FormatIdentifier(ident, bold_global_opts).GetDebugString());
// Hide global qualification.
FormatIdentifierOptions bold_no_global_opts;
bold_no_global_opts.show_global_qual = true;
bold_no_global_opts.bold_last = true;
EXPECT_EQ("::Foo<int, char*>::Bar<>", FormatIdentifier(ident, bold_no_global_opts).AsString());
// With template eliding.
FormatIdentifierOptions elide_opts = bold_no_global_opts;
elide_opts.elide_templates = true;
EXPECT_EQ("::Foo<…>::Bar<>", FormatIdentifier(ident, elide_opts).AsString());
// With an anonymous namespace.
ParsedIdentifier anon(ParsedIdentifierComponent(""));
anon.AppendComponent(ParsedIdentifierComponent("Function"));
EXPECT_EQ(
"kComment \"$anon\", "
"kNormal \"::\", "
"kHeading \"Function\"",
FormatIdentifier(anon, bold_no_global_opts).GetDebugString());
}
TEST(FormatName, Operators) {
FormatIdentifierOptions opts;
// C++ operator functions shouldn't be escaped.
ParsedIdentifier op_ident;
op_ident.AppendComponent(ParsedIdentifierComponent("MyClass"));
op_ident.AppendComponent(ParsedIdentifierComponent("operator++"));
EXPECT_EQ("MyClass::operator++", FormatIdentifier(op_ident, opts).AsString());
}
TEST(FormatName, FormatSpecialIdentifier) {
FormatIdentifierOptions global_opts;
global_opts.show_global_qual = true;
// Hierarchical name.
ParsedIdentifier ident;
ident.AppendComponent(ParsedIdentifierComponent(SpecialIdentifier::kAnon));
ident.AppendComponent(ParsedIdentifierComponent(SpecialIdentifier::kPlt, "zx_channel_write"));
ident.AppendComponent(ParsedIdentifierComponent(SpecialIdentifier::kMain));
EXPECT_EQ(
"kComment \"$anon\", " // $anon always dim.
"kNormal \"::\", "
"kComment \"$plt(\", " // PLT annotation dim
"kNormal \"zx_channel_write\", " // PLT function name regular.
"kComment \")\", "
"kNormal \"::$main\"",
FormatIdentifier(ident, global_opts).GetDebugString());
}
// Tests printing components with respect to escaping.
TEST(FormatName, EscapeComponent) {
FormatIdentifierOptions global_opts;
// Begins with a digit.
ParsedIdentifier ident;
ident.AppendComponent(ParsedIdentifierComponent("1two"));
EXPECT_EQ(
"kComment \"$(\", "
"kNormal \"1two\", "
"kComment \")\"",
FormatIdentifier(ident, global_opts).GetDebugString());
// Destructors don't need escaping: ~ is a valid character.
ident.components()[0] = ParsedIdentifierComponent("~MyClass");
EXPECT_EQ("kNormal \"~MyClass\"", FormatIdentifier(ident, global_opts).GetDebugString());
// Random bad characters.
ident.components()[0] = ParsedIdentifierComponent("h$ello \\world");
EXPECT_EQ(
"kComment \"$(\", "
"kNormal \"h\\$ello \\\\world\", "
"kComment \")\"",
FormatIdentifier(ident, global_opts).GetDebugString());
// Balanced parens - don't need backslash escaping for them.
ident.components()[0] = ParsedIdentifierComponent("a(b)c");
EXPECT_EQ(
"kComment \"$(\", "
"kNormal \"a(b)c\", "
"kComment \")\"",
FormatIdentifier(ident, global_opts).GetDebugString());
// Disordered parens require backslash escaping.
ident.components()[0] = ParsedIdentifierComponent("a)b(c");
EXPECT_EQ(
"kComment \"$(\", "
"kNormal \"a\\)b\\(c\", "
"kComment \")\"",
FormatIdentifier(ident, global_opts).GetDebugString());
// Imbalanced parens require backslash escaping.
ident.components()[0] = ParsedIdentifierComponent("a(b)c)");
EXPECT_EQ(
"kComment \"$(\", "
"kNormal \"a\\(b\\)c\\)\", "
"kComment \")\"",
FormatIdentifier(ident, global_opts).GetDebugString());
}
} // namespace zxdb