teemperor created this revision.
teemperor added a reviewer: LLDB.
teemperor added a project: LLDB.
Herald added subscribers: JDevlieghere, mgorny.
teemperor requested review of this revision.

A large chunk of the `ValueObject` implementation is the hand-written 
expression path parser
and nearly all of the enums in the class are just concerned with expression 
paths. The `ValueObject`
code could be far more concise if we had all the parsing logic in its own 
file/class. I also want to
merge the tab-completion parser for expression paths (which is currently its 
own separate implementation
in `Variable.cpp`) into the normal parser which is less awkward if the parser 
wasn't just an implementation
detail of ValueObject.

As I anyway had to touch all the enums I changed them to scoped enums.


https://reviews.llvm.org/D97307

Files:
  lldb/include/lldb/Core/ExpressionPath.h
  lldb/include/lldb/Core/ValueObject.h
  lldb/include/lldb/Core/ValueObjectRegister.h
  lldb/source/Commands/CommandObjectFrame.cpp
  lldb/source/Core/CMakeLists.txt
  lldb/source/Core/ExpressionPath.cpp
  lldb/source/Core/FormatEntity.cpp
  lldb/source/Core/ValueObject.cpp
  lldb/source/Core/ValueObjectRegister.cpp
  lldb/source/Plugins/Language/CPlusPlus/LibCxx.cpp
  lldb/source/Target/Process.cpp

Index: lldb/source/Target/Process.cpp
===================================================================
--- lldb/source/Target/Process.cpp
+++ lldb/source/Target/Process.cpp
@@ -859,9 +859,8 @@
             ValueObjectSP valobj_sp = StopInfo::GetCrashingDereference(
                 curr_thread_stop_info_sp, &crashing_address);
             if (valobj_sp) {
-              const ValueObject::GetExpressionPathFormat format =
-                  ValueObject::GetExpressionPathFormat::
-                      eGetExpressionPathFormatHonorPointers;
+              const ExpressionPath::PathFormat format =
+                  ExpressionPath::PathFormat::HonorPointers;
               stream->PutCString("Likely cause: ");
               valobj_sp->GetExpressionPath(*stream, format);
               stream->Printf(" accessed 0x%" PRIx64 "\n", crashing_address);
Index: lldb/source/Plugins/Language/CPlusPlus/LibCxx.cpp
===================================================================
--- lldb/source/Plugins/Language/CPlusPlus/LibCxx.cpp
+++ lldb/source/Plugins/Language/CPlusPlus/LibCxx.cpp
@@ -236,10 +236,10 @@
   m_pair_ptr = valobj_sp
                    ->GetValueForExpressionPath(
                        ".__i_.__ptr_->__value_", nullptr, nullptr,
-                       ValueObject::GetValueForExpressionPathOptions()
+                       ExpressionPath::GetValueOptions()
                            .DontCheckDotVsArrowSyntax()
                            .SetSyntheticChildrenTraversal(
-                               ValueObject::GetValueForExpressionPathOptions::
+                               ExpressionPath::GetValueOptions::
                                    SyntheticChildrenTraversal::None),
                        nullptr)
                    .get();
@@ -248,10 +248,10 @@
     m_pair_ptr = valobj_sp
                      ->GetValueForExpressionPath(
                          ".__i_.__ptr_", nullptr, nullptr,
-                         ValueObject::GetValueForExpressionPathOptions()
+                         ExpressionPath::GetValueOptions()
                              .DontCheckDotVsArrowSyntax()
                              .SetSyntheticChildrenTraversal(
-                                 ValueObject::GetValueForExpressionPathOptions::
+                                 ExpressionPath::GetValueOptions::
                                      SyntheticChildrenTraversal::None),
                          nullptr)
                      .get();
Index: lldb/source/Core/ValueObjectRegister.cpp
===================================================================
--- lldb/source/Core/ValueObjectRegister.cpp
+++ lldb/source/Core/ValueObjectRegister.cpp
@@ -299,7 +299,7 @@
   return false;
 }
 
