llvm-project/lldb/include/lldb/Core/FormatEntity.h
Jonas Devlieghere 3e235a7c60
[lldb] Support alternatives for scope format entries (#137751)
This PR implements support for specifying multiple alternatives for
scope format entries. Scopes are used to enclose things that should only
be printed when everything in the scope resolves.

For example, the following scope only resolves if both
`${line.file.basename}` and `${line.number}` resolve. `

```
{ at ${line.file.basename}:${line.number}}
```

However, the current implementation doesn't let you specify what to
print when they don't resolve. This PR adds support for specifying
multiple alternative scopes, which are evaluated left-to-right.

For example:

```
{ at ${line.file.basename}:${line.number}| in ${function.name}| <unknown location>}
```

This will resolve to:

- ` at ${line.file.basename}:${line.number}` if the corresponding
variables resolve.
- Otherwise, this resolves to ` in ${function.name}` if
`${function.name}` resolves.
- Otherwise, this resolves to ` <unknown location>` which always
resolves.

This PR makes the `|` character a special character within a scope, but
allows it to be escaped.

I ended up with this approach because it fit quite nicely in the
existing architecture of the format entries and by limiting the
functionality to scopes, it sidesteps some complexity, like dealing with
recursion.
2025-05-05 16:10:57 -07:00

264 lines
7.3 KiB
C++

//===-- FormatEntity.h ------------------------------------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#ifndef LLDB_CORE_FORMATENTITY_H
#define LLDB_CORE_FORMATENTITY_H
#include "lldb/lldb-enumerations.h"
#include "lldb/lldb-types.h"
#include "llvm/ADT/SmallVector.h"
#include <algorithm>
#include <cstddef>
#include <cstdint>
#include <string>
#include <vector>
namespace lldb_private {
class Address;
class CompletionRequest;
class ExecutionContext;
class FileSpec;
class Status;
class Stream;
class StringList;
class SymbolContext;
class ValueObject;
}
namespace llvm {
class StringRef;
}
namespace lldb_private {
namespace FormatEntity {
struct Entry {
enum class Type {
Invalid,
ParentNumber,
ParentString,
EscapeCode,
Root,
String,
Scope,
Variable,
VariableSynthetic,
ScriptVariable,
ScriptVariableSynthetic,
AddressLoad,
AddressFile,
AddressLoadOrFile,
ProcessID,
ProcessFile,
ScriptProcess,
ThreadID,
ThreadProtocolID,
ThreadIndexID,
ThreadName,
ThreadQueue,
ThreadStopReason,
ThreadStopReasonRaw,
ThreadReturnValue,
ThreadCompletedExpression,
ScriptThread,
ThreadInfo,
TargetArch,
TargetFile,
ScriptTarget,
ModuleFile,
File,
Lang,
FrameIndex,
FrameNoDebug,
FrameRegisterPC,
FrameRegisterSP,
FrameRegisterFP,
FrameRegisterFlags,
FrameRegisterByName,
FrameIsArtificial,
ScriptFrame,
FunctionID,
FunctionDidChange,
FunctionInitialFunction,
FunctionName,
FunctionNameWithArgs,
FunctionNameNoArgs,
FunctionMangledName,
FunctionScope,
FunctionBasename,
FunctionTemplateArguments,
FunctionFormattedArguments,
FunctionReturnLeft,
FunctionReturnRight,
FunctionQualifiers,
FunctionSuffix,
FunctionAddrOffset,
FunctionAddrOffsetConcrete,
FunctionLineOffset,
FunctionPCOffset,
FunctionInitial,
FunctionChanged,
FunctionIsOptimized,
LineEntryFile,
LineEntryLineNumber,
LineEntryColumn,
LineEntryStartAddress,
LineEntryEndAddress,
CurrentPCArrow,
ProgressCount,
ProgressMessage,
Separator,
};
struct Definition {
/// The name/string placeholder that corresponds to this definition.
const char *name;
/// Insert this exact string into the output
const char *string = nullptr;
/// Entry::Type corresponding to this definition.
const Entry::Type type;
/// Data that is returned as the value of the format string.
const uint64_t data = 0;
/// The number of children of this node in the tree of format strings.
const uint32_t num_children = 0;
/// An array of "num_children" Definition entries.
const Definition *children = nullptr;
/// Whether the separator is kept during parsing or not. It's used
/// for entries with parameters.
const bool keep_separator = false;
constexpr Definition(const char *name, const FormatEntity::Entry::Type t)
: name(name), type(t) {}
constexpr Definition(const char *name, const char *string)
: name(name), string(string), type(Entry::Type::EscapeCode) {}
constexpr Definition(const char *name, const FormatEntity::Entry::Type t,
const uint64_t data)
: name(name), type(t), data(data) {}
constexpr Definition(const char *name, const FormatEntity::Entry::Type t,
const uint64_t num_children,
const Definition *children,
const bool keep_separator = false)
: name(name), type(t), num_children(num_children), children(children),
keep_separator(keep_separator) {}
};
template <size_t N>
static constexpr Definition
DefinitionWithChildren(const char *name, const FormatEntity::Entry::Type t,
const Definition (&children)[N],
bool keep_separator = false) {
return Definition(name, t, N, children, keep_separator);
}
Entry(Type t = Type::Invalid, const char *s = nullptr,
const char *f = nullptr);
Entry(llvm::StringRef s);
Entry(char ch);
void AppendChar(char ch);
void AppendText(const llvm::StringRef &s);
void AppendText(const char *cstr);
void AppendEntry(const Entry &&entry);
void StartAlternative();
void Clear() {
string.clear();
printf_format.clear();
children_stack.clear();
children_stack.emplace_back();
type = Type::Invalid;
fmt = lldb::eFormatDefault;
number = 0;
level = 0;
deref = false;
}
static const char *TypeToCString(Type t);
void Dump(Stream &s, int depth = 0) const;
bool operator==(const Entry &rhs) const {
if (string != rhs.string)
return false;
if (printf_format != rhs.printf_format)
return false;
if (children_stack != rhs.children_stack)
return false;
if (type != rhs.type)
return false;
if (fmt != rhs.fmt)
return false;
if (deref != rhs.deref)
return false;
return true;
}
std::vector<Entry> &GetChildren();
std::string string;
std::string printf_format;
/// A stack of children entries, used by Scope entries to provide alterantive
/// children. All other entries have a stack of size 1.
/// @{
llvm::SmallVector<std::vector<Entry>, 1> children_stack;
size_t level = 0;
/// @}
Type type;
lldb::Format fmt = lldb::eFormatDefault;
lldb::addr_t number = 0;
bool deref = false;
};
bool Format(const Entry &entry, Stream &s, const SymbolContext *sc,
const ExecutionContext *exe_ctx, const Address *addr,
ValueObject *valobj, bool function_changed, bool initial_function);
bool FormatStringRef(const llvm::StringRef &format, Stream &s,
const SymbolContext *sc, const ExecutionContext *exe_ctx,
const Address *addr, ValueObject *valobj,
bool function_changed, bool initial_function);
Status Parse(const llvm::StringRef &format, Entry &entry);
Status ExtractVariableInfo(llvm::StringRef &format_str,
llvm::StringRef &variable_name,
llvm::StringRef &variable_format);
void AutoComplete(lldb_private::CompletionRequest &request);
// Format the current elements into the stream \a s.
//
// The root element will be stripped off and the format str passed in will be
// either an empty string (print a description of this object), or contain a
// `.`-separated series like a domain name that identifies further
// sub-elements to display.
bool FormatFileSpec(const FileSpec &file, Stream &s, llvm::StringRef elements,
llvm::StringRef element_format);
/// For each variable in 'args' this function writes the variable
/// name and it's pretty-printed value representation to 'out_stream'
/// in following format:
///
/// \verbatim
/// name_1=repr_1, name_2=repr_2 ...
/// \endverbatim
void PrettyPrintFunctionArguments(Stream &out_stream, VariableList const &args,
ExecutionContextScope *exe_scope);
} // namespace FormatEntity
} // namespace lldb_private
#endif // LLDB_CORE_FORMATENTITY_H