blob: 8a3513c962c5646300381c9c3336c5d57fa51868 [file] [log] [blame]
// 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_