[Lldb-commits] [lldb] [lldb] Treat user aliases the same as built-ins when tab completing (PR #65974)
David Spickett via lldb-commits
lldb-commits at lists.llvm.org
Mon Sep 11 09:18:33 PDT 2023
https://github.com/DavidSpickett updated https://github.com/llvm/llvm-project/pull/65974:
>From ea172bdf8b1d66087076b7ed842021749a49b9fb Mon Sep 17 00:00:00 2001
From: David Spickett <david.spickett at linaro.org>
Date: Mon, 11 Sep 2023 09:09:31 +0000
Subject: [PATCH 1/3] [lldb] Improve completion tests
* Assert no completions for tests that should not find completions.
* Remove regex mode from complete_from_to, which was unused.
This exposed bugs in 2 of the tests, target stop-hook and
process unload. These were fixed in previous commits but
couldn't be tested properly until this patch.
---
.../Python/lldbsuite/test/lldbtest.py | 30 +++---
.../completion/TestExprCompletion.py | 92 +++++++++----------
.../completion/TestCompletion.py | 14 +--
3 files changed, 62 insertions(+), 74 deletions(-)
diff --git a/lldb/packages/Python/lldbsuite/test/lldbtest.py b/lldb/packages/Python/lldbsuite/test/lldbtest.py
index 49355d61911837f..4f8bdcd7263dc35 100644
--- a/lldb/packages/Python/lldbsuite/test/lldbtest.py
+++ b/lldb/packages/Python/lldbsuite/test/lldbtest.py
@@ -2223,10 +2223,7 @@ def check_completion_with_desc(
)
self.assertFalse(got_failure, error_msg)
- def complete_exactly(self, str_input, patterns):
- self.complete_from_to(str_input, patterns, True)
-
- def complete_from_to(self, str_input, patterns, turn_off_re_match=False):
+ def complete_from_to(self, str_input, patterns):
"""Test that the completion mechanism completes str_input to patterns,
where patterns could be a pattern-string or a list of pattern-strings"""
# Patterns should not be None in order to proceed.
@@ -2254,21 +2251,18 @@ def complete_from_to(self, str_input, patterns, turn_off_re_match=False):
for idx in range(1, num_matches + 1):
compare_string += match_strings.GetStringAtIndex(idx) + "\n"
+ # If the singular pattern is the same as the input, assume that we
+ # shouldn't have any completions.
+ if len(patterns) == 1 and str_input == patterns[0] and num_matches:
+ self.fail("Expected no completions but got:\n" + compare_string)
+
for p in patterns:
- if turn_off_re_match:
- self.expect(
- compare_string,
- msg=COMPLETION_MSG(str_input, p, match_strings),
- exe=False,
- substrs=[p],
- )
- else:
- self.expect(
- compare_string,
- msg=COMPLETION_MSG(str_input, p, match_strings),
- exe=False,
- patterns=[p],
- )
+ self.expect(
+ compare_string,
+ msg=COMPLETION_MSG(str_input, p, match_strings),
+ exe=False,
+ substrs=[p],
+ )
def completions_match(self, command, completions):
"""Checks that the completions for the given command are equal to the
diff --git a/lldb/test/API/commands/expression/completion/TestExprCompletion.py b/lldb/test/API/commands/expression/completion/TestExprCompletion.py
index 3c354a3bce1a9b5..ada818989c789a1 100644
--- a/lldb/test/API/commands/expression/completion/TestExprCompletion.py
+++ b/lldb/test/API/commands/expression/completion/TestExprCompletion.py
@@ -29,34 +29,34 @@ def test_expr_completion(self):
)
# Completing member functions
- self.complete_exactly(
+ self.complete_from_to(
"expr some_expr.FooNoArgs", "expr some_expr.FooNoArgsBar()"
)
- self.complete_exactly(
+ self.complete_from_to(
"expr some_expr.FooWithArgs", "expr some_expr.FooWithArgsBar("
)
- self.complete_exactly(
+ self.complete_from_to(
"expr some_expr.FooWithMultipleArgs",
"expr some_expr.FooWithMultipleArgsBar(",
)
- self.complete_exactly(
+ self.complete_from_to(
"expr some_expr.FooUnderscore", "expr some_expr.FooUnderscoreBar_()"
)
- self.complete_exactly(
+ self.complete_from_to(
"expr some_expr.FooNumbers", "expr some_expr.FooNumbersBar1()"
)
- self.complete_exactly(
+ self.complete_from_to(
"expr some_expr.StaticMemberMethod",
"expr some_expr.StaticMemberMethodBar()",
)
# Completing static functions
- self.complete_exactly(
+ self.complete_from_to(
"expr Expr::StaticMemberMethod", "expr Expr::StaticMemberMethodBar()"
)
# Completing member variables
- self.complete_exactly(
+ self.complete_from_to(
"expr some_expr.MemberVariab", "expr some_expr.MemberVariableBar"
)
@@ -94,43 +94,43 @@ def test_expr_completion(self):
self.completions_contain("expr 1+", ["1+some_expr", "1+static_cast"])
# Test with spaces
- self.complete_exactly(
+ self.complete_from_to(
"expr some_expr .FooNoArgs", "expr some_expr .FooNoArgsBar()"
)
- self.complete_exactly(
+ self.complete_from_to(
"expr some_expr .FooNoArgs", "expr some_expr .FooNoArgsBar()"
)
- self.complete_exactly(
+ self.complete_from_to(
"expr some_expr .FooNoArgs", "expr some_expr .FooNoArgsBar()"
)
- self.complete_exactly(
+ self.complete_from_to(
"expr some_expr. FooNoArgs", "expr some_expr. FooNoArgsBar()"
)
- self.complete_exactly(
+ self.complete_from_to(
"expr some_expr . FooNoArgs", "expr some_expr . FooNoArgsBar()"
)
- self.complete_exactly(
+ self.complete_from_to(
"expr Expr :: StaticMemberMethod", "expr Expr :: StaticMemberMethodBar()"
)
- self.complete_exactly(
+ self.complete_from_to(
"expr Expr ::StaticMemberMethod", "expr Expr ::StaticMemberMethodBar()"
)
- self.complete_exactly(
+ self.complete_from_to(
"expr Expr:: StaticMemberMethod", "expr Expr:: StaticMemberMethodBar()"
)
# Test that string literals don't break our parsing logic.
- self.complete_exactly(
+ self.complete_from_to(
'expr const char *cstr = "some_e"; char c = *cst',
'expr const char *cstr = "some_e"; char c = *cstr',
)
- self.complete_exactly(
+ self.complete_from_to(
'expr const char *cstr = "some_e" ; char c = *cst',
'expr const char *cstr = "some_e" ; char c = *cstr',
)
# Requesting completions inside an incomplete string doesn't provide any
# completions.
- self.complete_exactly(
+ self.complete_from_to(
'expr const char *cstr = "some_e', 'expr const char *cstr = "some_e'
)
@@ -139,110 +139,110 @@ def test_expr_completion(self):
self.assume_no_completions("expr -i0 -- some_expr.", 11)
# Test with expr arguments
- self.complete_exactly(
+ self.complete_from_to(
"expr -i0 -- some_expr .FooNoArgs", "expr -i0 -- some_expr .FooNoArgsBar()"
)
- self.complete_exactly(
+ self.complete_from_to(
"expr -i0 -- some_expr .FooNoArgs",
"expr -i0 -- some_expr .FooNoArgsBar()",
)
# Addrof and deref
- self.complete_exactly(
+ self.complete_from_to(
"expr (*(&some_expr)).FooNoArgs", "expr (*(&some_expr)).FooNoArgsBar()"
)
- self.complete_exactly(
+ self.complete_from_to(
"expr (*(&some_expr)) .FooNoArgs", "expr (*(&some_expr)) .FooNoArgsBar()"
)
- self.complete_exactly(
+ self.complete_from_to(
"expr (* (&some_expr)) .FooNoArgs", "expr (* (&some_expr)) .FooNoArgsBar()"
)
- self.complete_exactly(
+ self.complete_from_to(
"expr (* (& some_expr)) .FooNoArgs",
"expr (* (& some_expr)) .FooNoArgsBar()",
)
# Addrof and deref (part 2)
- self.complete_exactly(
+ self.complete_from_to(
"expr (&some_expr)->FooNoArgs", "expr (&some_expr)->FooNoArgsBar()"
)
- self.complete_exactly(
+ self.complete_from_to(
"expr (&some_expr) ->FooNoArgs", "expr (&some_expr) ->FooNoArgsBar()"
)
- self.complete_exactly(
+ self.complete_from_to(
"expr (&some_expr) -> FooNoArgs", "expr (&some_expr) -> FooNoArgsBar()"
)
- self.complete_exactly(
+ self.complete_from_to(
"expr (&some_expr)-> FooNoArgs", "expr (&some_expr)-> FooNoArgsBar()"
)
# Builtin arg
- self.complete_exactly("expr static_ca", "expr static_cast")
+ self.complete_from_to("expr static_ca", "expr static_cast")
# From other files
- self.complete_exactly(
+ self.complete_from_to(
"expr fwd_decl_ptr->Hidden", "expr fwd_decl_ptr->HiddenMember"
)
# Types
- self.complete_exactly("expr LongClassNa", "expr LongClassName")
- self.complete_exactly(
+ self.complete_from_to("expr LongClassNa", "expr LongClassName")
+ self.complete_from_to(
"expr LongNamespaceName::NestedCla", "expr LongNamespaceName::NestedClass"
)
# Namespaces
- self.complete_exactly("expr LongNamespaceNa", "expr LongNamespaceName::")
+ self.complete_from_to("expr LongNamespaceNa", "expr LongNamespaceName::")
# Multiple arguments
- self.complete_exactly(
+ self.complete_from_to(
"expr &some_expr + &some_e", "expr &some_expr + &some_expr"
)
- self.complete_exactly(
+ self.complete_from_to(
"expr SomeLongVarNameWithCapitals + SomeLongVarName",
"expr SomeLongVarNameWithCapitals + SomeLongVarNameWithCapitals",
)
- self.complete_exactly(
+ self.complete_from_to(
"expr SomeIntVar + SomeIntV", "expr SomeIntVar + SomeIntVar"
)
# Multiple statements
- self.complete_exactly(
+ self.complete_from_to(
"expr long LocalVariable = 0; LocalVaria",
"expr long LocalVariable = 0; LocalVariable",
)
# Custom Decls
- self.complete_exactly(
+ self.complete_from_to(
"expr auto l = [](int LeftHandSide, int bx){ return LeftHandS",
"expr auto l = [](int LeftHandSide, int bx){ return LeftHandSide",
)
- self.complete_exactly(
+ self.complete_from_to(
"expr struct LocalStruct { long MemberName; } ; LocalStruct S; S.Mem",
"expr struct LocalStruct { long MemberName; } ; LocalStruct S; S.MemberName",
)
# Completing function call arguments
- self.complete_exactly(
+ self.complete_from_to(
"expr some_expr.FooWithArgsBar(some_exp",
"expr some_expr.FooWithArgsBar(some_expr",
)
- self.complete_exactly(
+ self.complete_from_to(
"expr some_expr.FooWithArgsBar(SomeIntV",
"expr some_expr.FooWithArgsBar(SomeIntVar",
)
- self.complete_exactly(
+ self.complete_from_to(
"expr some_expr.FooWithMultipleArgsBar(SomeIntVar, SomeIntVa",
"expr some_expr.FooWithMultipleArgsBar(SomeIntVar, SomeIntVar",
)
# Function return values
- self.complete_exactly(
+ self.complete_from_to(
"expr some_expr.Self().FooNoArgs", "expr some_expr.Self().FooNoArgsBar()"
)
- self.complete_exactly(
+ self.complete_from_to(
"expr some_expr.Self() .FooNoArgs", "expr some_expr.Self() .FooNoArgsBar()"
)
- self.complete_exactly(
+ self.complete_from_to(
"expr some_expr.Self(). FooNoArgs", "expr some_expr.Self(). FooNoArgsBar()"
)
diff --git a/lldb/test/API/functionalities/completion/TestCompletion.py b/lldb/test/API/functionalities/completion/TestCompletion.py
index cc2a20dcd0dca76..8898bf2e2b5da9e 100644
--- a/lldb/test/API/functionalities/completion/TestCompletion.py
+++ b/lldb/test/API/functionalities/completion/TestCompletion.py
@@ -237,16 +237,13 @@ def test_log_dir(self):
src_dir = os.path.dirname(os.path.realpath(__file__))
self.complete_from_to(
"log enable lldb expr -f " + src_dir,
- [src_dir + os.sep],
- turn_off_re_match=True,
- )
+ [src_dir + os.sep])
# <rdar://problem/11052829>
def test_infinite_loop_while_completing(self):
"""Test that 'process print hello\' completes to itself and does not infinite loop."""
self.complete_from_to(
- "process print hello\\", "process print hello\\", turn_off_re_match=True
- )
+ "process print hello\\", "process print hello\\")
def test_watchpoint_co(self):
"""Test that 'watchpoint co' completes to 'watchpoint command '."""
@@ -727,9 +724,7 @@ def test_symbol_name(self):
self.dbg.CreateTarget(self.getBuildArtifact("a.out"))
self.complete_from_to(
"breakpoint set -n Fo",
- "breakpoint set -n Foo::Bar(int,\\ int)",
- turn_off_re_match=True,
- )
+ "breakpoint set -n Foo::Bar(int,\\ int)")
# No completion for Qu because the candidate is
# (anonymous namespace)::Quux().
self.complete_from_to("breakpoint set -n Qu", "")
@@ -822,8 +817,7 @@ def test_common_completion_target_stophook_ids(self):
for subcommand in subcommands:
self.complete_from_to(
"target stop-hook " + subcommand + " 1 ",
- "target stop-hook " + subcommand + " 1 ",
- )
+ "target stop-hook " + subcommand + " 1 ")
def test_common_completion_type_language(self):
self.complete_from_to("type category -l ", ["c"])
>From 15228bc0a2311df5b422b0878fd7da23ad480d6e Mon Sep 17 00:00:00 2001
From: David Spickett <david.spickett at linaro.org>
Date: Mon, 11 Sep 2023 16:17:03 +0000
Subject: [PATCH 2/3] Format with black
---
.../functionalities/completion/TestCompletion.py | 14 ++++++--------
1 file changed, 6 insertions(+), 8 deletions(-)
diff --git a/lldb/test/API/functionalities/completion/TestCompletion.py b/lldb/test/API/functionalities/completion/TestCompletion.py
index 8898bf2e2b5da9e..e01bc0ce03f5c95 100644
--- a/lldb/test/API/functionalities/completion/TestCompletion.py
+++ b/lldb/test/API/functionalities/completion/TestCompletion.py
@@ -235,15 +235,12 @@ def test_log_file(self):
def test_log_dir(self):
# Complete our source directory.
src_dir = os.path.dirname(os.path.realpath(__file__))
- self.complete_from_to(
- "log enable lldb expr -f " + src_dir,
- [src_dir + os.sep])
+ self.complete_from_to("log enable lldb expr -f " + src_dir, [src_dir + os.sep])
# <rdar://problem/11052829>
def test_infinite_loop_while_completing(self):
"""Test that 'process print hello\' completes to itself and does not infinite loop."""
- self.complete_from_to(
- "process print hello\\", "process print hello\\")
+ self.complete_from_to("process print hello\\", "process print hello\\")
def test_watchpoint_co(self):
"""Test that 'watchpoint co' completes to 'watchpoint command '."""
@@ -723,8 +720,8 @@ def test_symbol_name(self):
self.build()
self.dbg.CreateTarget(self.getBuildArtifact("a.out"))
self.complete_from_to(
- "breakpoint set -n Fo",
- "breakpoint set -n Foo::Bar(int,\\ int)")
+ "breakpoint set -n Fo", "breakpoint set -n Foo::Bar(int,\\ int)"
+ )
# No completion for Qu because the candidate is
# (anonymous namespace)::Quux().
self.complete_from_to("breakpoint set -n Qu", "")
@@ -817,7 +814,8 @@ def test_common_completion_target_stophook_ids(self):
for subcommand in subcommands:
self.complete_from_to(
"target stop-hook " + subcommand + " 1 ",
- "target stop-hook " + subcommand + " 1 ")
+ "target stop-hook " + subcommand + " 1 ",
+ )
def test_common_completion_type_language(self):
self.complete_from_to("type category -l ", ["c"])
>From 70d397e89198320744238d82f19f71ccd3364b77 Mon Sep 17 00:00:00 2001
From: David Spickett <david.spickett at linaro.org>
Date: Mon, 11 Sep 2023 11:42:45 +0000
Subject: [PATCH 3/3] [lldb] Treat user aliases the same as built-ins when tab
completing
Previously we would check all built-ins first for suggestions,
then check built-ins and aliases. This meant that if you had
an alias brkpt -> breakpoint, "br" would complete to "breakpoint".
Instead of giving you the choice of "brkpt" or "breakpoint".
---
.../source/Interpreter/CommandInterpreter.cpp | 35 +++----------------
.../abbreviation/TestAbbreviations.py | 2 +-
.../completion/TestCompletion.py | 18 ++++------
3 files changed, 13 insertions(+), 42 deletions(-)
diff --git a/lldb/source/Interpreter/CommandInterpreter.cpp b/lldb/source/Interpreter/CommandInterpreter.cpp
index 6d1ad799f2d10fb..fc94bf6fbfe117c 100644
--- a/lldb/source/Interpreter/CommandInterpreter.cpp
+++ b/lldb/source/Interpreter/CommandInterpreter.cpp
@@ -1227,36 +1227,11 @@ CommandObject *
CommandInterpreter::GetCommandObject(llvm::StringRef cmd_str,
StringList *matches,
StringList *descriptions) const {
- CommandObject *command_obj =
- GetCommandSP(cmd_str, false, true, matches, descriptions).get();
-
- // If we didn't find an exact match to the command string in the commands,
- // look in the aliases.
-
- if (command_obj)
- return command_obj;
-
- command_obj = GetCommandSP(cmd_str, true, true, matches, descriptions).get();
-
- if (command_obj)
- return command_obj;
-
- // If there wasn't an exact match then look for an inexact one in just the
- // commands
- command_obj = GetCommandSP(cmd_str, false, false, nullptr).get();
-
- // Finally, if there wasn't an inexact match among the commands, look for an
- // inexact match in both the commands and aliases.
-
- if (command_obj) {
- if (matches)
- matches->AppendString(command_obj->GetCommandName());
- if (descriptions)
- descriptions->AppendString(command_obj->GetHelp());
- return command_obj;
- }
-
- return GetCommandSP(cmd_str, true, false, matches, descriptions).get();
+ // Try to find a match among commands and aliases. Allowing inexact matches,
+ // but perferring exact matches.
+ return GetCommandSP(cmd_str, /*include_aliases=*/true, /*exact=*/false,
+ matches, descriptions)
+ .get();
}
CommandObject *CommandInterpreter::GetUserCommandObject(
diff --git a/lldb/test/API/functionalities/abbreviation/TestAbbreviations.py b/lldb/test/API/functionalities/abbreviation/TestAbbreviations.py
index 10431e41dc81a2e..cade8d87e7f76f5 100644
--- a/lldb/test/API/functionalities/abbreviation/TestAbbreviations.py
+++ b/lldb/test/API/functionalities/abbreviation/TestAbbreviations.py
@@ -20,7 +20,7 @@ def test_command_abbreviations_and_aliases(self):
self.assertTrue(result.Succeeded())
self.assertEqual("apropos script", result.GetOutput())
- command_interpreter.ResolveCommand("h", result)
+ command_interpreter.ResolveCommand("he", result)
self.assertTrue(result.Succeeded())
self.assertEqual("help", result.GetOutput())
diff --git a/lldb/test/API/functionalities/completion/TestCompletion.py b/lldb/test/API/functionalities/completion/TestCompletion.py
index e01bc0ce03f5c95..b1c2d37d3e22192 100644
--- a/lldb/test/API/functionalities/completion/TestCompletion.py
+++ b/lldb/test/API/functionalities/completion/TestCompletion.py
@@ -618,19 +618,15 @@ def test_command_unalias(self):
def test_command_aliases(self):
self.runCmd("command alias brkpt breakpoint")
- # If there is an unambiguous completion from the built-in commands,
- # we choose that.
- self.complete_from_to("br", "breakpoint")
- # Only if there is not, do we then look for an unambiguous completion
- # from the user defined aliases.
+ # Exact matches are chosen if possible, even if there are longer
+ # completions we could use.
+ self.complete_from_to("b", "b ")
+ # Aliases are included in possible completions.
+ self.complete_from_to("br", ["breakpoint", "brkpt"])
+ # An alias can be the chosen completion.
self.complete_from_to("brk", "brkpt")
- # Aliases are included when there's no exact match.
- self.runCmd("command alias play breakpoint")
- self.complete_from_to("pl", ["plugin", "platform", "play"])
-
- # That list can also contain only aliases if there's no built-ins to
- # match.
+ # The list can contain only aliases if there's no built-ins to match.
self.runCmd("command alias test_1 breakpoint")
self.runCmd("command alias test_2 breakpoint")
self.complete_from_to("test_", ["test_1", "test_2"])
More information about the lldb-commits
mailing list