[lldb/DataFormatters] Display null C++ pointers as nullptr
Display null pointer as `nullptr`, `nil` and `NULL` for C++, Objective-C/Objective-C++ and C respectively. The original motivation for this patch was to display a null std::string pointer as nullptr instead of "", but the fix seemed generic enough to be done for all summary providers. Differential revision: https://reviews.llvm.org/D77153
This commit is contained in:
parent
5ab1702129
commit
406ad18748
|
@ -211,6 +211,10 @@ public:
|
|||
// 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
|
||||
|
|
|
@ -355,22 +355,33 @@ void ValueObjectPrinter::GetValueSummaryError(std::string &value,
|
|||
if (err_cstr)
|
||||
error.assign(err_cstr);
|
||||
|
||||
if (ShouldPrintValueObject()) {
|
||||
if (IsNil())
|
||||
summary.assign("nil");
|
||||
else if (IsUninitialized())
|
||||
summary.assign("<uninitialized>");
|
||||
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 {
|
||||
// We treat C as the fallback language rather than as a separate Language
|
||||
// plugin.
|
||||
summary.assign("NULL");
|
||||
}
|
||||
} else if (IsUninitialized()) {
|
||||
summary.assign("<uninitialized>");
|
||||
} 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 +414,9 @@ bool ValueObjectPrinter::PrintValueAndSummaryIfNeeded(bool &value_printed,
|
|||
// 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) ||
|
||||
|
|
|
@ -358,6 +358,7 @@ UserExpression::Evaluate(ExecutionContext &exe_ctx,
|
|||
} else {
|
||||
if (expr_result) {
|
||||
result_valobj_sp = expr_result->GetValueObject();
|
||||
result_valobj_sp->SetPreferredDisplayLanguage(language);
|
||||
|
||||
LLDB_LOG(log,
|
||||
"== [UserExpression::Evaluate] Execution completed "
|
||||
|
|
|
@ -1134,6 +1134,15 @@ CPlusPlusLanguage::GetHardcodedSynthetics() {
|
|||
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++"};
|
||||
|
|
|
@ -88,6 +88,10 @@ public:
|
|||
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; }
|
||||
|
|
|
@ -119,6 +119,8 @@ public:
|
|||
|
||||
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; }
|
||||
|
|
|
@ -27,6 +27,8 @@ public:
|
|||
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; }
|
||||
|
|
|
@ -39,4 +39,4 @@ class TestCase(TestBase):
|
|||
# Both `std::vector` and the type of the member have forward
|
||||
# declarations before their definitions.
|
||||
self.expect("expr --raw -- v",
|
||||
substrs=['(std::__1::vector<int>) $0 = {', 'f = 0x', '}'])
|
||||
substrs=['(std::__1::vector<int>) $0 = {', 'f = nullptr', '}'])
|
||||
|
|
|
@ -80,6 +80,7 @@ class LibcxxStringDataFormatterTestCase(TestBase):
|
|||
'(%s::u32string) u32_empty = ""'%ns,
|
||||
'(%s::basic_string<unsigned char, %s::char_traits<unsigned char>, '
|
||||
'%s::allocator<unsigned char> >) uchar = "aaaaa"'%(ns,ns,ns),
|
||||
'(%s::string *) null_str = nullptr'%ns,
|
||||
])
|
||||
|
||||
self.runCmd("n")
|
||||
|
@ -117,6 +118,7 @@ class LibcxxStringDataFormatterTestCase(TestBase):
|
|||
'(%s::u32string) u32_empty = ""'%ns,
|
||||
'(%s::basic_string<unsigned char, %s::char_traits<unsigned char>, '
|
||||
'%s::allocator<unsigned char> >) uchar = "aaaaa"'%(ns,ns,ns),
|
||||
'(%s::string *) null_str = nullptr'%ns,
|
||||
])
|
||||
|
||||
# The test assumes that std::string is in its cap-size-data layout.
|
||||
|
|
|
@ -74,6 +74,7 @@ int main()
|
|||
std::u32string u32_string(U"🍄🍅🍆🍌");
|
||||
std::u32string u32_empty(U"");
|
||||
std::basic_string<unsigned char> uchar(5, 'a');
|
||||
std::string *null_str = nullptr;
|
||||
|
||||
#if _LIBCPP_ABI_VERSION == 1
|
||||
std::string garbage1, garbage2, garbage3, garbage4, garbage5;
|
||||
|
|
|
@ -62,7 +62,7 @@ class AnonymousTestCase(TestBase):
|
|||
|
||||
# These should display correctly.
|
||||
self.expect("expression pz", VARIABLES_DISPLAYED_CORRECTLY,
|
||||
substrs=["(type_z *) $", " = 0x0000"])
|
||||
substrs=["(type_z *) $", " = NULL"])
|
||||
|
||||
self.expect("expression z.y", VARIABLES_DISPLAYED_CORRECTLY,
|
||||
substrs=["(type_y) $", "dummy = 2"])
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
"""Test that the expression parser doesn't get confused by 'id' and 'Class'"""
|
||||
|
||||
|
||||
|
||||
import lldb
|
||||
from lldbsuite.test.decorators import *
|
||||
from lldbsuite.test.lldbtest import *
|
||||
|
@ -21,7 +19,6 @@ class TestObjCBuiltinTypes(TestBase):
|
|||
self.main_source, '// Set breakpoint here.')
|
||||
|
||||
@add_test_categories(['pyapi'])
|
||||
#<rdar://problem/10591460> [regression] Can't print ivar value: error: reference to 'id' is ambiguous
|
||||
def test_with_python_api(self):
|
||||
"""Test expression parser respect for ObjC built-in types."""
|
||||
self.build()
|
||||
|
@ -57,4 +54,7 @@ class TestObjCBuiltinTypes(TestBase):
|
|||
|
||||
self.expect("expr (foo)", patterns=["\(ns::id\) \$.* = 0"])
|
||||
|
||||
self.expect("expr id my_id = 0; my_id", patterns=["\(id\) \$.* = nil"])
|
||||
self.expect("expr --language Objective-C++ -- id my_id = 0; my_id",
|
||||
patterns=["\(id\) \$.* = nil"])
|
||||
self.expect("expr --language C++ -- id my_id = 0; my_id",
|
||||
patterns=["\(id\) \$.* = nullptr"])
|
Loading…
Reference in New Issue