-void ValueObjectRegister::GetExpressionPath(Stream &s,
-                                            GetExpressionPathFormat epformat) {
+void ValueObjectRegister::GetExpressionPath(
+    Stream &s, ExpressionPath::PathFormat epformat) {
   s.Printf("$%s", m_reg_info.name);
 }
Index: lldb/source/Core/ValueObject.cpp
===================================================================
--- lldb/source/Core/ValueObject.cpp
+++ lldb/source/Core/ValueObject.cpp
@@ -1835,9 +1835,8 @@
     // make one and cache it for any future reference.
     synthetic_child_sp = GetValueForExpressionPath(
         expression, nullptr, nullptr,
-        GetValueForExpressionPathOptions().SetSyntheticChildrenTraversal(
-            GetValueForExpressionPathOptions::SyntheticChildrenTraversal::
-                None));
+        ExpressionPath::GetValueOptions().SetSyntheticChildrenTraversal(
+            ExpressionPath::GetValueOptions::SyntheticChildrenTraversal::None));
 
     // Cache the value if we got one back...
     if (synthetic_child_sp.get()) {
@@ -1949,7 +1948,7 @@
 }
 
 void ValueObject::GetExpressionPath(Stream &s,
-                                    GetExpressionPathFormat epformat) {
+                                    ExpressionPath::PathFormat epformat) {
   // synthetic children do not actually "exist" as part of the hierarchy, and
   // sometimes they are consed up in ways that don't make sense from an
   // underlying language/API standpoint. So, use a special code path here to
@@ -1985,7 +1984,7 @@
   const bool is_deref_of_parent = IsDereferenceOfParent();
 
   if (is_deref_of_parent &&
-      epformat == eGetExpressionPathFormatDereferencePointers) {
+      epformat == ExpressionPath::PathFormat::DereferencePointers) {
     // this is the original format of GetExpressionPath() producing code like
     // *(a_ptr).memberName, which is entirely fine, until you put this into
     // StackFrame::GetValueForVariableExpressionPath() which prefers to see
@@ -2003,7 +2002,7 @@
   // made up to allow ptr[%d] syntax to work in variable printing, then add our
   // name ([%d]) to the expression path
   if (m_flags.m_is_array_item_for_pointer &&
-      epformat == eGetExpressionPathFormatHonorPointers)
+      epformat == ExpressionPath::PathFormat::HonorPointers)
     s.PutCString(m_name.GetStringRef());
 
   if (!IsBaseClass()) {
@@ -2015,7 +2014,7 @@
             non_base_class_parent->GetCompilerType();
         if (non_base_class_parent_compiler_type) {
           if (parent && parent->IsDereferenceOfParent() &&
-              epformat == eGetExpressionPathFormatHonorPointers) {
+              epformat == ExpressionPath::PathFormat::HonorPointers) {
             s.PutCString("->");
           } else {
             const uint32_t non_base_class_parent_type_info =
@@ -2038,71 +2037,68 @@
   }
 
   if (is_deref_of_parent &&
-      epformat == eGetExpressionPathFormatDereferencePointers) {
+      epformat == ExpressionPath::PathFormat::DereferencePointers) {
     s.PutChar(')');
   }
 }
-
 ValueObjectSP ValueObject::GetValueForExpressionPath(
-    llvm::StringRef expression, ExpressionPathScanEndReason *reason_to_stop,
-    ExpressionPathEndResultType *final_value_type,
-    const GetValueForExpressionPathOptions &options,
-    ExpressionPathAftermath *final_task_on_target) {
-
-  ExpressionPathScanEndReason dummy_reason_to_stop =
-      ValueObject::eExpressionPathScanEndReasonUnknown;
-  ExpressionPathEndResultType dummy_final_value_type =
-      ValueObject::eExpressionPathEndResultTypeInvalid;
-  ExpressionPathAftermath dummy_final_task_on_target =
-      ValueObject::eExpressionPathAftermathNothing;
-
-  ValueObjectSP ret_val = GetValueForExpressionPath_Impl(
-      expression, reason_to_stop ? reason_to_stop : &dummy_reason_to_stop,
+    llvm::StringRef expression, ExpressionPath::ScanEndReason *reason_to_stop,
+    ExpressionPath::EndResultType *final_value_type,
+    const ExpressionPath::GetValueOptions &options,
+    ExpressionPath::Aftermath *final_task_on_target) {
+
+  ExpressionPath::ScanEndReason dummy_reason_to_stop =
+      ExpressionPath::ScanEndReason::Unknown;
+  ExpressionPath::EndResultType dummy_final_value_type =
+      ExpressionPath::EndResultType::Invalid;
+  ExpressionPath::Aftermath dummy_final_task_on_target =
+      ExpressionPath::Aftermath::Nothing;
+
+  ValueObjectSP ret_val = ExpressionPath::Parse(
+      GetSP(), expression,
+      reason_to_stop ? reason_to_stop : &dummy_reason_to_stop,
       final_value_type ? final_value_type : &dummy_final_value_type, options,
       final_task_on_target ? final_task_on_target
                            : &dummy_final_task_on_target);
 
   if (!final_task_on_target ||
-      *final_task_on_target == ValueObject::eExpressionPathAftermathNothing)
+      *final_task_on_target == ExpressionPath::Aftermath::Nothing)
     return ret_val;
 
   if (ret_val.get() &&
       ((final_value_type ? *final_value_type : dummy_final_value_type) ==
-       eExpressionPathEndResultTypePlain)) // I can only deref and takeaddress
-                                           // of plain objects
+       ExpressionPath::EndResultType::Plain)) // I can only deref and
+                                              // takeaddress of plain objects
   {
     if ((final_task_on_target ? *final_task_on_target
                               : dummy_final_task_on_target) ==
-        ValueObject::eExpressionPathAftermathDereference) {
+        ExpressionPath::Aftermath::Dereference) {
       Status error;
       ValueObjectSP final_value = ret_val->Dereference(error);
       if (error.Fail() || !final_value.get()) {
         if (reason_to_stop)
-          *reason_to_stop =
-              ValueObject::eExpressionPathScanEndReasonDereferencingFailed;
+          *reason_to_stop = ExpressionPath::ScanEndReason::DereferencingFailed;
         if (final_value_type)
-          *final_value_type = ValueObject::eExpressionPathEndResultTypeInvalid;
+          *final_value_type = ExpressionPath::EndResultType::Invalid;
         return ValueObjectSP();
       } else {
         if (final_task_on_target)
-          *final_task_on_target = ValueObject::eExpressionPathAftermathNothing;
+          *final_task_on_target = ExpressionPath::Aftermath::Nothing;
         return final_value;
       }
     }
-    if (*final_task_on_target ==
-        ValueObject::eExpressionPathAftermathTakeAddress) {
+    if (*final_task_on_target == ExpressionPath::Aftermath::TakeAddress) {
       Status error;
       ValueObjectSP final_value = ret_val->AddressOf(error);
       if (error.Fail() || !final_value.get()) {
         if (reason_to_stop)
-          *reason_to_stop =
-              ValueObject::eExpressionPathScanEndReasonTakingAddressFailed;
+          *reason_to_stop = ExpressionPath::ScanEndReason::TakingAddressFailed;
         if (final_value_type)
-          *final_value_type = ValueObject::eExpressionPathEndResultTypeInvalid;
+          *final_value_type = ExpressionPath::EndResultType::Invalid;
         return ValueObjectSP();
       } else {
         if (final_task_on_target)
-          *final_task_on_target = ValueObject::eExpressionPathAftermathNothing;
+          *final_task_on_target = ExpressionPath::Aftermath::Nothing;
         return final_value;
       }
     }
@@ -2111,512 +2107,6 @@
                   // you know I did not do it
 }
 
-ValueObjectSP ValueObject::GetValueForExpressionPath_Impl(
-    llvm::StringRef expression, ExpressionPathScanEndReason *reason_to_stop,
-    ExpressionPathEndResultType *final_result,
-    const GetValueForExpressionPathOptions &options,
-    ExpressionPathAftermath *what_next) {
-  ValueObjectSP root = GetSP();
-
-  if (!root)
-    return nullptr;
-
-  llvm::StringRef remainder = expression;
-
-  while (true) {
-    llvm::StringRef temp_expression = remainder;
-
-    CompilerType root_compiler_type = root->GetCompilerType();
-    CompilerType pointee_compiler_type;
-    Flags pointee_compiler_type_info;
-
-    Flags root_compiler_type_info(
-        root_compiler_type.GetTypeInfo(&pointee_compiler_type));
-    if (pointee_compiler_type)
-      pointee_compiler_type_info.Reset(pointee_compiler_type.GetTypeInfo());
-
-    if (temp_expression.empty()) {
-      *reason_to_stop = ValueObject::eExpressionPathScanEndReasonEndOfString;
-      return root;
-    }
-
-    switch (temp_expression.front()) {
-    case '-': {
-      temp_expression = temp_expression.drop_front();
-      if (options.m_check_dot_vs_arrow_syntax &&
-          root_compiler_type_info.Test(eTypeIsPointer)) // if you are trying to
-                                                        // use -> on a
-                                                        // non-pointer and I
-                                                        // must catch the error
-      {
-        *reason_to_stop =
-            ValueObject::eExpressionPathScanEndReasonArrowInsteadOfDot;
-        *final_result = ValueObject::eExpressionPathEndResultTypeInvalid;
-        return ValueObjectSP();
-      }
-      if (root_compiler_type_info.Test(eTypeIsObjC) && // if yo are trying to
-                                                       // extract an ObjC IVar
-                                                       // when this is forbidden
-          root_compiler_type_info.Test(eTypeIsPointer) &&
-          options.m_no_fragile_ivar) {
-        *reason_to_stop =
-            ValueObject::eExpressionPathScanEndReasonFragileIVarNotAllowed;
-        *final_result = ValueObject::eExpressionPathEndResultTypeInvalid;
-        return ValueObjectSP();
-      }
-      if (!temp_expression.startswith(">")) {
-        *reason_to_stop =
-            ValueObject::eExpressionPathScanEndReasonUnexpectedSymbol;
-        *final_result = ValueObject::eExpressionPathEndResultTypeInvalid;
-        return ValueObjectSP();
-      }
-    }
-      LLVM_FALLTHROUGH;
-    case '.': // or fallthrough from ->
-    {
-      if (options.m_check_dot_vs_arrow_syntax &&
-          temp_expression.front() == '.' &&
-          root_compiler_type_info.Test(eTypeIsPointer)) // if you are trying to
-                                                        // use . on a pointer
-                                                        // and I must catch the
-                                                        // error
-      {
-        *reason_to_stop =
-            ValueObject::eExpressionPathScanEndReasonDotInsteadOfArrow;
-        *final_result = ValueObject::eExpressionPathEndResultTypeInvalid;
-        return nullptr;
-      }
-      temp_expression = temp_expression.drop_front(); // skip . or >
-
-      size_t next_sep_pos = temp_expression.find_first_of("-.[", 1);
-      ConstString child_name;
-      if (next_sep_pos == llvm::StringRef::npos) // if no other separator just
-                                                 // expand this last layer
-      {
-        child_name.SetString(temp_expression);
-        ValueObjectSP child_valobj_sp =
-            root->GetChildMemberWithName(child_name, true);
-
-        if (child_valobj_sp.get()) // we know we are done, so just return
-        {
-          *reason_to_stop =
-              ValueObject::eExpressionPathScanEndReasonEndOfString;
-          *final_result = ValueObject::eExpressionPathEndResultTypePlain;
-          return child_valobj_sp;
-        } else {
-          switch (options.m_synthetic_children_traversal) {
-          case GetValueForExpressionPathOptions::SyntheticChildrenTraversal::
-              None:
-            break;
-          case GetValueForExpressionPathOptions::SyntheticChildrenTraversal::
-              FromSynthetic:
-            if (root->IsSynthetic()) {
-              child_valobj_sp = root->GetNonSyntheticValue();
-              if (child_valobj_sp.get())
-                child_valobj_sp =
-                    child_valobj_sp->GetChildMemberWithName(child_name, true);
-            }
-            break;
-          case GetValueForExpressionPathOptions::SyntheticChildrenTraversal::
-              ToSynthetic:
-            if (!root->IsSynthetic()) {
-              child_valobj_sp = root->GetSyntheticValue();
-              if (child_valobj_sp.get())
-                child_valobj_sp =
-                    child_valobj_sp->GetChildMemberWithName(child_name, true);
-            }
-            break;
-          case GetValueForExpressionPathOptions::SyntheticChildrenTraversal::
-              Both:
-            if (root->IsSynthetic()) {
-              child_valobj_sp = root->GetNonSyntheticValue();
-              if (child_valobj_sp.get())
-                child_valobj_sp =
-                    child_valobj_sp->GetChildMemberWithName(child_name, true);
-            } else {
-              child_valobj_sp = root->GetSyntheticValue();
-              if (child_valobj_sp.get())
-                child_valobj_sp =
-                    child_valobj_sp->GetChildMemberWithName(child_name, true);
-            }
-            break;
-          }
-        }
-
-        // if we are here and options.m_no_synthetic_children is true,
-        // child_valobj_sp is going to be a NULL SP, so we hit the "else"
-        // branch, and return an error
-        if (child_valobj_sp.get()) // if it worked, just return
-        {
-          *reason_to_stop =
-              ValueObject::eExpressionPathScanEndReasonEndOfString;
-          *final_result = ValueObject::eExpressionPathEndResultTypePlain;
-          return child_valobj_sp;
-        } else {
-          *reason_to_stop =
-              ValueObject::eExpressionPathScanEndReasonNoSuchChild;
-          *final_result = ValueObject::eExpressionPathEndResultTypeInvalid;
-          return nullptr;
-        }
-      } else // other layers do expand
-      {
-        llvm::StringRef next_separator = temp_expression.substr(next_sep_pos);
-
-        child_name.SetString(temp_expression.slice(0, next_sep_pos));
-
-        ValueObjectSP child_valobj_sp =
-            root->GetChildMemberWithName(child_name, true);
-        if (child_valobj_sp.get()) // store the new root and move on
-        {
-          root = child_valobj_sp;
-          remainder = next_separator;
-          *final_result = ValueObject::eExpressionPathEndResultTypePlain;
-          continue;
-        } else {
-          switch (options.m_synthetic_children_traversal) {
-          case GetValueForExpressionPathOptions::SyntheticChildrenTraversal::
-              None:
-            break;
-          case GetValueForExpressionPathOptions::SyntheticChildrenTraversal::
-              FromSynthetic:
-            if (root->IsSynthetic()) {
-              child_valobj_sp = root->GetNonSyntheticValue();
-              if (child_valobj_sp.get())
-                child_valobj_sp =
-                    child_valobj_sp->GetChildMemberWithName(child_name, true);
-            }
-            break;
-          case GetValueForExpressionPathOptions::SyntheticChildrenTraversal::
-              ToSynthetic:
-            if (!root->IsSynthetic()) {
-              child_valobj_sp = root->GetSyntheticValue();
-              if (child_valobj_sp.get())
-                child_valobj_sp =
-                    child_valobj_sp->GetChildMemberWithName(child_name, true);
-            }
-            break;
-          case GetValueForExpressionPathOptions::SyntheticChildrenTraversal::
-              Both:
-            if (root->IsSynthetic()) {
-              child_valobj_sp = root->GetNonSyntheticValue();
-              if (child_valobj_sp.get())
-                child_valobj_sp =
-                    child_valobj_sp->GetChildMemberWithName(child_name, true);
-            } else {
-              child_valobj_sp = root->GetSyntheticValue();
-              if (child_valobj_sp.get())
-                child_valobj_sp =
-                    child_valobj_sp->GetChildMemberWithName(child_name, true);
-            }
-            break;
-          }
-        }
-
-        // if we are here and options.m_no_synthetic_children is true,
-        // child_valobj_sp is going to be a NULL SP, so we hit the "else"
-        // branch, and return an error
-        if (child_valobj_sp.get()) // if it worked, move on
-        {
-          root = child_valobj_sp;
-          remainder = next_separator;
-          *final_result = ValueObject::eExpressionPathEndResultTypePlain;
-          continue;
-        } else {
-          *reason_to_stop =
-              ValueObject::eExpressionPathScanEndReasonNoSuchChild;
-          *final_result = ValueObject::eExpressionPathEndResultTypeInvalid;
-          return nullptr;
-        }
-      }
-      break;
-    }
-    case '[': {
-      if (!root_compiler_type_info.Test(eTypeIsArray) &&
-          !root_compiler_type_info.Test(eTypeIsPointer) &&
-          !root_compiler_type_info.Test(
-              eTypeIsVector)) // if this is not a T[] nor a T*
-      {
-        if (!root_compiler_type_info.Test(
-                eTypeIsScalar)) // if this is not even a scalar...
-        {
-          if (options.m_synthetic_children_traversal ==
-              GetValueForExpressionPathOptions::SyntheticChildrenTraversal::
-                  None) // ...only chance left is synthetic
-          {
-            *reason_to_stop =
-                ValueObject::eExpressionPathScanEndReasonRangeOperatorInvalid;
-            *final_result = ValueObject::eExpressionPathEndResultTypeInvalid;
-            return ValueObjectSP();
-          }
-        } else if (!options.m_allow_bitfields_syntax) // if this is a scalar,
-                                                      // check that we can
-                                                      // expand bitfields
-        {
-          *reason_to_stop =
-              ValueObject::eExpressionPathScanEndReasonRangeOperatorNotAllowed;
-          *final_result = ValueObject::eExpressionPathEndResultTypeInvalid;
-          return ValueObjectSP();
-        }
-      }
-      if (temp_expression[1] ==
-          ']') // if this is an unbounded range it only works for arrays
-      {
-        if (!root_compiler_type_info.Test(eTypeIsArray)) {
-          *reason_to_stop =
-              ValueObject::eExpressionPathScanEndReasonEmptyRangeNotAllowed;
-          *final_result = ValueObject::eExpressionPathEndResultTypeInvalid;
-          return nullptr;
-        } else // even if something follows, we cannot expand unbounded ranges,
-               // just let the caller do it
-        {
-          *reason_to_stop =
-              ValueObject::eExpressionPathScanEndReasonArrayRangeOperatorMet;
-          *final_result =
-              ValueObject::eExpressionPathEndResultTypeUnboundedRange;
-          return root;
-        }
-      }
-
-      size_t close_bracket_position = temp_expression.find(']', 1);
-      if (close_bracket_position ==
-          llvm::StringRef::npos) // if there is no ], this is a syntax error
-      {
-        *reason_to_stop =
-            ValueObject::eExpressionPathScanEndReasonUnexpectedSymbol;
-        *final_result = ValueObject::eExpressionPathEndResultTypeInvalid;
-        return nullptr;
-      }
-
-      llvm::StringRef bracket_expr =
-          temp_expression.slice(1, close_bracket_position);
-
-      // If this was an empty expression it would have been caught by the if
-      // above.
-      assert(!bracket_expr.empty());
-
-      if (!bracket_expr.contains('-')) {
-        // if no separator, this is of the form [N].  Note that this cannot be
-        // an unbounded range of the form [], because that case was handled
-        // above with an unconditional return.
-        unsigned long index = 0;
-        if (bracket_expr.getAsInteger(0, index)) {
-          *reason_to_stop =
-              ValueObject::eExpressionPathScanEndReasonUnexpectedSymbol;
-          *final_result = ValueObject::eExpressionPathEndResultTypeInvalid;
-          return nullptr;
-        }
-
-        // from here on we do have a valid index
-        if (root_compiler_type_info.Test(eTypeIsArray)) {
-          ValueObjectSP child_valobj_sp = root->GetChildAtIndex(index, true);
-          if (!child_valobj_sp)
-            child_valobj_sp = root->GetSyntheticArrayMember(index, true);
-          if (!child_valobj_sp)
-            if (root->HasSyntheticValue() &&
-                root->GetSyntheticValue()->GetNumChildren() > index)
-              child_valobj_sp =
-                  root->GetSyntheticValue()->GetChildAtIndex(index, true);
-          if (child_valobj_sp) {
-            root = child_valobj_sp;
-            remainder =
-                temp_expression.substr(close_bracket_position + 1); // skip ]
-            *final_result = ValueObject::eExpressionPathEndResultTypePlain;
-            continue;
-          } else {
-            *reason_to_stop =
-                ValueObject::eExpressionPathScanEndReasonNoSuchChild;
-            *final_result = ValueObject::eExpressionPathEndResultTypeInvalid;
-            return nullptr;
-          }
-        } else if (root_compiler_type_info.Test(eTypeIsPointer)) {
-          if (*what_next ==
-                  ValueObject::
-                      eExpressionPathAftermathDereference && // if this is a
-                                                             // ptr-to-scalar, I
-                                                             // am accessing it
-                                                             // by index and I
-                                                             // would have
-                                                             // deref'ed anyway,
-                                                             // then do it now
-                                                             // and use this as
-                                                             // a bitfield
-              pointee_compiler_type_info.Test(eTypeIsScalar)) {
-            Status error;
-            root = root->Dereference(error);
-            if (error.Fail() || !root) {
-              *reason_to_stop =
-                  ValueObject::eExpressionPathScanEndReasonDereferencingFailed;
-              *final_result = ValueObject::eExpressionPathEndResultTypeInvalid;
-              return nullptr;
-            } else {
-              *what_next = eExpressionPathAftermathNothing;
-              continue;
-            }
-          } else {
-            if (root->GetCompilerType().GetMinimumLanguage() ==
-                    eLanguageTypeObjC &&
-                pointee_compiler_type_info.AllClear(eTypeIsPointer) &&
-                root->HasSyntheticValue() &&
-                (options.m_synthetic_children_traversal ==
-                     GetValueForExpressionPathOptions::
-                         SyntheticChildrenTraversal::ToSynthetic ||
-                 options.m_synthetic_children_traversal ==
-                     GetValueForExpressionPathOptions::
-                         SyntheticChildrenTraversal::Both)) {
-              root = root->GetSyntheticValue()->GetChildAtIndex(index, true);
-            } else
-              root = root->GetSyntheticArrayMember(index, true);
-            if (!root) {
-              *reason_to_stop =
-                  ValueObject::eExpressionPathScanEndReasonNoSuchChild;
-              *final_result = ValueObject::eExpressionPathEndResultTypeInvalid;
-              return nullptr;
-            } else {
-              remainder =
-                  temp_expression.substr(close_bracket_position + 1); // skip ]
-              *final_result = ValueObject::eExpressionPathEndResultTypePlain;
-              continue;
-            }
-          }
-        } else if (root_compiler_type_info.Test(eTypeIsScalar)) {
-          root = root->GetSyntheticBitFieldChild(index, index, true);
-          if (!root) {
-            *reason_to_stop =
-                ValueObject::eExpressionPathScanEndReasonNoSuchChild;
-            *final_result = ValueObject::eExpressionPathEndResultTypeInvalid;
-            return nullptr;
-          } else // we do not know how to expand members of bitfields, so we
-                 // just return and let the caller do any further processing
-          {
-            *reason_to_stop = ValueObject::
-                eExpressionPathScanEndReasonBitfieldRangeOperatorMet;
-            *final_result = ValueObject::eExpressionPathEndResultTypeBitfield;
-            return root;
-          }
-        } else if (root_compiler_type_info.Test(eTypeIsVector)) {
-          root = root->GetChildAtIndex(index, true);
-          if (!root) {
-            *reason_to_stop =
-                ValueObject::eExpressionPathScanEndReasonNoSuchChild;
-            *final_result = ValueObject::eExpressionPathEndResultTypeInvalid;
-            return ValueObjectSP();
-          } else {
-            remainder =
-                temp_expression.substr(close_bracket_position + 1); // skip ]
-            *final_result = ValueObject::eExpressionPathEndResultTypePlain;
-            continue;
-          }
-        } else if (options.m_synthetic_children_traversal ==
-                       GetValueForExpressionPathOptions::
-                           SyntheticChildrenTraversal::ToSynthetic ||
-                   options.m_synthetic_children_traversal ==
-                       GetValueForExpressionPathOptions::
-                           SyntheticChildrenTraversal::Both) {
-          if (root->HasSyntheticValue())
-            root = root->GetSyntheticValue();
-          else if (!root->IsSynthetic()) {
-            *reason_to_stop =
-                ValueObject::eExpressionPathScanEndReasonSyntheticValueMissing;
-            *final_result = ValueObject::eExpressionPathEndResultTypeInvalid;
-            return nullptr;
-          }
-          // if we are here, then root itself is a synthetic VO.. should be
-          // good to go
-
-          if (!root) {
-            *reason_to_stop =
-                ValueObject::eExpressionPathScanEndReasonSyntheticValueMissing;
-            *final_result = ValueObject::eExpressionPathEndResultTypeInvalid;
-            return nullptr;
-          }
-          root = root->GetChildAtIndex(index, true);
-          if (!root) {
-            *reason_to_stop =
-                ValueObject::eExpressionPathScanEndReasonNoSuchChild;
-            *final_result = ValueObject::eExpressionPathEndResultTypeInvalid;
-            return nullptr;
-          } else {
-            remainder =
-                temp_expression.substr(close_bracket_position + 1); // skip ]
-            *final_result = ValueObject::eExpressionPathEndResultTypePlain;
-            continue;
-          }
-        } else {
-          *reason_to_stop =
-              ValueObject::eExpressionPathScanEndReasonNoSuchChild;
-          *final_result = ValueObject::eExpressionPathEndResultTypeInvalid;
-          return nullptr;
-        }
-      } else {
-        // we have a low and a high index
-        llvm::StringRef sleft, sright;
-        unsigned long low_index, high_index;
-        std::tie(sleft, sright) = bracket_expr.split('-');
-        if (sleft.getAsInteger(0, low_index) ||
-            sright.getAsInteger(0, high_index)) {
-          *reason_to_stop =
-              ValueObject::eExpressionPathScanEndReasonUnexpectedSymbol;
-          *final_result = ValueObject::eExpressionPathEndResultTypeInvalid;
-          return nullptr;
-        }
-
-        if (low_index > high_index) // swap indices if required
-          std::swap(low_index, high_index);
-
-        if (root_compiler_type_info.Test(
-                eTypeIsScalar)) // expansion only works for scalars
-        {
-          root = root->GetSyntheticBitFieldChild(low_index, high_index, true);
-          if (!root) {
-            *reason_to_stop =
-                ValueObject::eExpressionPathScanEndReasonNoSuchChild;
-            *final_result = ValueObject::eExpressionPathEndResultTypeInvalid;
-            return nullptr;
-          } else {
-            *reason_to_stop = ValueObject::
-                eExpressionPathScanEndReasonBitfieldRangeOperatorMet;
-            *final_result = ValueObject::eExpressionPathEndResultTypeBitfield;
-            return root;
-          }
-        } else if (root_compiler_type_info.Test(
-                       eTypeIsPointer) && // if this is a ptr-to-scalar, I am
-                                          // accessing it by index and I would
-                                          // have deref'ed anyway, then do it
-                                          // now and use this as a bitfield
-                   *what_next ==
-                       ValueObject::eExpressionPathAftermathDereference &&
-                   pointee_compiler_type_info.Test(eTypeIsScalar)) {
-          Status error;
-          root = root->Dereference(error);
-          if (error.Fail() || !root) {
-            *reason_to_stop =
-                ValueObject::eExpressionPathScanEndReasonDereferencingFailed;
-            *final_result = ValueObject::eExpressionPathEndResultTypeInvalid;
-            return nullptr;
-          } else {
-            *what_next = ValueObject::eExpressionPathAftermathNothing;
-            continue;
-          }
-        } else {
-          *reason_to_stop =
-              ValueObject::eExpressionPathScanEndReasonArrayRangeOperatorMet;
-          *final_result = ValueObject::eExpressionPathEndResultTypeBoundedRange;
-          return root;
-        }
-      }
-      break;
-    }
-    default: // some non-separator is in the way
-    {
-      *reason_to_stop =
-          ValueObject::eExpressionPathScanEndReasonUnexpectedSymbol;
-      *final_result = ValueObject::eExpressionPathEndResultTypeInvalid;
-      return nullptr;
-    }
-    }
-  }
-}
-
 void ValueObject::Dump(Stream &s) { Dump(s, DumpValueObjectOptions(*this)); }
 
 void ValueObject::Dump(Stream &s, const DumpValueObjectOptions &options) {
Index: lldb/source/Core/FormatEntity.cpp
===================================================================
--- lldb/source/Core/FormatEntity.cpp
+++ lldb/source/Core/FormatEntity.cpp
@@ -632,12 +632,12 @@
   ::sprintf(&ptr_deref_buffer[0], ptr_deref_format, index);
   LLDB_LOGF(log, "[ExpandIndexedExpression] name to deref: %s",
             ptr_deref_buffer.c_str());
-  ValueObject::GetValueForExpressionPathOptions options;
-  ValueObject::ExpressionPathEndResultType final_value_type;
-  ValueObject::ExpressionPathScanEndReason reason_to_stop;
-  ValueObject::ExpressionPathAftermath what_next =
-      (deref_pointer ? ValueObject::eExpressionPathAftermathDereference
-                     : ValueObject::eExpressionPathAftermathNothing);
+  ExpressionPath::GetValueOptions options;
+  ExpressionPath::EndResultType final_value_type;
+  ExpressionPath::ScanEndReason reason_to_stop;
+  ExpressionPath::Aftermath what_next =
+      (deref_pointer ? ExpressionPath::Aftermath::Dereference
+                     : ExpressionPath::Aftermath::Nothing);
   ValueObjectSP item = valobj->GetValueForExpressionPath(
       ptr_deref_buffer.c_str(), &reason_to_stop, &final_value_type, options,
       &what_next);
@@ -723,16 +723,15 @@
   if (valobj == nullptr)
     return false;
 
-  ValueObject::ExpressionPathAftermath what_next =
-      (do_deref_pointer ? ValueObject::eExpressionPathAftermathDereference
-                        : ValueObject::eExpressionPathAftermathNothing);
-  ValueObject::GetValueForExpressionPathOptions options;
+  ExpressionPath::Aftermath what_next =
+      (do_deref_pointer ? ExpressionPath::Aftermath::Dereference
+                        : ExpressionPath::Aftermath::Nothing);
+  ExpressionPath::GetValueOptions options;
   options.DontCheckDotVsArrowSyntax()
       .DoAllowBitfieldSyntax()
       .DoAllowFragileIVar()
       .SetSyntheticChildrenTraversal(
-          ValueObject::GetValueForExpressionPathOptions::
-              SyntheticChildrenTraversal::Both);
+          ExpressionPath::GetValueOptions::SyntheticChildrenTraversal::Both);
   ValueObject *target = nullptr;
   const char *var_name_final_if_array_range = nullptr;
   size_t close_bracket_index = llvm::StringRef::npos;
@@ -742,10 +741,10 @@
   bool was_plain_var = false;
   bool was_var_format = false;
   bool was_var_indexed = false;
-  ValueObject::ExpressionPathScanEndReason reason_to_stop =
-      ValueObject::eExpressionPathScanEndReasonEndOfString;
-  ValueObject::ExpressionPathEndResultType final_value_type =
-      ValueObject::eExpressionPathEndResultTypePlain;
+  ExpressionPath::ScanEndReason reason_to_stop =
+      ExpressionPath::ScanEndReason::EndOfString;
+  ExpressionPath::EndResultType final_value_type =
+      ExpressionPath::EndResultType::Plain;
 
   if (is_script) {
     return RunScriptFormatKeyword(s, sc, exe_ctx, valobj, entry.string.c_str());
@@ -800,13 +799,10 @@
   }
 
   is_array_range =
-      (final_value_type ==
-           ValueObject::eExpressionPathEndResultTypeBoundedRange ||
-       final_value_type ==
-           ValueObject::eExpressionPathEndResultTypeUnboundedRange);
+      (final_value_type == ExpressionPath::EndResultType::BoundedRange ||
+       final_value_type == ExpressionPath::EndResultType::UnboundedRange);
 
-  do_deref_pointer =
-      (what_next == ValueObject::eExpressionPathAftermathDereference);
+  do_deref_pointer = (what_next == ExpressionPath::Aftermath::Dereference);
 
   if (do_deref_pointer && !is_array_range) {
     // I have not deref-ed yet, let's do it
Index: lldb/source/Core/ExpressionPath.cpp
===================================================================
--- /dev/null
+++ lldb/source/Core/ExpressionPath.cpp
@@ -0,0 +1,474 @@
+//===-- ExpressionPath.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/Core/ExpressionPath.h"
+#include "lldb/Core/ValueObject.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+ValueObjectSP ExpressionPath::Parse(ValueObjectSP root,
+                                    llvm::StringRef expression,
+                                    ScanEndReason *reason_to_stop,
+                                    EndResultType *final_result,
+                                    const GetValueOptions &options,
+                                    Aftermath *what_next) {
+
+  if (!root)
+    return nullptr;
+
+  llvm::StringRef remainder = expression;
+
+  while (true) {
+    llvm::StringRef temp_expression = remainder;
+
+    CompilerType root_compiler_type = root->GetCompilerType();
+    CompilerType pointee_compiler_type;
+    Flags pointee_compiler_type_info;
+
+    Flags root_compiler_type_info(
+        root_compiler_type.GetTypeInfo(&pointee_compiler_type));
+    if (pointee_compiler_type)
+      pointee_compiler_type_info.Reset(pointee_compiler_type.GetTypeInfo());
+
+    if (temp_expression.empty()) {
+      *reason_to_stop = ScanEndReason::EndOfString;
+      return root;
+    }
+
+    switch (temp_expression.front()) {
+    case '-': {
+      temp_expression = temp_expression.drop_front();
+      if (options.m_check_dot_vs_arrow_syntax &&
+          root_compiler_type_info.Test(eTypeIsPointer)) // if you are trying to
+                                                        // use -> on a
+                                                        // non-pointer and I
+                                                        // must catch the error
+      {
+        *reason_to_stop = ScanEndReason::ArrowInsteadOfDot;
+        *final_result = EndResultType::Invalid;
+        return ValueObjectSP();
+      }
+      if (root_compiler_type_info.Test(eTypeIsObjC) && // if yo are trying to
+                                                       // extract an ObjC IVar
+                                                       // when this is forbidden
+          root_compiler_type_info.Test(eTypeIsPointer) &&
+          options.m_no_fragile_ivar) {
+        *reason_to_stop = ScanEndReason::FragileIVarNotAllowed;
+        *final_result = EndResultType::Invalid;
+        return ValueObjectSP();
+      }
+      if (!temp_expression.startswith(">")) {
+        *reason_to_stop = ScanEndReason::UnexpectedSymbol;
+        *final_result = EndResultType::Invalid;
+        return ValueObjectSP();
+      }
+    }
+      LLVM_FALLTHROUGH;
+    case '.': // or fallthrough from ->
+    {
+      if (options.m_check_dot_vs_arrow_syntax &&
+          temp_expression.front() == '.' &&
+          root_compiler_type_info.Test(eTypeIsPointer)) // if you are trying to
+                                                        // use . on a pointer
+                                                        // and I must catch the
+                                                        // error
+      {
+        *reason_to_stop = ScanEndReason::DotInsteadOfArrow;
+        *final_result = EndResultType::Invalid;
+        return nullptr;
+      }
+      temp_expression = temp_expression.drop_front(); // skip . or >
+
+      size_t next_sep_pos = temp_expression.find_first_of("-.[", 1);
+      ConstString child_name;
+      if (next_sep_pos == llvm::StringRef::npos) // if no other separator just
+                                                 // expand this last layer
+      {
+        child_name.SetString(temp_expression);
+        ValueObjectSP child_valobj_sp =
+            root->GetChildMemberWithName(child_name, true);
+
+        if (child_valobj_sp.get()) // we know we are done, so just return
+        {
+          *reason_to_stop = ScanEndReason::EndOfString;
+          *final_result = EndResultType::Plain;
+          return child_valobj_sp;
+        } else {
+          switch (options.m_synthetic_children_traversal) {
+          case GetValueOptions::SyntheticChildrenTraversal::None:
+            break;
+          case GetValueOptions::SyntheticChildrenTraversal::FromSynthetic:
+            if (root->IsSynthetic()) {
+              child_valobj_sp = root->GetNonSyntheticValue();
+              if (child_valobj_sp.get())
+                child_valobj_sp =
+                    child_valobj_sp->GetChildMemberWithName(child_name, true);
+            }
+            break;
+          case GetValueOptions::SyntheticChildrenTraversal::ToSynthetic:
+            if (!root->IsSynthetic()) {
+              child_valobj_sp = root->GetSyntheticValue();
+              if (child_valobj_sp.get())
+                child_valobj_sp =
+                    child_valobj_sp->GetChildMemberWithName(child_name, true);
+            }
+            break;
+          case GetValueOptions::SyntheticChildrenTraversal::Both:
+            if (root->IsSynthetic()) {
+              child_valobj_sp = root->GetNonSyntheticValue();
+              if (child_valobj_sp.get())
+                child_valobj_sp =
+                    child_valobj_sp->GetChildMemberWithName(child_name, true);
+            } else {
+              child_valobj_sp = root->GetSyntheticValue();
+              if (child_valobj_sp.get())
+                child_valobj_sp =
+                    child_valobj_sp->GetChildMemberWithName(child_name, true);
+            }
+            break;
+          }
+        }
+
+        // if we are here and options.m_no_synthetic_children is true,
+        // child_valobj_sp is going to be a NULL SP, so we hit the "else"
+        // branch, and return an error
+        if (child_valobj_sp.get()) // if it worked, just return
+        {
+          *reason_to_stop = ScanEndReason::EndOfString;
+          *final_result = EndResultType::Plain;
+          return child_valobj_sp;
+        } else {
+          *reason_to_stop = ScanEndReason::NoSuchChild;
+          *final_result = EndResultType::Invalid;
+          return nullptr;
+        }
+      } else // other layers do expand
+      {
+        llvm::StringRef next_separator = temp_expression.substr(next_sep_pos);
+
+        child_name.SetString(temp_expression.slice(0, next_sep_pos));
+
+        ValueObjectSP child_valobj_sp =
+            root->GetChildMemberWithName(child_name, true);
+        if (child_valobj_sp.get()) // store the new root and move on
+        {
+          root = child_valobj_sp;
+          remainder = next_separator;
+          *final_result = EndResultType::Plain;
+          continue;
+        } else {
+          switch (options.m_synthetic_children_traversal) {
+          case GetValueOptions::SyntheticChildrenTraversal::None:
+            break;
+          case GetValueOptions::SyntheticChildrenTraversal::FromSynthetic:
+            if (root->IsSynthetic()) {
+              child_valobj_sp = root->GetNonSyntheticValue();
+              if (child_valobj_sp.get())
+                child_valobj_sp =
+                    child_valobj_sp->GetChildMemberWithName(child_name, true);
+            }
+            break;
+          case GetValueOptions::SyntheticChildrenTraversal::ToSynthetic:
+            if (!root->IsSynthetic()) {
+              child_valobj_sp = root->GetSyntheticValue();
+              if (child_valobj_sp.get())
+                child_valobj_sp =
+                    child_valobj_sp->GetChildMemberWithName(child_name, true);
+            }
+            break;
+          case GetValueOptions::SyntheticChildrenTraversal::Both:
+            if (root->IsSynthetic()) {
+              child_valobj_sp = root->GetNonSyntheticValue();
+              if (child_valobj_sp.get())
+                child_valobj_sp =
+                    child_valobj_sp->GetChildMemberWithName(child_name, true);
+            } else {
+              child_valobj_sp = root->GetSyntheticValue();
+              if (child_valobj_sp.get())
+                child_valobj_sp =
+                    child_valobj_sp->GetChildMemberWithName(child_name, true);
+            }
+            break;
+          }
+        }
+
+        // if we are here and options.m_no_synthetic_children is true,
+        // child_valobj_sp is going to be a NULL SP, so we hit the "else"
+        // branch, and return an error
+        if (child_valobj_sp.get()) // if it worked, move on
+        {
+          root = child_valobj_sp;
+          remainder = next_separator;
+          *final_result = EndResultType::Plain;
+          continue;
+        } else {
+          *reason_to_stop = ScanEndReason::NoSuchChild;
+          *final_result = EndResultType::Invalid;
+          return nullptr;
+        }
+      }
+      break;
+    }
+    case '[': {
+      if (!root_compiler_type_info.Test(eTypeIsArray) &&
+          !root_compiler_type_info.Test(eTypeIsPointer) &&
+          !root_compiler_type_info.Test(
+              eTypeIsVector)) // if this is not a T[] nor a T*
+      {
+        if (!root_compiler_type_info.Test(
+                eTypeIsScalar)) // if this is not even a scalar...
+        {
+          if (options.m_synthetic_children_traversal ==
+              GetValueOptions::SyntheticChildrenTraversal::
+                  None) // ...only chance left is synthetic
+          {
+            *reason_to_stop = ScanEndReason::RangeOperatorInvalid;
+            *final_result = EndResultType::Invalid;
+            return ValueObjectSP();
+          }
+        } else if (!options.m_allow_bitfields_syntax) // if this is a scalar,
+                                                      // check that we can
+                                                      // expand bitfields
+        {
+          *reason_to_stop = ScanEndReason::RangeOperatorNotAllowed;
+          *final_result = EndResultType::Invalid;
+          return ValueObjectSP();
+        }
+      }
+      if (temp_expression[1] ==
+          ']') // if this is an unbounded range it only works for arrays
+      {
+        if (!root_compiler_type_info.Test(eTypeIsArray)) {
+          *reason_to_stop = ScanEndReason::EmptyRangeNotAllowed;
+          *final_result = EndResultType::Invalid;
+          return nullptr;
+        } else // even if something follows, we cannot expand unbounded ranges,
+               // just let the caller do it
+        {
+          *reason_to_stop = ScanEndReason::ArrayRangeOperatorMet;
+          *final_result = EndResultType::UnboundedRange;
+          return root;
+        }
+      }
+
+      size_t close_bracket_position = temp_expression.find(']', 1);
+      if (close_bracket_position ==
+          llvm::StringRef::npos) // if there is no ], this is a syntax error
+      {
+        *reason_to_stop = ScanEndReason::UnexpectedSymbol;
+        *final_result = EndResultType::Invalid;
+        return nullptr;
+      }
+
+      llvm::StringRef bracket_expr =
+          temp_expression.slice(1, close_bracket_position);
+
+      // If this was an empty expression it would have been caught by the if
+      // above.
+      assert(!bracket_expr.empty());
+
+      if (!bracket_expr.contains('-')) {
+        // if no separator, this is of the form [N].  Note that this cannot be
+        // an unbounded range of the form [], because that case was handled
+        // above with an unconditional return.
+        unsigned long index = 0;
+        if (bracket_expr.getAsInteger(0, index)) {
+          *reason_to_stop = ScanEndReason::UnexpectedSymbol;
+          *final_result = EndResultType::Invalid;
+          return nullptr;
+        }
+
+        // from here on we do have a valid index
+        if (root_compiler_type_info.Test(eTypeIsArray)) {
+          ValueObjectSP child_valobj_sp = root->GetChildAtIndex(index, true);
+          if (!child_valobj_sp)
+            child_valobj_sp = root->GetSyntheticArrayMember(index, true);
+          if (!child_valobj_sp)
+            if (root->HasSyntheticValue() &&
+                root->GetSyntheticValue()->GetNumChildren() > index)
+              child_valobj_sp =
+                  root->GetSyntheticValue()->GetChildAtIndex(index, true);
+          if (child_valobj_sp) {
+            root = child_valobj_sp;
+            remainder =
+                temp_expression.substr(close_bracket_position + 1); // skip ]
+            *final_result = EndResultType::Plain;
+            continue;
+          } else {
+            *reason_to_stop = ScanEndReason::NoSuchChild;
+            *final_result = EndResultType::Invalid;
+            return nullptr;
+          }
+        } else if (root_compiler_type_info.Test(eTypeIsPointer)) {
+          if (*what_next == Aftermath::Dereference && // if this is a
+                                                      // ptr-to-scalar, I
+                                                      // am accessing it
+                                                      // by index and I
+                                                      // would have
+                                                      // deref'ed anyway,
+                                                      // then do it now
+                                                      // and use this as
+                                                      // a bitfield
+              pointee_compiler_type_info.Test(eTypeIsScalar)) {
+            Status error;
+            root = root->Dereference(error);
+            if (error.Fail() || !root) {
+              *reason_to_stop = ScanEndReason::DereferencingFailed;
+              *final_result = EndResultType::Invalid;
+              return nullptr;
+            } else {
+              *what_next = Aftermath::Nothing;
+              continue;
+            }
+          } else {
+            if (root->GetCompilerType().GetMinimumLanguage() ==
+                    eLanguageTypeObjC &&
+                pointee_compiler_type_info.AllClear(eTypeIsPointer) &&
+                root->HasSyntheticValue() &&
+                (options.m_synthetic_children_traversal ==
+                     GetValueOptions::SyntheticChildrenTraversal::ToSynthetic ||
+                 options.m_synthetic_children_traversal ==
+                     GetValueOptions::SyntheticChildrenTraversal::Both)) {
+              root = root->GetSyntheticValue()->GetChildAtIndex(index, true);
+            } else
+              root = root->GetSyntheticArrayMember(index, true);
+            if (!root) {
+              *reason_to_stop = ScanEndReason::NoSuchChild;
+              *final_result = EndResultType::Invalid;
+              return nullptr;
+            } else {
+              remainder =
+                  temp_expression.substr(close_bracket_position + 1); // skip ]
+              *final_result = EndResultType::Plain;
+              continue;
+            }
+          }
+        } else if (root_compiler_type_info.Test(eTypeIsScalar)) {
+          root = root->GetSyntheticBitFieldChild(index, index, true);
+          if (!root) {
+            *reason_to_stop = ScanEndReason::NoSuchChild;
+            *final_result = EndResultType::Invalid;
+            return nullptr;
+          } else // we do not know how to expand members of bitfields, so we
+                 // just return and let the caller do any further processing
+          {
+            *reason_to_stop = ScanEndReason::BitfieldRangeOperatorMet;
+            *final_result = EndResultType::Bitfield;
+            return root;
+          }
+        } else if (root_compiler_type_info.Test(eTypeIsVector)) {
+          root = root->GetChildAtIndex(index, true);
+          if (!root) {
+            *reason_to_stop = ScanEndReason::NoSuchChild;
+            *final_result = EndResultType::Invalid;
+            return ValueObjectSP();
+          } else {
+            remainder =
+                temp_expression.substr(close_bracket_position + 1); // skip ]
+            *final_result = EndResultType::Plain;
+            continue;
+          }
+        } else if (options.m_synthetic_children_traversal ==
+                       GetValueOptions::SyntheticChildrenTraversal::
+                           ToSynthetic ||
+                   options.m_synthetic_children_traversal ==
+                       GetValueOptions::SyntheticChildrenTraversal::Both) {
+          if (root->HasSyntheticValue())
+            root = root->GetSyntheticValue();
+          else if (!root->IsSynthetic()) {
+            *reason_to_stop = ScanEndReason::SyntheticValueMissing;
+            *final_result = EndResultType::Invalid;
+            return nullptr;
+          }
+          // if we are here, then root itself is a synthetic VO.. should be
+          // good to go
+
+          if (!root) {
+            *reason_to_stop = ScanEndReason::SyntheticValueMissing;
+            *final_result = EndResultType::Invalid;
+            return nullptr;
+          }
+          root = root->GetChildAtIndex(index, true);
+          if (!root) {
+            *reason_to_stop = ScanEndReason::NoSuchChild;
+            *final_result = EndResultType::Invalid;
+            return nullptr;
+          } else {
+            remainder =
+                temp_expression.substr(close_bracket_position + 1); // skip ]
+            *final_result = EndResultType::Plain;
+            continue;
+          }
+        } else {
+          *reason_to_stop = ScanEndReason::NoSuchChild;
+          *final_result = EndResultType::Invalid;
+          return nullptr;
+        }
+      } else {
+        // we have a low and a high index
+        llvm::StringRef sleft, sright;
+        unsigned long low_index, high_index;
+        std::tie(sleft, sright) = bracket_expr.split('-');
+        if (sleft.getAsInteger(0, low_index) ||
+            sright.getAsInteger(0, high_index)) {
+          *reason_to_stop = ScanEndReason::UnexpectedSymbol;
+          *final_result = EndResultType::Invalid;
+          return nullptr;
+        }
+
+        if (low_index > high_index) // swap indices if required
+          std::swap(low_index, high_index);
+
+        if (root_compiler_type_info.Test(
+                eTypeIsScalar)) // expansion only works for scalars
+        {
+          root = root->GetSyntheticBitFieldChild(low_index, high_index, true);
+          if (!root) {
+            *reason_to_stop = ScanEndReason::NoSuchChild;
+            *final_result = EndResultType::Invalid;
+            return nullptr;
+          } else {
+            *reason_to_stop = ScanEndReason::BitfieldRangeOperatorMet;
+            *final_result = EndResultType::Bitfield;
+            return root;
+          }
+        } else if (root_compiler_type_info.Test(
+                       eTypeIsPointer) && // if this is a ptr-to-scalar, I am
+                                          // accessing it by index and I would
+                                          // have deref'ed anyway, then do it
+                                          // now and use this as a bitfield
+                   *what_next == Aftermath::Dereference &&
+                   pointee_compiler_type_info.Test(eTypeIsScalar)) {
+          Status error;
+          root = root->Dereference(error);
+          if (error.Fail() || !root) {
+            *reason_to_stop = ScanEndReason::DereferencingFailed;
+            *final_result = EndResultType::Invalid;
+            return nullptr;
+          } else {
+            *what_next = ExpressionPath::Aftermath::Nothing;
+            continue;
+          }
+        } else {
+          *reason_to_stop = ScanEndReason::ArrayRangeOperatorMet;
+          *final_result = EndResultType::BoundedRange;
+          return root;
+        }
+      }
+      break;
+    }
+    default: // some non-separator is in the way
+    {
+      *reason_to_stop = ScanEndReason::UnexpectedSymbol;
+      *final_result = EndResultType::Invalid;
+      return nullptr;
+    }
+    }
+  }
+}
Index: lldb/source/Core/CMakeLists.txt
===================================================================
--- lldb/source/Core/CMakeLists.txt
+++ lldb/source/Core/CMakeLists.txt
@@ -32,6 +32,7 @@
   DumpRegisterValue.cpp
   DynamicLoader.cpp
   EmulateInstruction.cpp
+  ExpressionPath.cpp
   FileLineResolver.cpp
   FileSpecList.cpp
   FormatEntity.cpp
Index: lldb/source/Commands/CommandObjectFrame.cpp
===================================================================
--- lldb/source/Commands/CommandObjectFrame.cpp
+++ lldb/source/Commands/CommandObjectFrame.cpp
@@ -169,8 +169,8 @@
         [&valobj_sp](ConstString type, ConstString var,
                      const DumpValueObjectOptions &opts,
                      Stream &stream) -> bool {
-      const ValueObject::GetExpressionPathFormat format = ValueObject::
-          GetExpressionPathFormat::eGetExpressionPathFormatHonorPointers;
+      const ExpressionPath::PathFormat format =
+          ExpressionPath::PathFormat::HonorPointers;
       valobj_sp->GetExpressionPath(stream, format);
       stream.PutCString(" =");
       return true;
Index: lldb/include/lldb/Core/ValueObjectRegister.h
===================================================================
--- lldb/include/lldb/Core/ValueObjectRegister.h
+++ lldb/include/lldb/Core/ValueObjectRegister.h
@@ -102,10 +102,9 @@
 
   bool ResolveValue(Scalar &scalar) override;
 
-  void
-  GetExpressionPath(Stream &s,
-                    GetExpressionPathFormat epformat =
-                        eGetExpressionPathFormatDereferencePointers) override;
+  void GetExpressionPath(
+      Stream &s, ExpressionPath::PathFormat epformat =
+                     ExpressionPath::PathFormat::DereferencePointers) override;
 
 protected:
   bool UpdateValue() override;
Index: lldb/include/lldb/Core/ValueObject.h
===================================================================
--- lldb/include/lldb/Core/ValueObject.h
+++ lldb/include/lldb/Core/ValueObject.h
@@ -9,6 +9,7 @@
 #ifndef LLDB_CORE_VALUEOBJECT_H
 #define LLDB_CORE_VALUEOBJECT_H
 
+#include "lldb/Core/ExpressionPath.h"
 #include "lldb/Core/Value.h"
 #include "lldb/Symbol/CompilerType.h"
 #include "lldb/Symbol/Type.h"
@@ -104,11 +105,6 @@
 
 class ValueObject {
 public:
-  enum GetExpressionPathFormat {
-    eGetExpressionPathFormatDereferencePointers = 1,
-    eGetExpressionPathFormatHonorPointers
-  };
-
   enum ValueObjectRepresentationStyle {
     eValueObjectRepresentationStyleValue = 1,
     eValueObjectRepresentationStyleSummary,
@@ -120,65 +116,6 @@
     eValueObjectRepresentationStyleExpressionPath
   };
 
-  enum ExpressionPathScanEndReason {
-    /// Out of data to parse.
-    eExpressionPathScanEndReasonEndOfString = 1,
-    /// Child element not found.
-    eExpressionPathScanEndReasonNoSuchChild,
-    /// (Synthetic) child  element not found.
-    eExpressionPathScanEndReasonNoSuchSyntheticChild,
-    /// [] only allowed for arrays.
-    eExpressionPathScanEndReasonEmptyRangeNotAllowed,
-    /// . used when -> should be used.
-    eExpressionPathScanEndReasonDotInsteadOfArrow,
-    /// -> used when . should be used.
-    eExpressionPathScanEndReasonArrowInsteadOfDot,
-    /// ObjC ivar expansion not allowed.
-    eExpressionPathScanEndReasonFragileIVarNotAllowed,
-    /// [] not allowed by options.
-    eExpressionPathScanEndReasonRangeOperatorNotAllowed,
-    /// [] not valid on objects  other than scalars, pointers or arrays.
-    eExpressionPathScanEndReasonRangeOperatorInvalid,
-    /// [] is good for arrays,  but I cannot parse it.
-    eExpressionPathScanEndReasonArrayRangeOperatorMet,
-    /// [] is good for bitfields, but I cannot parse after it.
-    eExpressionPathScanEndReasonBitfieldRangeOperatorMet,
-    /// Something is malformed in he expression.
-    eExpressionPathScanEndReasonUnexpectedSymbol,
-    /// Impossible to apply &  operator.
-    eExpressionPathScanEndReasonTakingAddressFailed,
-    /// Impossible to apply *  operator.
-    eExpressionPathScanEndReasonDereferencingFailed,
-    /// [] was expanded into a  VOList.
-    eExpressionPathScanEndReasonRangeOperatorExpanded,
-    /// getting the synthetic children failed.
-    eExpressionPathScanEndReasonSyntheticValueMissing,
-    eExpressionPathScanEndReasonUnknown = 0xFFFF
-  };
-
-  enum ExpressionPathEndResultType {
-    /// Anything but...
-    eExpressionPathEndResultTypePlain = 1,
-    /// A bitfield.
-    eExpressionPathEndResultTypeBitfield,
-    /// A range [low-high].
-    eExpressionPathEndResultTypeBoundedRange,
-    /// A range [].
-    eExpressionPathEndResultTypeUnboundedRange,
-    /// Several items in a VOList.
-    eExpressionPathEndResultTypeValueObjectList,
-    eExpressionPathEndResultTypeInvalid = 0xFFFF
-  };
-
-  enum ExpressionPathAftermath {
-    /// Just return it.
-    eExpressionPathAftermathNothing = 1,
-    /// Dereference the target.
-    eExpressionPathAftermathDereference,
-    /// Take target's address.
-    eExpressionPathAftermathTakeAddress
-  };
-
   enum ClearUserVisibleDataItems {
     eClearUserVisibleDataItemsNothing = 1u << 0,
     eClearUserVisibleDataItemsValue = 1u << 1,
@@ -193,70 +130,6 @@
     eClearUserVisibleDataItemsAll = 0xFFFF
   };
 
-  struct GetValueForExpressionPathOptions {
-    enum class SyntheticChildrenTraversal {
-      None,
-      ToSynthetic,
-      FromSynthetic,
-      Both
-    };
-
-    bool m_check_dot_vs_arrow_syntax;
-    bool m_no_fragile_ivar;
-    bool m_allow_bitfields_syntax;
-    SyntheticChildrenTraversal m_synthetic_children_traversal;
-
-    GetValueForExpressionPathOptions(
-        bool dot = false, bool no_ivar = false, bool bitfield = true,
-        SyntheticChildrenTraversal synth_traverse =
-            SyntheticChildrenTraversal::ToSynthetic)
-        : m_check_dot_vs_arrow_syntax(dot), m_no_fragile_ivar(no_ivar),
-          m_allow_bitfields_syntax(bitfield),
-          m_synthetic_children_traversal(synth_traverse) {}
-
-    GetValueForExpressionPathOptions &DoCheckDotVsArrowSyntax() {
-      m_check_dot_vs_arrow_syntax = true;
-      return *this;
-    }
-
-    GetValueForExpressionPathOptions &DontCheckDotVsArrowSyntax() {
-      m_check_dot_vs_arrow_syntax = false;
-      return *this;
-    }
-
-    GetValueForExpressionPathOptions &DoAllowFragileIVar() {
-      m_no_fragile_ivar = false;
-      return *this;
-    }
-
-    GetValueForExpressionPathOptions &DontAllowFragileIVar() {
-      m_no_fragile_ivar = true;
-      return *this;
-    }
-
-    GetValueForExpressionPathOptions &DoAllowBitfieldSyntax() {
-      m_allow_bitfields_syntax = true;
-      return *this;
-    }
-
-    GetValueForExpressionPathOptions &DontAllowBitfieldSyntax() {
-      m_allow_bitfields_syntax = false;
-      return *this;
-    }
-
-    GetValueForExpressionPathOptions &
-    SetSyntheticChildrenTraversal(SyntheticChildrenTraversal traverse) {
-      m_synthetic_children_traversal = traverse;
-      return *this;
-    }
-
-    static const GetValueForExpressionPathOptions DefaultOptions() {
-      static GetValueForExpressionPathOptions g_default_options;
-
-      return g_default_options;
-    }
-  };
-
   class EvaluationPoint {
   public:
     EvaluationPoint();
@@ -395,17 +268,18 @@
 
   bool IsIntegerType(bool &is_signed);
 
-  virtual void GetExpressionPath(
-      Stream &s,
-      GetExpressionPathFormat = eGetExpressionPathFormatDereferencePointers);
+  virtual void
+  GetExpressionPath(Stream &s,
+                    ExpressionPath::PathFormat =
+                        ExpressionPath::PathFormat::DereferencePointers);
 
   lldb::ValueObjectSP GetValueForExpressionPath(
       llvm::StringRef expression,
-      ExpressionPathScanEndReason *reason_to_stop = nullptr,
-      ExpressionPathEndResultType *final_value_type = nullptr,
-      const GetValueForExpressionPathOptions &options =
-          GetValueForExpressionPathOptions::DefaultOptions(),
-      ExpressionPathAftermath *final_task_on_target = nullptr);
+      ExpressionPath::ScanEndReason *reason_to_stop = nullptr,
+      ExpressionPath::EndResultType *final_value_type = nullptr,
+      const ExpressionPath::GetValueOptions &options =
+          ExpressionPath::GetValueOptions::DefaultOptions(),
+      ExpressionPath::Aftermath *final_task_on_target = nullptr);
 
   virtual bool IsInScope() { return true; }
 
@@ -988,13 +862,6 @@
     GetRoot()->DoUpdateChildrenAddressType(*this);
   }
 
-  lldb::ValueObjectSP GetValueForExpressionPath_Impl(
-      llvm::StringRef expression_cstr,
-      ExpressionPathScanEndReason *reason_to_stop,
-      ExpressionPathEndResultType *final_value_type,
-      const GetValueForExpressionPathOptions &options,
-      ExpressionPathAftermath *final_task_on_target);
-
   ValueObject(const ValueObject &) = delete;
   const ValueObject &operator=(const ValueObject &) = delete;
 };
Index: lldb/include/lldb/Core/ExpressionPath.h
===================================================================
--- /dev/null
+++ lldb/include/lldb/Core/ExpressionPath.h
@@ -0,0 +1,160 @@
+//===-- ExpressionPath.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_EXPRESSIONPATH_H
+#define LLDB_CORE_EXPRESSIONPATH_H
+
+#include "lldb/lldb-defines.h"
+#include "lldb/lldb-enumerations.h"
+#include "lldb/lldb-forward.h"
+#include "lldb/lldb-private-enumerations.h"
+#include "lldb/lldb-types.h"
+
+namespace lldb_private {
+
+/// Responsible for evaluating LLDB's expression paths which is the
+/// domain-specific language of 'frame var' or SBValue::GetExpressionPath.
+class ExpressionPath {
+  // Right now the parser has no state so there is no need to construct one.
+  ExpressionPath() = default;
+
+public:
+  enum class PathFormat { DereferencePointers = 1, HonorPointers };
+
+  enum class ScanEndReason {
+    /// Out of data to parse.
+    EndOfString = 1,
+    /// Child element not found.
+    NoSuchChild,
+    /// (Synthetic) child  element not found.
+    NoSuchSyntheticChild,
+    /// [] only allowed for arrays.
+    EmptyRangeNotAllowed,
+    /// . used when -> should be used.
+    DotInsteadOfArrow,
+    /// -> used when . should be used.
+    ArrowInsteadOfDot,
+    /// ObjC ivar expansion not allowed.
+    FragileIVarNotAllowed,
+    /// [] not allowed by options.
+    RangeOperatorNotAllowed,
+    /// [] not valid on objects  other than scalars, pointers or arrays.
+    RangeOperatorInvalid,
+    /// [] is good for arrays,  but I cannot parse it.
+    ArrayRangeOperatorMet,
+    /// [] is good for bitfields, but I cannot parse after it.
+    BitfieldRangeOperatorMet,
+    /// Something is malformed in he expression.
+    UnexpectedSymbol,
+    /// Impossible to apply &  operator.
+    TakingAddressFailed,
+    /// Impossible to apply *  operator.
+    DereferencingFailed,
+    /// [] was expanded into a  VOList.
+    RangeOperatorExpanded,
+    /// getting the synthetic children failed.
+    SyntheticValueMissing,
+    Unknown = 0xFFFF
+  };
+
+  enum class EndResultType {
+    /// Anything but...
+    Plain = 1,
+    /// A bitfield.
+    Bitfield,
+    /// A range [low-high].
+    BoundedRange,
+    /// A range [].
+    UnboundedRange,
+    /// Several items in a VOList.
+    ValueObjectList,
+    Invalid = 0xFFFF
+  };
+
+  enum class Aftermath {
+    /// Just return it.
+    Nothing = 1,
+    /// Dereference the target.
+    Dereference,
+    /// Take target's address.
+    TakeAddress
+  };
+
+  struct GetValueOptions {
+    enum class SyntheticChildrenTraversal {
+      None,
+      ToSynthetic,
+      FromSynthetic,
+      Both
+    };
+
+    bool m_check_dot_vs_arrow_syntax;
+    bool m_no_fragile_ivar;
+    bool m_allow_bitfields_syntax;
+    SyntheticChildrenTraversal m_synthetic_children_traversal;
+
+    GetValueOptions(bool dot = false, bool no_ivar = false,
+                    bool bitfield = true,
+                    SyntheticChildrenTraversal synth_traverse =
+                        SyntheticChildrenTraversal::ToSynthetic)
+        : m_check_dot_vs_arrow_syntax(dot), m_no_fragile_ivar(no_ivar),
+          m_allow_bitfields_syntax(bitfield),
+          m_synthetic_children_traversal(synth_traverse) {}
+
+    GetValueOptions &DoCheckDotVsArrowSyntax() {
+      m_check_dot_vs_arrow_syntax = true;
+      return *this;
+    }
+
+    GetValueOptions &DontCheckDotVsArrowSyntax() {
+      m_check_dot_vs_arrow_syntax = false;
+      return *this;
+    }
+
+    GetValueOptions &DoAllowFragileIVar() {
+      m_no_fragile_ivar = false;
+      return *this;
+    }
+
+    GetValueOptions &DontAllowFragileIVar() {
+      m_no_fragile_ivar = true;
+      return *this;
+    }
+
+    GetValueOptions &DoAllowBitfieldSyntax() {
+      m_allow_bitfields_syntax = true;
+      return *this;
+    }
+
+    GetValueOptions &DontAllowBitfieldSyntax() {
+      m_allow_bitfields_syntax = false;
+      return *this;
+    }
+
+    GetValueOptions &
+    SetSyntheticChildrenTraversal(SyntheticChildrenTraversal traverse) {
+      m_synthetic_children_traversal = traverse;
+      return *this;
+    }
+
+    static const GetValueOptions DefaultOptions() {
+      static GetValueOptions g_default_options;
+
+      return g_default_options;
+    }
+  };
+
+  static lldb::ValueObjectSP
+  Parse(lldb::ValueObjectSP root, llvm::StringRef expression,
+        ScanEndReason *reason_to_stop, EndResultType *final_result,
+        const GetValueOptions &options, Aftermath *what_next);
+};
+
+} // namespace lldb_private
+
+#endif // LLDB_CORE_VALUEOBJECT_H
_______________________________________________
lldb-commits mailing list
lldb-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits

Reply via email to