xref: /src/contrib/llvm-project/lldb/source/Interpreter/CommandAlias.cpp (revision 5f757f3ff9144b609b3c433dfd370cc6bdc191ad)
1cfca06d7SDimitry Andric //===-- CommandAlias.cpp --------------------------------------------------===//
2f3fbd1c0SDimitry Andric //
35f29bb8aSDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
45f29bb8aSDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
55f29bb8aSDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6f3fbd1c0SDimitry Andric //
7f3fbd1c0SDimitry Andric //===----------------------------------------------------------------------===//
8f3fbd1c0SDimitry Andric 
9f3fbd1c0SDimitry Andric #include "lldb/Interpreter/CommandAlias.h"
10f3fbd1c0SDimitry Andric 
117fa27ce4SDimitry Andric #include "llvm/ADT/STLExtras.h"
12f3fbd1c0SDimitry Andric #include "llvm/Support/ErrorHandling.h"
13f3fbd1c0SDimitry Andric 
1414f1b3e8SDimitry Andric #include "lldb/Interpreter/CommandInterpreter.h"
15f3fbd1c0SDimitry Andric #include "lldb/Interpreter/CommandObject.h"
16f3fbd1c0SDimitry Andric #include "lldb/Interpreter/CommandReturnObject.h"
17f3fbd1c0SDimitry Andric #include "lldb/Interpreter/Options.h"
1874a628f7SDimitry Andric #include "lldb/Utility/StreamString.h"
19f3fbd1c0SDimitry Andric 
20f3fbd1c0SDimitry Andric using namespace lldb;
21f3fbd1c0SDimitry Andric using namespace lldb_private;
22f3fbd1c0SDimitry Andric 
ProcessAliasOptionsArgs(lldb::CommandObjectSP & cmd_obj_sp,llvm::StringRef options_args,OptionArgVectorSP & option_arg_vector_sp)2314f1b3e8SDimitry Andric static bool ProcessAliasOptionsArgs(lldb::CommandObjectSP &cmd_obj_sp,
2414f1b3e8SDimitry Andric                                     llvm::StringRef options_args,
2514f1b3e8SDimitry Andric                                     OptionArgVectorSP &option_arg_vector_sp) {
26f3fbd1c0SDimitry Andric   bool success = true;
27f3fbd1c0SDimitry Andric   OptionArgVector *option_arg_vector = option_arg_vector_sp.get();
28f3fbd1c0SDimitry Andric 
2914f1b3e8SDimitry Andric   if (options_args.size() < 1)
30f3fbd1c0SDimitry Andric     return true;
31f3fbd1c0SDimitry Andric 
32f3fbd1c0SDimitry Andric   Args args(options_args);
3314f1b3e8SDimitry Andric   std::string options_string(options_args);
345f29bb8aSDimitry Andric   // TODO: Find a way to propagate errors in this CommandReturnObject up the
355f29bb8aSDimitry Andric   // stack.
36cfca06d7SDimitry Andric   CommandReturnObject result(false);
37f3fbd1c0SDimitry Andric   // Check to see if the command being aliased can take any command options.
38f3fbd1c0SDimitry Andric   Options *options = cmd_obj_sp->GetOptions();
3914f1b3e8SDimitry Andric   if (options) {
4014f1b3e8SDimitry Andric     // See if any options were specified as part of the alias;  if so, handle
4114f1b3e8SDimitry Andric     // them appropriately.
4214f1b3e8SDimitry Andric     ExecutionContext exe_ctx =
4314f1b3e8SDimitry Andric         cmd_obj_sp->GetCommandInterpreter().GetExecutionContext();
4414f1b3e8SDimitry Andric     options->NotifyOptionParsingStarting(&exe_ctx);
45f73363f1SDimitry Andric 
46f73363f1SDimitry Andric     llvm::Expected<Args> args_or =
47f73363f1SDimitry Andric         options->ParseAlias(args, option_arg_vector, options_string);
48f73363f1SDimitry Andric     if (!args_or) {
49f73363f1SDimitry Andric       result.AppendError(toString(args_or.takeError()));
50f73363f1SDimitry Andric       result.AppendError("Unable to create requested alias.\n");
51f73363f1SDimitry Andric       return false;
52f73363f1SDimitry Andric     }
53f73363f1SDimitry Andric     args = std::move(*args_or);
54f3fbd1c0SDimitry Andric     options->VerifyPartialOptions(result);
5514f1b3e8SDimitry Andric     if (!result.Succeeded() &&
5614f1b3e8SDimitry Andric         result.GetStatus() != lldb::eReturnStatusStarted) {
57f3fbd1c0SDimitry Andric       result.AppendError("Unable to create requested alias.\n");
58f3fbd1c0SDimitry Andric       return false;
59f3fbd1c0SDimitry Andric     }
60f3fbd1c0SDimitry Andric   }
61f3fbd1c0SDimitry Andric 
6214f1b3e8SDimitry Andric   if (!options_string.empty()) {
63f3fbd1c0SDimitry Andric     if (cmd_obj_sp->WantsRawCommandString())
64e3b55780SDimitry Andric       option_arg_vector->emplace_back(CommandInterpreter::g_argument,
65e3b55780SDimitry Andric                                       -1, options_string);
6614f1b3e8SDimitry Andric     else {
6714f1b3e8SDimitry Andric       for (auto &entry : args.entries()) {
68ead24645SDimitry Andric         if (!entry.ref().empty())
69e3b55780SDimitry Andric           option_arg_vector->emplace_back(std::string(CommandInterpreter::g_argument), -1,
70cfca06d7SDimitry Andric                                           std::string(entry.ref()));
7114f1b3e8SDimitry Andric       }
72f3fbd1c0SDimitry Andric     }
73f3fbd1c0SDimitry Andric   }
74f3fbd1c0SDimitry Andric 
75f3fbd1c0SDimitry Andric   return success;
76f3fbd1c0SDimitry Andric }
77f3fbd1c0SDimitry Andric 
CommandAlias(CommandInterpreter & interpreter,lldb::CommandObjectSP cmd_sp,llvm::StringRef options_args,llvm::StringRef name,llvm::StringRef help,llvm::StringRef syntax,uint32_t flags)78f3fbd1c0SDimitry Andric CommandAlias::CommandAlias(CommandInterpreter &interpreter,
79f3fbd1c0SDimitry Andric                            lldb::CommandObjectSP cmd_sp,
8014f1b3e8SDimitry Andric                            llvm::StringRef options_args, llvm::StringRef name,
8114f1b3e8SDimitry Andric                            llvm::StringRef help, llvm::StringRef syntax,
8214f1b3e8SDimitry Andric                            uint32_t flags)
8314f1b3e8SDimitry Andric     : CommandObject(interpreter, name, help, syntax, flags),
84344a3780SDimitry Andric       m_option_string(std::string(options_args)),
85f3fbd1c0SDimitry Andric       m_option_args_sp(new OptionArgVector),
8614f1b3e8SDimitry Andric       m_is_dashdash_alias(eLazyBoolCalculate), m_did_set_help(false),
8714f1b3e8SDimitry Andric       m_did_set_help_long(false) {
8814f1b3e8SDimitry Andric   if (ProcessAliasOptionsArgs(cmd_sp, options_args, m_option_args_sp)) {
89f3fbd1c0SDimitry Andric     m_underlying_command_sp = cmd_sp;
90f3fbd1c0SDimitry Andric     for (int i = 0;
91f3fbd1c0SDimitry Andric          auto cmd_entry = m_underlying_command_sp->GetArgumentEntryAtIndex(i);
9214f1b3e8SDimitry Andric          i++) {
93f3fbd1c0SDimitry Andric       m_arguments.push_back(*cmd_entry);
94f3fbd1c0SDimitry Andric     }
9514f1b3e8SDimitry Andric     if (!help.empty()) {
96f3fbd1c0SDimitry Andric       StreamString sstr;
97f3fbd1c0SDimitry Andric       StreamString translation_and_help;
98f3fbd1c0SDimitry Andric       GetAliasExpansion(sstr);
99f3fbd1c0SDimitry Andric 
10014f1b3e8SDimitry Andric       translation_and_help.Printf(
10114f1b3e8SDimitry Andric           "(%s)  %s", sstr.GetData(),
10214f1b3e8SDimitry Andric           GetUnderlyingCommand()->GetHelp().str().c_str());
10314f1b3e8SDimitry Andric       SetHelp(translation_and_help.GetString());
104f3fbd1c0SDimitry Andric     }
105f3fbd1c0SDimitry Andric   }
106f3fbd1c0SDimitry Andric }
107f3fbd1c0SDimitry Andric 
WantsRawCommandString()10814f1b3e8SDimitry Andric bool CommandAlias::WantsRawCommandString() {
109f3fbd1c0SDimitry Andric   if (IsValid())
110f3fbd1c0SDimitry Andric     return m_underlying_command_sp->WantsRawCommandString();
111f3fbd1c0SDimitry Andric   return false;
112f3fbd1c0SDimitry Andric }
113f3fbd1c0SDimitry Andric 
WantsCompletion()11414f1b3e8SDimitry Andric bool CommandAlias::WantsCompletion() {
115f3fbd1c0SDimitry Andric   if (IsValid())
116f3fbd1c0SDimitry Andric     return m_underlying_command_sp->WantsCompletion();
117f3fbd1c0SDimitry Andric   return false;
118f3fbd1c0SDimitry Andric }
119f3fbd1c0SDimitry Andric 
HandleCompletion(CompletionRequest & request)120ead24645SDimitry Andric void CommandAlias::HandleCompletion(CompletionRequest &request) {
121f3fbd1c0SDimitry Andric   if (IsValid())
122ead24645SDimitry Andric     m_underlying_command_sp->HandleCompletion(request);
123f3fbd1c0SDimitry Andric }
124f3fbd1c0SDimitry Andric 
HandleArgumentCompletion(CompletionRequest & request,OptionElementVector & opt_element_vector)125ead24645SDimitry Andric void CommandAlias::HandleArgumentCompletion(
126f73363f1SDimitry Andric     CompletionRequest &request, OptionElementVector &opt_element_vector) {
127f3fbd1c0SDimitry Andric   if (IsValid())
128ead24645SDimitry Andric     m_underlying_command_sp->HandleArgumentCompletion(request,
129ead24645SDimitry Andric                                                       opt_element_vector);
130f3fbd1c0SDimitry Andric }
131f3fbd1c0SDimitry Andric 
GetOptions()13214f1b3e8SDimitry Andric Options *CommandAlias::GetOptions() {
133f3fbd1c0SDimitry Andric   if (IsValid())
134f3fbd1c0SDimitry Andric     return m_underlying_command_sp->GetOptions();
135f3fbd1c0SDimitry Andric   return nullptr;
136f3fbd1c0SDimitry Andric }
137f3fbd1c0SDimitry Andric 
Execute(const char * args_string,CommandReturnObject & result)138b1c73532SDimitry Andric void CommandAlias::Execute(const char *args_string,
13914f1b3e8SDimitry Andric                            CommandReturnObject &result) {
140f3fbd1c0SDimitry Andric   llvm_unreachable("CommandAlias::Execute is not to be called");
141f3fbd1c0SDimitry Andric }
142f3fbd1c0SDimitry Andric 
GetAliasExpansion(StreamString & help_string) const14314f1b3e8SDimitry Andric void CommandAlias::GetAliasExpansion(StreamString &help_string) const {
14414f1b3e8SDimitry Andric   llvm::StringRef command_name = m_underlying_command_sp->GetCommandName();
14514f1b3e8SDimitry Andric   help_string.Printf("'%*s", (int)command_name.size(), command_name.data());
146f3fbd1c0SDimitry Andric 
14714f1b3e8SDimitry Andric   if (!m_option_args_sp) {
14814f1b3e8SDimitry Andric     help_string.Printf("'");
14914f1b3e8SDimitry Andric     return;
15014f1b3e8SDimitry Andric   }
15114f1b3e8SDimitry Andric 
152f3fbd1c0SDimitry Andric   OptionArgVector *options = m_option_args_sp.get();
15314f1b3e8SDimitry Andric   std::string opt;
15414f1b3e8SDimitry Andric   std::string value;
15514f1b3e8SDimitry Andric 
15614f1b3e8SDimitry Andric   for (const auto &opt_entry : *options) {
15714f1b3e8SDimitry Andric     std::tie(opt, std::ignore, value) = opt_entry;
158e3b55780SDimitry Andric     if (opt == CommandInterpreter::g_argument) {
159f3fbd1c0SDimitry Andric       help_string.Printf(" %s", value.c_str());
16014f1b3e8SDimitry Andric     } else {
161f3fbd1c0SDimitry Andric       help_string.Printf(" %s", opt.c_str());
162e3b55780SDimitry Andric       if ((value != CommandInterpreter::g_no_argument)
163e3b55780SDimitry Andric            && (value != CommandInterpreter::g_need_argument)) {
164f3fbd1c0SDimitry Andric         help_string.Printf(" %s", value.c_str());
165f3fbd1c0SDimitry Andric       }
166f3fbd1c0SDimitry Andric     }
167f3fbd1c0SDimitry Andric   }
168f3fbd1c0SDimitry Andric 
169f3fbd1c0SDimitry Andric   help_string.Printf("'");
170f3fbd1c0SDimitry Andric }
171f3fbd1c0SDimitry Andric 
IsDashDashCommand()17214f1b3e8SDimitry Andric bool CommandAlias::IsDashDashCommand() {
17314f1b3e8SDimitry Andric   if (m_is_dashdash_alias != eLazyBoolCalculate)
17414f1b3e8SDimitry Andric     return (m_is_dashdash_alias == eLazyBoolYes);
175f3fbd1c0SDimitry Andric   m_is_dashdash_alias = eLazyBoolNo;
17614f1b3e8SDimitry Andric   if (!IsValid())
17714f1b3e8SDimitry Andric     return false;
17814f1b3e8SDimitry Andric 
17914f1b3e8SDimitry Andric   std::string opt;
18014f1b3e8SDimitry Andric   std::string value;
18114f1b3e8SDimitry Andric 
18214f1b3e8SDimitry Andric   for (const auto &opt_entry : *GetOptionArguments()) {
18314f1b3e8SDimitry Andric     std::tie(opt, std::ignore, value) = opt_entry;
184e3b55780SDimitry Andric     if (opt == CommandInterpreter::g_argument && !value.empty() &&
185312c0ed1SDimitry Andric         llvm::StringRef(value).ends_with("--")) {
186f3fbd1c0SDimitry Andric       m_is_dashdash_alias = eLazyBoolYes;
187f3fbd1c0SDimitry Andric       break;
188f3fbd1c0SDimitry Andric     }
189f3fbd1c0SDimitry Andric   }
19014f1b3e8SDimitry Andric 
191f73363f1SDimitry Andric   // if this is a nested alias, it may be adding arguments on top of an already
192f73363f1SDimitry Andric   // dash-dash alias
193f3fbd1c0SDimitry Andric   if ((m_is_dashdash_alias == eLazyBoolNo) && IsNestedAlias())
19414f1b3e8SDimitry Andric     m_is_dashdash_alias =
19514f1b3e8SDimitry Andric         (GetUnderlyingCommand()->IsDashDashCommand() ? eLazyBoolYes
19614f1b3e8SDimitry Andric                                                      : eLazyBoolNo);
197f3fbd1c0SDimitry Andric   return (m_is_dashdash_alias == eLazyBoolYes);
198f3fbd1c0SDimitry Andric }
199f3fbd1c0SDimitry Andric 
IsNestedAlias()20014f1b3e8SDimitry Andric bool CommandAlias::IsNestedAlias() {
201f3fbd1c0SDimitry Andric   if (GetUnderlyingCommand())
202f3fbd1c0SDimitry Andric     return GetUnderlyingCommand()->IsAlias();
203f3fbd1c0SDimitry Andric   return false;
204f3fbd1c0SDimitry Andric }
205f3fbd1c0SDimitry Andric 
Desugar()20614f1b3e8SDimitry Andric std::pair<lldb::CommandObjectSP, OptionArgVectorSP> CommandAlias::Desugar() {
207f3fbd1c0SDimitry Andric   auto underlying = GetUnderlyingCommand();
208f3fbd1c0SDimitry Andric   if (!underlying)
209f3fbd1c0SDimitry Andric     return {nullptr, nullptr};
210f3fbd1c0SDimitry Andric 
21114f1b3e8SDimitry Andric   if (underlying->IsAlias()) {
212e3b55780SDimitry Andric     // FIXME: This doesn't work if the original alias fills a slot in the
213e3b55780SDimitry Andric     // underlying alias, since this just appends the two lists.
214f3fbd1c0SDimitry Andric     auto desugared = ((CommandAlias *)underlying.get())->Desugar();
2157fa27ce4SDimitry Andric     OptionArgVectorSP options = std::make_shared<OptionArgVector>();
2167fa27ce4SDimitry Andric     llvm::append_range(*options, *desugared.second);
2177fa27ce4SDimitry Andric     llvm::append_range(*options, *GetOptionArguments());
218f3fbd1c0SDimitry Andric     return {desugared.first, options};
219f3fbd1c0SDimitry Andric   }
220f3fbd1c0SDimitry Andric 
221f3fbd1c0SDimitry Andric   return {underlying, GetOptionArguments()};
222f3fbd1c0SDimitry Andric }
223f3fbd1c0SDimitry Andric 
22414f1b3e8SDimitry Andric // allow CommandAlias objects to provide their own help, but fallback to the
225f73363f1SDimitry Andric // info for the underlying command if no customization has been provided
SetHelp(llvm::StringRef str)22614f1b3e8SDimitry Andric void CommandAlias::SetHelp(llvm::StringRef str) {
227f3fbd1c0SDimitry Andric   this->CommandObject::SetHelp(str);
228f3fbd1c0SDimitry Andric   m_did_set_help = true;
229f3fbd1c0SDimitry Andric }
230f3fbd1c0SDimitry Andric 
SetHelpLong(llvm::StringRef str)23114f1b3e8SDimitry Andric void CommandAlias::SetHelpLong(llvm::StringRef str) {
232f3fbd1c0SDimitry Andric   this->CommandObject::SetHelpLong(str);
233f3fbd1c0SDimitry Andric   m_did_set_help_long = true;
234f3fbd1c0SDimitry Andric }
235f3fbd1c0SDimitry Andric 
GetHelp()23614f1b3e8SDimitry Andric llvm::StringRef CommandAlias::GetHelp() {
237f3fbd1c0SDimitry Andric   if (!m_cmd_help_short.empty() || m_did_set_help)
23814f1b3e8SDimitry Andric     return m_cmd_help_short;
239f3fbd1c0SDimitry Andric   if (IsValid())
240f3fbd1c0SDimitry Andric     return m_underlying_command_sp->GetHelp();
24114f1b3e8SDimitry Andric   return llvm::StringRef();
242f3fbd1c0SDimitry Andric }
243f3fbd1c0SDimitry Andric 
GetHelpLong()24414f1b3e8SDimitry Andric llvm::StringRef CommandAlias::GetHelpLong() {
245f3fbd1c0SDimitry Andric   if (!m_cmd_help_long.empty() || m_did_set_help_long)
24614f1b3e8SDimitry Andric     return m_cmd_help_long;
247f3fbd1c0SDimitry Andric   if (IsValid())
248f3fbd1c0SDimitry Andric     return m_underlying_command_sp->GetHelpLong();
24914f1b3e8SDimitry Andric   return llvm::StringRef();
250f3fbd1c0SDimitry Andric }
251