[Lldb-commits] [lldb] 8334d2b - [lldb/Interpreter] Fix ambiguous partial command resolution (#101934)

via lldb-commits lldb-commits at lists.llvm.org
Thu Aug 8 12:55:13 PDT 2024


Author: Med Ismail Bennani
Date: 2024-08-08T12:55:10-07:00
New Revision: 8334d2bfd34e2666db173269525d17352afa7bac

URL: https://github.com/llvm/llvm-project/commit/8334d2bfd34e2666db173269525d17352afa7bac
DIFF: https://github.com/llvm/llvm-project/commit/8334d2bfd34e2666db173269525d17352afa7bac.diff

LOG: [lldb/Interpreter] Fix ambiguous partial command resolution (#101934)

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>

Added: 
    lldb/test/API/functionalities/ambigous_commands/TestAmbiguousCommands.py
    lldb/test/API/functionalities/ambigous_commands/categories

Modified: 
    lldb/docs/use/tutorial.rst
    lldb/include/lldb/Interpreter/CommandInterpreter.h
    lldb/source/Commands/CommandObjectCommands.cpp
    lldb/source/Interpreter/CommandInterpreter.cpp

Removed: 
    


################################################################################
diff  --git a/lldb/docs/use/tutorial.rst b/lldb/docs/use/tutorial.rst
index 22354c6720e14a..00e7befdd087a4 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 48f6618ab0e392..2bafc30cc8e23a 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 c63445b7c8c868..7c439f4ddb93e3 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 71c928ec811fc6..e45112530404b8 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 00000000000000..14c66fefea7efd
--- /dev/null
+++ b/lldb/test/API/functionalities/ambigous_commands/TestAmbiguousCommands.py
@@ -0,0 +1,35 @@
+"""
+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_with_alias(self):
+        command_interpreter = self.dbg.GetCommandInterpreter()
+        self.assertTrue(command_interpreter, VALID_COMMAND_INTERPRETER)
+        result = lldb.SBCommandReturnObject()
+
+        command_interpreter.HandleCommand(
+            "command alias corefile target create -c %0", result
+        )
+        self.assertTrue(result.Succeeded())
+
+        command_interpreter.ResolveCommand("co", result)
+        self.assertFalse(result.Succeeded())
+        self.assertEqual(
+            result.GetError(),
+            "Ambiguous command 'co'. Possible matches:\n\tcommand\n\tcontinue\n\tcorefile\n",
+        )
+
+        command_interpreter.HandleCommand("command unalias continue", result)
+        self.assertTrue(result.Succeeded())
+
+        command_interpreter.ResolveCommand("co", result)
+        self.assertTrue(result.Succeeded())
+        self.assertEqual(result.GetOutput(), "target create -c %0")

diff  --git a/lldb/test/API/functionalities/ambigous_commands/categories b/lldb/test/API/functionalities/ambigous_commands/categories
new file mode 100644
index 00000000000000..3a3f4df6416b9c
--- /dev/null
+++ b/lldb/test/API/functionalities/ambigous_commands/categories
@@ -0,0 +1 @@
+cmdline


        


More information about the lldb-commits mailing list