Author: Ebuka Ezike Date: 2025-12-19T12:04:27Z New Revision: 4160320225da2a4d6acd3de62da65c8f999cf878
URL: https://github.com/llvm/llvm-project/commit/4160320225da2a4d6acd3de62da65c8f999cf878 DIFF: https://github.com/llvm/llvm-project/commit/4160320225da2a4d6acd3de62da65c8f999cf878.diff LOG: [lldb-dap] Do not show warnings on Completions request. (#172917) Why typing a command that happens to start with a variable, there are warnings complaining about the ambiguous expression. Added: Modified: lldb/test/API/tools/lldb-dap/completions/TestDAP_completions.py lldb/tools/lldb-dap/DAP.cpp lldb/tools/lldb-dap/DAP.h Removed: ################################################################################ diff --git a/lldb/test/API/tools/lldb-dap/completions/TestDAP_completions.py b/lldb/test/API/tools/lldb-dap/completions/TestDAP_completions.py index 954a3a4b7d14e..0ebecf6872a7d 100644 --- a/lldb/test/API/tools/lldb-dap/completions/TestDAP_completions.py +++ b/lldb/test/API/tools/lldb-dap/completions/TestDAP_completions.py @@ -244,6 +244,10 @@ def test_auto_completions(self): self.assertTrue(res["success"]) self.continue_to_next_stop() + + # Stopped at breakpoint 1 + # 'var' variable is in scope, completions should not show any warning. + self.dap_server.get_completions("var ") self.continue_to_next_stop() # We are stopped inside `main`. Variables `var1` and `var2` are in scope. @@ -268,3 +272,10 @@ def test_auto_completions(self): variable_var2_completion, ], ) + + self.continue_to_exit() + console_str = self.get_console() + # we check in console to avoid waiting for output event. + self.assertNotIn( + "Expression 'var' is both an LLDB command and variable", console_str + ) diff --git a/lldb/tools/lldb-dap/DAP.cpp b/lldb/tools/lldb-dap/DAP.cpp index 58c9922214583..23a30e94644e9 100644 --- a/lldb/tools/lldb-dap/DAP.cpp +++ b/lldb/tools/lldb-dap/DAP.cpp @@ -594,63 +594,58 @@ lldb::SBFrame DAP::GetLLDBFrame(const llvm::json::Object &arguments) { return GetLLDBFrame(frame_id); } -ReplMode DAP::DetectReplMode(lldb::SBFrame frame, std::string &expression, +ReplMode DAP::DetectReplMode(lldb::SBFrame &frame, std::string &expression, bool partial_expression) { // Check for the escape hatch prefix. - if (!expression.empty() && - llvm::StringRef(expression) - .starts_with(configuration.commandEscapePrefix)) { - expression = expression.substr(configuration.commandEscapePrefix.size()); + if (llvm::StringRef expr_ref = expression; + expr_ref.consume_front(configuration.commandEscapePrefix)) { + expression = expr_ref; return ReplMode::Command; } - switch (repl_mode) { - case ReplMode::Variable: - return ReplMode::Variable; - case ReplMode::Command: - return ReplMode::Command; - case ReplMode::Auto: - // To determine if the expression is a command or not, check if the first - // term is a variable or command. If it's a variable in scope we will prefer - // that behavior and give a warning to the user if they meant to invoke the - // operation as a command. - // - // Example use case: - // int p and expression "p + 1" > variable - // int i and expression "i" > variable - // int var and expression "va" > command - std::pair<llvm::StringRef, llvm::StringRef> token = - llvm::getToken(expression); - - // If the first token is not fully finished yet, we can't - // determine whether this will be a variable or a lldb command. - if (partial_expression && token.second.empty()) - return ReplMode::Auto; - - std::string term = token.first.str(); - lldb::SBCommandInterpreter interpreter = debugger.GetCommandInterpreter(); - bool term_is_command = interpreter.CommandExists(term.c_str()) || - interpreter.UserCommandExists(term.c_str()) || - interpreter.AliasExists(term.c_str()); - bool term_is_variable = frame.FindVariable(term.c_str()).IsValid(); - - // If we have both a variable and command, warn the user about the conflict. - if (term_is_command && term_is_variable) { - llvm::errs() - << "Warning: Expression '" << term - << "' is both an LLDB command and variable. It will be evaluated as " - "a variable. To evaluate the expression as an LLDB command, use '" - << configuration.commandEscapePrefix << "' as a prefix.\n"; - } - - // Variables take preference to commands in auto, since commands can always - // be called using the command_escape_prefix - return term_is_variable ? ReplMode::Variable - : term_is_command ? ReplMode::Command - : ReplMode::Variable; + if (repl_mode != ReplMode::Auto) + return repl_mode; + // To determine if the expression is a command or not, check if the first + // term is a variable or command. If it's a variable in scope we will prefer + // that behavior and give a warning to the user if they meant to invoke the + // operation as a command. + // + // Example use case: + // int p and expression "p + 1" > variable + // int i and expression "i" > variable + // int var and expression "va" > command + const auto [first_tok, remaining] = llvm::getToken(expression); + + // If the first token is not fully finished yet, we can't + // determine whether this will be a variable or a lldb command. + if (partial_expression && remaining.empty()) + return ReplMode::Auto; + + std::string first = first_tok.str(); + const char *first_cstr = first.c_str(); + lldb::SBCommandInterpreter interpreter = debugger.GetCommandInterpreter(); + const bool is_command = interpreter.CommandExists(first_cstr) || + interpreter.UserCommandExists(first_cstr) || + interpreter.AliasExists(first_cstr); + const bool is_variable = frame.FindVariable(first_cstr).IsValid(); + + // If we have both a variable and command, warn the user about the conflict. + if (!partial_expression && is_command && is_variable) { + const std::string warning_msg = + llvm::formatv("warning: Expression '{}' is both an LLDB command and " + "variable. It will be evaluated as " + "a variable. To evaluate the expression as an LLDB " + "command, use '{}' as a prefix.\n", + first, configuration.commandEscapePrefix); + SendOutput(OutputType::Console, warning_msg); } - llvm_unreachable("enum cases exhausted."); + // Variables take preference to commands in auto, since commands can always + // be called using the command_escape_prefix + if (is_variable) + return ReplMode::Variable; + + return is_command ? ReplMode::Command : ReplMode::Variable; } std::optional<protocol::Source> DAP::ResolveSource(const lldb::SBFrame &frame) { diff --git a/lldb/tools/lldb-dap/DAP.h b/lldb/tools/lldb-dap/DAP.h index 01139221ea37f..9500c82a853c8 100644 --- a/lldb/tools/lldb-dap/DAP.h +++ b/lldb/tools/lldb-dap/DAP.h @@ -270,7 +270,7 @@ struct DAP final : public DAPTransport::MessageHandler { /// either an expression or a statement, depending on the rest of /// the expression. /// \return the expression mode - ReplMode DetectReplMode(lldb::SBFrame frame, std::string &expression, + ReplMode DetectReplMode(lldb::SBFrame &frame, std::string &expression, bool partial_expression); /// Create a `protocol::Source` object as described in the debug adapter _______________________________________________ lldb-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits
