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