250 lines · cpp
1//===-- CommandAlias.cpp --------------------------------------------------===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8 9#include "lldb/Interpreter/CommandAlias.h"10 11#include "llvm/ADT/STLExtras.h"12#include "llvm/Support/ErrorHandling.h"13#include "llvm/Support/FormatAdapters.h"14 15#include "lldb/Interpreter/CommandInterpreter.h"16#include "lldb/Interpreter/CommandObject.h"17#include "lldb/Interpreter/CommandReturnObject.h"18#include "lldb/Interpreter/Options.h"19#include "lldb/Utility/StreamString.h"20 21using namespace lldb;22using namespace lldb_private;23 24static llvm::Error25ProcessAliasOptionsArgs(lldb::CommandObjectSP &cmd_obj_sp,26 llvm::StringRef options_args,27 OptionArgVectorSP &option_arg_vector_sp) {28 OptionArgVector *option_arg_vector = option_arg_vector_sp.get();29 30 if (options_args.size() < 1)31 return llvm::Error::success();32 33 Args args(options_args);34 std::string options_string(options_args);35 // Check to see if the command being aliased can take any command options.36 Options *options = cmd_obj_sp->GetOptions();37 if (options) {38 // See if any options were specified as part of the alias; if so, handle39 // them appropriately.40 ExecutionContext exe_ctx =41 cmd_obj_sp->GetCommandInterpreter().GetExecutionContext();42 options->NotifyOptionParsingStarting(&exe_ctx);43 44 llvm::Expected<Args> args_or =45 options->ParseAlias(args, option_arg_vector, options_string);46 if (!args_or)47 return llvm::createStringError(48 llvm::formatv("unable to create alias: {0}",49 llvm::fmt_consume(args_or.takeError())));50 args = std::move(*args_or);51 if (llvm::Error error = options->VerifyPartialOptions())52 return error;53 }54 55 if (!options_string.empty()) {56 if (cmd_obj_sp->WantsRawCommandString()) {57 option_arg_vector->emplace_back(CommandInterpreter::g_argument, -1,58 options_string);59 } else {60 for (auto &entry : args.entries()) {61 if (!entry.ref().empty())62 option_arg_vector->emplace_back(63 std::string(CommandInterpreter::g_argument), -1,64 std::string(entry.ref()));65 }66 }67 }68 69 return llvm::Error::success();70}71 72CommandAlias::CommandAlias(CommandInterpreter &interpreter,73 lldb::CommandObjectSP cmd_sp,74 llvm::StringRef options_args, llvm::StringRef name,75 llvm::StringRef help, llvm::StringRef syntax,76 uint32_t flags)77 : CommandObject(interpreter, name, help, syntax, flags),78 m_option_string(std::string(options_args)),79 m_option_args_sp(new OptionArgVector),80 m_is_dashdash_alias(eLazyBoolCalculate), m_did_set_help(false),81 m_did_set_help_long(false) {82 if (llvm::Error error =83 ProcessAliasOptionsArgs(cmd_sp, options_args, m_option_args_sp)) {84 // FIXME: Find a way to percolate this error up.85 LLDB_LOG_ERROR(GetLog(LLDBLog::Host), std::move(error),86 "ProcessAliasOptionsArgs failed: {0}");87 } else {88 m_underlying_command_sp = cmd_sp;89 for (int i = 0;90 auto *cmd_entry = m_underlying_command_sp->GetArgumentEntryAtIndex(i);91 i++) {92 m_arguments.push_back(*cmd_entry);93 }94 if (!help.empty()) {95 StreamString sstr;96 StreamString translation_and_help;97 GetAliasExpansion(sstr);98 99 translation_and_help.Printf(100 "(%s) %s", sstr.GetData(),101 GetUnderlyingCommand()->GetHelp().str().c_str());102 SetHelp(translation_and_help.GetString());103 }104 }105}106 107bool CommandAlias::WantsRawCommandString() {108 if (IsValid())109 return m_underlying_command_sp->WantsRawCommandString();110 return false;111}112 113bool CommandAlias::WantsCompletion() {114 if (IsValid())115 return m_underlying_command_sp->WantsCompletion();116 return false;117}118 119void CommandAlias::HandleCompletion(CompletionRequest &request) {120 if (IsValid())121 m_underlying_command_sp->HandleCompletion(request);122}123 124void CommandAlias::HandleArgumentCompletion(125 CompletionRequest &request, OptionElementVector &opt_element_vector) {126 if (IsValid())127 m_underlying_command_sp->HandleArgumentCompletion(request,128 opt_element_vector);129}130 131Options *CommandAlias::GetOptions() {132 if (IsValid())133 return m_underlying_command_sp->GetOptions();134 return nullptr;135}136 137void CommandAlias::Execute(const char *args_string,138 CommandReturnObject &result) {139 llvm_unreachable("CommandAlias::Execute is not to be called");140}141 142void CommandAlias::GetAliasExpansion(StreamString &help_string) const {143 llvm::StringRef command_name = m_underlying_command_sp->GetCommandName();144 help_string.Printf("'%*s", (int)command_name.size(), command_name.data());145 146 if (!m_option_args_sp) {147 help_string.Printf("'");148 return;149 }150 151 OptionArgVector *options = m_option_args_sp.get();152 std::string opt;153 std::string value;154 155 for (const auto &opt_entry : *options) {156 std::tie(opt, std::ignore, value) = opt_entry;157 if (opt == CommandInterpreter::g_argument) {158 help_string.Printf(" %s", value.c_str());159 } else {160 help_string.Printf(" %s", opt.c_str());161 if ((value != CommandInterpreter::g_no_argument) 162 && (value != CommandInterpreter::g_need_argument)) {163 help_string.Printf(" %s", value.c_str());164 }165 }166 }167 168 help_string.Printf("'");169}170 171bool CommandAlias::IsDashDashCommand() {172 if (m_is_dashdash_alias != eLazyBoolCalculate)173 return (m_is_dashdash_alias == eLazyBoolYes);174 m_is_dashdash_alias = eLazyBoolNo;175 if (!IsValid())176 return false;177 178 std::string opt;179 std::string value;180 181 for (const auto &opt_entry : *GetOptionArguments()) {182 std::tie(opt, std::ignore, value) = opt_entry;183 if (opt == CommandInterpreter::g_argument && !value.empty() &&184 llvm::StringRef(value).ends_with("--")) {185 m_is_dashdash_alias = eLazyBoolYes;186 break;187 }188 }189 190 // if this is a nested alias, it may be adding arguments on top of an already191 // dash-dash alias192 if ((m_is_dashdash_alias == eLazyBoolNo) && IsNestedAlias())193 m_is_dashdash_alias =194 (GetUnderlyingCommand()->IsDashDashCommand() ? eLazyBoolYes195 : eLazyBoolNo);196 return (m_is_dashdash_alias == eLazyBoolYes);197}198 199bool CommandAlias::IsNestedAlias() {200 if (GetUnderlyingCommand())201 return GetUnderlyingCommand()->IsAlias();202 return false;203}204 205std::pair<lldb::CommandObjectSP, OptionArgVectorSP> CommandAlias::Desugar() {206 auto underlying = GetUnderlyingCommand();207 if (!underlying)208 return {nullptr, nullptr};209 210 if (underlying->IsAlias()) {211 // FIXME: This doesn't work if the original alias fills a slot in the212 // underlying alias, since this just appends the two lists.213 auto desugared = ((CommandAlias *)underlying.get())->Desugar();214 OptionArgVectorSP options = std::make_shared<OptionArgVector>();215 llvm::append_range(*options, *desugared.second);216 llvm::append_range(*options, *GetOptionArguments());217 return {desugared.first, options};218 }219 220 return {underlying, GetOptionArguments()};221}222 223// allow CommandAlias objects to provide their own help, but fallback to the224// info for the underlying command if no customization has been provided225void CommandAlias::SetHelp(llvm::StringRef str) {226 this->CommandObject::SetHelp(str);227 m_did_set_help = true;228}229 230void CommandAlias::SetHelpLong(llvm::StringRef str) {231 this->CommandObject::SetHelpLong(str);232 m_did_set_help_long = true;233}234 235llvm::StringRef CommandAlias::GetHelp() {236 if (!m_cmd_help_short.empty() || m_did_set_help)237 return m_cmd_help_short;238 if (IsValid())239 return m_underlying_command_sp->GetHelp();240 return llvm::StringRef();241}242 243llvm::StringRef CommandAlias::GetHelpLong() {244 if (!m_cmd_help_long.empty() || m_did_set_help_long)245 return m_cmd_help_long;246 if (IsValid())247 return m_underlying_command_sp->GetHelpLong();248 return llvm::StringRef();249}250