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