[Lldb-commits] [lldb] [lldb/Interpreter] Fix ambiguous partial command resolution (PR #101934)
Med Ismail Bennani via lldb-commits
lldb-commits at lists.llvm.org
Wed Aug 7 16:33:25 PDT 2024
https://github.com/medismailben updated https://github.com/llvm/llvm-project/pull/101934
>From e5a9e2187bf1b5db2c45bb3efafb57b90aab309b Mon Sep 17 00:00:00 2001
From: Med Ismail Bennani <ismail at bennani.ma>
Date: Wed, 7 Aug 2024 16:33:02 -0700
Subject: [PATCH] [lldb/Interpreter] Fix ambiguous partial command resolution
This patch is a follow-up to #97263 that fix ambigous abbreviated
command resolution.
When multiple commands are resolved, instead of failing to pick a command to
run, this patch changes to resolution logic to check if there is a single
alias match and if so, it will run the alias instead of the other matches.
This has as a side-effect that we don't need to make aliases for every
substring of aliases to support abbrivated alias resolution.
Signed-off-by: Med Ismail Bennani <ismail at bennani.ma>
---
lldb/docs/use/tutorial.rst | 4 +
.../lldb/Interpreter/CommandInterpreter.h | 4 +
.../source/Commands/CommandObjectCommands.cpp | 8 +-
.../source/Interpreter/CommandInterpreter.cpp | 84 ++++++++++++++-----
.../TestAmbiguousCommands.py | 19 +++++
.../ambigous_commands/categories | 1 +
6 files changed, 100 insertions(+), 20 deletions(-)
create mode 100644 lldb/test/API/functionalities/ambigous_commands/TestAmbiguousCommands.py
create mode 100644 lldb/test/API/functionalities/ambigous_commands/categories
diff --git a/lldb/docs/use/tutorial.rst b/lldb/docs/use/tutorial.rst
index 22354c6720e14..00e7befdd087a 100644
--- a/lldb/docs/use/tutorial.rst
+++ b/lldb/docs/use/tutorial.rst
@@ -168,6 +168,10 @@ is more convenient to make the basic commands unique down to a letter or two,
and then learn these sequences than to fill the namespace with lots of aliases,
and then have to type them all the way out.
+If the alias abbreviation or the full alias command collides with another
+existing command, the command resolver will prefer to use the alias over any
+other command as far as there is only one alias command match.
+
However, users are free to customize LLDB's command set however they like, and
since LLDB reads the file ``~/.lldbinit`` at startup, you can store all your
aliases there and they will be generally available to you. Your aliases are
diff --git a/lldb/include/lldb/Interpreter/CommandInterpreter.h b/lldb/include/lldb/Interpreter/CommandInterpreter.h
index 48f6618ab0e39..2bafc30cc8e23 100644
--- a/lldb/include/lldb/Interpreter/CommandInterpreter.h
+++ b/lldb/include/lldb/Interpreter/CommandInterpreter.h
@@ -295,6 +295,10 @@ class CommandInterpreter : public Broadcaster,
StringList *matches = nullptr,
StringList *descriptions = nullptr) const;
+ CommandObject *
+ GetAliasCommandObject(llvm::StringRef cmd, StringList *matches = nullptr,
+ StringList *descriptions = nullptr) const;
+
/// Determine whether a root level, built-in command with this name exists.
bool CommandExists(llvm::StringRef cmd) const;
diff --git a/lldb/source/Commands/CommandObjectCommands.cpp b/lldb/source/Commands/CommandObjectCommands.cpp
index c63445b7c8c86..7c439f4ddb93e 100644
--- a/lldb/source/Commands/CommandObjectCommands.cpp
+++ b/lldb/source/Commands/CommandObjectCommands.cpp
@@ -322,7 +322,13 @@ rather than using a positional placeholder:"
(lldb) command alias bl3 breakpoint set -f %1 -l 3
- Always sets a breakpoint on line 3 of whatever file is indicated.)");
+ Always sets a breakpoint on line 3 of whatever file is indicated.
+
+)"
+
+ "If the alias abbreviation or the full alias command collides with another \
+existing command, the command resolver will prefer to use the alias over any \
+other command as far as there is only one alias command match.");
CommandArgumentEntry arg1;
CommandArgumentEntry arg2;
diff --git a/lldb/source/Interpreter/CommandInterpreter.cpp b/lldb/source/Interpreter/CommandInterpreter.cpp
index fc07168b6c0ac..f3cabbd28e976 100644
--- a/lldb/source/Interpreter/CommandInterpreter.cpp
+++ b/lldb/source/Interpreter/CommandInterpreter.cpp
@@ -520,10 +520,6 @@ void CommandInterpreter::Initialize() {
cmd_obj_sp = GetCommandSPExact("scripting run");
if (cmd_obj_sp) {
- AddAlias("sc", cmd_obj_sp);
- AddAlias("scr", cmd_obj_sp);
- AddAlias("scri", cmd_obj_sp);
- AddAlias("scrip", cmd_obj_sp);
AddAlias("script", cmd_obj_sp);
}
@@ -1302,6 +1298,39 @@ CommandObject *CommandInterpreter::GetUserCommandObject(
return {};
}
+CommandObject *CommandInterpreter::GetAliasCommandObject(
+ llvm::StringRef cmd, StringList *matches, StringList *descriptions) const {
+ auto find_exact =
+ [&](const CommandObject::CommandMap &map) -> CommandObject * {
+ auto found_elem = map.find(cmd.str());
+ if (found_elem == map.end())
+ return (CommandObject *)nullptr;
+ CommandObject *exact_cmd = found_elem->second.get();
+ if (!exact_cmd)
+ return nullptr;
+
+ if (matches)
+ matches->AppendString(exact_cmd->GetCommandName());
+
+ if (descriptions)
+ descriptions->AppendString(exact_cmd->GetHelp());
+
+ return exact_cmd;
+ return nullptr;
+ };
+
+ CommandObject *exact_cmd = find_exact(GetAliases());
+ if (exact_cmd)
+ return exact_cmd;
+
+ // We didn't have an exact command, so now look for partial matches.
+ StringList tmp_list;
+ StringList *matches_ptr = matches ? matches : &tmp_list;
+ AddNamesMatchingPartialString(GetAliases(), cmd, *matches_ptr);
+
+ return {};
+}
+
bool CommandInterpreter::CommandExists(llvm::StringRef cmd) const {
return m_command_dict.find(std::string(cmd)) != m_command_dict.end();
}
@@ -3421,6 +3450,19 @@ CommandInterpreter::ResolveCommandImpl(std::string &command_line,
std::string next_word;
StringList matches;
bool done = false;
+
+ auto build_alias_cmd = [&](std::string &full_name) {
+ revised_command_line.Clear();
+ matches.Clear();
+ std::string alias_result;
+ cmd_obj =
+ BuildAliasResult(full_name, scratch_command, alias_result, result);
+ revised_command_line.Printf("%s", alias_result.c_str());
+ if (cmd_obj) {
+ wants_raw_input = cmd_obj->WantsRawCommandString();
+ }
+ };
+
while (!done) {
char quote_char = '\0';
std::string suffix;
@@ -3432,14 +3474,7 @@ CommandInterpreter::ResolveCommandImpl(std::string &command_line,
bool is_real_command =
(!is_alias) || (cmd_obj != nullptr && !cmd_obj->IsAlias());
if (!is_real_command) {
- matches.Clear();
- std::string alias_result;
- cmd_obj =
- BuildAliasResult(full_name, scratch_command, alias_result, result);
- revised_command_line.Printf("%s", alias_result.c_str());
- if (cmd_obj) {
- wants_raw_input = cmd_obj->WantsRawCommandString();
- }
+ build_alias_cmd(full_name);
} else {
if (cmd_obj) {
llvm::StringRef cmd_name = cmd_obj->GetCommandName();
@@ -3486,21 +3521,32 @@ CommandInterpreter::ResolveCommandImpl(std::string &command_line,
if (cmd_obj == nullptr) {
const size_t num_matches = matches.GetSize();
if (matches.GetSize() > 1) {
- StreamString error_msg;
- error_msg.Printf("Ambiguous command '%s'. Possible matches:\n",
- next_word.c_str());
+ StringList alias_matches;
+ GetAliasCommandObject(next_word, &alias_matches);
+
+ if (alias_matches.GetSize() == 1) {
+ std::string full_name;
+ GetAliasFullName(alias_matches.GetStringAtIndex(0), full_name);
+ build_alias_cmd(full_name);
+ done = static_cast<bool>(cmd_obj);
+ } else {
+ StreamString error_msg;
+ error_msg.Printf("Ambiguous command '%s'. Possible matches:\n",
+ next_word.c_str());
- for (uint32_t i = 0; i < num_matches; ++i) {
- error_msg.Printf("\t%s\n", matches.GetStringAtIndex(i));
+ for (uint32_t i = 0; i < num_matches; ++i) {
+ error_msg.Printf("\t%s\n", matches.GetStringAtIndex(i));
+ }
+ result.AppendRawError(error_msg.GetString());
}
- result.AppendRawError(error_msg.GetString());
} else {
// We didn't have only one match, otherwise we wouldn't get here.
lldbassert(num_matches == 0);
result.AppendErrorWithFormat("'%s' is not a valid command.\n",
next_word.c_str());
}
- return nullptr;
+ if (!done)
+ return nullptr;
}
if (cmd_obj->IsMultiwordObject()) {
diff --git a/lldb/test/API/functionalities/ambigous_commands/TestAmbiguousCommands.py b/lldb/test/API/functionalities/ambigous_commands/TestAmbiguousCommands.py
new file mode 100644
index 0000000000000..a9adf5172b01c
--- /dev/null
+++ b/lldb/test/API/functionalities/ambigous_commands/TestAmbiguousCommands.py
@@ -0,0 +1,19 @@
+"""
+Test how lldb reacts to ambiguous commands
+"""
+
+import lldb
+from lldbsuite.test.decorators import *
+from lldbsuite.test.lldbtest import *
+from lldbsuite.test import lldbutil
+
+
+class AmbiguousCommandTestCase(TestBase):
+ @no_debug_info_test
+ def test_ambiguous_command(self):
+ command_interpreter = self.dbg.GetCommandInterpreter()
+ self.assertTrue(command_interpreter, VALID_COMMAND_INTERPRETER)
+ result = lldb.SBCommandReturnObject()
+
+ command_interpreter.HandleCommand("scr 1+1", result)
+ self.assertTrue(result.Succeeded())
diff --git a/lldb/test/API/functionalities/ambigous_commands/categories b/lldb/test/API/functionalities/ambigous_commands/categories
new file mode 100644
index 0000000000000..3a3f4df6416b9
--- /dev/null
+++ b/lldb/test/API/functionalities/ambigous_commands/categories
@@ -0,0 +1 @@
+cmdline
More information about the lldb-commits
mailing list