Index: include/lldb/DataFormatters/TypeSummary.h =================================================================== --- include/lldb/DataFormatters/TypeSummary.h +++ include/lldb/DataFormatters/TypeSummary.h @@ -162,6 +162,22 @@ m_flags &= ~lldb::eTypeOptionHideChildren; return *this; } + + bool + GetHideEmptyAggregates () const + { + return (m_flags & lldb::eTypeOptionHideEmptyAggregates) == lldb::eTypeOptionHideEmptyAggregates; + } + + Flags& + SetHideEmptyAggregates (bool value = true) + { + if (value) + m_flags |= lldb::eTypeOptionHideEmptyAggregates; + else + m_flags &= ~lldb::eTypeOptionHideEmptyAggregates; + return *this; + } bool GetDontShowValue () const @@ -279,6 +295,12 @@ { return !m_flags.GetDontShowChildren(); } + + virtual bool + DoesPrintEmptyAggregates () const + { + return !m_flags.GetHideEmptyAggregates(); + } virtual bool DoesPrintValue (ValueObject* valobj) const Index: include/lldb/DataFormatters/ValueObjectPrinter.h =================================================================== --- include/lldb/DataFormatters/ValueObjectPrinter.h +++ include/lldb/DataFormatters/ValueObjectPrinter.h @@ -344,6 +344,9 @@ bool ShouldPrintChildren (bool is_failed_description, uint32_t& curr_ptr_depth); + + bool + ShouldExpandEmptyAggregates (); ValueObject* GetValueObjectForChildrenGeneration (); Index: include/lldb/lldb-enumerations.h =================================================================== --- include/lldb/lldb-enumerations.h +++ include/lldb/lldb-enumerations.h @@ -729,15 +729,16 @@ //---------------------------------------------------------------------- FLAGS_ENUM(TypeOptions) { - eTypeOptionNone = (0u), - eTypeOptionCascade = (1u << 0), - eTypeOptionSkipPointers = (1u << 1), - eTypeOptionSkipReferences = (1u << 2), - eTypeOptionHideChildren = (1u << 3), - eTypeOptionHideValue = (1u << 4), - eTypeOptionShowOneLiner = (1u << 5), - eTypeOptionHideNames = (1u << 6), - eTypeOptionNonCacheable = (1u << 7) + eTypeOptionNone = (0u), + eTypeOptionCascade = (1u << 0), + eTypeOptionSkipPointers = (1u << 1), + eTypeOptionSkipReferences = (1u << 2), + eTypeOptionHideChildren = (1u << 3), + eTypeOptionHideValue = (1u << 4), + eTypeOptionShowOneLiner = (1u << 5), + eTypeOptionHideNames = (1u << 6), + eTypeOptionNonCacheable = (1u << 7), + eTypeOptionHideEmptyAggregates = (1u << 8) }; //---------------------------------------------------------------------- Index: source/Commands/CommandObjectType.cpp =================================================================== --- source/Commands/CommandObjectType.cpp +++ source/Commands/CommandObjectType.cpp @@ -1438,6 +1438,9 @@ case 'e': m_flags.SetDontShowChildren(false); break; + case 'h': + m_flags.SetHideEmptyAggregates(true); + break; case 'v': m_flags.SetDontShowValue(true); break; @@ -1924,6 +1927,7 @@ { LLDB_OPT_SET_3, false, "python-function", 'F', OptionParser::eRequiredArgument, NULL, NULL, 0, eArgTypePythonFunction, "Give the name of a Python function to use for this type."}, { LLDB_OPT_SET_3, false, "input-python", 'P', OptionParser::eNoArgument, NULL, NULL, 0, eArgTypeNone, "Input Python code to use for this type manually."}, { LLDB_OPT_SET_2 | LLDB_OPT_SET_3, false, "expand", 'e', OptionParser::eNoArgument, NULL, NULL, 0, eArgTypeNone, "Expand aggregate data types to show children on separate lines."}, + { LLDB_OPT_SET_2 | LLDB_OPT_SET_3, false, "hide-empty", 'h', OptionParser::eNoArgument, NULL, NULL, 0, eArgTypeNone, "Do not expand aggregate data types with no children."}, { LLDB_OPT_SET_2 | LLDB_OPT_SET_3, false, "name", 'n', OptionParser::eRequiredArgument, NULL, NULL, 0, eArgTypeName, "A name for this summary string."}, { 0, false, NULL, 0, 0, NULL, NULL, 0, eArgTypeNone, NULL } }; Index: source/DataFormatters/ValueObjectPrinter.cpp =================================================================== --- source/DataFormatters/ValueObjectPrinter.cpp +++ source/DataFormatters/ValueObjectPrinter.cpp @@ -480,6 +480,17 @@ return false; } +bool +ValueObjectPrinter::ShouldExpandEmptyAggregates () +{ + TypeSummaryImpl* entry = GetSummaryFormatter(); + + if (!entry) + return true; + + return entry->DoesPrintEmptyAggregates(); +} + ValueObject* ValueObjectPrinter::GetValueObjectForChildrenGeneration () { @@ -582,7 +593,7 @@ if (ShouldPrintValueObject()) { // if it has a synthetic value, then don't print {}, the synthetic children are probably only being used to vend a value - if (m_valobj->DoesProvideSyntheticValue()) + if (m_valobj->DoesProvideSyntheticValue() || !ShouldExpandEmptyAggregates()) m_stream->PutCString( "\n"); else m_stream->PutCString(" {}\n"); Index: test/functionalities/data-formatter/data-formatter-synth/TestDataFormatterSynth.py =================================================================== --- test/functionalities/data-formatter/data-formatter-synth/TestDataFormatterSynth.py +++ test/functionalities/data-formatter/data-formatter-synth/TestDataFormatterSynth.py @@ -208,6 +208,12 @@ self.expect('frame variable bag_bag', substrs = ['x.z = 12']) + self.runCmd('type summary add -e -s "I am always empty but have" EmptyStruct') + self.expect('frame variable es', substrs = ["I am always empty but have {}"]) + self.runCmd('type summary add -e -h -s "I am really empty" EmptyStruct') + self.expect('frame variable es', substrs = ["I am really empty"]) + self.expect('frame variable es', substrs = ["I am really empty {}"], matching=False) + if __name__ == '__main__': import atexit Index: test/functionalities/data-formatter/data-formatter-synth/main.cpp =================================================================== --- test/functionalities/data-formatter/data-formatter-synth/main.cpp +++ test/functionalities/data-formatter/data-formatter-synth/main.cpp @@ -46,6 +46,8 @@ q(20.11) {} }; +struct EmptyStruct {}; + struct Plenty { BagOfInts *some_values; @@ -70,6 +72,7 @@ BagOfFloats float_bag(2.71); BagOfBags bag_bag; + EmptyStruct es; Plenty plenty_of_stuff(5,true,false); Index: test/python_api/formatters/TestFormattersSBAPI.py =================================================================== --- test/python_api/formatters/TestFormattersSBAPI.py +++ test/python_api/formatters/TestFormattersSBAPI.py @@ -302,6 +302,10 @@ self.assertTrue(summary.IsValid(), "no summary found for foo* when one was in place") self.assertTrue(summary.GetData() == "hello static world", "wrong summary found for foo*") + self.expect("frame variable e1", substrs=["I am an empty Empty1 {}"]) + self.expect("frame variable e2", substrs=["I am an empty Empty2"]) + self.expect("frame variable e2", substrs=["I am an empty Empty2 {}"], matching=False) + def force_synth_off(self): """Test that one can have the public API return non-synthetic SBValues if desired""" self.runCmd("file no_synth", CURRENT_EXECUTABLE_SET) Index: test/python_api/formatters/main.cpp =================================================================== --- test/python_api/formatters/main.cpp +++ test/python_api/formatters/main.cpp @@ -26,6 +26,9 @@ int a, b, c; }; +struct Empty1 { void *data; }; +struct Empty2 { void *data; }; + int main(int argc, char const *argv[]) { JustAStruct foo; @@ -49,5 +52,8 @@ CCC ccc = {111, 222, 333}; + Empty1 e1; + Empty2 e2; + return 0; // Set break point at this line. } Index: test/python_api/formatters/synth.py =================================================================== --- test/python_api/formatters/synth.py +++ test/python_api/formatters/synth.py @@ -48,6 +48,36 @@ return self._sbvalue.GetChildMemberWithName("c") +def empty1_summary(sbvalue, internal_dict): + return "I am an empty Empty1" + + +class Empty1SynthProvider(object): + def __init__(self, sbvalue, internal_dict): + self._sbvalue = sbvalue + + def num_children(self): + return 0 + + def get_child_at_index(self, index): + return None + + +def empty2_summary(sbvalue, internal_dict): + return "I am an empty Empty2" + + +class Empty2SynthProvider(object): + def __init__(self, sbvalue, internal_dict): + self._sbvalue = sbvalue + + def num_children(self): + return 0 + + def get_child_at_index(self, index): + return None + + def __lldb_init_module(debugger,dict): debugger.CreateCategory("JASSynth").AddTypeSynthetic(lldb.SBTypeNameSpecifier("JustAStruct"), lldb.SBTypeSynthetic.CreateWithClassName("synth.jasSynthProvider")) @@ -60,5 +90,16 @@ lldb.SBTypeNameSpecifier("CCC"), lldb.SBTypeSummary.CreateWithFunctionName("synth.ccc_summary", lldb.eTypeOptionCascade)) - - + cat.AddTypeSynthetic( + lldb.SBTypeNameSpecifier("Empty1"), + lldb.SBTypeSynthetic.CreateWithClassName("synth.Empty1SynthProvider")) + cat.AddTypeSummary( + lldb.SBTypeNameSpecifier("Empty1"), + lldb.SBTypeSummary.CreateWithFunctionName("synth.empty1_summary")) + cat.AddTypeSynthetic( + lldb.SBTypeNameSpecifier("Empty2"), + lldb.SBTypeSynthetic.CreateWithClassName("synth.Empty2SynthProvider")) + cat.AddTypeSummary( + lldb.SBTypeNameSpecifier("Empty2"), + lldb.SBTypeSummary.CreateWithFunctionName("synth.empty2_summary", + lldb.eTypeOptionHideEmptyAggregates))