diff --git a/lldb/include/lldb/Symbol/VariableList.h b/lldb/include/lldb/Symbol/VariableList.h --- a/lldb/include/lldb/Symbol/VariableList.h +++ b/lldb/include/lldb/Symbol/VariableList.h @@ -75,6 +75,10 @@ const_iterator begin() const { return m_variables.begin(); } const_iterator end() const { return m_variables.end(); } + llvm::ArrayRef toArrayRef() { + return llvm::makeArrayRef(m_variables); + } + protected: collection m_variables; diff --git a/lldb/source/Commands/CommandObjectFrame.cpp b/lldb/source/Commands/CommandObjectFrame.cpp --- a/lldb/source/Commands/CommandObjectFrame.cpp +++ b/lldb/source/Commands/CommandObjectFrame.cpp @@ -499,6 +499,31 @@ } } + /// Finds all the variables in `all_variables` whose name matches `regex`, + /// inserting them into `matches`. Variables already contained in `matches` + /// are not inserted again. + /// Nullopt is returned in case of no matches. + /// A sub-range of `matches` with all newly inserted variables is returned. + /// This may be empty if all matches were already contained in `matches`. + std::optional> + findUniqueRegexMatches(RegularExpression ®ex, + VariableList &matches, + const VariableList &all_variables) { + bool any_matches = false; + const size_t previous_num_vars = matches.GetSize(); + + for (const VariableSP &var : all_variables) { + if (!var->NameMatches(regex) || !ScopeRequested(var->GetScope())) + continue; + any_matches = true; + matches.AddVariableIfUnique(var); + } + + if (any_matches) + return matches.toArrayRef().drop_front(previous_num_vars); + return std::nullopt; + } + bool DoExecute(Args &command, CommandReturnObject &result) override { // No need to check "frame" for validity as eCommandRequiresFrame ensures // it is valid @@ -506,6 +531,10 @@ Stream &s = result.GetOutputStream(); + // Using a regex should behave like looking for an exact name match: it + // also finds globals. + m_option_variable.show_globals |= m_option_variable.use_regex; + // Be careful about the stack frame, if any summary formatter runs code, it // might clear the StackFrameList for the thread. So hold onto a shared // pointer to the frame so it stays alive. @@ -518,7 +547,6 @@ result.AppendError(error.AsCString()); } - VariableSP var_sp; ValueObjectSP valobj_sp; TypeSummaryImplSP summary_format_sp; @@ -551,46 +579,38 @@ // objects from them... for (auto &entry : command) { if (m_option_variable.use_regex) { - const size_t regex_start_index = regex_var_list.GetSize(); llvm::StringRef name_str = entry.ref(); RegularExpression regex(name_str); if (regex.IsValid()) { - size_t num_matches = 0; - const size_t num_new_regex_vars = - variable_list->AppendVariablesIfUnique(regex, regex_var_list, - num_matches); - if (num_new_regex_vars > 0) { - for (size_t regex_idx = regex_start_index, - end_index = regex_var_list.GetSize(); - regex_idx < end_index; ++regex_idx) { - var_sp = regex_var_list.GetVariableAtIndex(regex_idx); - if (var_sp) { - valobj_sp = frame->GetValueObjectForFrameVariable( - var_sp, m_varobj_options.use_dynamic); - if (valobj_sp) { - std::string scope_string; - if (m_option_variable.show_scope) - scope_string = GetScopeString(var_sp).str(); - - if (!scope_string.empty()) - s.PutCString(scope_string); - - if (m_option_variable.show_decl && - var_sp->GetDeclaration().GetFile()) { - bool show_fullpaths = false; - bool show_module = true; - if (var_sp->DumpDeclaration(&s, show_fullpaths, - show_module)) - s.PutCString(": "); - } - valobj_sp->Dump(result.GetOutputStream(), options); - } - } - } - } else if (num_matches == 0) { + std::optional> results = + findUniqueRegexMatches(regex, regex_var_list, *variable_list); + if (!results) { result.AppendErrorWithFormat( "no variables matched the regular expression '%s'.", entry.c_str()); + continue; + } + for (const VariableSP &var_sp : *results) { + valobj_sp = frame->GetValueObjectForFrameVariable( + var_sp, m_varobj_options.use_dynamic); + if (valobj_sp) { + std::string scope_string; + if (m_option_variable.show_scope) + scope_string = GetScopeString(var_sp).str(); + + if (!scope_string.empty()) + s.PutCString(scope_string); + + if (m_option_variable.show_decl && + var_sp->GetDeclaration().GetFile()) { + bool show_fullpaths = false; + bool show_module = true; + if (var_sp->DumpDeclaration(&s, show_fullpaths, + show_module)) + s.PutCString(": "); + } + valobj_sp->Dump(result.GetOutputStream(), options); + } } } else { if (llvm::Error err = regex.GetError()) @@ -648,7 +668,7 @@ const size_t num_variables = variable_list->GetSize(); if (num_variables > 0) { for (size_t i = 0; i < num_variables; i++) { - var_sp = variable_list->GetVariableAtIndex(i); + VariableSP var_sp = variable_list->GetVariableAtIndex(i); if (!ScopeRequested(var_sp->GetScope())) continue; std::string scope_string; diff --git a/lldb/test/API/commands/frame/var/TestFrameVar.py b/lldb/test/API/commands/frame/var/TestFrameVar.py --- a/lldb/test/API/commands/frame/var/TestFrameVar.py +++ b/lldb/test/API/commands/frame/var/TestFrameVar.py @@ -58,6 +58,16 @@ command_result = lldb.SBCommandReturnObject() interp = self.dbg.GetCommandInterpreter() + # Ensure --regex can find globals if it is the very first frame var command. + self.expect("frame var --regex g_", substrs=["g_var"]) + + # Ensure the requested scope is respected: + self.expect( + "frame var --regex argc --no-args", + error=True, + substrs=["no variables matched the regular expression 'argc'"], + ) + # Just get args: result = interp.HandleCommand("frame var -l", command_result) self.assertEqual(