[Lldb-commits] [lldb] 46e2c07 - [LLDB] Add DIL code for handling plain variable names. (#120971)
via lldb-commits
lldb-commits at lists.llvm.org
Thu Apr 3 21:39:34 PDT 2025
Author: cmtice
Date: 2025-04-03T21:39:30-07:00
New Revision: 46e2c07fa28bd42da8f8ca52e93603297114afa2
URL: https://github.com/llvm/llvm-project/commit/46e2c07fa28bd42da8f8ca52e93603297114afa2
DIFF: https://github.com/llvm/llvm-project/commit/46e2c07fa28bd42da8f8ca52e93603297114afa2.diff
LOG: [LLDB] Add DIL code for handling plain variable names. (#120971)
Add the Data Inspection Language (DIL) implementation pieces for
handling plain local and global variable names.
See https://discourse.llvm.org/t/rfc-data-inspection-language/69893 for
information about DIL.
This change includes the basic AST, Lexer, Parser and Evaluator pieces,
as well as some tests.
Added:
lldb/docs/dil-expr-lang.ebnf
lldb/include/lldb/ValueObject/DILAST.h
lldb/include/lldb/ValueObject/DILEval.h
lldb/include/lldb/ValueObject/DILParser.h
lldb/source/ValueObject/DILAST.cpp
lldb/source/ValueObject/DILEval.cpp
lldb/source/ValueObject/DILParser.cpp
lldb/test/API/commands/frame/var-dil/basics/GlobalVariableLookup/Makefile
lldb/test/API/commands/frame/var-dil/basics/GlobalVariableLookup/TestFrameVarDILGlobalVariableLookup.py
lldb/test/API/commands/frame/var-dil/basics/GlobalVariableLookup/main.cpp
lldb/test/API/commands/frame/var-dil/basics/InstanceVariables/Makefile
lldb/test/API/commands/frame/var-dil/basics/InstanceVariables/TestFrameVarDILInstanceVariables.py
lldb/test/API/commands/frame/var-dil/basics/InstanceVariables/main.cpp
lldb/test/API/commands/frame/var-dil/basics/LocalVars/Makefile
lldb/test/API/commands/frame/var-dil/basics/LocalVars/TestFrameVarDILLocalVars.py
lldb/test/API/commands/frame/var-dil/basics/LocalVars/main.cpp
Modified:
lldb/include/lldb/ValueObject/DILLexer.h
lldb/source/Target/StackFrame.cpp
lldb/source/ValueObject/CMakeLists.txt
lldb/unittests/ValueObject/DILLexerTests.cpp
Removed:
################################################################################
diff --git a/lldb/docs/dil-expr-lang.ebnf b/lldb/docs/dil-expr-lang.ebnf
new file mode 100644
index 0000000000000..0bbbecbdc78c1
--- /dev/null
+++ b/lldb/docs/dil-expr-lang.ebnf
@@ -0,0 +1,42 @@
+(* Data Inspection Language (DIL) definition - LLDB Debug Expressions *)
+
+(* This is currently a subset of the final DIL Language, matching the current
+ DIL implementation. *)
+
+expression = primary_expression ;
+
+primary_expression = id_expression
+ | "(" expression ")";
+
+id_expression = unqualified_id
+ | qualified_id
+ | register ;
+
+unqualified_id = identifier ;
+
+qualified_id = ["::"] [nested_name_specifier] unqualified_id
+ | ["::"] identifier ;
+
+identifier = ? C99 Identifier ? ;
+
+register = "$" ? Register name ? ;
+
+nested_name_specifier = type_name "::"
+ | namespace_name '::'
+ | nested_name_specifier identifier "::" ;
+
+type_name = class_name
+ | enum_name
+ | typedef_name;
+
+class_name = identifier ;
+
+enum_name = identifier ;
+
+typedef_name = identifier ;
+
+namespace_name = identifier ;
+
+
+
+
diff --git a/lldb/include/lldb/ValueObject/DILAST.h b/lldb/include/lldb/ValueObject/DILAST.h
new file mode 100644
index 0000000000000..05d87e9cc4b6b
--- /dev/null
+++ b/lldb/include/lldb/ValueObject/DILAST.h
@@ -0,0 +1,97 @@
+//===-- DILAST.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_VALUEOBJECT_DILAST_H
+#define LLDB_VALUEOBJECT_DILAST_H
+
+#include "lldb/ValueObject/ValueObject.h"
+#include "llvm/Support/Error.h"
+#include <cstdint>
+#include <string>
+
+namespace lldb_private::dil {
+
+/// The various types DIL AST nodes (used by the DIL parser).
+enum class NodeKind {
+ eErrorNode,
+ eIdentifierNode,
+};
+
+/// Forward declaration, for use in DIL AST nodes. Definition is at the very
+/// end of this file.
+class Visitor;
+
+/// The rest of the classes in this file, except for the Visitor class at the
+/// very end, define all the types of AST nodes used by the DIL parser and
+/// expression evaluator. The DIL parser parses the input string and creates
+/// the AST parse tree from the AST nodes. The resulting AST node tree gets
+/// passed to the DIL expression evaluator, which evaluates the DIL AST nodes
+/// and creates/returns a ValueObjectSP containing the result.
+
+/// Base class for AST nodes used by the Data Inspection Language (DIL) parser.
+/// All of the specialized types of AST nodes inherit from this (virtual) base
+/// class.
+class ASTNode {
+public:
+ ASTNode(uint32_t location, NodeKind kind)
+ : m_location(location), m_kind(kind) {}
+ virtual ~ASTNode() = default;
+
+ virtual llvm::Expected<lldb::ValueObjectSP> Accept(Visitor *v) const = 0;
+
+ uint32_t GetLocation() const { return m_location; }
+ NodeKind GetKind() const { return m_kind; }
+
+private:
+ uint32_t m_location;
+ const NodeKind m_kind;
+};
+
+using ASTNodeUP = std::unique_ptr<ASTNode>;
+
+class ErrorNode : public ASTNode {
+public:
+ ErrorNode() : ASTNode(0, NodeKind::eErrorNode) {}
+ llvm::Expected<lldb::ValueObjectSP> Accept(Visitor *v) const override;
+
+ static bool classof(const ASTNode *node) {
+ return node->GetKind() == NodeKind::eErrorNode;
+ }
+};
+
+class IdentifierNode : public ASTNode {
+public:
+ IdentifierNode(uint32_t location, std::string name)
+ : ASTNode(location, NodeKind::eIdentifierNode), m_name(std::move(name)) {}
+
+ llvm::Expected<lldb::ValueObjectSP> Accept(Visitor *v) const override;
+
+ std::string GetName() const { return m_name; }
+
+ static bool classof(const ASTNode *node) {
+ return node->GetKind() == NodeKind::eIdentifierNode;
+ }
+
+private:
+ std::string m_name;
+};
+
+/// This class contains one Visit method for each specialized type of
+/// DIL AST node. The Visit methods are used to dispatch a DIL AST node to
+/// the correct function in the DIL expression evaluator for evaluating that
+/// type of AST node.
+class Visitor {
+public:
+ virtual ~Visitor() = default;
+ virtual llvm::Expected<lldb::ValueObjectSP>
+ Visit(const IdentifierNode *node) = 0;
+};
+
+} // namespace lldb_private::dil
+
+#endif // LLDB_VALUEOBJECT_DILAST_H
diff --git a/lldb/include/lldb/ValueObject/DILEval.h b/lldb/include/lldb/ValueObject/DILEval.h
new file mode 100644
index 0000000000000..335035d3f9248
--- /dev/null
+++ b/lldb/include/lldb/ValueObject/DILEval.h
@@ -0,0 +1,63 @@
+//===-- DILEval.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_VALUEOBJECT_DILEVAL_H
+#define LLDB_VALUEOBJECT_DILEVAL_H
+
+#include "lldb/ValueObject/DILAST.h"
+#include "lldb/ValueObject/DILParser.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Support/Error.h"
+#include <memory>
+#include <vector>
+
+namespace lldb_private::dil {
+
+/// Given the name of an identifier (variable name, member name, type name,
+/// etc.), find the ValueObject for that name (if it exists), excluding global
+/// variables, and create and return an IdentifierInfo object containing all
+/// the relevant information about that object (for DIL parsing and
+/// evaluating).
+lldb::ValueObjectSP LookupIdentifier(llvm::StringRef name_ref,
+ std::shared_ptr<StackFrame> frame_sp,
+ lldb::DynamicValueType use_dynamic,
+ CompilerType *scope_ptr = nullptr);
+
+/// Given the name of an identifier, check to see if it matches the name of a
+/// global variable. If so, find the ValueObject for that global variable, and
+/// create and return an IdentifierInfo object containing all the relevant
+/// informatin about it.
+lldb::ValueObjectSP LookupGlobalIdentifier(llvm::StringRef name_ref,
+ std::shared_ptr<StackFrame> frame_sp,
+ lldb::TargetSP target_sp,
+ lldb::DynamicValueType use_dynamic,
+ CompilerType *scope_ptr = nullptr);
+
+class Interpreter : Visitor {
+public:
+ Interpreter(lldb::TargetSP target, llvm::StringRef expr,
+ lldb::DynamicValueType use_dynamic,
+ std::shared_ptr<StackFrame> frame_sp);
+
+ llvm::Expected<lldb::ValueObjectSP> Evaluate(const ASTNode *node);
+
+private:
+ llvm::Expected<lldb::ValueObjectSP>
+ Visit(const IdentifierNode *node) override;
+
+ // Used by the interpreter to create objects, perform casts, etc.
+ lldb::TargetSP m_target;
+ llvm::StringRef m_expr;
+ lldb::ValueObjectSP m_scope;
+ lldb::DynamicValueType m_default_dynamic;
+ std::shared_ptr<StackFrame> m_exe_ctx_scope;
+};
+
+} // namespace lldb_private::dil
+
+#endif // LLDB_VALUEOBJECT_DILEVAL_H
diff --git a/lldb/include/lldb/ValueObject/DILLexer.h b/lldb/include/lldb/ValueObject/DILLexer.h
index e1182da5b20ab..d15fc382d1623 100644
--- a/lldb/include/lldb/ValueObject/DILLexer.h
+++ b/lldb/include/lldb/ValueObject/DILLexer.h
@@ -11,6 +11,7 @@
#include "llvm/ADT/StringRef.h"
#include "llvm/Support/Error.h"
+#include "llvm/Support/FormatVariadic.h"
#include <cstdint>
#include <memory>
#include <string>
@@ -41,10 +42,8 @@ class Token {
bool IsNot(Kind kind) const { return m_kind != kind; }
- bool IsOneOf(Kind kind1, Kind kind2) const { return Is(kind1) || Is(kind2); }
-
- template <typename... Ts> bool IsOneOf(Kind kind, Ts... Ks) const {
- return Is(kind) || IsOneOf(Ks...);
+ bool IsOneOf(llvm::ArrayRef<Kind> kinds) const {
+ return llvm::is_contained(kinds, m_kind);
}
uint32_t GetLocation() const { return m_start_pos; }
@@ -120,4 +119,24 @@ class DILLexer {
} // namespace lldb_private::dil
+namespace llvm {
+
+template <> struct format_provider<lldb_private::dil::Token::Kind> {
+ static void format(const lldb_private::dil::Token::Kind &k, raw_ostream &OS,
+ llvm::StringRef Options) {
+ OS << "'" << lldb_private::dil::Token::GetTokenName(k) << "'";
+ }
+};
+
+template <> struct format_provider<lldb_private::dil::Token> {
+ static void format(const lldb_private::dil::Token &t, raw_ostream &OS,
+ llvm::StringRef Options) {
+ lldb_private::dil::Token::Kind kind = t.GetKind();
+ OS << "<'" << t.GetSpelling() << "' ("
+ << lldb_private::dil::Token::GetTokenName(kind) << ")>";
+ }
+};
+
+} // namespace llvm
+
#endif // LLDB_VALUEOBJECT_DILLEXER_H
diff --git a/lldb/include/lldb/ValueObject/DILParser.h b/lldb/include/lldb/ValueObject/DILParser.h
new file mode 100644
index 0000000000000..9b7a6cd487939
--- /dev/null
+++ b/lldb/include/lldb/ValueObject/DILParser.h
@@ -0,0 +1,125 @@
+//===-- DILParser.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_VALUEOBJECT_DILPARSER_H
+#define LLDB_VALUEOBJECT_DILPARSER_H
+
+#include "lldb/Target/ExecutionContextScope.h"
+#include "lldb/Utility/DiagnosticsRendering.h"
+#include "lldb/Utility/Status.h"
+#include "lldb/ValueObject/DILAST.h"
+#include "lldb/ValueObject/DILLexer.h"
+#include "llvm/Support/Error.h"
+#include <memory>
+#include <optional>
+#include <string>
+#include <system_error>
+#include <tuple>
+#include <vector>
+
+namespace lldb_private::dil {
+
+enum class ErrorCode : unsigned char {
+ kOk = 0,
+ kInvalidExpressionSyntax,
+ kUndeclaredIdentifier,
+ kUnknown,
+};
+
+// The following is modeled on class OptionParseError.
+class DILDiagnosticError
+ : public llvm::ErrorInfo<DILDiagnosticError, DiagnosticError> {
+ DiagnosticDetail m_detail;
+
+public:
+ using llvm::ErrorInfo<DILDiagnosticError, DiagnosticError>::ErrorInfo;
+ DILDiagnosticError(DiagnosticDetail detail)
+ : ErrorInfo(make_error_code(std::errc::invalid_argument)),
+ m_detail(std::move(detail)) {}
+
+ DILDiagnosticError(llvm::StringRef expr, const std::string &message,
+ uint32_t loc, uint16_t err_len);
+
+ std::unique_ptr<CloneableError> Clone() const override {
+ return std::make_unique<DILDiagnosticError>(m_detail);
+ }
+
+ llvm::ArrayRef<DiagnosticDetail> GetDetails() const override {
+ return {m_detail};
+ }
+
+ std::string message() const override { return m_detail.rendered; }
+};
+
+/// Pure recursive descent parser for C++ like expressions.
+/// EBNF grammar for the parser is described in lldb/docs/dil-expr-lang.ebnf
+class DILParser {
+public:
+ static llvm::Expected<ASTNodeUP> Parse(llvm::StringRef dil_input_expr,
+ DILLexer lexer,
+ std::shared_ptr<StackFrame> frame_sp,
+ lldb::DynamicValueType use_dynamic,
+ bool use_synthetic, bool fragile_ivar,
+ bool check_ptr_vs_member);
+
+ ~DILParser() = default;
+
+ bool UseSynthetic() { return m_use_synthetic; }
+
+ lldb::DynamicValueType UseDynamic() { return m_use_dynamic; }
+
+private:
+ explicit DILParser(llvm::StringRef dil_input_expr, DILLexer lexer,
+ std::shared_ptr<StackFrame> frame_sp,
+ lldb::DynamicValueType use_dynamic, bool use_synthetic,
+ bool fragile_ivar, bool check_ptr_vs_member,
+ llvm::Error &error);
+
+ ASTNodeUP Run();
+
+ ASTNodeUP ParseExpression();
+ ASTNodeUP ParsePrimaryExpression();
+
+ std::string ParseNestedNameSpecifier();
+
+ std::string ParseIdExpression();
+ std::string ParseUnqualifiedId();
+
+ void BailOut(const std::string &error, uint32_t loc, uint16_t err_len);
+
+ void Expect(Token::Kind kind);
+
+ void TentativeParsingRollback(uint32_t saved_idx) {
+ if (m_error)
+ llvm::consumeError(std::move(m_error));
+ m_dil_lexer.ResetTokenIdx(saved_idx);
+ }
+
+ Token CurToken() { return m_dil_lexer.GetCurrentToken(); }
+
+ // Parser doesn't own the evaluation context. The produced AST may depend on
+ // it (for example, for source locations), so it's expected that expression
+ // context will outlive the parser.
+ std::shared_ptr<StackFrame> m_ctx_scope;
+
+ llvm::StringRef m_input_expr;
+
+ DILLexer m_dil_lexer;
+
+ // Holds an error if it occures during parsing.
+ llvm::Error &m_error;
+
+ lldb::DynamicValueType m_use_dynamic;
+ bool m_use_synthetic;
+ bool m_fragile_ivar;
+ bool m_check_ptr_vs_member;
+}; // class DILParser
+
+} // namespace lldb_private::dil
+
+#endif // LLDB_VALUEOBJECT_DILPARSER_H
diff --git a/lldb/source/Target/StackFrame.cpp b/lldb/source/Target/StackFrame.cpp
index bab36e9aa1033..0306f68169a98 100644
--- a/lldb/source/Target/StackFrame.cpp
+++ b/lldb/source/Target/StackFrame.cpp
@@ -31,6 +31,9 @@
#include "lldb/Utility/LLDBLog.h"
#include "lldb/Utility/Log.h"
#include "lldb/Utility/RegisterValue.h"
+#include "lldb/ValueObject/DILEval.h"
+#include "lldb/ValueObject/DILLexer.h"
+#include "lldb/ValueObject/DILParser.h"
#include "lldb/ValueObject/ValueObjectConstResult.h"
#include "lldb/ValueObject/ValueObjectMemory.h"
#include "lldb/ValueObject/ValueObjectVariable.h"
@@ -523,10 +526,42 @@ ValueObjectSP StackFrame::GetValueForVariableExpressionPath(
ValueObjectSP StackFrame::DILGetValueForVariableExpressionPath(
llvm::StringRef var_expr, lldb::DynamicValueType use_dynamic,
uint32_t options, lldb::VariableSP &var_sp, Status &error) {
- // This is a place-holder for the calls into the DIL parser and
- // evaluator. For now, just call the "real" frame variable implementation.
- return LegacyGetValueForVariableExpressionPath(var_expr, use_dynamic, options,
- var_sp, error);
+
+ const bool check_ptr_vs_member =
+ (options & eExpressionPathOptionCheckPtrVsMember) != 0;
+ const bool no_fragile_ivar =
+ (options & eExpressionPathOptionsNoFragileObjcIvar) != 0;
+ const bool no_synth_child =
+ (options & eExpressionPathOptionsNoSyntheticChildren) != 0;
+
+ // Lex the expression.
+ auto lex_or_err = dil::DILLexer::Create(var_expr);
+ if (!lex_or_err) {
+ error = Status::FromError(lex_or_err.takeError());
+ return ValueObjectSP();
+ }
+
+ // Parse the expression.
+ auto tree_or_error = dil::DILParser::Parse(
+ var_expr, std::move(*lex_or_err), shared_from_this(), use_dynamic,
+ !no_synth_child, !no_fragile_ivar, check_ptr_vs_member);
+ if (!tree_or_error) {
+ error = Status::FromError(tree_or_error.takeError());
+ return ValueObjectSP();
+ }
+
+ // Evaluate the parsed expression.
+ lldb::TargetSP target = this->CalculateTarget();
+ dil::Interpreter interpreter(target, var_expr, use_dynamic,
+ shared_from_this());
+
+ auto valobj_or_error = interpreter.Evaluate((*tree_or_error).get());
+ if (!valobj_or_error) {
+ error = Status::FromError(valobj_or_error.takeError());
+ return ValueObjectSP();
+ }
+
+ return *valobj_or_error;
}
ValueObjectSP StackFrame::LegacyGetValueForVariableExpressionPath(
diff --git a/lldb/source/ValueObject/CMakeLists.txt b/lldb/source/ValueObject/CMakeLists.txt
index 30c34472289e7..92683916f5a52 100644
--- a/lldb/source/ValueObject/CMakeLists.txt
+++ b/lldb/source/ValueObject/CMakeLists.txt
@@ -1,5 +1,8 @@
add_lldb_library(lldbValueObject
+ DILAST.cpp
+ DILEval.cpp
DILLexer.cpp
+ DILParser.cpp
ValueObject.cpp
ValueObjectCast.cpp
ValueObjectChild.cpp
diff --git a/lldb/source/ValueObject/DILAST.cpp b/lldb/source/ValueObject/DILAST.cpp
new file mode 100644
index 0000000000000..e75958d784627
--- /dev/null
+++ b/lldb/source/ValueObject/DILAST.cpp
@@ -0,0 +1,22 @@
+//===-- DILAST.cpp --------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/ValueObject/DILAST.h"
+#include "llvm/Support/ErrorHandling.h"
+
+namespace lldb_private::dil {
+
+llvm::Expected<lldb::ValueObjectSP> ErrorNode::Accept(Visitor *v) const {
+ llvm_unreachable("Attempting to Visit a DIL ErrorNode.");
+}
+
+llvm::Expected<lldb::ValueObjectSP> IdentifierNode::Accept(Visitor *v) const {
+ return v->Visit(this);
+}
+
+} // namespace lldb_private::dil
diff --git a/lldb/source/ValueObject/DILEval.cpp b/lldb/source/ValueObject/DILEval.cpp
new file mode 100644
index 0000000000000..4889834c7a3c1
--- /dev/null
+++ b/lldb/source/ValueObject/DILEval.cpp
@@ -0,0 +1,235 @@
+//===-- DILEval.cpp -------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/ValueObject/DILEval.h"
+#include "lldb/Symbol/VariableList.h"
+#include "lldb/Target/RegisterContext.h"
+#include "lldb/ValueObject/DILAST.h"
+#include "lldb/ValueObject/ValueObject.h"
+#include "lldb/ValueObject/ValueObjectRegister.h"
+#include "lldb/ValueObject/ValueObjectVariable.h"
+#include "llvm/Support/FormatAdapters.h"
+#include <memory>
+
+namespace lldb_private::dil {
+
+static lldb::ValueObjectSP LookupStaticIdentifier(
+ VariableList &variable_list, std::shared_ptr<StackFrame> exe_scope,
+ llvm::StringRef name_ref, llvm::StringRef unqualified_name) {
+ // First look for an exact match to the (possibly) qualified name.
+ for (const lldb::VariableSP &var_sp : variable_list) {
+ lldb::ValueObjectSP valobj_sp(
+ ValueObjectVariable::Create(exe_scope.get(), var_sp));
+ if (valobj_sp && valobj_sp->GetVariable() &&
+ (valobj_sp->GetVariable()->NameMatches(ConstString(name_ref))))
+ return valobj_sp;
+ }
+
+ // If the qualified name is the same as the unqualfied name, there's nothing
+ // more to be done.
+ if (name_ref == unqualified_name)
+ return nullptr;
+
+ // We didn't match the qualified name; try to match the unqualified name.
+ for (const lldb::VariableSP &var_sp : variable_list) {
+ lldb::ValueObjectSP valobj_sp(
+ ValueObjectVariable::Create(exe_scope.get(), var_sp));
+ if (valobj_sp && valobj_sp->GetVariable() &&
+ (valobj_sp->GetVariable()->NameMatches(ConstString(unqualified_name))))
+ return valobj_sp;
+ }
+
+ return nullptr;
+}
+
+static lldb::VariableSP DILFindVariable(ConstString name,
+ lldb::VariableListSP variable_list) {
+ lldb::VariableSP exact_match;
+ std::vector<lldb::VariableSP> possible_matches;
+
+ for (lldb::VariableSP var_sp : *variable_list) {
+ llvm::StringRef str_ref_name = var_sp->GetName().GetStringRef();
+ // Check for global vars, which might start with '::'.
+ str_ref_name.consume_front("::");
+
+ if (str_ref_name == name.GetStringRef())
+ possible_matches.push_back(var_sp);
+ else if (var_sp->NameMatches(name))
+ possible_matches.push_back(var_sp);
+ }
+
+ // Look for exact matches (favors local vars over global vars)
+ auto exact_match_it =
+ llvm::find_if(possible_matches, [&](lldb::VariableSP var_sp) {
+ return var_sp->GetName() == name;
+ });
+
+ if (exact_match_it != possible_matches.end())
+ return *exact_match_it;
+
+ // Look for a global var exact match.
+ for (auto var_sp : possible_matches) {
+ llvm::StringRef str_ref_name = var_sp->GetName().GetStringRef();
+ str_ref_name.consume_front("::");
+ if (str_ref_name == name.GetStringRef())
+ return var_sp;
+ }
+
+ // If there's a single non-exact match, take it.
+ if (possible_matches.size() == 1)
+ return possible_matches[0];
+
+ return nullptr;
+}
+
+lldb::ValueObjectSP LookupGlobalIdentifier(
+ llvm::StringRef name_ref, std::shared_ptr<StackFrame> stack_frame,
+ lldb::TargetSP target_sp, lldb::DynamicValueType use_dynamic,
+ CompilerType *scope_ptr) {
+ // First look for match in "local" global variables.
+ lldb::VariableListSP variable_list(stack_frame->GetInScopeVariableList(true));
+ name_ref.consume_front("::");
+
+ lldb::ValueObjectSP value_sp;
+ if (variable_list) {
+ lldb::VariableSP var_sp =
+ DILFindVariable(ConstString(name_ref), variable_list);
+ if (var_sp)
+ value_sp =
+ stack_frame->GetValueObjectForFrameVariable(var_sp, use_dynamic);
+ }
+
+ if (value_sp)
+ return value_sp;
+
+ // Also check for static global vars.
+ if (variable_list) {
+ const char *type_name = "";
+ if (scope_ptr)
+ type_name = scope_ptr->GetCanonicalType().GetTypeName().AsCString();
+ std::string name_with_type_prefix =
+ llvm::formatv("{0}::{1}", type_name, name_ref).str();
+ value_sp = LookupStaticIdentifier(*variable_list, stack_frame,
+ name_with_type_prefix, name_ref);
+ if (!value_sp)
+ value_sp = LookupStaticIdentifier(*variable_list, stack_frame, name_ref,
+ name_ref);
+ }
+
+ if (value_sp)
+ return value_sp;
+
+ // Check for match in modules global variables.
+ VariableList modules_var_list;
+ target_sp->GetImages().FindGlobalVariables(
+ ConstString(name_ref), std::numeric_limits<uint32_t>::max(),
+ modules_var_list);
+ if (modules_var_list.Empty())
+ return nullptr;
+
+ for (const lldb::VariableSP &var_sp : modules_var_list) {
+ std::string qualified_name = llvm::formatv("::{0}", name_ref).str();
+ if (var_sp->NameMatches(ConstString(name_ref)) ||
+ var_sp->NameMatches(ConstString(qualified_name))) {
+ value_sp = ValueObjectVariable::Create(stack_frame.get(), var_sp);
+ break;
+ }
+ }
+
+ if (value_sp)
+ return value_sp;
+
+ return nullptr;
+}
+
+lldb::ValueObjectSP LookupIdentifier(llvm::StringRef name_ref,
+ std::shared_ptr<StackFrame> stack_frame,
+ lldb::DynamicValueType use_dynamic,
+ CompilerType *scope_ptr) {
+ // Support $rax as a special syntax for accessing registers.
+ // Will return an invalid value in case the requested register doesn't exist.
+ if (name_ref.consume_front("$")) {
+ lldb::RegisterContextSP reg_ctx(stack_frame->GetRegisterContext());
+ if (!reg_ctx)
+ return nullptr;
+
+ if (const RegisterInfo *reg_info = reg_ctx->GetRegisterInfoByName(name_ref))
+ return ValueObjectRegister::Create(stack_frame.get(), reg_ctx, reg_info);
+
+ return nullptr;
+ }
+
+ lldb::VariableListSP variable_list(
+ stack_frame->GetInScopeVariableList(false));
+
+ if (!name_ref.contains("::")) {
+ if (!scope_ptr || !scope_ptr->IsValid()) {
+ // Lookup in the current frame.
+ // Try looking for a local variable in current scope.
+ lldb::ValueObjectSP value_sp;
+ if (variable_list) {
+ lldb::VariableSP var_sp =
+ DILFindVariable(ConstString(name_ref), variable_list);
+ if (var_sp)
+ value_sp =
+ stack_frame->GetValueObjectForFrameVariable(var_sp, use_dynamic);
+ }
+ if (!value_sp)
+ value_sp = stack_frame->FindVariable(ConstString(name_ref));
+
+ if (value_sp)
+ return value_sp;
+
+ // Try looking for an instance variable (class member).
+ SymbolContext sc = stack_frame->GetSymbolContext(
+ lldb::eSymbolContextFunction | lldb::eSymbolContextBlock);
+ llvm::StringRef ivar_name = sc.GetInstanceVariableName();
+ value_sp = stack_frame->FindVariable(ConstString(ivar_name));
+ if (value_sp)
+ value_sp = value_sp->GetChildMemberWithName(name_ref);
+
+ if (value_sp)
+ return value_sp;
+ }
+ }
+ return nullptr;
+}
+
+Interpreter::Interpreter(lldb::TargetSP target, llvm::StringRef expr,
+ lldb::DynamicValueType use_dynamic,
+ std::shared_ptr<StackFrame> frame_sp)
+ : m_target(std::move(target)), m_expr(expr), m_default_dynamic(use_dynamic),
+ m_exe_ctx_scope(frame_sp) {}
+
+llvm::Expected<lldb::ValueObjectSP> Interpreter::Evaluate(const ASTNode *node) {
+
+ // Traverse an AST pointed by the `node`.
+ return node->Accept(this);
+}
+
+llvm::Expected<lldb::ValueObjectSP>
+Interpreter::Visit(const IdentifierNode *node) {
+ lldb::DynamicValueType use_dynamic = m_default_dynamic;
+
+ lldb::ValueObjectSP identifier =
+ LookupIdentifier(node->GetName(), m_exe_ctx_scope, use_dynamic);
+
+ if (!identifier)
+ identifier = LookupGlobalIdentifier(node->GetName(), m_exe_ctx_scope,
+ m_target, use_dynamic);
+ if (!identifier) {
+ std::string errMsg =
+ llvm::formatv("use of undeclared identifier '{0}'", node->GetName());
+ return llvm::make_error<DILDiagnosticError>(
+ m_expr, errMsg, node->GetLocation(), node->GetName().size());
+ }
+
+ return identifier;
+}
+
+} // namespace lldb_private::dil
diff --git a/lldb/source/ValueObject/DILParser.cpp b/lldb/source/ValueObject/DILParser.cpp
new file mode 100644
index 0000000000000..a8baba2c06e7a
--- /dev/null
+++ b/lldb/source/ValueObject/DILParser.cpp
@@ -0,0 +1,260 @@
+//===-- DILParser.cpp -----------------------------------------------------===//
+//
+// 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
+//
+// This implements the recursive descent parser for the Data Inspection
+// Language (DIL), and its helper functions, which will eventually underlie the
+// 'frame variable' command. The language that this parser recognizes is
+// described in lldb/docs/dil-expr-lang.ebnf
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/ValueObject/DILParser.h"
+#include "lldb/Target/ExecutionContextScope.h"
+#include "lldb/Utility/DiagnosticsRendering.h"
+#include "lldb/ValueObject/DILAST.h"
+#include "lldb/ValueObject/DILEval.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Support/FormatAdapters.h"
+#include <cstdlib>
+#include <limits.h>
+#include <memory>
+#include <sstream>
+#include <string>
+
+namespace lldb_private::dil {
+
+DILDiagnosticError::DILDiagnosticError(llvm::StringRef expr,
+ const std::string &message, uint32_t loc,
+ uint16_t err_len)
+ : ErrorInfo(make_error_code(std::errc::invalid_argument)) {
+ DiagnosticDetail::SourceLocation sloc = {
+ FileSpec{}, /*line=*/1, static_cast<uint16_t>(loc + 1),
+ err_len, false, /*in_user_input=*/true};
+ std::string rendered_msg =
+ llvm::formatv("<user expression 0>:1:{0}: {1}\n 1 | {2}\n | ^",
+ loc + 1, message, expr);
+ m_detail.source_location = sloc;
+ m_detail.severity = lldb::eSeverityError;
+ m_detail.message = message;
+ m_detail.rendered = std::move(rendered_msg);
+}
+
+llvm::Expected<ASTNodeUP>
+DILParser::Parse(llvm::StringRef dil_input_expr, DILLexer lexer,
+ std::shared_ptr<StackFrame> frame_sp,
+ lldb::DynamicValueType use_dynamic, bool use_synthetic,
+ bool fragile_ivar, bool check_ptr_vs_member) {
+ llvm::Error error = llvm::Error::success();
+ DILParser parser(dil_input_expr, lexer, frame_sp, use_dynamic, use_synthetic,
+ fragile_ivar, check_ptr_vs_member, error);
+
+ ASTNodeUP node_up = parser.Run();
+
+ if (error)
+ return error;
+
+ return node_up;
+}
+
+DILParser::DILParser(llvm::StringRef dil_input_expr, DILLexer lexer,
+ std::shared_ptr<StackFrame> frame_sp,
+ lldb::DynamicValueType use_dynamic, bool use_synthetic,
+ bool fragile_ivar, bool check_ptr_vs_member,
+ llvm::Error &error)
+ : m_ctx_scope(frame_sp), m_input_expr(dil_input_expr),
+ m_dil_lexer(std::move(lexer)), m_error(error), m_use_dynamic(use_dynamic),
+ m_use_synthetic(use_synthetic), m_fragile_ivar(fragile_ivar),
+ m_check_ptr_vs_member(check_ptr_vs_member) {}
+
+ASTNodeUP DILParser::Run() {
+ ASTNodeUP expr = ParseExpression();
+
+ Expect(Token::Kind::eof);
+
+ return expr;
+}
+
+// Parse an expression.
+//
+// expression:
+// primary_expression
+//
+ASTNodeUP DILParser::ParseExpression() { return ParsePrimaryExpression(); }
+
+// Parse a primary_expression.
+//
+// primary_expression:
+// id_expression
+// "(" expression ")"
+//
+ASTNodeUP DILParser::ParsePrimaryExpression() {
+ if (CurToken().IsOneOf({Token::coloncolon, Token::identifier})) {
+ // Save the source location for the diagnostics message.
+ uint32_t loc = CurToken().GetLocation();
+ auto identifier = ParseIdExpression();
+
+ return std::make_unique<IdentifierNode>(loc, identifier);
+ }
+
+ if (CurToken().Is(Token::l_paren)) {
+ m_dil_lexer.Advance();
+ auto expr = ParseExpression();
+ Expect(Token::r_paren);
+ m_dil_lexer.Advance();
+ return expr;
+ }
+
+ BailOut(llvm::formatv("Unexpected token: {0}", CurToken()),
+ CurToken().GetLocation(), CurToken().GetSpelling().length());
+ return std::make_unique<ErrorNode>();
+}
+
+// Parse nested_name_specifier.
+//
+// nested_name_specifier:
+// type_name "::"
+// namespace_name "::"
+// nested_name_specifier identifier "::"
+//
+std::string DILParser::ParseNestedNameSpecifier() {
+ // The first token in nested_name_specifier is always an identifier, or
+ // '(anonymous namespace)'.
+ switch (CurToken().GetKind()) {
+ case Token::l_paren: {
+ // Anonymous namespaces need to be treated specially: They are
+ // represented the the string '(anonymous namespace)', which has a
+ // space in it (throwing off normal parsing) and is not actually
+ // proper C++> Check to see if we're looking at
+ // '(anonymous namespace)::...'
+
+ // Look for all the pieces, in order:
+ // l_paren 'anonymous' 'namespace' r_paren coloncolon
+ if (m_dil_lexer.LookAhead(1).Is(Token::identifier) &&
+ (m_dil_lexer.LookAhead(1).GetSpelling() == "anonymous") &&
+ m_dil_lexer.LookAhead(2).Is(Token::identifier) &&
+ (m_dil_lexer.LookAhead(2).GetSpelling() == "namespace") &&
+ m_dil_lexer.LookAhead(3).Is(Token::r_paren) &&
+ m_dil_lexer.LookAhead(4).Is(Token::coloncolon)) {
+ m_dil_lexer.Advance(4);
+
+ assert(
+ (CurToken().Is(Token::identifier) || CurToken().Is(Token::l_paren)) &&
+ "Expected an identifier or anonymous namespace, but not found.");
+ // Continue parsing the nested_namespace_specifier.
+ std::string identifier2 = ParseNestedNameSpecifier();
+ if (identifier2.empty()) {
+ Expect(Token::identifier);
+ identifier2 = CurToken().GetSpelling();
+ m_dil_lexer.Advance();
+ }
+ return "(anonymous namespace)::" + identifier2;
+ }
+
+ return "";
+ } // end of special handling for '(anonymous namespace)'
+ case Token::identifier: {
+ // If the next token is scope ("::"), then this is indeed a
+ // nested_name_specifier
+ if (m_dil_lexer.LookAhead(1).Is(Token::coloncolon)) {
+ // This nested_name_specifier is a single identifier.
+ std::string identifier = CurToken().GetSpelling();
+ m_dil_lexer.Advance(1);
+ Expect(Token::coloncolon);
+ m_dil_lexer.Advance();
+ // Continue parsing the nested_name_specifier.
+ return identifier + "::" + ParseNestedNameSpecifier();
+ }
+
+ return "";
+ }
+ default:
+ return "";
+ }
+}
+
+// Parse an id_expression.
+//
+// id_expression:
+// unqualified_id
+// qualified_id
+//
+// qualified_id:
+// ["::"] [nested_name_specifier] unqualified_id
+// ["::"] identifier
+//
+// identifier:
+// ? Token::identifier ?
+//
+std::string DILParser::ParseIdExpression() {
+ // Try parsing optional global scope operator.
+ bool global_scope = false;
+ if (CurToken().Is(Token::coloncolon)) {
+ global_scope = true;
+ m_dil_lexer.Advance();
+ }
+
+ // Try parsing optional nested_name_specifier.
+ std::string nested_name_specifier = ParseNestedNameSpecifier();
+
+ // If nested_name_specifier is present, then it's qualified_id production.
+ // Follow the first production rule.
+ if (!nested_name_specifier.empty()) {
+ // Parse unqualified_id and construct a fully qualified id expression.
+ auto unqualified_id = ParseUnqualifiedId();
+
+ return llvm::formatv("{0}{1}{2}", global_scope ? "::" : "",
+ nested_name_specifier, unqualified_id);
+ }
+
+ // No nested_name_specifier, but with global scope -- this is also a
+ // qualified_id production. Follow the second production rule.
+ if (global_scope) {
+ Expect(Token::identifier);
+ std::string identifier = CurToken().GetSpelling();
+ m_dil_lexer.Advance();
+ return llvm::formatv("{0}{1}", global_scope ? "::" : "", identifier);
+ }
+
+ // This is unqualified_id production.
+ return ParseUnqualifiedId();
+}
+
+// Parse an unqualified_id.
+//
+// unqualified_id:
+// identifier
+//
+// identifier:
+// ? Token::identifier ?
+//
+std::string DILParser::ParseUnqualifiedId() {
+ Expect(Token::identifier);
+ std::string identifier = CurToken().GetSpelling();
+ m_dil_lexer.Advance();
+ return identifier;
+}
+
+void DILParser::BailOut(const std::string &error, uint32_t loc,
+ uint16_t err_len) {
+ if (m_error)
+ // If error is already set, then the parser is in the "bail-out" mode. Don't
+ // do anything and keep the original error.
+ return;
+
+ m_error =
+ llvm::make_error<DILDiagnosticError>(m_input_expr, error, loc, err_len);
+ // Advance the lexer token index to the end of the lexed tokens vector.
+ m_dil_lexer.ResetTokenIdx(m_dil_lexer.NumLexedTokens() - 1);
+}
+
+void DILParser::Expect(Token::Kind kind) {
+ if (CurToken().IsNot(kind)) {
+ BailOut(llvm::formatv("expected {0}, got: {1}", kind, CurToken()),
+ CurToken().GetLocation(), CurToken().GetSpelling().length());
+ }
+}
+
+} // namespace lldb_private::dil
diff --git a/lldb/test/API/commands/frame/var-dil/basics/GlobalVariableLookup/Makefile b/lldb/test/API/commands/frame/var-dil/basics/GlobalVariableLookup/Makefile
new file mode 100644
index 0000000000000..99998b20bcb05
--- /dev/null
+++ b/lldb/test/API/commands/frame/var-dil/basics/GlobalVariableLookup/Makefile
@@ -0,0 +1,3 @@
+CXX_SOURCES := main.cpp
+
+include Makefile.rules
diff --git a/lldb/test/API/commands/frame/var-dil/basics/GlobalVariableLookup/TestFrameVarDILGlobalVariableLookup.py b/lldb/test/API/commands/frame/var-dil/basics/GlobalVariableLookup/TestFrameVarDILGlobalVariableLookup.py
new file mode 100644
index 0000000000000..edb013c7b3526
--- /dev/null
+++ b/lldb/test/API/commands/frame/var-dil/basics/GlobalVariableLookup/TestFrameVarDILGlobalVariableLookup.py
@@ -0,0 +1,51 @@
+"""
+Make sure 'frame var' using DIL parser/evaultor works for local variables.
+"""
+
+import lldb
+from lldbsuite.test.lldbtest import *
+from lldbsuite.test.decorators import *
+from lldbsuite.test import lldbutil
+
+import os
+import shutil
+import time
+
+
+class TestFrameVarDILGlobalVariableLookup(TestBase):
+ # If your test case doesn't stress debug info, then
+ # set this to true. That way it won't be run once for
+ # each debug info format.
+ NO_DEBUG_INFO_TESTCASE = True
+
+ def test_frame_var(self):
+ self.build()
+ lldbutil.run_to_source_breakpoint(
+ self, "Set a breakpoint here", lldb.SBFileSpec("main.cpp")
+ )
+
+ self.runCmd("settings set target.experimental.use-DIL true")
+ self.expect_var_path("globalVar", type="int", value="-559038737") # 0xDEADBEEF
+ self.expect_var_path("globalPtr", type="int *")
+ self.expect_var_path("globalRef", type="int &")
+ self.expect_var_path("::globalVar", value="-559038737")
+ self.expect_var_path("::globalPtr", type="int *")
+ self.expect_var_path("::globalRef", type="int &")
+
+ self.expect(
+ "frame variable 'externGlobalVar'",
+ error=True,
+ substrs=["use of undeclared identifier"],
+ ) # 0x00C0FFEE
+ self.expect(
+ "frame variable '::externGlobalVar'",
+ error=True,
+ substrs=["use of undeclared identifier"],
+ ) # ["12648430"])
+
+ self.expect_var_path("ns::globalVar", value="13")
+ self.expect_var_path("ns::globalPtr", type="int *")
+ self.expect_var_path("ns::globalRef", type="int &")
+ self.expect_var_path("::ns::globalVar", value="13")
+ self.expect_var_path("::ns::globalPtr", type="int *")
+ self.expect_var_path("::ns::globalRef", type="int &")
diff --git a/lldb/test/API/commands/frame/var-dil/basics/GlobalVariableLookup/main.cpp b/lldb/test/API/commands/frame/var-dil/basics/GlobalVariableLookup/main.cpp
new file mode 100644
index 0000000000000..5bae4fd423e32
--- /dev/null
+++ b/lldb/test/API/commands/frame/var-dil/basics/GlobalVariableLookup/main.cpp
@@ -0,0 +1,15 @@
+int globalVar = 0xDEADBEEF;
+extern int externGlobalVar;
+
+int *globalPtr = &globalVar;
+int &globalRef = globalVar;
+
+namespace ns {
+int globalVar = 13;
+int *globalPtr = &globalVar;
+int &globalRef = globalVar;
+} // namespace ns
+
+int main(int argc, char **argv) {
+ return 0; // Set a breakpoint here
+}
diff --git a/lldb/test/API/commands/frame/var-dil/basics/InstanceVariables/Makefile b/lldb/test/API/commands/frame/var-dil/basics/InstanceVariables/Makefile
new file mode 100644
index 0000000000000..99998b20bcb05
--- /dev/null
+++ b/lldb/test/API/commands/frame/var-dil/basics/InstanceVariables/Makefile
@@ -0,0 +1,3 @@
+CXX_SOURCES := main.cpp
+
+include Makefile.rules
diff --git a/lldb/test/API/commands/frame/var-dil/basics/InstanceVariables/TestFrameVarDILInstanceVariables.py b/lldb/test/API/commands/frame/var-dil/basics/InstanceVariables/TestFrameVarDILInstanceVariables.py
new file mode 100644
index 0000000000000..cf230928fc117
--- /dev/null
+++ b/lldb/test/API/commands/frame/var-dil/basics/InstanceVariables/TestFrameVarDILInstanceVariables.py
@@ -0,0 +1,29 @@
+"""
+Make sure 'frame var' using DIL parser/evaultor works for local variables.
+"""
+
+import lldb
+from lldbsuite.test.lldbtest import *
+from lldbsuite.test.decorators import *
+from lldbsuite.test import lldbutil
+
+import os
+import shutil
+import time
+
+
+class TestFrameVarDILInstanceVariables(TestBase):
+ # If your test case doesn't stress debug info, then
+ # set this to true. That way it won't be run once for
+ # each debug info format.
+ NO_DEBUG_INFO_TESTCASE = True
+
+ def test_frame_var(self):
+ self.build()
+ lldbutil.run_to_source_breakpoint(
+ self, "Set a breakpoint here", lldb.SBFileSpec("main.cpp")
+ )
+
+ self.runCmd("settings set target.experimental.use-DIL true")
+ self.expect_var_path("this", type="TestMethods *")
+ self.expect_var_path("c", children=[ValueCheck(name="field_", value="-1")])
diff --git a/lldb/test/API/commands/frame/var-dil/basics/InstanceVariables/main.cpp b/lldb/test/API/commands/frame/var-dil/basics/InstanceVariables/main.cpp
new file mode 100644
index 0000000000000..7a559c4007415
--- /dev/null
+++ b/lldb/test/API/commands/frame/var-dil/basics/InstanceVariables/main.cpp
@@ -0,0 +1,23 @@
+#include <string>
+
+class C {
+public:
+ int field_ = 1337;
+};
+
+class TestMethods {
+public:
+ void TestInstanceVariables() {
+ C c;
+ c.field_ = -1;
+
+ return; // Set a breakpoint here
+ }
+};
+
+int main(int argc, char **argv) {
+ TestMethods tm;
+
+ tm.TestInstanceVariables();
+ return 0;
+}
diff --git a/lldb/test/API/commands/frame/var-dil/basics/LocalVars/Makefile b/lldb/test/API/commands/frame/var-dil/basics/LocalVars/Makefile
new file mode 100644
index 0000000000000..99998b20bcb05
--- /dev/null
+++ b/lldb/test/API/commands/frame/var-dil/basics/LocalVars/Makefile
@@ -0,0 +1,3 @@
+CXX_SOURCES := main.cpp
+
+include Makefile.rules
diff --git a/lldb/test/API/commands/frame/var-dil/basics/LocalVars/TestFrameVarDILLocalVars.py b/lldb/test/API/commands/frame/var-dil/basics/LocalVars/TestFrameVarDILLocalVars.py
new file mode 100644
index 0000000000000..0f6618fe47984
--- /dev/null
+++ b/lldb/test/API/commands/frame/var-dil/basics/LocalVars/TestFrameVarDILLocalVars.py
@@ -0,0 +1,31 @@
+"""
+Make sure 'frame var' using DIL parser/evaultor works for local variables.
+"""
+
+import lldb
+from lldbsuite.test.lldbtest import *
+from lldbsuite.test.decorators import *
+from lldbsuite.test import lldbutil
+
+import os
+import shutil
+import time
+
+
+class TestFrameVarDILLocalVars(TestBase):
+ # If your test case doesn't stress debug info, then
+ # set this to true. That way it won't be run once for
+ # each debug info format.
+ NO_DEBUG_INFO_TESTCASE = True
+
+ def test_frame_var(self):
+ self.build()
+ lldbutil.run_to_source_breakpoint(
+ self, "Set a breakpoint here", lldb.SBFileSpec("main.cpp")
+ )
+
+ self.runCmd("settings set target.experimental.use-DIL true")
+ self.expect_var_path("a", value="1")
+ self.expect_var_path("b", value="2")
+ self.expect_var_path("c", value="'\\xfd'")
+ self.expect_var_path("s", value="4")
diff --git a/lldb/test/API/commands/frame/var-dil/basics/LocalVars/main.cpp b/lldb/test/API/commands/frame/var-dil/basics/LocalVars/main.cpp
new file mode 100644
index 0000000000000..04c73539c5f89
--- /dev/null
+++ b/lldb/test/API/commands/frame/var-dil/basics/LocalVars/main.cpp
@@ -0,0 +1,9 @@
+int main(int argc, char **argv) {
+ int a = 1;
+ int b = 2;
+
+ char c = -3;
+ unsigned short s = 4;
+
+ return 0; // Set a breakpoint here
+}
diff --git a/lldb/unittests/ValueObject/DILLexerTests.cpp b/lldb/unittests/ValueObject/DILLexerTests.cpp
index 9e5b8efd7af80..9afa957901ae7 100644
--- a/lldb/unittests/ValueObject/DILLexerTests.cpp
+++ b/lldb/unittests/ValueObject/DILLexerTests.cpp
@@ -54,9 +54,9 @@ TEST(DILLexerTests, TokenKindTest) {
EXPECT_TRUE(token.Is(Token::identifier));
EXPECT_FALSE(token.Is(Token::l_paren));
- EXPECT_TRUE(token.IsOneOf(Token::eof, Token::identifier));
- EXPECT_FALSE(token.IsOneOf(Token::l_paren, Token::r_paren, Token::coloncolon,
- Token::eof));
+ EXPECT_TRUE(token.IsOneOf({Token::eof, Token::identifier}));
+ EXPECT_FALSE(token.IsOneOf(
+ {Token::l_paren, Token::r_paren, Token::coloncolon, Token::eof}));
}
TEST(DILLexerTests, LookAheadTest) {
@@ -150,7 +150,7 @@ TEST(DILLexerTests, IdentifiersTest) {
DILLexer lexer(*maybe_lexer);
Token token = lexer.GetCurrentToken();
EXPECT_TRUE(token.IsNot(Token::identifier));
- EXPECT_TRUE(token.IsOneOf(Token::eof, Token::coloncolon, Token::l_paren,
- Token::r_paren));
+ EXPECT_TRUE(token.IsOneOf(
+ {Token::eof, Token::coloncolon, Token::l_paren, Token::r_paren}));
}
}
More information about the lldb-commits
mailing list