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