1 //===-- CommandCompletions.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-exception 6 // 7 //===----------------------------------------------------------------------===// 8 9 #include "llvm/ADT/SmallString.h" 10 #include "llvm/ADT/StringSet.h" 11 12 #include "lldb/Core/FileSpecList.h" 13 #include "lldb/Core/Module.h" 14 #include "lldb/Core/PluginManager.h" 15 #include "lldb/Host/FileSystem.h" 16 #include "lldb/Interpreter/CommandCompletions.h" 17 #include "lldb/Interpreter/CommandInterpreter.h" 18 #include "lldb/Interpreter/OptionValueProperties.h" 19 #include "lldb/Symbol/CompileUnit.h" 20 #include "lldb/Symbol/Variable.h" 21 #include "lldb/Target/RegisterContext.h" 22 #include "lldb/Utility/FileSpec.h" 23 #include "lldb/Utility/StreamString.h" 24 #include "lldb/Utility/TildeExpressionResolver.h" 25 26 #include "llvm/Support/FileSystem.h" 27 #include "llvm/Support/Path.h" 28 29 using namespace lldb_private; 30 31 // This is the command completion callback that is used to complete the 32 // argument of the option it is bound to (in the OptionDefinition table 33 // below). 34 typedef void (*CompletionCallback)(CommandInterpreter &interpreter, 35 CompletionRequest &request, 36 // A search filter to limit the search... 37 lldb_private::SearchFilter *searcher); 38 39 struct CommonCompletionElement { 40 uint32_t type; 41 CompletionCallback callback; 42 }; 43 44 bool CommandCompletions::InvokeCommonCompletionCallbacks( 45 CommandInterpreter &interpreter, uint32_t completion_mask, 46 CompletionRequest &request, SearchFilter *searcher) { 47 bool handled = false; 48 49 const CommonCompletionElement common_completions[] = { 50 {eSourceFileCompletion, CommandCompletions::SourceFiles}, 51 {eDiskFileCompletion, CommandCompletions::DiskFiles}, 52 {eDiskDirectoryCompletion, CommandCompletions::DiskDirectories}, 53 {eSymbolCompletion, CommandCompletions::Symbols}, 54 {eModuleCompletion, CommandCompletions::Modules}, 55 {eSettingsNameCompletion, CommandCompletions::SettingsNames}, 56 {ePlatformPluginCompletion, CommandCompletions::PlatformPluginNames}, 57 {eArchitectureCompletion, CommandCompletions::ArchitectureNames}, 58 {eVariablePathCompletion, CommandCompletions::VariablePath}, 59 {eRegisterCompletion, CommandCompletions::Registers}, 60 {eNoCompletion, nullptr} // This one has to be last in the list. 61 }; 62 63 for (int i = 0;; i++) { 64 if (common_completions[i].type == eNoCompletion) 65 break; 66 else if ((common_completions[i].type & completion_mask) == 67 common_completions[i].type && 68 common_completions[i].callback != nullptr) { 69 handled = true; 70 common_completions[i].callback(interpreter, request, searcher); 71 } 72 } 73 return handled; 74 } 75 76 namespace { 77 // The Completer class is a convenient base class for building searchers that 78 // go along with the SearchFilter passed to the standard Completer functions. 79 class Completer : public Searcher { 80 public: 81 Completer(CommandInterpreter &interpreter, CompletionRequest &request) 82 : m_interpreter(interpreter), m_request(request) {} 83 84 ~Completer() override = default; 85 86 CallbackReturn SearchCallback(SearchFilter &filter, SymbolContext &context, 87 Address *addr) override = 0; 88 89 lldb::SearchDepth GetDepth() override = 0; 90 91 virtual void DoCompletion(SearchFilter *filter) = 0; 92 93 protected: 94 CommandInterpreter &m_interpreter; 95 CompletionRequest &m_request; 96 97 private: 98 DISALLOW_COPY_AND_ASSIGN(Completer); 99 }; 100 } // namespace 101 102 // SourceFileCompleter implements the source file completer 103 namespace { 104 class SourceFileCompleter : public Completer { 105 public: 106 SourceFileCompleter(CommandInterpreter &interpreter, 107 CompletionRequest &request) 108 : Completer(interpreter, request), m_matching_files() { 109 FileSpec partial_spec(m_request.GetCursorArgumentPrefix()); 110 m_file_name = partial_spec.GetFilename().GetCString(); 111 m_dir_name = partial_spec.GetDirectory().GetCString(); 112 } 113 114 lldb::SearchDepth GetDepth() override { return lldb::eSearchDepthCompUnit; } 115 116 Searcher::CallbackReturn SearchCallback(SearchFilter &filter, 117 SymbolContext &context, 118 Address *addr) override { 119 if (context.comp_unit != nullptr) { 120 const char *cur_file_name = 121 context.comp_unit->GetPrimaryFile().GetFilename().GetCString(); 122 const char *cur_dir_name = 123 context.comp_unit->GetPrimaryFile().GetDirectory().GetCString(); 124 125 bool match = false; 126 if (m_file_name && cur_file_name && 127 strstr(cur_file_name, m_file_name) == cur_file_name) 128 match = true; 129 130 if (match && m_dir_name && cur_dir_name && 131 strstr(cur_dir_name, m_dir_name) != cur_dir_name) 132 match = false; 133 134 if (match) { 135 m_matching_files.AppendIfUnique(context.comp_unit->GetPrimaryFile()); 136 } 137 } 138 return Searcher::eCallbackReturnContinue; 139 } 140 141 void DoCompletion(SearchFilter *filter) override { 142 filter->Search(*this); 143 // Now convert the filelist to completions: 144 for (size_t i = 0; i < m_matching_files.GetSize(); i++) { 145 m_request.AddCompletion( 146 m_matching_files.GetFileSpecAtIndex(i).GetFilename().GetCString()); 147 } 148 } 149 150 private: 151 FileSpecList m_matching_files; 152 const char *m_file_name; 153 const char *m_dir_name; 154 155 DISALLOW_COPY_AND_ASSIGN(SourceFileCompleter); 156 }; 157 } // namespace 158 159 static bool regex_chars(const char comp) { 160 return llvm::StringRef("[](){}+.*|^$\\?").contains(comp); 161 } 162 163 namespace { 164 class SymbolCompleter : public Completer { 165 166 public: 167 SymbolCompleter(CommandInterpreter &interpreter, CompletionRequest &request) 168 : Completer(interpreter, request) { 169 std::string regex_str; 170 if (!m_request.GetCursorArgumentPrefix().empty()) { 171 regex_str.append("^"); 172 regex_str.append(std::string(m_request.GetCursorArgumentPrefix())); 173 } else { 174 // Match anything since the completion string is empty 175 regex_str.append("."); 176 } 177 std::string::iterator pos = 178 find_if(regex_str.begin() + 1, regex_str.end(), regex_chars); 179 while (pos < regex_str.end()) { 180 pos = regex_str.insert(pos, '\\'); 181 pos = find_if(pos + 2, regex_str.end(), regex_chars); 182 } 183 m_regex = RegularExpression(regex_str); 184 } 185 186 lldb::SearchDepth GetDepth() override { return lldb::eSearchDepthModule; } 187 188 Searcher::CallbackReturn SearchCallback(SearchFilter &filter, 189 SymbolContext &context, 190 Address *addr) override { 191 if (context.module_sp) { 192 SymbolContextList sc_list; 193 const bool include_symbols = true; 194 const bool include_inlines = true; 195 context.module_sp->FindFunctions(m_regex, include_symbols, 196 include_inlines, sc_list); 197 198 SymbolContext sc; 199 // Now add the functions & symbols to the list - only add if unique: 200 for (uint32_t i = 0; i < sc_list.GetSize(); i++) { 201 if (sc_list.GetContextAtIndex(i, sc)) { 202 ConstString func_name = sc.GetFunctionName(Mangled::ePreferDemangled); 203 // Ensure that the function name matches the regex. This is more than 204 // a sanity check. It is possible that the demangled function name 205 // does not start with the prefix, for example when it's in an 206 // anonymous namespace. 207 if (!func_name.IsEmpty() && m_regex.Execute(func_name.GetStringRef())) 208 m_match_set.insert(func_name); 209 } 210 } 211 } 212 return Searcher::eCallbackReturnContinue; 213 } 214 215 void DoCompletion(SearchFilter *filter) override { 216 filter->Search(*this); 217 collection::iterator pos = m_match_set.begin(), end = m_match_set.end(); 218 for (pos = m_match_set.begin(); pos != end; pos++) 219 m_request.AddCompletion((*pos).GetCString()); 220 } 221 222 private: 223 RegularExpression m_regex; 224 typedef std::set<ConstString> collection; 225 collection m_match_set; 226 227 DISALLOW_COPY_AND_ASSIGN(SymbolCompleter); 228 }; 229 } // namespace 230 231 namespace { 232 class ModuleCompleter : public Completer { 233 public: 234 ModuleCompleter(CommandInterpreter &interpreter, CompletionRequest &request) 235 : Completer(interpreter, request) { 236 FileSpec partial_spec(m_request.GetCursorArgumentPrefix()); 237 m_file_name = partial_spec.GetFilename().GetCString(); 238 m_dir_name = partial_spec.GetDirectory().GetCString(); 239 } 240 241 lldb::SearchDepth GetDepth() override { return lldb::eSearchDepthModule; } 242 243 Searcher::CallbackReturn SearchCallback(SearchFilter &filter, 244 SymbolContext &context, 245 Address *addr) override { 246 if (context.module_sp) { 247 const char *cur_file_name = 248 context.module_sp->GetFileSpec().GetFilename().GetCString(); 249 const char *cur_dir_name = 250 context.module_sp->GetFileSpec().GetDirectory().GetCString(); 251 252 bool match = false; 253 if (m_file_name && cur_file_name && 254 strstr(cur_file_name, m_file_name) == cur_file_name) 255 match = true; 256 257 if (match && m_dir_name && cur_dir_name && 258 strstr(cur_dir_name, m_dir_name) != cur_dir_name) 259 match = false; 260 261 if (match) { 262 m_request.AddCompletion(cur_file_name); 263 } 264 } 265 return Searcher::eCallbackReturnContinue; 266 } 267 268 void DoCompletion(SearchFilter *filter) override { filter->Search(*this); } 269 270 private: 271 const char *m_file_name; 272 const char *m_dir_name; 273 274 DISALLOW_COPY_AND_ASSIGN(ModuleCompleter); 275 }; 276 } // namespace 277 278 void CommandCompletions::SourceFiles(CommandInterpreter &interpreter, 279 CompletionRequest &request, 280 SearchFilter *searcher) { 281 SourceFileCompleter completer(interpreter, request); 282 283 if (searcher == nullptr) { 284 lldb::TargetSP target_sp = interpreter.GetDebugger().GetSelectedTarget(); 285 SearchFilterForUnconstrainedSearches null_searcher(target_sp); 286 completer.DoCompletion(&null_searcher); 287 } else { 288 completer.DoCompletion(searcher); 289 } 290 } 291 292 static void DiskFilesOrDirectories(const llvm::Twine &partial_name, 293 bool only_directories, 294 CompletionRequest &request, 295 TildeExpressionResolver &Resolver) { 296 llvm::SmallString<256> CompletionBuffer; 297 llvm::SmallString<256> Storage; 298 partial_name.toVector(CompletionBuffer); 299 300 if (CompletionBuffer.size() >= PATH_MAX) 301 return; 302 303 namespace path = llvm::sys::path; 304 305 llvm::StringRef SearchDir; 306 llvm::StringRef PartialItem; 307 308 if (CompletionBuffer.startswith("~")) { 309 llvm::StringRef Buffer(CompletionBuffer); 310 size_t FirstSep = 311 Buffer.find_if([](char c) { return path::is_separator(c); }); 312 313 llvm::StringRef Username = Buffer.take_front(FirstSep); 314 llvm::StringRef Remainder; 315 if (FirstSep != llvm::StringRef::npos) 316 Remainder = Buffer.drop_front(FirstSep + 1); 317 318 llvm::SmallString<256> Resolved; 319 if (!Resolver.ResolveExact(Username, Resolved)) { 320 // We couldn't resolve it as a full username. If there were no slashes 321 // then this might be a partial username. We try to resolve it as such 322 // but after that, we're done regardless of any matches. 323 if (FirstSep == llvm::StringRef::npos) { 324 llvm::StringSet<> MatchSet; 325 Resolver.ResolvePartial(Username, MatchSet); 326 for (const auto &S : MatchSet) { 327 Resolved = S.getKey(); 328 path::append(Resolved, path::get_separator()); 329 request.AddCompletion(Resolved, "", CompletionMode::Partial); 330 } 331 } 332 return; 333 } 334 335 // If there was no trailing slash, then we're done as soon as we resolve 336 // the expression to the correct directory. Otherwise we need to continue 337 // looking for matches within that directory. 338 if (FirstSep == llvm::StringRef::npos) { 339 // Make sure it ends with a separator. 340 path::append(CompletionBuffer, path::get_separator()); 341 request.AddCompletion(CompletionBuffer, "", CompletionMode::Partial); 342 return; 343 } 344 345 // We want to keep the form the user typed, so we special case this to 346 // search in the fully resolved directory, but CompletionBuffer keeps the 347 // unmodified form that the user typed. 348 Storage = Resolved; 349 llvm::StringRef RemainderDir = path::parent_path(Remainder); 350 if (!RemainderDir.empty()) { 351 // Append the remaining path to the resolved directory. 352 Storage.append(path::get_separator()); 353 Storage.append(RemainderDir); 354 } 355 SearchDir = Storage; 356 } else { 357 SearchDir = path::parent_path(CompletionBuffer); 358 } 359 360 size_t FullPrefixLen = CompletionBuffer.size(); 361 362 PartialItem = path::filename(CompletionBuffer); 363 364 // path::filename() will return "." when the passed path ends with a 365 // directory separator. We have to filter those out, but only when the 366 // "." doesn't come from the completion request itself. 367 if (PartialItem == "." && path::is_separator(CompletionBuffer.back())) 368 PartialItem = llvm::StringRef(); 369 370 if (SearchDir.empty()) { 371 llvm::sys::fs::current_path(Storage); 372 SearchDir = Storage; 373 } 374 assert(!PartialItem.contains(path::get_separator())); 375 376 // SearchDir now contains the directory to search in, and Prefix contains the 377 // text we want to match against items in that directory. 378 379 FileSystem &fs = FileSystem::Instance(); 380 std::error_code EC; 381 llvm::vfs::directory_iterator Iter = fs.DirBegin(SearchDir, EC); 382 llvm::vfs::directory_iterator End; 383 for (; Iter != End && !EC; Iter.increment(EC)) { 384 auto &Entry = *Iter; 385 llvm::ErrorOr<llvm::vfs::Status> Status = fs.GetStatus(Entry.path()); 386 387 if (!Status) 388 continue; 389 390 auto Name = path::filename(Entry.path()); 391 392 // Omit ".", ".." 393 if (Name == "." || Name == ".." || !Name.startswith(PartialItem)) 394 continue; 395 396 bool is_dir = Status->isDirectory(); 397 398 // If it's a symlink, then we treat it as a directory as long as the target 399 // is a directory. 400 if (Status->isSymlink()) { 401 FileSpec symlink_filespec(Entry.path()); 402 FileSpec resolved_filespec; 403 auto error = fs.ResolveSymbolicLink(symlink_filespec, resolved_filespec); 404 if (error.Success()) 405 is_dir = fs.IsDirectory(symlink_filespec); 406 } 407 408 if (only_directories && !is_dir) 409 continue; 410 411 // Shrink it back down so that it just has the original prefix the user 412 // typed and remove the part of the name which is common to the located 413 // item and what the user typed. 414 CompletionBuffer.resize(FullPrefixLen); 415 Name = Name.drop_front(PartialItem.size()); 416 CompletionBuffer.append(Name); 417 418 if (is_dir) { 419 path::append(CompletionBuffer, path::get_separator()); 420 } 421 422 CompletionMode mode = 423 is_dir ? CompletionMode::Partial : CompletionMode::Normal; 424 request.AddCompletion(CompletionBuffer, "", mode); 425 } 426 } 427 428 static void DiskFilesOrDirectories(const llvm::Twine &partial_name, 429 bool only_directories, StringList &matches, 430 TildeExpressionResolver &Resolver) { 431 CompletionResult result; 432 std::string partial_name_str = partial_name.str(); 433 CompletionRequest request(partial_name_str, partial_name_str.size(), result); 434 DiskFilesOrDirectories(partial_name, only_directories, request, Resolver); 435 result.GetMatches(matches); 436 } 437 438 static void DiskFilesOrDirectories(CompletionRequest &request, 439 bool only_directories) { 440 StandardTildeExpressionResolver resolver; 441 DiskFilesOrDirectories(request.GetCursorArgumentPrefix(), only_directories, 442 request, resolver); 443 } 444 445 void CommandCompletions::DiskFiles(CommandInterpreter &interpreter, 446 CompletionRequest &request, 447 SearchFilter *searcher) { 448 DiskFilesOrDirectories(request, /*only_dirs*/ false); 449 } 450 451 void CommandCompletions::DiskFiles(const llvm::Twine &partial_file_name, 452 StringList &matches, 453 TildeExpressionResolver &Resolver) { 454 DiskFilesOrDirectories(partial_file_name, false, matches, Resolver); 455 } 456 457 void CommandCompletions::DiskDirectories(CommandInterpreter &interpreter, 458 CompletionRequest &request, 459 SearchFilter *searcher) { 460 DiskFilesOrDirectories(request, /*only_dirs*/ true); 461 } 462 463 void CommandCompletions::DiskDirectories(const llvm::Twine &partial_file_name, 464 StringList &matches, 465 TildeExpressionResolver &Resolver) { 466 DiskFilesOrDirectories(partial_file_name, true, matches, Resolver); 467 } 468 469 void CommandCompletions::Modules(CommandInterpreter &interpreter, 470 CompletionRequest &request, 471 SearchFilter *searcher) { 472 ModuleCompleter completer(interpreter, request); 473 474 if (searcher == nullptr) { 475 lldb::TargetSP target_sp = interpreter.GetDebugger().GetSelectedTarget(); 476 SearchFilterForUnconstrainedSearches null_searcher(target_sp); 477 completer.DoCompletion(&null_searcher); 478 } else { 479 completer.DoCompletion(searcher); 480 } 481 } 482 483 void CommandCompletions::Symbols(CommandInterpreter &interpreter, 484 CompletionRequest &request, 485 SearchFilter *searcher) { 486 SymbolCompleter completer(interpreter, request); 487 488 if (searcher == nullptr) { 489 lldb::TargetSP target_sp = interpreter.GetDebugger().GetSelectedTarget(); 490 SearchFilterForUnconstrainedSearches null_searcher(target_sp); 491 completer.DoCompletion(&null_searcher); 492 } else { 493 completer.DoCompletion(searcher); 494 } 495 } 496 497 void CommandCompletions::SettingsNames(CommandInterpreter &interpreter, 498 CompletionRequest &request, 499 SearchFilter *searcher) { 500 // Cache the full setting name list 501 static StringList g_property_names; 502 if (g_property_names.GetSize() == 0) { 503 // Generate the full setting name list on demand 504 lldb::OptionValuePropertiesSP properties_sp( 505 interpreter.GetDebugger().GetValueProperties()); 506 if (properties_sp) { 507 StreamString strm; 508 properties_sp->DumpValue(nullptr, strm, OptionValue::eDumpOptionName); 509 const std::string &str = std::string(strm.GetString()); 510 g_property_names.SplitIntoLines(str.c_str(), str.size()); 511 } 512 } 513 514 for (const std::string &s : g_property_names) 515 request.TryCompleteCurrentArg(s); 516 } 517 518 void CommandCompletions::PlatformPluginNames(CommandInterpreter &interpreter, 519 CompletionRequest &request, 520 SearchFilter *searcher) { 521 PluginManager::AutoCompletePlatformName(request.GetCursorArgumentPrefix(), 522 request); 523 } 524 525 void CommandCompletions::ArchitectureNames(CommandInterpreter &interpreter, 526 CompletionRequest &request, 527 SearchFilter *searcher) { 528 ArchSpec::AutoComplete(request); 529 } 530 531 void CommandCompletions::VariablePath(CommandInterpreter &interpreter, 532 CompletionRequest &request, 533 SearchFilter *searcher) { 534 Variable::AutoComplete(interpreter.GetExecutionContext(), request); 535 } 536 537 void CommandCompletions::Registers(CommandInterpreter &interpreter, 538 CompletionRequest &request, 539 SearchFilter *searcher) { 540 std::string reg_prefix = ""; 541 if (request.GetCursorArgumentPrefix().startswith("$")) 542 reg_prefix = "$"; 543 544 RegisterContext *reg_ctx = 545 interpreter.GetExecutionContext().GetRegisterContext(); 546 const size_t reg_num = reg_ctx->GetRegisterCount(); 547 for (size_t reg_idx = 0; reg_idx < reg_num; ++reg_idx) { 548 const RegisterInfo *reg_info = reg_ctx->GetRegisterInfoAtIndex(reg_idx); 549 request.TryCompleteCurrentArg(reg_prefix + reg_info->name, 550 reg_info->alt_name); 551 } 552 }