| // Copyright 2018 The Fuchsia Authors. All rights reserved. | |
| // Use of this source code is governed by a BSD-style license that can be | |
| // found in the LICENSE file. | |
| #ifndef SRC_DEVELOPER_DEBUG_ZXDB_EXPR_FIND_NAME_H_ | |
| #define SRC_DEVELOPER_DEBUG_ZXDB_EXPR_FIND_NAME_H_ | |
| #include <limits> | |
| #include <string> | |
| #include "lib/fit/function.h" | |
| #include "src/developer/debug/zxdb/expr/parsed_identifier.h" | |
| #include "src/developer/debug/zxdb/symbols/symbol_context.h" | |
| #include "src/developer/debug/zxdb/symbols/visit_scopes.h" | |
| namespace zxdb { | |
| class CodeBlock; | |
| class Collection; | |
| class DataMember; | |
| class FoundMember; | |
| class FoundName; | |
| class Location; | |
| class ModuleSymbols; | |
| class ProcessSymbols; | |
| class SymbolContext; | |
| class TargetSymbols; | |
| class Variable; | |
| // ========== | |
| // OVERVIEW | |
| // ========== | |
| // | |
| // FindName is the general lookup for names of things. It understands the hierarchy of naming of the | |
| // current context and follows C++ rules for resolving names. It can also do prefix searches for | |
| // autocompletion. | |
| // | |
| // It provides a superset of the symbol lookup functionality of the symbol system's | |
| // ResolveInputLocation() functions. The symbol system provides only exact matching. | |
| // FindName can search for different levels of things depending on how much context it's given. This | |
| // class encapsulates all of these variants. | |
| struct FindNameContext { | |
| // No symbol context. This can be useful when searching for names on structures where there is no | |
| // environmental state needed. | |
| FindNameContext() = default; | |
| // Search everything given a live context. The current module is extracted from the given symbol | |
| // context if possible. This can be SymbolContext::ForRelativeAddresses() to skip this. | |
| // | |
| // Note that this tolerates a null ProcessSymbols which sets no symbol paths. This is useful for | |
| // some tests. | |
| FindNameContext(const ProcessSymbols* ps, | |
| const SymbolContext& symbol_context = SymbolContext::ForRelativeAddresses(), | |
| const CodeBlock* cb = nullptr); | |
| // Searches a target's symbols. This is used to search for symbols in a non-running program. | |
| explicit FindNameContext(const TargetSymbols* ts); | |
| // Together TargetSymbols and ModuleSymbols control what is searched. They are both optional, | |
| // producing this behavior: | |
| // | |
| // - Both TargetSymbols and ModuleSymbols: All modules will be searched with the given one | |
| // searched first. This is to give preference to the current module in the case of multiple | |
| // matches. | |
| // | |
| // - TargetSymbols but not ModuleSymbols: All modules will be searched in an arbitrary order. | |
| // | |
| // - ModuleSymbols but not TargetSymbols: Only the given module will be searched for symbols. | |
| // | |
| // - Neither TargetSymbols nor ModuleSymbols: No symbol lookups are done. | |
| const TargetSymbols* target_symbols = nullptr; | |
| const ModuleSymbols* module_symbols = nullptr; | |
| // If given, local variables, local types, and |this| will be searched. Otherwise, only global | |
| // symbols will be searched. | |
| const CodeBlock* block = nullptr; | |
| }; | |
| // By default this will find the first exact match of any kind. | |
| struct FindNameOptions { | |
| // How to match the name. | |
| // | |
| // Note that prefix matching doesn't currently work for templates. Prefix matching is currently | |
| // used for autocomplete where the full type name is desired, not just the base template name. And | |
| // supporting this requires uniquifying names (since many template types could be the same | |
| // underlying template) that's annoying to implement. | |
| enum HowMatch { kPrefix, kExact }; | |
| HowMatch how = kExact; | |
| enum SearchMode { | |
| // A lexical search is a normal search starting from the current scope and searching outward | |
| // from there. This is the normal search that programmers expect when typing names in a | |
| // language. | |
| kLexical, | |
| // An "all namespaces" search ignores the current scope and recursively searches all namespaces | |
| // for matches for a given name. This can be the desired behavior for things like finding | |
| // functions for breakpoints, but this search will never find local or class variables. | |
| // | |
| // Fully qualified identifiers ("::Foo") will not get implicit namespace searching, even when | |
| // requested. They will only match the toplevel. | |
| // | |
| // This mode is only valid for full index searches via FindName() and FindIndexedName(). | |
| // The local searching variants like FindLocalVariable() and FindMember() do not support it. | |
| kAllNamespaces, | |
| }; | |
| SearchMode search_mode = kLexical; | |
| // This constructor's argument indicates whether the caller wants to default to finding all or no | |
| // types (presumably in the "no types" case, the caller will set one or more to true afterward). | |
| enum InitialKinds : bool { kNoKinds = false, kAllKinds = true }; | |
| explicit FindNameOptions(InitialKinds initial) | |
| : find_types(initial), | |
| find_type_defs(initial), | |
| find_functions(initial), | |
| find_vars(initial), | |
| find_templates(initial), | |
| find_namespaces(initial) {} | |
| // The types of named things that will be matched. | |
| bool find_types = true; | |
| bool find_type_defs = true; // Subset of "types": definitions only, not forward declarations. | |
| bool find_functions = true; // Global and member functions. | |
| bool find_vars = true; // Local and "this" member vars. | |
| bool find_templates = true; // Templatized types without <...>. | |
| bool find_namespaces = true; | |
| constexpr static size_t kAllResults = std::numeric_limits<size_t>::max(); | |
| size_t max_results = 1; // Use kAllResults to get everything. | |
| }; | |
| // Main variable and type name finding function. Searches the local, "this", and global scopes for | |
| // one or more things with a matching name. The first version finds the first exact match of any | |
| // type, the second uses the options to customize what and how many results are returned. | |
| // | |
| // The variant that returns a single value ignores the max_results of the options and always returns | |
| // the first thing. | |
| FoundName FindName(const FindNameContext& context, const FindNameOptions& options, | |
| const ParsedIdentifier& identifier); | |
| void FindName(const FindNameContext& context, const FindNameOptions& options, | |
| const ParsedIdentifier& looking_for, std::vector<FoundName>* results); | |
| // Type-specific finding --------------------------------------------------------------------------- | |
| // Searches the code block for local variables. This includes all nested code blocks and function | |
| // parameters, but does not go into the "this" class or any non-function scopes like the current or | |
| // global namespace (that's what the later functions do). | |
| // | |
| // The "visit" variant calls the callback for every variable in order of priority for as long as the | |
| // visitor reports "continue." The "find" variant does an exact string search, the "prefix" variant | |
| // does a prefix search. | |
| VisitResult VisitLocalVariables(const CodeBlock* block, | |
| fit::function<VisitResult(const Variable*)> visitor); | |
| void FindLocalVariable(const FindNameOptions& options, const CodeBlock* block, | |
| const ParsedIdentifier& looking_for, std::vector<FoundName>* results); | |
| // Searches for the named variable or type on the given collection. This is the lower-level function | |
| // and assumes a valid object. The result can be either a kType or a kMemberVariable. | |
| // | |
| // If the ProcessSymbols is non-null, this function will also search for type names defined in the | |
| // collection. Otherwise, only data members will be searched. | |
| // | |
| // The optional symbol context is the symbol context for the current code. it will be used to | |
| // prioritize symbol searching to the current module if given. | |
| // | |
| // If the result is a member variable, the optional_object_ptr will be used to construct the | |
| // FoundName object. It can be null if the caller does not have a variable for the object it's | |
| // looking up (just doing a type query). | |
| void FindMember(const FindNameContext& context, const FindNameOptions& options, | |
| const Collection* object, const ParsedIdentifier& looking_for, | |
| const Variable* optional_object_ptr, std::vector<FoundName>* result); | |
| // Attempts the resolve the given named member variable or type on the "this" pointer associated | |
| // with the given code block. Fails if the function has no "this" pointer or the type name / data | |
| // member isn't found. | |
| // | |
| // If the ProcessSymbols is non-null, this function will also search for type names defined in the | |
| // collection. Otherwise, only data members will be searched. | |
| // | |
| // The optional symbol context is the symbol context for the current code. it will be used to | |
| // prioritize symbol searching to the current module if given. | |
| void FindMemberOnThis(const FindNameContext& context, const FindNameOptions& options, | |
| const ParsedIdentifier& looking_for, std::vector<FoundName>* result); | |
| // Attempts to resolve the named |looking_for| in the index. | |
| // | |
| // The |current_scope| is the namespace to start looking in. If |search_containing| is true, parent | |
| // scopes of the |current_scope| are also searched, otherwise only exact matches in that scope will | |
| // be found. | |
| VisitResult FindIndexedName(const FindNameContext& context, const FindNameOptions& options, | |
| const ParsedIdentifier& current_scope, | |
| const ParsedIdentifier& looking_for, bool search_containing, | |
| std::vector<FoundName>* results); | |
| // Searches a specific index and current namespace for a global variable or type of the given name. | |
| // The current_scope would be the current namespace + class from where to start the search. | |
| void FindIndexedNameInModule(const FindNameOptions& options, const ModuleSymbols* module_symbols, | |
| const ParsedIdentifier& current_scope, | |
| const ParsedIdentifier& looking_for, bool search_containing, | |
| std::vector<FoundName>* results); | |
| // In many contexts (like function parameters and local variables) an identifier name can't have any | |
| // :: or template parameters and can have only one component. If this identifier satisfies this | |
| // requirement, a pointer to the single string is returned. If there is zero or more than one | |
| // component or any template specs, returns null. | |
| // | |
| // The returned pointer will be invalidated if the Identifier is mutated. | |
| const std::string* GetSingleComponentIdentifierName(const ParsedIdentifier& ident); | |
| } // namespace zxdb | |
| #endif // SRC_DEVELOPER_DEBUG_ZXDB_EXPR_FIND_NAME_H_ |