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 bool exact_match = false; 313 314 for (const std::string &s : g_property_names) { 315 if (llvm::StringRef(s).startswith(request.GetCursorArgumentPrefix())) { 316 if (request.GetCursorArgumentPrefix() == s) 317 exact_match = true; 318 request.AddCompletion(s); 319 } 320 } 321 322 request.SetWordComplete(exact_match); 323 324 return request.GetNumberOfMatches(); 325 } 326 327 int CommandCompletions::PlatformPluginNames(CommandInterpreter &interpreter, 328 CompletionRequest &request, 329 SearchFilter *searcher) { 330 StringList new_matches; 331 std::size_t num_matches = PluginManager::AutoCompletePlatformName( 332 request.GetCursorArgumentPrefix(), new_matches); 333 request.SetWordComplete(num_matches == 1); 334 request.AddCompletions(new_matches); 335 return request.GetNumberOfMatches(); 336 } 337 338 int CommandCompletions::ArchitectureNames(CommandInterpreter &interpreter, 339 CompletionRequest &request, 340 SearchFilter *searcher) { 341 const uint32_t num_matches = ArchSpec::AutoComplete(request); 342 request.SetWordComplete(num_matches == 1); 343 return num_matches; 344 } 345 346 int CommandCompletions::VariablePath(CommandInterpreter &interpreter, 347 CompletionRequest &request, 348 SearchFilter *searcher) { 349 return Variable::AutoComplete(interpreter.GetExecutionContext(), request); 350 } 351 352 CommandCompletions::Completer::Completer(CommandInterpreter &interpreter, 353 CompletionRequest &request) 354 : m_interpreter(interpreter), m_request(request) {} 355 356 CommandCompletions::Completer::~Completer() = default; 357 358 // SourceFileCompleter 359 360 CommandCompletions::SourceFileCompleter::SourceFileCompleter( 361 CommandInterpreter &interpreter, bool include_support_files, 362 CompletionRequest &request) 363 : CommandCompletions::Completer(interpreter, request), 364 m_include_support_files(include_support_files), m_matching_files() { 365 FileSpec partial_spec(m_request.GetCursorArgumentPrefix()); 366 m_file_name = partial_spec.GetFilename().GetCString(); 367 m_dir_name = partial_spec.GetDirectory().GetCString(); 368 } 369 370 lldb::SearchDepth CommandCompletions::SourceFileCompleter::GetDepth() { 371 return lldb::eSearchDepthCompUnit; 372 } 373 374 Searcher::CallbackReturn 375 CommandCompletions::SourceFileCompleter::SearchCallback(SearchFilter &filter, 376 SymbolContext &context, 377 Address *addr, 378 bool complete) { 379 if (context.comp_unit != nullptr) { 380 if (m_include_support_files) { 381 FileSpecList supporting_files = context.comp_unit->GetSupportFiles(); 382 for (size_t sfiles = 0; sfiles < supporting_files.GetSize(); sfiles++) { 383 const FileSpec &sfile_spec = 384 supporting_files.GetFileSpecAtIndex(sfiles); 385 const char *sfile_file_name = sfile_spec.GetFilename().GetCString(); 386 const char *sfile_dir_name = sfile_spec.GetFilename().GetCString(); 387 bool match = false; 388 if (m_file_name && sfile_file_name && 389 strstr(sfile_file_name, m_file_name) == sfile_file_name) 390 match = true; 391 if (match && m_dir_name && sfile_dir_name && 392 strstr(sfile_dir_name, m_dir_name) != sfile_dir_name) 393 match = false; 394 395 if (match) { 396 m_matching_files.AppendIfUnique(sfile_spec); 397 } 398 } 399 } else { 400 const char *cur_file_name = context.comp_unit->GetFilename().GetCString(); 401 const char *cur_dir_name = context.comp_unit->GetDirectory().GetCString(); 402 403 bool match = false; 404 if (m_file_name && cur_file_name && 405 strstr(cur_file_name, m_file_name) == cur_file_name) 406 match = true; 407 408 if (match && m_dir_name && cur_dir_name && 409 strstr(cur_dir_name, m_dir_name) != cur_dir_name) 410 match = false; 411 412 if (match) { 413 m_matching_files.AppendIfUnique(context.comp_unit); 414 } 415 } 416 } 417 return Searcher::eCallbackReturnContinue; 418 } 419 420 size_t 421 CommandCompletions::SourceFileCompleter::DoCompletion(SearchFilter *filter) { 422 filter->Search(*this); 423 // Now convert the filelist to completions: 424 for (size_t i = 0; i < m_matching_files.GetSize(); i++) { 425 m_request.AddCompletion( 426 m_matching_files.GetFileSpecAtIndex(i).GetFilename().GetCString()); 427 } 428 return m_request.GetNumberOfMatches(); 429 } 430 431 // SymbolCompleter 432 433 static bool regex_chars(const char comp) { 434 return (comp == '[' || comp == ']' || comp == '(' || comp == ')' || 435 comp == '{' || comp == '}' || comp == '+' || comp == '.' || 436 comp == '*' || comp == '|' || comp == '^' || comp == '$' || 437 comp == '\\' || comp == '?'); 438 } 439 440 CommandCompletions::SymbolCompleter::SymbolCompleter( 441 CommandInterpreter &interpreter, CompletionRequest &request) 442 : CommandCompletions::Completer(interpreter, request) { 443 std::string regex_str; 444 if (!m_request.GetCursorArgumentPrefix().empty()) { 445 regex_str.append("^"); 446 regex_str.append(m_request.GetCursorArgumentPrefix()); 447 } else { 448 // Match anything since the completion string is empty 449 regex_str.append("."); 450 } 451 std::string::iterator pos = 452 find_if(regex_str.begin() + 1, regex_str.end(), regex_chars); 453 while (pos < regex_str.end()) { 454 pos = regex_str.insert(pos, '\\'); 455 pos = find_if(pos + 2, regex_str.end(), regex_chars); 456 } 457 m_regex = RegularExpression(regex_str); 458 } 459 460 lldb::SearchDepth CommandCompletions::SymbolCompleter::GetDepth() { 461 return lldb::eSearchDepthModule; 462 } 463 464 Searcher::CallbackReturn CommandCompletions::SymbolCompleter::SearchCallback( 465 SearchFilter &filter, SymbolContext &context, Address *addr, 466 bool complete) { 467 if (context.module_sp) { 468 SymbolContextList sc_list; 469 const bool include_symbols = true; 470 const bool include_inlines = true; 471 const bool append = true; 472 context.module_sp->FindFunctions(m_regex, include_symbols, include_inlines, 473 append, sc_list); 474 475 SymbolContext sc; 476 // Now add the functions & symbols to the list - only add if unique: 477 for (uint32_t i = 0; i < sc_list.GetSize(); i++) { 478 if (sc_list.GetContextAtIndex(i, sc)) { 479 ConstString func_name = sc.GetFunctionName(Mangled::ePreferDemangled); 480 // Ensure that the function name matches the regex. This is more than a 481 // sanity check. It is possible that the demangled function name does 482 // not start with the prefix, for example when it's in an anonymous 483 // namespace. 484 if (!func_name.IsEmpty() && m_regex.Execute(func_name.GetStringRef())) 485 m_match_set.insert(func_name); 486 } 487 } 488 } 489 return Searcher::eCallbackReturnContinue; 490 } 491 492 size_t CommandCompletions::SymbolCompleter::DoCompletion(SearchFilter *filter) { 493 filter->Search(*this); 494 collection::iterator pos = m_match_set.begin(), end = m_match_set.end(); 495 for (pos = m_match_set.begin(); pos != end; pos++) 496 m_request.AddCompletion((*pos).GetCString()); 497 498 return m_request.GetNumberOfMatches(); 499 } 500 501 // ModuleCompleter 502 CommandCompletions::ModuleCompleter::ModuleCompleter( 503 CommandInterpreter &interpreter, CompletionRequest &request) 504 : CommandCompletions::Completer(interpreter, request) { 505 FileSpec partial_spec(m_request.GetCursorArgumentPrefix()); 506 m_file_name = partial_spec.GetFilename().GetCString(); 507 m_dir_name = partial_spec.GetDirectory().GetCString(); 508 } 509 510 lldb::SearchDepth CommandCompletions::ModuleCompleter::GetDepth() { 511 return lldb::eSearchDepthModule; 512 } 513 514 Searcher::CallbackReturn CommandCompletions::ModuleCompleter::SearchCallback( 515 SearchFilter &filter, SymbolContext &context, Address *addr, 516 bool complete) { 517 if (context.module_sp) { 518 const char *cur_file_name = 519 context.module_sp->GetFileSpec().GetFilename().GetCString(); 520 const char *cur_dir_name = 521 context.module_sp->GetFileSpec().GetDirectory().GetCString(); 522 523 bool match = false; 524 if (m_file_name && cur_file_name && 525 strstr(cur_file_name, m_file_name) == cur_file_name) 526 match = true; 527 528 if (match && m_dir_name && cur_dir_name && 529 strstr(cur_dir_name, m_dir_name) != cur_dir_name) 530 match = false; 531 532 if (match) { 533 m_request.AddCompletion(cur_file_name); 534 } 535 } 536 return Searcher::eCallbackReturnContinue; 537 } 538 539 size_t CommandCompletions::ModuleCompleter::DoCompletion(SearchFilter *filter) { 540 filter->Search(*this); 541 return m_request.GetNumberOfMatches(); 542 } 543