diff --git a/lldb/include/lldb/Interpreter/OptionGroupFormat.h b/lldb/include/lldb/Interpreter/OptionGroupFormat.h --- a/lldb/include/lldb/Interpreter/OptionGroupFormat.h +++ b/lldb/include/lldb/Interpreter/OptionGroupFormat.h @@ -16,6 +16,9 @@ namespace lldb_private { +typedef std::vector> + OptionGroupFormatUsageTextVector; + // OptionGroupFormat class OptionGroupFormat : public OptionGroup { @@ -30,7 +33,8 @@ uint64_t default_byte_size = UINT64_MAX, // Pass UINT64_MAX to disable the "--size" option uint64_t default_count = - UINT64_MAX); // Pass UINT64_MAX to disable the "--count" option + UINT64_MAX, // Pass UINT64_MAX to disable the "--count" option + OptionGroupFormatUsageTextVector usage_text_vector = {}); ~OptionGroupFormat() override = default; @@ -73,6 +77,9 @@ char m_prev_gdb_format; char m_prev_gdb_size; bool m_has_gdb_format; + OptionDefinition m_option_definition[4]; + std::string m_format_usage_text; + std::string m_byte_size_usage_text; }; } // namespace lldb_private diff --git a/lldb/source/Commands/CommandObjectMemory.cpp b/lldb/source/Commands/CommandObjectMemory.cpp --- a/lldb/source/Commands/CommandObjectMemory.cpp +++ b/lldb/source/Commands/CommandObjectMemory.cpp @@ -1165,6 +1165,17 @@ #define LLDB_OPTIONS_memory_write #include "CommandOptions.inc" +static OptionGroupFormatUsageTextVector MemoryWriteFormatOptionsUsageText() { + OptionGroupFormatUsageTextVector usage_text_vec; + usage_text_vec.push_back(std::make_tuple( + eArgTypeFormat, + "The format to use for each of the value to be written.")); + usage_text_vec.push_back(std::make_tuple( + eArgTypeByteSize, + "The size in bytes to write from the input file or each value.")); + return usage_text_vec; +} + // Write memory to the inferior process class CommandObjectMemoryWrite : public CommandObjectParsed { public: @@ -1222,7 +1233,8 @@ interpreter, "memory write", "Write to the memory of the current target process.", nullptr, eCommandRequiresProcess | eCommandProcessMustBeLaunched), - m_option_group(), m_format_options(eFormatBytes, 1, UINT64_MAX), + m_option_group(), m_format_options(eFormatBytes, 1, UINT64_MAX, + MemoryWriteFormatOptionsUsageText()), m_memory_options() { CommandArgumentEntry arg1; CommandArgumentEntry arg2; @@ -1240,6 +1252,7 @@ // Define the first (and only) variant of this arg. value_arg.arg_type = eArgTypeValue; value_arg.arg_repetition = eArgRepeatPlus; + value_arg.arg_opt_set_association = LLDB_OPT_SET_1; // There is only one variant this argument could be; put it into the // argument entry. @@ -1278,6 +1291,12 @@ m_cmd_name.c_str()); return false; } + if (argc > 1) { + result.AppendErrorWithFormat( + "%s takes only a destination address when writing file contents.\n", + m_cmd_name.c_str()); + return false; + } } else if (argc < 2) { result.AppendErrorWithFormat( "%s takes a destination address and at least one value.\n", diff --git a/lldb/source/Interpreter/CommandObject.cpp b/lldb/source/Interpreter/CommandObject.cpp --- a/lldb/source/Interpreter/CommandObject.cpp +++ b/lldb/source/Interpreter/CommandObject.cpp @@ -454,6 +454,8 @@ opt_set_mask == LLDB_OPT_SET_ALL ? m_arguments[i] : OptSetFiltered(opt_set_mask, m_arguments[i]); + if (arg_entry.empty()) + continue; int num_alternatives = arg_entry.size(); if ((num_alternatives == 2) && IsPairType(arg_entry[0].arg_repetition)) { diff --git a/lldb/source/Interpreter/OptionGroupFormat.cpp b/lldb/source/Interpreter/OptionGroupFormat.cpp --- a/lldb/source/Interpreter/OptionGroupFormat.cpp +++ b/lldb/source/Interpreter/OptionGroupFormat.cpp @@ -16,31 +16,76 @@ using namespace lldb; using namespace lldb_private; -OptionGroupFormat::OptionGroupFormat(lldb::Format default_format, - uint64_t default_byte_size, - uint64_t default_count) +OptionGroupFormat::OptionGroupFormat( + lldb::Format default_format, uint64_t default_byte_size, + uint64_t default_count, OptionGroupFormatUsageTextVector usage_text_vector) : m_format(default_format, default_format), m_byte_size(default_byte_size, default_byte_size), m_count(default_count, default_count), m_prev_gdb_format('x'), - m_prev_gdb_size('w') {} - -static constexpr OptionDefinition g_option_table[] = { - {LLDB_OPT_SET_1, false, "format", 'f', OptionParser::eRequiredArgument, - nullptr, {}, 0, eArgTypeFormat, - "Specify a format to be used for display."}, - {LLDB_OPT_SET_2, false, "gdb-format", 'G', OptionParser::eRequiredArgument, - nullptr, {}, 0, eArgTypeGDBFormat, - "Specify a format using a GDB format specifier string."}, - {LLDB_OPT_SET_3, false, "size", 's', OptionParser::eRequiredArgument, - nullptr, {}, 0, eArgTypeByteSize, - "The size in bytes to use when displaying with the selected format."}, - {LLDB_OPT_SET_4, false, "count", 'c', OptionParser::eRequiredArgument, - nullptr, {}, 0, eArgTypeCount, - "The number of total items to display."}, -}; + m_prev_gdb_size('w'), + m_option_definition{ + {LLDB_OPT_SET_1, + false, + "format", + 'f', + OptionParser::eRequiredArgument, + nullptr, + {}, + 0, + eArgTypeFormat, + "Specify a format to be used for display."}, + {LLDB_OPT_SET_2, + false, + "gdb-format", + 'G', + OptionParser::eRequiredArgument, + nullptr, + {}, + 0, + eArgTypeGDBFormat, + "Specify a format using a GDB format specifier string."}, + {LLDB_OPT_SET_3, + false, + "size", + 's', + OptionParser::eRequiredArgument, + nullptr, + {}, + 0, + eArgTypeByteSize, + "The size in bytes to use when displaying with the selected " + "format."}, + {LLDB_OPT_SET_4, + false, + "count", + 'c', + OptionParser::eRequiredArgument, + nullptr, + {}, + 0, + eArgTypeCount, + "The number of total items to display."}, + } { + if (!usage_text_vector.empty()) { + for (auto usage_text_tuple : usage_text_vector) { + switch (std::get<0>(usage_text_tuple)) { + case eArgTypeFormat: + m_format_usage_text.append(std::get<1>(usage_text_tuple)); + m_option_definition[0].usage_text = m_format_usage_text.data(); + break; + case eArgTypeByteSize: + m_byte_size_usage_text.append(std::get<1>(usage_text_tuple)); + m_option_definition[2].usage_text = m_byte_size_usage_text.data(); + break; + default: + llvm_unreachable("Unimplemented option"); + } + } + } +} llvm::ArrayRef OptionGroupFormat::GetDefinitions() { - auto result = llvm::makeArrayRef(g_option_table); + auto result = llvm::makeArrayRef(m_option_definition); if (m_byte_size.GetDefaultValue() < UINT64_MAX) { if (m_count.GetDefaultValue() < UINT64_MAX) return result; @@ -54,7 +99,7 @@ llvm::StringRef option_arg, ExecutionContext *execution_context) { Status error; - const int short_option = g_option_table[option_idx].short_option; + const int short_option = m_option_definition[option_idx].short_option; switch (short_option) { case 'f': @@ -262,4 +307,6 @@ m_byte_size.Clear(); m_count.Clear(); m_has_gdb_format = false; + m_format_usage_text.clear(); + m_byte_size_usage_text.clear(); } diff --git a/lldb/test/API/commands/help/TestHelp.py b/lldb/test/API/commands/help/TestHelp.py --- a/lldb/test/API/commands/help/TestHelp.py +++ b/lldb/test/API/commands/help/TestHelp.py @@ -225,3 +225,25 @@ "help format", matching=True, substrs=[' -- One of the format names']) + + @skipIf(bugnumber="llvm.org/pr49018") + @no_debug_info_test + def test_help_option_group_format_options_usage(self): + """Test that help on commands that use OptionGroupFormat options provide relevant help specific to that command.""" + self.expect( + "help memory read", + matching=True, + substrs=[ + "-f ( --format )", "Specify a format to be used for display.", + "-s ( --size )", "The size in bytes to use when displaying with the selected format."]) + + self.expect( + "help memory write", + matching=True, + substrs=[ + "Command Options Usage:", + "memory write [-f ] [-s ]
[ [...]]", + "memory write -i [-s ] [-o ]
", + "-f ( --format )", "The format to use for each of the value to be written.", + "-s ( --size )", "The size in bytes to write from the input file or each value."]) +