[llvm] 889dad7 - [ItaniumDemangle] Add customizable printLeft/printRight APIs to OutputBuffer (#133249)

via llvm-commits llvm-commits at lists.llvm.org
Thu Apr 17 13:53:36 PDT 2025


Author: Michael Buch
Date: 2025-04-17T21:53:32+01:00
New Revision: 889dad7f40932ea68c9e287e62441507f4f0f261

URL: https://github.com/llvm/llvm-project/commit/889dad7f40932ea68c9e287e62441507f4f0f261
DIFF: https://github.com/llvm/llvm-project/commit/889dad7f40932ea68c9e287e62441507f4f0f261.diff

LOG: [ItaniumDemangle] Add customizable printLeft/printRight APIs to OutputBuffer (#133249)

This patch includes the necessary changes for the LLDB feature proposed
in
https://discourse.llvm.org/t/rfc-lldb-highlighting-function-names-in-lldb-backtraces/85309.
The TL;DR is that we want to track where certain parts of a demangled
name begin/end so we can highlight them in backtraces.

We introduce a new `printLeft`/`printRight` API on `OutputBuffer` that a
client (in our case LLDB) can implement to track state while printing
the demangle tree. This requires redirecting all calls to to
`printLeft`/`printRight` to the `OutputBuffer`. One quirk with the new
API is that `Utility.h` would now depend on `ItaniumDemangle.h` and
vice-versa. To keep these files header-only I made the definitions
`inline` and implement the new APIs in `ItaniumDemangle.h` (so the
definition of `Node` is available to them).

Also introduces `notifyInsertion`/`notifyDeletion` APIs that a client can override to respond to cases where the `OutputBuffer` changes arbitrary parts of the name.

Added: 
    

Modified: 
    libcxxabi/src/demangle/ItaniumDemangle.h
    libcxxabi/src/demangle/Utility.h
    llvm/include/llvm/Demangle/ItaniumDemangle.h
    llvm/include/llvm/Demangle/Utility.h
    llvm/unittests/Demangle/ItaniumDemangleTest.cpp
    llvm/unittests/Demangle/OutputBufferTest.cpp

Removed: 
    


################################################################################
diff  --git a/libcxxabi/src/demangle/ItaniumDemangle.h b/libcxxabi/src/demangle/ItaniumDemangle.h
index eca9ddad66f92..69932e63669bf 100644
--- a/libcxxabi/src/demangle/ItaniumDemangle.h
+++ b/libcxxabi/src/demangle/ItaniumDemangle.h
@@ -283,20 +283,11 @@ class Node {
   }
 
   void print(OutputBuffer &OB) const {
-    printLeft(OB);
+    OB.printLeft(*this);
     if (RHSComponentCache != Cache::No)
-      printRight(OB);
+      OB.printRight(*this);
   }
 
-  // Print the "left" side of this Node into OutputBuffer.
-  virtual void printLeft(OutputBuffer &) const = 0;
-
-  // Print the "right". This distinction is necessary to represent C++ types
-  // that appear on the RHS of their subtype, such as arrays or functions.
-  // Since most types don't have such a component, provide a default
-  // implementation.
-  virtual void printRight(OutputBuffer &) const {}
-
   // Print an initializer list of this type. Returns true if we printed a custom
   // representation, false if nothing has been printed and the default
   // representation should be used.
@@ -312,6 +303,24 @@ class Node {
 #ifndef NDEBUG
   DEMANGLE_DUMP_METHOD void dump() const;
 #endif
+
+private:
+  friend class OutputBuffer;
+
+  // Print the "left" side of this Node into OutputBuffer.
+  //
+  // Note, should only be called from OutputBuffer implementations.
+  // Call \ref OutputBuffer::printLeft instead.
+  virtual void printLeft(OutputBuffer &) const = 0;
+
+  // Print the "right". This distinction is necessary to represent C++ types
+  // that appear on the RHS of their subtype, such as arrays or functions.
+  // Since most types don't have such a component, provide a default
+  // implementation.
+  //
+  // Note, should only be called from OutputBuffer implementations.
+  // Call \ref OutputBuffer::printRight instead.
+  virtual void printRight(OutputBuffer &) const {}
 };
 
 class NodeArray {
@@ -460,11 +469,11 @@ class QualType final : public Node {
   }
 
   void printLeft(OutputBuffer &OB) const override {
-    Child->printLeft(OB);
+    OB.printLeft(*Child);
     printQuals(OB);
   }
 
-  void printRight(OutputBuffer &OB) const override { Child->printRight(OB); }
+  void printRight(OutputBuffer &OB) const override { OB.printRight(*Child); }
 };
 
 class ConversionOperatorType final : public Node {
@@ -493,7 +502,7 @@ class PostfixQualifiedType final : public Node {
   template<typename Fn> void match(Fn F) const { F(Ty, Postfix); }
 
   void printLeft(OutputBuffer &OB) const override {
-    Ty->printLeft(OB);
+    OB.printLeft(*Ty);
     OB += Postfix;
   }
 };
@@ -579,7 +588,7 @@ struct AbiTagAttr : Node {
   std::string_view getBaseName() const override { return Base->getBaseName(); }
 
   void printLeft(OutputBuffer &OB) const override {
-    Base->printLeft(OB);
+    OB.printLeft(*Base);
     OB += "[abi:";
     OB += Tag;
     OB += "]";
@@ -646,7 +655,7 @@ class PointerType final : public Node {
     // We rewrite objc_object<SomeProtocol>* into id<SomeProtocol>.
     if (Pointee->getKind() != KObjCProtoName ||
         !static_cast<const ObjCProtoName *>(Pointee)->isObjCObject()) {
-      Pointee->printLeft(OB);
+      OB.printLeft(*Pointee);
       if (Pointee->hasArray(OB))
         OB += " ";
       if (Pointee->hasArray(OB) || Pointee->hasFunction(OB))
@@ -665,7 +674,7 @@ class PointerType final : public Node {
         !static_cast<const ObjCProtoName *>(Pointee)->isObjCObject()) {
       if (Pointee->hasArray(OB) || Pointee->hasFunction(OB))
         OB += ")";
-      Pointee->printRight(OB);
+      OB.printRight(*Pointee);
     }
   }
 };
@@ -731,7 +740,7 @@ class ReferenceType : public Node {
     std::pair<ReferenceKind, const Node *> Collapsed = collapse(OB);
     if (!Collapsed.second)
       return;
-    Collapsed.second->printLeft(OB);
+    OB.printLeft(*Collapsed.second);
     if (Collapsed.second->hasArray(OB))
       OB += " ";
     if (Collapsed.second->hasArray(OB) || Collapsed.second->hasFunction(OB))
@@ -748,7 +757,7 @@ class ReferenceType : public Node {
       return;
     if (Collapsed.second->hasArray(OB) || Collapsed.second->hasFunction(OB))
       OB += ")";
-    Collapsed.second->printRight(OB);
+    OB.printRight(*Collapsed.second);
   }
 };
 
@@ -768,7 +777,7 @@ class PointerToMemberType final : public Node {
   }
 
   void printLeft(OutputBuffer &OB) const override {
-    MemberType->printLeft(OB);
+    OB.printLeft(*MemberType);
     if (MemberType->hasArray(OB) || MemberType->hasFunction(OB))
       OB += "(";
     else
@@ -780,7 +789,7 @@ class PointerToMemberType final : public Node {
   void printRight(OutputBuffer &OB) const override {
     if (MemberType->hasArray(OB) || MemberType->hasFunction(OB))
       OB += ")";
-    MemberType->printRight(OB);
+    OB.printRight(*MemberType);
   }
 };
 
@@ -800,7 +809,7 @@ class ArrayType final : public Node {
   bool hasRHSComponentSlow(OutputBuffer &) const override { return true; }
   bool hasArraySlow(OutputBuffer &) const override { return true; }
 
-  void printLeft(OutputBuffer &OB) const override { Base->printLeft(OB); }
+  void printLeft(OutputBuffer &OB) const override { OB.printLeft(*Base); }
 
   void printRight(OutputBuffer &OB) const override {
     if (OB.back() != ']')
@@ -809,7 +818,7 @@ class ArrayType final : public Node {
     if (Dimension)
       Dimension->print(OB);
     OB += "]";
-    Base->printRight(OB);
+    OB.printRight(*Base);
   }
 
   bool printInitListAsType(OutputBuffer &OB,
@@ -853,7 +862,7 @@ class FunctionType final : public Node {
   // by printing out the return types's left, then print our parameters, then
   // finally print right of the return type.
   void printLeft(OutputBuffer &OB) const override {
-    Ret->printLeft(OB);
+    OB.printLeft(*Ret);
     OB += " ";
   }
 
@@ -861,7 +870,7 @@ class FunctionType final : public Node {
     OB.printOpen();
     Params.printWithComma(OB);
     OB.printClose();
-    Ret->printRight(OB);
+    OB.printRight(*Ret);
 
     if (CVQuals & QualConst)
       OB += " const";
@@ -966,6 +975,8 @@ class FunctionEncoding final : public Node {
   FunctionRefQual getRefQual() const { return RefQual; }
   NodeArray getParams() const { return Params; }
   const Node *getReturnType() const { return Ret; }
+  const Node *getAttrs() const { return Attrs; }
+  const Node *getRequires() const { return Requires; }
 
   bool hasRHSComponentSlow(OutputBuffer &) const override { return true; }
   bool hasFunctionSlow(OutputBuffer &) const override { return true; }
@@ -974,10 +985,11 @@ class FunctionEncoding final : public Node {
 
   void printLeft(OutputBuffer &OB) const override {
     if (Ret) {
-      Ret->printLeft(OB);
+      OB.printLeft(*Ret);
       if (!Ret->hasRHSComponent(OB))
         OB += " ";
     }
+
     Name->print(OB);
   }
 
@@ -985,8 +997,9 @@ class FunctionEncoding final : public Node {
     OB.printOpen();
     Params.printWithComma(OB);
     OB.printClose();
+
     if (Ret)
-      Ret->printRight(OB);
+      OB.printRight(*Ret);
 
     if (CVQuals & QualConst)
       OB += " const";
@@ -1326,14 +1339,14 @@ class NonTypeTemplateParamDecl final : public Node {
   template<typename Fn> void match(Fn F) const { F(Name, Type); }
 
   void printLeft(OutputBuffer &OB) const override {
-    Type->printLeft(OB);
+    OB.printLeft(*Type);
     if (!Type->hasRHSComponent(OB))
       OB += " ";
   }
 
   void printRight(OutputBuffer &OB) const override {
     Name->print(OB);
-    Type->printRight(OB);
+    OB.printRight(*Type);
   }
 };
 
@@ -1378,11 +1391,11 @@ class TemplateParamPackDecl final : public Node {
   template<typename Fn> void match(Fn F) const { F(Param); }
 
   void printLeft(OutputBuffer &OB) const override {
-    Param->printLeft(OB);
+    OB.printLeft(*Param);
     OB += "...";
   }
 
-  void printRight(OutputBuffer &OB) const override { Param->printRight(OB); }
+  void printRight(OutputBuffer &OB) const override { OB.printRight(*Param); }
 };
 
 /// An unexpanded parameter pack (either in the expression or type context). If
@@ -1447,13 +1460,13 @@ class ParameterPack final : public Node {
     initializePackExpansion(OB);
     size_t Idx = OB.CurrentPackIndex;
     if (Idx < Data.size())
-      Data[Idx]->printLeft(OB);
+      OB.printLeft(*Data[Idx]);
   }
   void printRight(OutputBuffer &OB) const override {
     initializePackExpansion(OB);
     size_t Idx = OB.CurrentPackIndex;
     if (Idx < Data.size())
-      Data[Idx]->printRight(OB);
+      OB.printRight(*Data[Idx]);
   }
 };
 
@@ -1611,13 +1624,13 @@ struct ForwardTemplateReference : Node {
     if (Printing)
       return;
     ScopedOverride<bool> SavePrinting(Printing, true);
-    Ref->printLeft(OB);
+    OB.printLeft(*Ref);
   }
   void printRight(OutputBuffer &OB) const override {
     if (Printing)
       return;
     ScopedOverride<bool> SavePrinting(Printing, true);
-    Ref->printRight(OB);
+    OB.printRight(*Ref);
   }
 };
 
@@ -1769,7 +1782,7 @@ class DtorName : public Node {
 
   void printLeft(OutputBuffer &OB) const override {
     OB += "~";
-    Base->printLeft(OB);
+    OB.printLeft(*Base);
   }
 };
 
@@ -2049,7 +2062,7 @@ class CastExpr : public Node {
     {
       ScopedOverride<unsigned> LT(OB.GtIsGt, 0);
       OB += "<";
-      To->printLeft(OB);
+      OB.printLeft(*To);
       OB += ">";
     }
     OB.printOpen();
@@ -6180,6 +6193,10 @@ struct ManglingParser : AbstractManglingParser<ManglingParser<Alloc>, Alloc> {
                                Alloc>::AbstractManglingParser;
 };
 
+inline void OutputBuffer::printLeft(const Node &N) { N.printLeft(*this); }
+
+inline void OutputBuffer::printRight(const Node &N) { N.printRight(*this); }
+
 DEMANGLE_NAMESPACE_END
 
 #if defined(__clang__)

diff  --git a/libcxxabi/src/demangle/Utility.h b/libcxxabi/src/demangle/Utility.h
index f1fad35d60d98..511983ad40f7a 100644
--- a/libcxxabi/src/demangle/Utility.h
+++ b/libcxxabi/src/demangle/Utility.h
@@ -27,6 +27,8 @@
 
 DEMANGLE_NAMESPACE_BEGIN
 
+class Node;
+
 // Stream that AST nodes write their string representation into after the AST
 // has been parsed.
 class OutputBuffer {
@@ -79,10 +81,24 @@ class OutputBuffer {
   OutputBuffer(const OutputBuffer &) = delete;
   OutputBuffer &operator=(const OutputBuffer &) = delete;
 
+  virtual ~OutputBuffer() {}
+
   operator std::string_view() const {
     return std::string_view(Buffer, CurrentPosition);
   }
 
+  /// Called by the demangler when printing the demangle tree. By
+  /// default calls into \c Node::print{Left|Right} but can be overriden
+  /// by clients to track additional state when printing the demangled name.
+  virtual void printLeft(const Node &N);
+  virtual void printRight(const Node &N);
+
+  /// Called when we write to this object anywhere other than the end.
+  virtual void notifyInsertion(size_t /*Position*/, size_t /*Count*/) {}
+
+  /// Called when we make the \c CurrentPosition of this object smaller.
+  virtual void notifyDeletion(size_t /*OldPos*/, size_t /*NewPos*/) {}
+
   /// If a ParameterPackExpansion (or similar type) is encountered, the offset
   /// into the pack that we're currently printing.
   unsigned CurrentPackIndex = std::numeric_limits<unsigned>::max();
@@ -126,6 +142,8 @@ class OutputBuffer {
     std::memcpy(Buffer, &*R.begin(), Size);
     CurrentPosition += Size;
 
+    notifyInsertion(/*Position=*/0, /*Count=*/Size);
+
     return *this;
   }
 
@@ -161,14 +179,20 @@ class OutputBuffer {
     DEMANGLE_ASSERT(Pos <= CurrentPosition, "");
     if (N == 0)
       return;
+
     grow(N);
     std::memmove(Buffer + Pos + N, Buffer + Pos, CurrentPosition - Pos);
     std::memcpy(Buffer + Pos, S, N);
     CurrentPosition += N;
+
+    notifyInsertion(Pos, N);
   }
 
   size_t getCurrentPosition() const { return CurrentPosition; }
-  void setCurrentPosition(size_t NewPos) { CurrentPosition = NewPos; }
+  void setCurrentPosition(size_t NewPos) {
+    notifyDeletion(CurrentPosition, NewPos);
+    CurrentPosition = NewPos;
+  }
 
   char back() const {
     DEMANGLE_ASSERT(CurrentPosition, "");

diff  --git a/llvm/include/llvm/Demangle/ItaniumDemangle.h b/llvm/include/llvm/Demangle/ItaniumDemangle.h
index 28fdfc5eff1b6..b4a4c72021fd1 100644
--- a/llvm/include/llvm/Demangle/ItaniumDemangle.h
+++ b/llvm/include/llvm/Demangle/ItaniumDemangle.h
@@ -283,20 +283,11 @@ class Node {
   }
 
   void print(OutputBuffer &OB) const {
-    printLeft(OB);
+    OB.printLeft(*this);
     if (RHSComponentCache != Cache::No)
-      printRight(OB);
+      OB.printRight(*this);
   }
 
-  // Print the "left" side of this Node into OutputBuffer.
-  virtual void printLeft(OutputBuffer &) const = 0;
-
-  // Print the "right". This distinction is necessary to represent C++ types
-  // that appear on the RHS of their subtype, such as arrays or functions.
-  // Since most types don't have such a component, provide a default
-  // implementation.
-  virtual void printRight(OutputBuffer &) const {}
-
   // Print an initializer list of this type. Returns true if we printed a custom
   // representation, false if nothing has been printed and the default
   // representation should be used.
@@ -312,6 +303,24 @@ class Node {
 #ifndef NDEBUG
   DEMANGLE_DUMP_METHOD void dump() const;
 #endif
+
+private:
+  friend class OutputBuffer;
+
+  // Print the "left" side of this Node into OutputBuffer.
+  //
+  // Note, should only be called from OutputBuffer implementations.
+  // Call \ref OutputBuffer::printLeft instead.
+  virtual void printLeft(OutputBuffer &) const = 0;
+
+  // Print the "right". This distinction is necessary to represent C++ types
+  // that appear on the RHS of their subtype, such as arrays or functions.
+  // Since most types don't have such a component, provide a default
+  // implementation.
+  //
+  // Note, should only be called from OutputBuffer implementations.
+  // Call \ref OutputBuffer::printRight instead.
+  virtual void printRight(OutputBuffer &) const {}
 };
 
 class NodeArray {
@@ -460,11 +469,11 @@ class QualType final : public Node {
   }
 
   void printLeft(OutputBuffer &OB) const override {
-    Child->printLeft(OB);
+    OB.printLeft(*Child);
     printQuals(OB);
   }
 
-  void printRight(OutputBuffer &OB) const override { Child->printRight(OB); }
+  void printRight(OutputBuffer &OB) const override { OB.printRight(*Child); }
 };
 
 class ConversionOperatorType final : public Node {
@@ -493,7 +502,7 @@ class PostfixQualifiedType final : public Node {
   template<typename Fn> void match(Fn F) const { F(Ty, Postfix); }
 
   void printLeft(OutputBuffer &OB) const override {
-    Ty->printLeft(OB);
+    OB.printLeft(*Ty);
     OB += Postfix;
   }
 };
@@ -579,7 +588,7 @@ struct AbiTagAttr : Node {
   std::string_view getBaseName() const override { return Base->getBaseName(); }
 
   void printLeft(OutputBuffer &OB) const override {
-    Base->printLeft(OB);
+    OB.printLeft(*Base);
     OB += "[abi:";
     OB += Tag;
     OB += "]";
@@ -646,7 +655,7 @@ class PointerType final : public Node {
     // We rewrite objc_object<SomeProtocol>* into id<SomeProtocol>.
     if (Pointee->getKind() != KObjCProtoName ||
         !static_cast<const ObjCProtoName *>(Pointee)->isObjCObject()) {
-      Pointee->printLeft(OB);
+      OB.printLeft(*Pointee);
       if (Pointee->hasArray(OB))
         OB += " ";
       if (Pointee->hasArray(OB) || Pointee->hasFunction(OB))
@@ -665,7 +674,7 @@ class PointerType final : public Node {
         !static_cast<const ObjCProtoName *>(Pointee)->isObjCObject()) {
       if (Pointee->hasArray(OB) || Pointee->hasFunction(OB))
         OB += ")";
-      Pointee->printRight(OB);
+      OB.printRight(*Pointee);
     }
   }
 };
@@ -731,7 +740,7 @@ class ReferenceType : public Node {
     std::pair<ReferenceKind, const Node *> Collapsed = collapse(OB);
     if (!Collapsed.second)
       return;
-    Collapsed.second->printLeft(OB);
+    OB.printLeft(*Collapsed.second);
     if (Collapsed.second->hasArray(OB))
       OB += " ";
     if (Collapsed.second->hasArray(OB) || Collapsed.second->hasFunction(OB))
@@ -748,7 +757,7 @@ class ReferenceType : public Node {
       return;
     if (Collapsed.second->hasArray(OB) || Collapsed.second->hasFunction(OB))
       OB += ")";
-    Collapsed.second->printRight(OB);
+    OB.printRight(*Collapsed.second);
   }
 };
 
@@ -768,7 +777,7 @@ class PointerToMemberType final : public Node {
   }
 
   void printLeft(OutputBuffer &OB) const override {
-    MemberType->printLeft(OB);
+    OB.printLeft(*MemberType);
     if (MemberType->hasArray(OB) || MemberType->hasFunction(OB))
       OB += "(";
     else
@@ -780,7 +789,7 @@ class PointerToMemberType final : public Node {
   void printRight(OutputBuffer &OB) const override {
     if (MemberType->hasArray(OB) || MemberType->hasFunction(OB))
       OB += ")";
-    MemberType->printRight(OB);
+    OB.printRight(*MemberType);
   }
 };
 
@@ -800,7 +809,7 @@ class ArrayType final : public Node {
   bool hasRHSComponentSlow(OutputBuffer &) const override { return true; }
   bool hasArraySlow(OutputBuffer &) const override { return true; }
 
-  void printLeft(OutputBuffer &OB) const override { Base->printLeft(OB); }
+  void printLeft(OutputBuffer &OB) const override { OB.printLeft(*Base); }
 
   void printRight(OutputBuffer &OB) const override {
     if (OB.back() != ']')
@@ -809,7 +818,7 @@ class ArrayType final : public Node {
     if (Dimension)
       Dimension->print(OB);
     OB += "]";
-    Base->printRight(OB);
+    OB.printRight(*Base);
   }
 
   bool printInitListAsType(OutputBuffer &OB,
@@ -853,7 +862,7 @@ class FunctionType final : public Node {
   // by printing out the return types's left, then print our parameters, then
   // finally print right of the return type.
   void printLeft(OutputBuffer &OB) const override {
-    Ret->printLeft(OB);
+    OB.printLeft(*Ret);
     OB += " ";
   }
 
@@ -861,7 +870,7 @@ class FunctionType final : public Node {
     OB.printOpen();
     Params.printWithComma(OB);
     OB.printClose();
-    Ret->printRight(OB);
+    OB.printRight(*Ret);
 
     if (CVQuals & QualConst)
       OB += " const";
@@ -966,6 +975,8 @@ class FunctionEncoding final : public Node {
   FunctionRefQual getRefQual() const { return RefQual; }
   NodeArray getParams() const { return Params; }
   const Node *getReturnType() const { return Ret; }
+  const Node *getAttrs() const { return Attrs; }
+  const Node *getRequires() const { return Requires; }
 
   bool hasRHSComponentSlow(OutputBuffer &) const override { return true; }
   bool hasFunctionSlow(OutputBuffer &) const override { return true; }
@@ -974,10 +985,11 @@ class FunctionEncoding final : public Node {
 
   void printLeft(OutputBuffer &OB) const override {
     if (Ret) {
-      Ret->printLeft(OB);
+      OB.printLeft(*Ret);
       if (!Ret->hasRHSComponent(OB))
         OB += " ";
     }
+
     Name->print(OB);
   }
 
@@ -985,8 +997,9 @@ class FunctionEncoding final : public Node {
     OB.printOpen();
     Params.printWithComma(OB);
     OB.printClose();
+
     if (Ret)
-      Ret->printRight(OB);
+      OB.printRight(*Ret);
 
     if (CVQuals & QualConst)
       OB += " const";
@@ -1326,14 +1339,14 @@ class NonTypeTemplateParamDecl final : public Node {
   template<typename Fn> void match(Fn F) const { F(Name, Type); }
 
   void printLeft(OutputBuffer &OB) const override {
-    Type->printLeft(OB);
+    OB.printLeft(*Type);
     if (!Type->hasRHSComponent(OB))
       OB += " ";
   }
 
   void printRight(OutputBuffer &OB) const override {
     Name->print(OB);
-    Type->printRight(OB);
+    OB.printRight(*Type);
   }
 };
 
@@ -1378,11 +1391,11 @@ class TemplateParamPackDecl final : public Node {
   template<typename Fn> void match(Fn F) const { F(Param); }
 
   void printLeft(OutputBuffer &OB) const override {
-    Param->printLeft(OB);
+    OB.printLeft(*Param);
     OB += "...";
   }
 
-  void printRight(OutputBuffer &OB) const override { Param->printRight(OB); }
+  void printRight(OutputBuffer &OB) const override { OB.printRight(*Param); }
 };
 
 /// An unexpanded parameter pack (either in the expression or type context). If
@@ -1447,13 +1460,13 @@ class ParameterPack final : public Node {
     initializePackExpansion(OB);
     size_t Idx = OB.CurrentPackIndex;
     if (Idx < Data.size())
-      Data[Idx]->printLeft(OB);
+      OB.printLeft(*Data[Idx]);
   }
   void printRight(OutputBuffer &OB) const override {
     initializePackExpansion(OB);
     size_t Idx = OB.CurrentPackIndex;
     if (Idx < Data.size())
-      Data[Idx]->printRight(OB);
+      OB.printRight(*Data[Idx]);
   }
 };
 
@@ -1611,13 +1624,13 @@ struct ForwardTemplateReference : Node {
     if (Printing)
       return;
     ScopedOverride<bool> SavePrinting(Printing, true);
-    Ref->printLeft(OB);
+    OB.printLeft(*Ref);
   }
   void printRight(OutputBuffer &OB) const override {
     if (Printing)
       return;
     ScopedOverride<bool> SavePrinting(Printing, true);
-    Ref->printRight(OB);
+    OB.printRight(*Ref);
   }
 };
 
@@ -1769,7 +1782,7 @@ class DtorName : public Node {
 
   void printLeft(OutputBuffer &OB) const override {
     OB += "~";
-    Base->printLeft(OB);
+    OB.printLeft(*Base);
   }
 };
 
@@ -2049,7 +2062,7 @@ class CastExpr : public Node {
     {
       ScopedOverride<unsigned> LT(OB.GtIsGt, 0);
       OB += "<";
-      To->printLeft(OB);
+      OB.printLeft(*To);
       OB += ">";
     }
     OB.printOpen();
@@ -6180,6 +6193,10 @@ struct ManglingParser : AbstractManglingParser<ManglingParser<Alloc>, Alloc> {
                                Alloc>::AbstractManglingParser;
 };
 
+inline void OutputBuffer::printLeft(const Node &N) { N.printLeft(*this); }
+
+inline void OutputBuffer::printRight(const Node &N) { N.printRight(*this); }
+
 DEMANGLE_NAMESPACE_END
 
 #if defined(__clang__)

diff  --git a/llvm/include/llvm/Demangle/Utility.h b/llvm/include/llvm/Demangle/Utility.h
index e893cceea2cdc..d59d74511dd4f 100644
--- a/llvm/include/llvm/Demangle/Utility.h
+++ b/llvm/include/llvm/Demangle/Utility.h
@@ -27,6 +27,8 @@
 
 DEMANGLE_NAMESPACE_BEGIN
 
+class Node;
+
 // Stream that AST nodes write their string representation into after the AST
 // has been parsed.
 class OutputBuffer {
@@ -79,10 +81,24 @@ class OutputBuffer {
   OutputBuffer(const OutputBuffer &) = delete;
   OutputBuffer &operator=(const OutputBuffer &) = delete;
 
+  virtual ~OutputBuffer() {}
+
   operator std::string_view() const {
     return std::string_view(Buffer, CurrentPosition);
   }
 
+  /// Called by the demangler when printing the demangle tree. By
+  /// default calls into \c Node::print{Left|Right} but can be overriden
+  /// by clients to track additional state when printing the demangled name.
+  virtual void printLeft(const Node &N);
+  virtual void printRight(const Node &N);
+
+  /// Called when we write to this object anywhere other than the end.
+  virtual void notifyInsertion(size_t /*Position*/, size_t /*Count*/) {}
+
+  /// Called when we make the \c CurrentPosition of this object smaller.
+  virtual void notifyDeletion(size_t /*OldPos*/, size_t /*NewPos*/) {}
+
   /// If a ParameterPackExpansion (or similar type) is encountered, the offset
   /// into the pack that we're currently printing.
   unsigned CurrentPackIndex = std::numeric_limits<unsigned>::max();
@@ -126,6 +142,8 @@ class OutputBuffer {
     std::memcpy(Buffer, &*R.begin(), Size);
     CurrentPosition += Size;
 
+    notifyInsertion(/*Position=*/0, /*Count=*/Size);
+
     return *this;
   }
 
@@ -161,14 +179,20 @@ class OutputBuffer {
     DEMANGLE_ASSERT(Pos <= CurrentPosition, "");
     if (N == 0)
       return;
+
     grow(N);
     std::memmove(Buffer + Pos + N, Buffer + Pos, CurrentPosition - Pos);
     std::memcpy(Buffer + Pos, S, N);
     CurrentPosition += N;
+
+    notifyInsertion(Pos, N);
   }
 
   size_t getCurrentPosition() const { return CurrentPosition; }
-  void setCurrentPosition(size_t NewPos) { CurrentPosition = NewPos; }
+  void setCurrentPosition(size_t NewPos) {
+    notifyDeletion(CurrentPosition, NewPos);
+    CurrentPosition = NewPos;
+  }
 
   char back() const {
     DEMANGLE_ASSERT(CurrentPosition, "");

diff  --git a/llvm/unittests/Demangle/ItaniumDemangleTest.cpp b/llvm/unittests/Demangle/ItaniumDemangleTest.cpp
index bc6ccc2e16e65..329f33215817a 100644
--- a/llvm/unittests/Demangle/ItaniumDemangleTest.cpp
+++ b/llvm/unittests/Demangle/ItaniumDemangleTest.cpp
@@ -98,7 +98,7 @@ TEST(ItaniumDemangle, HalfType) {
     Node *parseType() {
       OutputBuffer OB;
       Node *N = AbstractManglingParser<TestParser, TestAllocator>::parseType();
-      N->printLeft(OB);
+      OB.printLeft(*N);
       std::string_view Name = N->getBaseName();
       if (!Name.empty())
         Types.push_back(std::string(Name.begin(), Name.end()));

diff  --git a/llvm/unittests/Demangle/OutputBufferTest.cpp b/llvm/unittests/Demangle/OutputBufferTest.cpp
index 76031e523d781..4a30e66eee48e 100644
--- a/llvm/unittests/Demangle/OutputBufferTest.cpp
+++ b/llvm/unittests/Demangle/OutputBufferTest.cpp
@@ -93,3 +93,40 @@ TEST(OutputBufferTest, Extend) {
 
   std::free(OB.getBuffer());
 }
+
+TEST(OutputBufferTest, Notifications) {
+  struct MyOutputBuffer : public OutputBuffer {
+    size_t Inserted = 0;
+    size_t LatestPos = 0;
+
+    void notifyDeletion(size_t OldPos, size_t NewPos) override {
+      LatestPos = NewPos;
+    }
+
+    void notifyInsertion(size_t Position, size_t Count) override {
+      Inserted += Count;
+      LatestPos = Position;
+    }
+  } OB;
+
+  OB.prepend("n");
+  EXPECT_EQ(OB.Inserted, 1U);
+  EXPECT_EQ(OB.LatestPos, 0U);
+
+  OB.prepend("");
+  EXPECT_EQ(OB.Inserted, 1U);
+  EXPECT_EQ(OB.LatestPos, 0U);
+
+  OB.prepend("abc");
+  EXPECT_EQ(OB.Inserted, 4U);
+  EXPECT_EQ(OB.LatestPos, 0U);
+
+  OB.insert(2, "abc", 3U);
+  EXPECT_EQ(OB.Inserted, 7U);
+  EXPECT_EQ(OB.LatestPos, 2U);
+
+  OB.setCurrentPosition(3U);
+  EXPECT_EQ(OB.LatestPos, 3U);
+
+  std::free(OB.getBuffer());
+}


        


More information about the llvm-commits mailing list