diff --git a/lldb/include/lldb/Target/Language.h b/lldb/include/lldb/Target/Language.h --- a/lldb/include/lldb/Target/Language.h +++ b/lldb/include/lldb/Target/Language.h @@ -215,6 +215,8 @@ // nil/null object, this method returns true virtual bool IsNilReference(ValueObject &valobj); + virtual llvm::StringRef NilReferenceSummaryString() { return {}; } + // for a ValueObject of some "reference type", if the language provides a // technique to decide whether the reference has ever been assigned to some // object, this method will return true if such detection is possible, and if diff --git a/lldb/source/DataFormatters/TypeSummary.cpp b/lldb/source/DataFormatters/TypeSummary.cpp --- a/lldb/source/DataFormatters/TypeSummary.cpp +++ b/lldb/source/DataFormatters/TypeSummary.cpp @@ -123,6 +123,17 @@ std::string &dest, const TypeSummaryOptions &options) { dest.clear(); + + if (valobj && valobj->IsPointerType()) { + bool success = true; + if (!valobj->GetValueAsUnsigned(0, &success)) { + if (!success) + return false; + dest = "nullptr"; + return true; + } + } + StreamString stream; if (!m_impl || !m_impl(*valobj, stream, options)) return false; diff --git a/lldb/source/DataFormatters/ValueObjectPrinter.cpp b/lldb/source/DataFormatters/ValueObjectPrinter.cpp --- a/lldb/source/DataFormatters/ValueObjectPrinter.cpp +++ b/lldb/source/DataFormatters/ValueObjectPrinter.cpp @@ -361,9 +361,11 @@ error.assign(err_cstr); if (ShouldPrintValueObject()) { - if (IsNil()) - summary.assign("nil"); - else if (IsUninitialized()) + if (IsNil()) { + Language *lang_plugin = + Language::FindPlugin(m_valobj->GetObjectRuntimeLanguage()); + summary.assign(lang_plugin->NilReferenceSummaryString().str()); + } else if (IsUninitialized()) summary.assign(""); else if (m_options.m_omit_summary_depth == 0) { TypeSummaryImpl *entry = GetSummaryFormatter(); diff --git a/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.h b/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.h --- a/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.h +++ b/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.h @@ -88,6 +88,10 @@ HardcodedFormatters::HardcodedSyntheticFinder GetHardcodedSynthetics() override; + bool IsNilReference(ValueObject &valobj) override; + + llvm::StringRef NilReferenceSummaryString() override { return "nullptr"; } + bool IsSourceFile(llvm::StringRef file_path) const override; const Highlighter *GetHighlighter() const override { return &m_highlighter; } diff --git a/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp b/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp --- a/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp +++ b/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp @@ -1133,6 +1133,17 @@ return g_formatters; } +bool CPlusPlusLanguage::IsNilReference(ValueObject &valobj) { + const uint32_t mask = eTypeIsCPlusPlus | eTypeIsPointer; + bool isCPPpointer = + (((valobj.GetCompilerType().GetTypeInfo(nullptr)) & mask) == mask); + if (!isCPPpointer) + return false; + bool canReadValue = true; + bool isZero = valobj.GetValueAsUnsigned(0, &canReadValue) == 0; + return canReadValue && isZero; +} + bool CPlusPlusLanguage::IsSourceFile(llvm::StringRef file_path) const { const auto suffixes = {".cpp", ".cxx", ".c++", ".cc", ".c", ".h", ".hh", ".hpp", ".hxx", ".h++"}; diff --git a/lldb/source/Plugins/Language/ObjC/ObjCLanguage.h b/lldb/source/Plugins/Language/ObjC/ObjCLanguage.h --- a/lldb/source/Plugins/Language/ObjC/ObjCLanguage.h +++ b/lldb/source/Plugins/Language/ObjC/ObjCLanguage.h @@ -119,6 +119,8 @@ bool IsNilReference(ValueObject &valobj) override; + llvm::StringRef NilReferenceSummaryString() override { return "nil"; } + bool IsSourceFile(llvm::StringRef file_path) const override; const Highlighter *GetHighlighter() const override { return &m_highlighter; } diff --git a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx/string/TestDataFormatterLibcxxString.py b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx/string/TestDataFormatterLibcxxString.py --- a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx/string/TestDataFormatterLibcxxString.py +++ b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx/string/TestDataFormatterLibcxxString.py @@ -77,6 +77,7 @@ '(%s::u32string) u32_empty = ""'%ns, '(%s::basic_string, ' '%s::allocator >) uchar = "aaaaa"'%(ns,ns,ns), + '(%s::string *) null_str = nullptr'%ns, ]) self.runCmd("n") @@ -114,6 +115,7 @@ '(%s::u32string) u32_empty = ""'%ns, '(%s::basic_string, ' '%s::allocator >) uchar = "aaaaa"'%(ns,ns,ns), + '(%s::string *) null_str = nullptr'%ns, ]) # The test assumes that std::string is in its cap-size-data layout. diff --git a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx/string/main.cpp b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx/string/main.cpp --- a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx/string/main.cpp +++ b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx/string/main.cpp @@ -71,6 +71,7 @@ std::u32string u32_string(U"🍄🍅🍆🍌"); std::u32string u32_empty(U""); std::basic_string uchar(5, 'a'); + std::string *null_str = nullptr; #if _LIBCPP_ABI_VERSION == 1 std::string garbage1, garbage2, garbage3, garbage4, garbage5;