Index: source/Symbol/Type.cpp =================================================================== --- source/Symbol/Type.cpp +++ source/Symbol/Type.cpp @@ -623,47 +623,62 @@ bool Type::GetTypeScopeAndBasename(const char *&name_cstr, std::string &scope, std::string &basename, TypeClass &type_class) { + type_class = eTypeClassAny; + // Protect against null c string. + if (!name_cstr || !name_cstr[0]) + return false; - type_class = eTypeClassAny; + llvm::StringRef name_strref(name_cstr); + if (name_strref.startswith("struct ")) { + name_cstr += 7; + type_class = eTypeClassStruct; + } else if (name_strref.startswith("class ")) { + name_cstr += 6; + type_class = eTypeClassClass; + } else if (name_strref.startswith("union ")) { + name_cstr += 6; + type_class = eTypeClassUnion; + } else if (name_strref.startswith("enum ")) { + name_cstr += 5; + type_class = eTypeClassEnumeration; + } else if (name_strref.startswith("typedef ")) { + name_cstr += 8; + type_class = eTypeClassTypedef; + } - if (name_cstr && name_cstr[0]) { - llvm::StringRef name_strref(name_cstr); - if (name_strref.startswith("struct ")) { - name_cstr += 7; - type_class = eTypeClassStruct; - } else if (name_strref.startswith("class ")) { - name_cstr += 6; - type_class = eTypeClassClass; - } else if (name_strref.startswith("union ")) { - name_cstr += 6; - type_class = eTypeClassUnion; - } else if (name_strref.startswith("enum ")) { - name_cstr += 5; - type_class = eTypeClassEnumeration; - } else if (name_strref.startswith("typedef ")) { - name_cstr += 8; - type_class = eTypeClassTypedef; - } - const char *basename_cstr = name_cstr; - const char *namespace_separator = ::strstr(basename_cstr, "::"); - if (namespace_separator) { - const char *template_arg_char = ::strchr(basename_cstr, '<'); - while (namespace_separator != nullptr) { - if (template_arg_char && - namespace_separator > template_arg_char) // but namespace'd template - // arguments are still good - // to go - break; - basename_cstr = namespace_separator + 2; - namespace_separator = strstr(basename_cstr, "::"); - } - if (basename_cstr > name_cstr) { - scope.assign(name_cstr, basename_cstr - name_cstr); - basename.assign(basename_cstr); - return true; + const char *basename_cstr = name_cstr; + const char *namespace_separator = ::strstr(basename_cstr, "::"); + const char *template_arg_char = ::strchr(basename_cstr, '<'); + if (!namespace_separator) + return false; + + while (namespace_separator) { + if (template_arg_char && namespace_separator > template_arg_char) { + const char *template_end = template_arg_char + 1; + size_t template_depth = 1; + while (template_depth > 0 && *template_end) { + if (*template_end == '<') + template_depth++; + else if (*template_end == '>') + template_depth--; + template_end++; } + if (template_depth != 0) + return false; // We have an invalid type name. Bail out. + if (!template_end[0]) + break; // The template ends at the end of the full name. + basename_cstr = template_end; + } else { + basename_cstr = namespace_separator + 2; } + namespace_separator = strstr(basename_cstr, "::"); + template_arg_char = ::strchr(basename_cstr, '<'); + } + if (basename_cstr > name_cstr) { + scope.assign(name_cstr, basename_cstr - name_cstr); + basename.assign(basename_cstr); + return true; } return false; } Index: unittests/Symbol/CMakeLists.txt =================================================================== --- unittests/Symbol/CMakeLists.txt +++ unittests/Symbol/CMakeLists.txt @@ -1,3 +1,4 @@ add_lldb_unittest(SymbolTests TestClangASTContext.cpp + TestType.cpp ) Index: unittests/Symbol/TestType.cpp =================================================================== --- /dev/null +++ unittests/Symbol/TestType.cpp @@ -0,0 +1,51 @@ +//===-- TestType.cpp --------------------------------------------*- C++ -*-===// +// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "gtest/gtest.h" + +#include "lldb/Symbol/Type.h" + +using namespace lldb; +using namespace lldb_private; + +namespace { +void TestGetTypeScopeAndBasenameHelper(const char *full_type, + bool expected_is_scoped, + const char *expected_scope, + const char *expected_name) { + std::string scope, name; + lldb::TypeClass type_class; + bool is_scoped = + Type::GetTypeScopeAndBasename(full_type, scope, name, type_class); + EXPECT_EQ(is_scoped, expected_is_scoped); + if (expected_is_scoped) { + EXPECT_EQ(scope, expected_scope); + EXPECT_EQ(name, expected_name); + } +} +}; + +TEST(Type, GetTypeScopeAndBasename) { + TestGetTypeScopeAndBasenameHelper("int", false, "", ""); + TestGetTypeScopeAndBasenameHelper("std::string", true, "std::", "string"); + TestGetTypeScopeAndBasenameHelper("std::set", true, "std::", "set"); + TestGetTypeScopeAndBasenameHelper("std::set>", true, + "std::", "set>"); + TestGetTypeScopeAndBasenameHelper("std::string::iterator", true, + "std::string::", "iterator"); + TestGetTypeScopeAndBasenameHelper("std::set::iterator", true, + "std::set::", "iterator"); + TestGetTypeScopeAndBasenameHelper( + "std::set>::iterator", true, + "std::set>::", "iterator"); + TestGetTypeScopeAndBasenameHelper( + "std::set>::iterator", true, + "std::set>::", "iterator"); +}