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}));
   }
 }


        
_______________________________________________
lldb-commits mailing list
lldb-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits

Reply via email to