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