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 }