Index: lldb/include/lldb/Target/Language.h =================================================================== --- lldb/include/lldb/Target/Language.h +++ lldb/include/lldb/Target/Language.h @@ -211,6 +211,10 @@ // nil/null object, this method returns true virtual bool IsNilReference(ValueObject &valobj); + /// Returns the summary string for ValueObjects for which IsNilReference() is + /// true. + virtual llvm::StringRef GetNilReferenceSummaryString() { 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 Index: lldb/source/DataFormatters/ValueObjectPrinter.cpp =================================================================== --- lldb/source/DataFormatters/ValueObjectPrinter.cpp +++ lldb/source/DataFormatters/ValueObjectPrinter.cpp @@ -355,22 +355,28 @@ if (err_cstr) error.assign(err_cstr); - if (ShouldPrintValueObject()) { - if (IsNil()) - summary.assign("nil"); - else if (IsUninitialized()) - summary.assign(""); - else if (m_options.m_omit_summary_depth == 0) { - TypeSummaryImpl *entry = GetSummaryFormatter(); - if (entry) - m_valobj->GetSummaryAsCString(entry, summary, - m_options.m_varformat_language); - else { - const char *sum_cstr = - m_valobj->GetSummaryAsCString(m_options.m_varformat_language); - if (sum_cstr) - summary.assign(sum_cstr); - } + if (!ShouldPrintValueObject()) + return; + + if (IsNil()) { + lldb::LanguageType lang_type = + (m_options.m_varformat_language == lldb::eLanguageTypeUnknown) + ? m_valobj->GetPreferredDisplayLanguage() + : m_options.m_varformat_language; + if (Language *lang_plugin = Language::FindPlugin(lang_type)) + summary.assign(lang_plugin->GetNilReferenceSummaryString().str()); + } else if (IsUninitialized()) { + summary.assign(""); + } else if (m_options.m_omit_summary_depth == 0) { + TypeSummaryImpl *entry = GetSummaryFormatter(); + if (entry) { + m_valobj->GetSummaryAsCString(entry, summary, + m_options.m_varformat_language); + } else { + const char *sum_cstr = + m_valobj->GetSummaryAsCString(m_options.m_varformat_language); + if (sum_cstr) + summary.assign(sum_cstr); } } } @@ -403,7 +409,9 @@ // this thing is nil (but show the value if the user passes a format // explicitly) TypeSummaryImpl *entry = GetSummaryFormatter(); - if (!IsNil() && !IsUninitialized() && !m_value.empty() && + const bool has_nil_or_uninitialized_summary = + (IsNil() || IsUninitialized()) && !m_summary.empty(); + if (!has_nil_or_uninitialized_summary && !m_value.empty() && (entry == nullptr || (entry->DoesPrintValue(m_valobj) || m_options.m_format != eFormatDefault) || Index: lldb/source/Expression/UserExpression.cpp =================================================================== --- lldb/source/Expression/UserExpression.cpp +++ lldb/source/Expression/UserExpression.cpp @@ -358,6 +358,7 @@ } else { if (expr_result) { result_valobj_sp = expr_result->GetValueObject(); + result_valobj_sp->SetPreferredDisplayLanguage(language); LLDB_LOG(log, "== [UserExpression::Evaluate] Execution completed " Index: lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.h =================================================================== --- lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.h +++ lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.h @@ -88,6 +88,10 @@ HardcodedFormatters::HardcodedSyntheticFinder GetHardcodedSynthetics() override; + bool IsNilReference(ValueObject &valobj) override; + + llvm::StringRef GetNilReferenceSummaryString() override { return "nullptr"; } + bool IsSourceFile(llvm::StringRef file_path) const override; const Highlighter *GetHighlighter() const override { return &m_highlighter; } Index: lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp =================================================================== --- lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp +++ lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp @@ -1134,6 +1134,15 @@ return g_formatters; } +bool CPlusPlusLanguage::IsNilReference(ValueObject &valobj) { + if (!Language::LanguageIsCPlusPlus(valobj.GetObjectRuntimeLanguage()) || + !valobj.IsPointerType()) + 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++"}; Index: lldb/source/Plugins/Language/ObjC/ObjCLanguage.h =================================================================== --- lldb/source/Plugins/Language/ObjC/ObjCLanguage.h +++ lldb/source/Plugins/Language/ObjC/ObjCLanguage.h @@ -119,6 +119,8 @@ bool IsNilReference(ValueObject &valobj) override; + llvm::StringRef GetNilReferenceSummaryString() override { return "nil"; } + bool IsSourceFile(llvm::StringRef file_path) const override; const Highlighter *GetHighlighter() const override { return &m_highlighter; } Index: lldb/source/Plugins/Language/ObjCPlusPlus/ObjCPlusPlusLanguage.h =================================================================== --- lldb/source/Plugins/Language/ObjCPlusPlus/ObjCPlusPlusLanguage.h +++ lldb/source/Plugins/Language/ObjCPlusPlus/ObjCPlusPlusLanguage.h @@ -27,6 +27,8 @@ return lldb::eLanguageTypeObjC_plus_plus; } + llvm::StringRef GetNilReferenceSummaryString() override { return "nil"; } + bool IsSourceFile(llvm::StringRef file_path) const override; const Highlighter *GetHighlighter() const override { return &m_highlighter; } Index: lldb/test/API/commands/expression/import-std-module/forward_decl_from_module/TestForwardDeclFromStdModule.py =================================================================== --- lldb/test/API/commands/expression/import-std-module/forward_decl_from_module/TestForwardDeclFromStdModule.py +++ lldb/test/API/commands/expression/import-std-module/forward_decl_from_module/TestForwardDeclFromStdModule.py @@ -39,4 +39,4 @@ # Both `std::vector` and the type of the member have forward # declarations before their definitions. self.expect("expr --raw -- v", - substrs=['(std::__1::vector) $0 = {', 'f = 0x', '}']) + substrs=['(std::__1::vector) $0 = {', 'f = nullptr', '}']) Index: lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx/string/TestDataFormatterLibcxxString.py =================================================================== --- lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx/string/TestDataFormatterLibcxxString.py +++ 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. Index: lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx/string/main.cpp =================================================================== --- lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx/string/main.cpp +++ lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx/string/main.cpp @@ -74,6 +74,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; Index: lldb/test/API/lang/c/anonymous/TestAnonymous.py =================================================================== --- lldb/test/API/lang/c/anonymous/TestAnonymous.py +++ lldb/test/API/lang/c/anonymous/TestAnonymous.py @@ -62,7 +62,11 @@ # These should display correctly. self.expect("expression pz", VARIABLES_DISPLAYED_CORRECTLY, - substrs=["(type_z *) $", " = 0x0000"]) + substrs=["(type_z *) $", " = 0x0"]) + self.expect("expression --format d -- pz", VARIABLES_DISPLAYED_CORRECTLY, + substrs=["(type_z *) $", " = 0"]) + self.expect("expression --format b -- pz", VARIABLES_DISPLAYED_CORRECTLY, + substrs=["(type_z *) $", " = 0b0"]) self.expect("expression z.y", VARIABLES_DISPLAYED_CORRECTLY, substrs=["(type_y) $", "dummy = 2"])