summaryrefslogtreecommitdiffstats
path: root/src/cmd/vendor/github.com/ianlancetaylor
diff options
context:
space:
mode:
Diffstat (limited to 'src/cmd/vendor/github.com/ianlancetaylor')
-rw-r--r--src/cmd/vendor/github.com/ianlancetaylor/demangle/.gitignore13
-rw-r--r--src/cmd/vendor/github.com/ianlancetaylor/demangle/LICENSE27
-rw-r--r--src/cmd/vendor/github.com/ianlancetaylor/demangle/README.md3
-rw-r--r--src/cmd/vendor/github.com/ianlancetaylor/demangle/SECURITY.md13
-rw-r--r--src/cmd/vendor/github.com/ianlancetaylor/demangle/ast.go4142
-rw-r--r--src/cmd/vendor/github.com/ianlancetaylor/demangle/demangle.go3362
-rw-r--r--src/cmd/vendor/github.com/ianlancetaylor/demangle/rust.go1165
7 files changed, 8725 insertions, 0 deletions
diff --git a/src/cmd/vendor/github.com/ianlancetaylor/demangle/.gitignore b/src/cmd/vendor/github.com/ianlancetaylor/demangle/.gitignore
new file mode 100644
index 0000000..4a8b38f
--- /dev/null
+++ b/src/cmd/vendor/github.com/ianlancetaylor/demangle/.gitignore
@@ -0,0 +1,13 @@
+*.o
+*.a
+*.so
+._*
+.nfs.*
+a.out
+*~
+*.orig
+*.rej
+*.exe
+.*.swp
+core
+demangle.test
diff --git a/src/cmd/vendor/github.com/ianlancetaylor/demangle/LICENSE b/src/cmd/vendor/github.com/ianlancetaylor/demangle/LICENSE
new file mode 100644
index 0000000..d29b372
--- /dev/null
+++ b/src/cmd/vendor/github.com/ianlancetaylor/demangle/LICENSE
@@ -0,0 +1,27 @@
+Copyright (c) 2015 The Go Authors. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the following disclaimer
+in the documentation and/or other materials provided with the
+distribution.
+ * Neither the name of Google Inc. nor the names of its
+contributors may be used to endorse or promote products derived from
+this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/src/cmd/vendor/github.com/ianlancetaylor/demangle/README.md b/src/cmd/vendor/github.com/ianlancetaylor/demangle/README.md
new file mode 100644
index 0000000..2c01cae
--- /dev/null
+++ b/src/cmd/vendor/github.com/ianlancetaylor/demangle/README.md
@@ -0,0 +1,3 @@
+# github.com/ianlancetaylor/demangle
+
+A Go package that can be used to demangle C++ and Rust symbol names.
diff --git a/src/cmd/vendor/github.com/ianlancetaylor/demangle/SECURITY.md b/src/cmd/vendor/github.com/ianlancetaylor/demangle/SECURITY.md
new file mode 100644
index 0000000..f4edf9e
--- /dev/null
+++ b/src/cmd/vendor/github.com/ianlancetaylor/demangle/SECURITY.md
@@ -0,0 +1,13 @@
+# Security Policy
+
+## Supported Versions
+
+Security updates are applied only to the latest release.
+
+## Reporting a Vulnerability
+
+If you have discovered a security vulnerability in this project, please report it privately. **Do not disclose it as a public issue.** This gives us time to work with you to fix the issue before public exposure, reducing the chance that the exploit will be used before a patch is released.
+
+Please disclose it at [security advisory](https://github.com/ianlancetaylor/demangle/security/advisories/new).
+
+This project is maintained by volunteers on a reasonable-effort basis. As such, please give us at least 90 days to work on a fix before public exposure.
diff --git a/src/cmd/vendor/github.com/ianlancetaylor/demangle/ast.go b/src/cmd/vendor/github.com/ianlancetaylor/demangle/ast.go
new file mode 100644
index 0000000..cdc98c3
--- /dev/null
+++ b/src/cmd/vendor/github.com/ianlancetaylor/demangle/ast.go
@@ -0,0 +1,4142 @@
+// Copyright 2015 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package demangle
+
+import (
+ "fmt"
+ "strings"
+)
+
+// AST is an abstract syntax tree representing a C++ declaration.
+// This is sufficient for the demangler but is by no means a general C++ AST.
+// This abstract syntax tree is only used for C++ symbols, not Rust symbols.
+type AST interface {
+ // Internal method to convert to demangled string.
+ print(*printState)
+
+ // Traverse each element of an AST. If the function returns
+ // false, traversal of children of that element is skipped.
+ Traverse(func(AST) bool)
+
+ // Copy an AST with possible transformations.
+ // If the skip function returns true, no copy is required.
+ // If the copy function returns nil, no copy is required.
+ // The Copy method will do the right thing if copy returns nil
+ // for some components of an AST but not others, so a good
+ // copy function will only return non-nil for AST values that
+ // need to change.
+ // Copy itself returns either a copy or nil.
+ Copy(copy func(AST) AST, skip func(AST) bool) AST
+
+ // Implement the fmt.GoStringer interface.
+ GoString() string
+ goString(indent int, field string) string
+}
+
+// ASTToString returns the demangled name of the AST.
+func ASTToString(a AST, options ...Option) string {
+ tparams := true
+ enclosingParams := true
+ llvmStyle := false
+ max := 0
+ for _, o := range options {
+ switch {
+ case o == NoTemplateParams:
+ tparams = false
+ case o == NoEnclosingParams:
+ enclosingParams = false
+ case o == LLVMStyle:
+ llvmStyle = true
+ case isMaxLength(o):
+ max = maxLength(o)
+ }
+ }
+
+ ps := printState{
+ tparams: tparams,
+ enclosingParams: enclosingParams,
+ llvmStyle: llvmStyle,
+ max: max,
+ }
+ a.print(&ps)
+ s := ps.buf.String()
+ if max > 0 && len(s) > max {
+ s = s[:max]
+ }
+ return s
+}
+
+// The printState type holds information needed to print an AST.
+type printState struct {
+ tparams bool // whether to print template parameters
+ enclosingParams bool // whether to print enclosing parameters
+ llvmStyle bool
+ max int // maximum output length
+
+ buf strings.Builder
+ last byte // Last byte written to buffer.
+
+ // The inner field is a list of items to print for a type
+ // name. This is used by types to implement the inside-out
+ // C++ declaration syntax.
+ inner []AST
+
+ // The printing field is a list of items we are currently
+ // printing. This avoids endless recursion if a substitution
+ // reference creates a cycle in the graph.
+ printing []AST
+}
+
+// writeByte adds a byte to the string being printed.
+func (ps *printState) writeByte(b byte) {
+ ps.last = b
+ ps.buf.WriteByte(b)
+}
+
+// writeString adds a string to the string being printed.
+func (ps *printState) writeString(s string) {
+ if len(s) > 0 {
+ ps.last = s[len(s)-1]
+ }
+ ps.buf.WriteString(s)
+}
+
+// Print an AST.
+func (ps *printState) print(a AST) {
+ if ps.max > 0 && ps.buf.Len() > ps.max {
+ return
+ }
+
+ c := 0
+ for _, v := range ps.printing {
+ if v == a {
+ // We permit the type to appear once, and
+ // return without printing anything if we see
+ // it twice. This is for a case like
+ // _Z6outer2IsEPFilES1_, where the
+ // substitution is printed differently the
+ // second time because the set of inner types
+ // is different.
+ c++
+ if c > 1 {
+ return
+ }
+ }
+ }
+ ps.printing = append(ps.printing, a)
+
+ a.print(ps)
+
+ ps.printing = ps.printing[:len(ps.printing)-1]
+}
+
+// Name is an unqualified name.
+type Name struct {
+ Name string
+}
+
+func (n *Name) print(ps *printState) {
+ ps.writeString(n.Name)
+}
+
+func (n *Name) Traverse(fn func(AST) bool) {
+ fn(n)
+}
+
+func (n *Name) Copy(fn func(AST) AST, skip func(AST) bool) AST {
+ if skip(n) {
+ return nil
+ }
+ return fn(n)
+}
+
+func (n *Name) GoString() string {
+ return n.goString(0, "Name: ")
+}
+
+func (n *Name) goString(indent int, field string) string {
+ return fmt.Sprintf("%*s%s%s", indent, "", field, n.Name)
+}
+
+// Typed is a typed name.
+type Typed struct {
+ Name AST
+ Type AST
+}
+
+func (t *Typed) print(ps *printState) {
+ // We are printing a typed name, so ignore the current set of
+ // inner names to print. Pass down our name as the one to use.
+ holdInner := ps.inner
+ defer func() { ps.inner = holdInner }()
+
+ ps.inner = []AST{t}
+ ps.print(t.Type)
+ if len(ps.inner) > 0 {
+ // The type did not print the name; print it now in
+ // the default location.
+ ps.writeByte(' ')
+ ps.print(t.Name)
+ }
+}
+
+func (t *Typed) printInner(ps *printState) {
+ ps.print(t.Name)
+}
+
+func (t *Typed) Traverse(fn func(AST) bool) {
+ if fn(t) {
+ t.Name.Traverse(fn)
+ t.Type.Traverse(fn)
+ }
+}
+
+func (t *Typed) Copy(fn func(AST) AST, skip func(AST) bool) AST {
+ if skip(t) {
+ return nil
+ }
+ name := t.Name.Copy(fn, skip)
+ typ := t.Type.Copy(fn, skip)
+ if name == nil && typ == nil {
+ return fn(t)
+ }
+ if name == nil {
+ name = t.Name
+ }
+ if typ == nil {
+ typ = t.Type
+ }
+ t = &Typed{Name: name, Type: typ}
+ if r := fn(t); r != nil {
+ return r
+ }
+ return t
+}
+
+func (t *Typed) GoString() string {
+ return t.goString(0, "")
+}
+
+func (t *Typed) goString(indent int, field string) string {
+ return fmt.Sprintf("%*s%sTyped:\n%s\n%s", indent, "", field,
+ t.Name.goString(indent+2, "Name: "),
+ t.Type.goString(indent+2, "Type: "))
+}
+
+// Qualified is a name in a scope.
+type Qualified struct {
+ Scope AST
+ Name AST
+
+ // The LocalName field is true if this is parsed as a
+ // <local-name>. We shouldn't really need this, but in some
+ // cases (for the unary sizeof operator) the standard
+ // demangler prints a local name slightly differently. We
+ // keep track of this for compatibility.
+ LocalName bool // A full local name encoding
+}
+
+func (q *Qualified) print(ps *printState) {
+ ps.print(q.Scope)
+ ps.writeString("::")
+ ps.print(q.Name)
+}
+
+func (q *Qualified) Traverse(fn func(AST) bool) {
+ if fn(q) {
+ q.Scope.Traverse(fn)
+ q.Name.Traverse(fn)
+ }
+}
+
+func (q *Qualified) Copy(fn func(AST) AST, skip func(AST) bool) AST {
+ if skip(q) {
+ return nil
+ }
+ scope := q.Scope.Copy(fn, skip)
+ name := q.Name.Copy(fn, skip)
+ if scope == nil && name == nil {
+ return fn(q)
+ }
+ if scope == nil {
+ scope = q.Scope
+ }
+ if name == nil {
+ name = q.Name
+ }
+ q = &Qualified{Scope: scope, Name: name, LocalName: q.LocalName}
+ if r := fn(q); r != nil {
+ return r
+ }
+ return q
+}
+
+func (q *Qualified) GoString() string {
+ return q.goString(0, "")
+}
+
+func (q *Qualified) goString(indent int, field string) string {
+ s := ""
+ if q.LocalName {
+ s = " LocalName: true"
+ }
+ return fmt.Sprintf("%*s%sQualified:%s\n%s\n%s", indent, "", field,
+ s, q.Scope.goString(indent+2, "Scope: "),
+ q.Name.goString(indent+2, "Name: "))
+}
+
+// Template is a template with arguments.
+type Template struct {
+ Name AST
+ Args []AST
+}
+
+func (t *Template) print(ps *printState) {
+ // Inner types apply to the template as a whole, they don't
+ // cross over into the template.
+ holdInner := ps.inner
+ defer func() { ps.inner = holdInner }()
+
+ ps.inner = nil
+ ps.print(t.Name)
+
+ if !ps.tparams {
+ // Do not print template parameters.
+ return
+ }
+ // We need an extra space after operator<.
+ if ps.last == '<' {
+ ps.writeByte(' ')
+ }
+
+ ps.writeByte('<')
+ first := true
+ for _, a := range t.Args {
+ if ps.isEmpty(a) {
+ continue
+ }
+ if !first {
+ ps.writeString(", ")
+ }
+ ps.print(a)
+ first = false
+ }
+ if ps.last == '>' {
+ // Avoid syntactic ambiguity in old versions of C++.
+ ps.writeByte(' ')
+ }
+ ps.writeByte('>')
+}
+
+func (t *Template) Traverse(fn func(AST) bool) {
+ if fn(t) {
+ t.Name.Traverse(fn)
+ for _, a := range t.Args {
+ a.Traverse(fn)
+ }
+ }
+}
+
+func (t *Template) Copy(fn func(AST) AST, skip func(AST) bool) AST {
+ if skip(t) {
+ return nil
+ }
+ name := t.Name.Copy(fn, skip)
+ changed := name != nil
+ args := make([]AST, len(t.Args))
+ for i, a := range t.Args {
+ ac := a.Copy(fn, skip)
+ if ac == nil {
+ args[i] = a
+ } else {
+ args[i] = ac
+ changed = true
+ }
+ }
+ if !changed {
+ return fn(t)
+ }
+ if name == nil {
+ name = t.Name
+ }
+ t = &Template{Name: name, Args: args}
+ if r := fn(t); r != nil {
+ return r
+ }
+ return t
+}
+
+func (t *Template) GoString() string {
+ return t.goString(0, "")
+}
+
+func (t *Template) goString(indent int, field string) string {
+ var args string
+ if len(t.Args) == 0 {
+ args = fmt.Sprintf("%*sArgs: nil", indent+2, "")
+ } else {
+ args = fmt.Sprintf("%*sArgs:", indent+2, "")
+ for i, a := range t.Args {
+ args += "\n"
+ args += a.goString(indent+4, fmt.Sprintf("%d: ", i))
+ }
+ }
+ return fmt.Sprintf("%*s%sTemplate (%p):\n%s\n%s", indent, "", field, t,
+ t.Name.goString(indent+2, "Name: "), args)
+}
+
+// TemplateParam is a template parameter. The Template field is
+// filled in while parsing the demangled string. We don't normally
+// see these while printing--they are replaced by the simplify
+// function.
+type TemplateParam struct {
+ Index int
+ Template *Template
+}
+
+func (tp *TemplateParam) print(ps *printState) {
+ if tp.Template == nil {
+ panic("TemplateParam Template field is nil")
+ }
+ if tp.Index >= len(tp.Template.Args) {
+ panic("TemplateParam Index out of bounds")
+ }
+ ps.print(tp.Template.Args[tp.Index])
+}
+
+func (tp *TemplateParam) Traverse(fn func(AST) bool) {
+ fn(tp)
+ // Don't traverse Template--it points elsewhere in the AST.
+}
+
+func (tp *TemplateParam) Copy(fn func(AST) AST, skip func(AST) bool) AST {
+ if skip(tp) {
+ return nil
+ }
+ return fn(tp)
+}
+
+func (tp *TemplateParam) GoString() string {
+ return tp.goString(0, "")
+}
+
+func (tp *TemplateParam) goString(indent int, field string) string {
+ return fmt.Sprintf("%*s%sTemplateParam: Template: %p; Index %d", indent, "", field, tp.Template, tp.Index)
+}
+
+// LambdaAuto is a lambda auto parameter.
+type LambdaAuto struct {
+ Index int
+}
+
+func (la *LambdaAuto) print(ps *printState) {
+ // We print the index plus 1 because that is what the standard
+ // demangler does.
+ if ps.llvmStyle {
+ ps.writeString("auto")
+ } else {
+ fmt.Fprintf(&ps.buf, "auto:%d", la.Index+1)
+ }
+}
+
+func (la *LambdaAuto) Traverse(fn func(AST) bool) {
+ fn(la)
+}
+
+func (la *LambdaAuto) Copy(fn func(AST) AST, skip func(AST) bool) AST {
+ if skip(la) {
+ return nil
+ }
+ return fn(la)
+}
+
+func (la *LambdaAuto) GoString() string {
+ return la.goString(0, "")
+}
+
+func (la *LambdaAuto) goString(indent int, field string) string {
+ return fmt.Sprintf("%*s%sLambdaAuto: Index %d", indent, "", field, la.Index)
+}
+
+// Qualifiers is an ordered list of type qualifiers.
+type Qualifiers struct {
+ Qualifiers []AST
+}
+
+func (qs *Qualifiers) print(ps *printState) {
+ first := true
+ for _, q := range qs.Qualifiers {
+ if !first {
+ ps.writeByte(' ')
+ }
+ q.print(ps)
+ first = false
+ }
+}
+
+func (qs *Qualifiers) Traverse(fn func(AST) bool) {
+ if fn(qs) {
+ for _, q := range qs.Qualifiers {
+ q.Traverse(fn)
+ }
+ }
+}
+
+func (qs *Qualifiers) Copy(fn func(AST) AST, skip func(AST) bool) AST {
+ if skip(qs) {
+ return nil
+ }
+ changed := false
+ qualifiers := make([]AST, len(qs.Qualifiers))
+ for i, q := range qs.Qualifiers {
+ qc := q.Copy(fn, skip)
+ if qc == nil {
+ qualifiers[i] = q
+ } else {
+ qualifiers[i] = qc
+ changed = true
+ }
+ }
+ if !changed {
+ return fn(qs)
+ }
+ qs = &Qualifiers{Qualifiers: qualifiers}
+ if r := fn(qs); r != nil {
+ return r
+ }
+ return qs
+}
+
+func (qs *Qualifiers) GoString() string {
+ return qs.goString(0, "")
+}
+
+func (qs *Qualifiers) goString(indent int, field string) string {
+ quals := fmt.Sprintf("%*s%s", indent, "", field)
+ for _, q := range qs.Qualifiers {
+ quals += "\n"
+ quals += q.goString(indent+2, "")
+ }
+ return quals
+}
+
+// Qualifier is a single type qualifier.
+type Qualifier struct {
+ Name string // qualifier name: const, volatile, etc.
+ Exprs []AST // can be non-nil for noexcept and throw
+}
+
+func (q *Qualifier) print(ps *printState) {
+ ps.writeString(q.Name)
+ if len(q.Exprs) > 0 {
+ ps.writeByte('(')
+ first := true
+ for _, e := range q.Exprs {
+ if el, ok := e.(*ExprList); ok && len(el.Exprs) == 0 {
+ continue
+ }
+ if !first {
+ ps.writeString(", ")
+ }
+ ps.print(e)
+ first = false
+ }
+ ps.writeByte(')')
+ }
+}
+
+func (q *Qualifier) Traverse(fn func(AST) bool) {
+ if fn(q) {
+ for _, e := range q.Exprs {
+ e.Traverse(fn)
+ }
+ }
+}
+
+func (q *Qualifier) Copy(fn func(AST) AST, skip func(AST) bool) AST {
+ if skip(q) {
+ return nil
+ }
+ exprs := make([]AST, len(q.Exprs))
+ changed := false
+ for i, e := range q.Exprs {
+ ec := e.Copy(fn, skip)
+ if ec == nil {
+ exprs[i] = e
+ } else {
+ exprs[i] = ec
+ changed = true
+ }
+ }
+ if !changed {
+ return fn(q)
+ }
+ q = &Qualifier{Name: q.Name, Exprs: exprs}
+ if r := fn(q); r != nil {
+ return r
+ }
+ return q
+}
+
+func (q *Qualifier) GoString() string {
+ return q.goString(0, "Qualifier: ")
+}
+
+func (q *Qualifier) goString(indent int, field string) string {
+ qs := fmt.Sprintf("%*s%s%s", indent, "", field, q.Name)
+ if len(q.Exprs) > 0 {
+ for i, e := range q.Exprs {
+ qs += "\n"
+ qs += e.goString(indent+2, fmt.Sprintf("%d: ", i))
+ }
+ }
+ return qs
+}
+
+// TypeWithQualifiers is a type with standard qualifiers.
+type TypeWithQualifiers struct {
+ Base AST
+ Qualifiers AST
+}
+
+func (twq *TypeWithQualifiers) print(ps *printState) {
+ // Give the base type a chance to print the inner types.
+ ps.inner = append(ps.inner, twq)
+ ps.print(twq.Base)
+ if len(ps.inner) > 0 {
+ // The qualifier wasn't printed by Base.
+ ps.writeByte(' ')
+ ps.print(twq.Qualifiers)
+ ps.inner = ps.inner[:len(ps.inner)-1]
+ }
+}
+
+// Print qualifiers as an inner type by just printing the qualifiers.
+func (twq *TypeWithQualifiers) printInner(ps *printState) {
+ ps.writeByte(' ')
+ ps.print(twq.Qualifiers)
+}
+
+func (twq *TypeWithQualifiers) Traverse(fn func(AST) bool) {
+ if fn(twq) {
+ twq.Base.Traverse(fn)
+ }
+}
+
+func (twq *TypeWithQualifiers) Copy(fn func(AST) AST, skip func(AST) bool) AST {
+ if skip(twq) {
+ return nil
+ }
+ base := twq.Base.Copy(fn, skip)
+ quals := twq.Qualifiers.Copy(fn, skip)
+ if base == nil && quals == nil {
+ return fn(twq)
+ }
+ if base == nil {
+ base = twq.Base
+ }
+ if quals == nil {
+ quals = twq.Qualifiers
+ }
+ twq = &TypeWithQualifiers{Base: base, Qualifiers: quals}
+ if r := fn(twq); r != nil {
+ return r
+ }
+ return twq
+}
+
+func (twq *TypeWithQualifiers) GoString() string {
+ return twq.goString(0, "")
+}
+
+func (twq *TypeWithQualifiers) goString(indent int, field string) string {
+ return fmt.Sprintf("%*s%sTypeWithQualifiers:\n%s\n%s", indent, "", field,
+ twq.Qualifiers.goString(indent+2, "Qualifiers: "),
+ twq.Base.goString(indent+2, "Base: "))
+}
+
+// MethodWithQualifiers is a method with qualifiers.
+type MethodWithQualifiers struct {
+ Method AST
+ Qualifiers AST
+ RefQualifier string // "" or "&" or "&&"
+}
+
+func (mwq *MethodWithQualifiers) print(ps *printState) {
+ // Give the base type a chance to print the inner types.
+ ps.inner = append(ps.inner, mwq)
+ ps.print(mwq.Method)
+ if len(ps.inner) > 0 {
+ if mwq.Qualifiers != nil {
+ ps.writeByte(' ')
+ ps.print(mwq.Qualifiers)
+ }
+ if mwq.RefQualifier != "" {
+ ps.writeByte(' ')
+ ps.writeString(mwq.RefQualifier)
+ }
+ ps.inner = ps.inner[:len(ps.inner)-1]
+ }
+}
+
+func (mwq *MethodWithQualifiers) printInner(ps *printState) {
+ if mwq.Qualifiers != nil {
+ ps.writeByte(' ')
+ ps.print(mwq.Qualifiers)
+ }
+ if mwq.RefQualifier != "" {
+ ps.writeByte(' ')
+ ps.writeString(mwq.RefQualifier)
+ }
+}
+
+func (mwq *MethodWithQualifiers) Traverse(fn func(AST) bool) {
+ if fn(mwq) {
+ mwq.Method.Traverse(fn)
+ }
+}
+
+func (mwq *MethodWithQualifiers) Copy(fn func(AST) AST, skip func(AST) bool) AST {
+ if skip(mwq) {
+ return nil
+ }
+ method := mwq.Method.Copy(fn, skip)
+ var quals AST
+ if mwq.Qualifiers != nil {
+ quals = mwq.Qualifiers.Copy(fn, skip)
+ }
+ if method == nil && quals == nil {
+ return fn(mwq)
+ }
+ if method == nil {
+ method = mwq.Method
+ }
+ if quals == nil {
+ quals = mwq.Qualifiers
+ }
+ mwq = &MethodWithQualifiers{Method: method, Qualifiers: quals, RefQualifier: mwq.RefQualifier}
+ if r := fn(mwq); r != nil {
+ return r
+ }
+ return mwq
+}
+
+func (mwq *MethodWithQualifiers) GoString() string {
+ return mwq.goString(0, "")
+}
+
+func (mwq *MethodWithQualifiers) goString(indent int, field string) string {
+ var q string
+ if mwq.Qualifiers != nil {
+ q += "\n" + mwq.Qualifiers.goString(indent+2, "Qualifiers: ")
+ }
+ if mwq.RefQualifier != "" {
+ if q != "" {
+ q += "\n"
+ }
+ q += fmt.Sprintf("%*s%s%s", indent+2, "", "RefQualifier: ", mwq.RefQualifier)
+ }
+ return fmt.Sprintf("%*s%sMethodWithQualifiers:%s\n%s", indent, "", field,
+ q, mwq.Method.goString(indent+2, "Method: "))
+}
+
+// BuiltinType is a builtin type, like "int".
+type BuiltinType struct {
+ Name string
+}
+
+func (bt *BuiltinType) print(ps *printState) {
+ name := bt.Name
+ if ps.llvmStyle && name == "decltype(nullptr)" {
+ name = "std::nullptr_t"
+ }
+ ps.writeString(name)
+}
+
+func (bt *BuiltinType) Traverse(fn func(AST) bool) {
+ fn(bt)
+}
+
+func (bt *BuiltinType) Copy(fn func(AST) AST, skip func(AST) bool) AST {
+ if skip(bt) {
+ return nil
+ }
+ return fn(bt)
+}
+
+func (bt *BuiltinType) GoString() string {
+ return bt.goString(0, "")
+}
+
+func (bt *BuiltinType) goString(indent int, field string) string {
+ return fmt.Sprintf("%*s%sBuiltinType: %s", indent, "", field, bt.Name)
+}
+
+// printBase is common print code for types that are printed with a
+// simple suffix.
+func printBase(ps *printState, qual, base AST) {
+ ps.inner = append(ps.inner, qual)
+ ps.print(base)
+ if len(ps.inner) > 0 {
+ qual.(innerPrinter).printInner(ps)
+ ps.inner = ps.inner[:len(ps.inner)-1]
+ }
+}
+
+// PointerType is a pointer type.
+type PointerType struct {
+ Base AST
+}
+
+func (pt *PointerType) print(ps *printState) {
+ printBase(ps, pt, pt.Base)
+}
+
+func (pt *PointerType) printInner(ps *printState) {
+ ps.writeString("*")
+}
+
+func (pt *PointerType) Traverse(fn func(AST) bool) {
+ if fn(pt) {
+ pt.Base.Traverse(fn)
+ }
+}
+
+func (pt *PointerType) Copy(fn func(AST) AST, skip func(AST) bool) AST {
+ if skip(pt) {
+ return nil
+ }
+ base := pt.Base.Copy(fn, skip)
+ if base == nil {
+ return fn(pt)
+ }
+ pt = &PointerType{Base: base}
+ if r := fn(pt); r != nil {
+ return r
+ }
+ return pt
+}
+
+func (pt *PointerType) GoString() string {
+ return pt.goString(0, "")
+}
+
+func (pt *PointerType) goString(indent int, field string) string {
+ return fmt.Sprintf("%*s%sPointerType:\n%s", indent, "", field,
+ pt.Base.goString(indent+2, ""))
+}
+
+// ReferenceType is a reference type.
+type ReferenceType struct {
+ Base AST
+}
+
+func (rt *ReferenceType) print(ps *printState) {
+ printBase(ps, rt, rt.Base)
+}
+
+func (rt *ReferenceType) printInner(ps *printState) {
+ ps.writeString("&")
+}
+
+func (rt *ReferenceType) Traverse(fn func(AST) bool) {
+ if fn(rt) {
+ rt.Base.Traverse(fn)
+ }
+}
+
+func (rt *ReferenceType) Copy(fn func(AST) AST, skip func(AST) bool) AST {
+ if skip(rt) {
+ return nil
+ }
+ base := rt.Base.Copy(fn, skip)
+ if base == nil {
+ return fn(rt)
+ }
+ rt = &ReferenceType{Base: base}
+ if r := fn(rt); r != nil {
+ return r
+ }
+ return rt
+}
+
+func (rt *ReferenceType) GoString() string {
+ return rt.goString(0, "")
+}
+
+func (rt *ReferenceType) goString(indent int, field string) string {
+ return fmt.Sprintf("%*s%sReferenceType:\n%s", indent, "", field,
+ rt.Base.goString(indent+2, ""))
+}
+
+// RvalueReferenceType is an rvalue reference type.
+type RvalueReferenceType struct {
+ Base AST
+}
+
+func (rt *RvalueReferenceType) print(ps *printState) {
+ printBase(ps, rt, rt.Base)
+}
+
+func (rt *RvalueReferenceType) printInner(ps *printState) {
+ ps.writeString("&&")
+}
+
+func (rt *RvalueReferenceType) Traverse(fn func(AST) bool) {
+ if fn(rt) {
+ rt.Base.Traverse(fn)
+ }
+}
+
+func (rt *RvalueReferenceType) Copy(fn func(AST) AST, skip func(AST) bool) AST {
+ if skip(rt) {
+ return nil
+ }
+ base := rt.Base.Copy(fn, skip)
+ if base == nil {
+ return fn(rt)
+ }
+ rt = &RvalueReferenceType{Base: base}
+ if r := fn(rt); r != nil {
+ return r
+ }
+ return rt
+}
+
+func (rt *RvalueReferenceType) GoString() string {
+ return rt.goString(0, "")
+}
+
+func (rt *RvalueReferenceType) goString(indent int, field string) string {
+ return fmt.Sprintf("%*s%sRvalueReferenceType:\n%s", indent, "", field,
+ rt.Base.goString(indent+2, ""))
+}
+
+// ComplexType is a complex type.
+type ComplexType struct {
+ Base AST
+}
+
+func (ct *ComplexType) print(ps *printState) {
+ printBase(ps, ct, ct.Base)
+}
+
+func (ct *ComplexType) printInner(ps *printState) {
+ ps.writeString(" _Complex")
+}
+
+func (ct *ComplexType) Traverse(fn func(AST) bool) {
+ if fn(ct) {
+ ct.Base.Traverse(fn)
+ }
+}
+
+func (ct *ComplexType) Copy(fn func(AST) AST, skip func(AST) bool) AST {
+ if skip(ct) {
+ return nil
+ }
+ base := ct.Base.Copy(fn, skip)
+ if base == nil {
+ return fn(ct)
+ }
+ ct = &ComplexType{Base: base}
+ if r := fn(ct); r != nil {
+ return r
+ }
+ return ct
+}
+
+func (ct *ComplexType) GoString() string {
+ return ct.goString(0, "")
+}
+
+func (ct *ComplexType) goString(indent int, field string) string {
+ return fmt.Sprintf("%*s%sComplexType:\n%s", indent, "", field,
+ ct.Base.goString(indent+2, ""))
+}
+
+// ImaginaryType is an imaginary type.
+type ImaginaryType struct {
+ Base AST
+}
+
+func (it *ImaginaryType) print(ps *printState) {
+ printBase(ps, it, it.Base)
+}
+
+func (it *ImaginaryType) printInner(ps *printState) {
+ ps.writeString(" _Imaginary")
+}
+
+func (it *ImaginaryType) Traverse(fn func(AST) bool) {
+ if fn(it) {
+ it.Base.Traverse(fn)
+ }
+}
+
+func (it *ImaginaryType) Copy(fn func(AST) AST, skip func(AST) bool) AST {
+ if skip(it) {
+ return nil
+ }
+ base := it.Base.Copy(fn, skip)
+ if base == nil {
+ return fn(it)
+ }
+ it = &ImaginaryType{Base: base}
+ if r := fn(it); r != nil {
+ return r
+ }
+ return it
+}
+
+func (it *ImaginaryType) GoString() string {
+ return it.goString(0, "")
+}
+
+func (it *ImaginaryType) goString(indent int, field string) string {
+ return fmt.Sprintf("%*s%sImaginaryType:\n%s", indent, "", field,
+ it.Base.goString(indent+2, ""))
+}
+
+// VendorQualifier is a type qualified by a vendor-specific qualifier.
+type VendorQualifier struct {
+ Qualifier AST
+ Type AST
+}
+
+func (vq *VendorQualifier) print(ps *printState) {
+ if ps.llvmStyle {
+ ps.print(vq.Type)
+ vq.printInner(ps)
+ } else {
+ ps.inner = append(ps.inner, vq)
+ ps.print(vq.Type)
+ if len(ps.inner) > 0 {
+ ps.printOneInner(nil)
+ }
+ }
+}
+
+func (vq *VendorQualifier) printInner(ps *printState) {
+ ps.writeByte(' ')
+ ps.print(vq.Qualifier)
+}
+
+func (vq *VendorQualifier) Traverse(fn func(AST) bool) {
+ if fn(vq) {
+ vq.Qualifier.Traverse(fn)
+ vq.Type.Traverse(fn)
+ }
+}
+
+func (vq *VendorQualifier) Copy(fn func(AST) AST, skip func(AST) bool) AST {
+ if skip(vq) {
+ return nil
+ }
+ qualifier := vq.Qualifier.Copy(fn, skip)
+ typ := vq.Type.Copy(fn, skip)
+ if qualifier == nil && typ == nil {
+ return fn(vq)
+ }
+ if qualifier == nil {
+ qualifier = vq.Qualifier
+ }
+ if typ == nil {
+ typ = vq.Type
+ }
+ vq = &VendorQualifier{Qualifier: qualifier, Type: vq.Type}
+ if r := fn(vq); r != nil {
+ return r
+ }
+ return vq
+}
+
+func (vq *VendorQualifier) GoString() string {
+ return vq.goString(0, "")
+}
+
+func (vq *VendorQualifier) goString(indent int, field string) string {
+ return fmt.Sprintf("%*s%sVendorQualifier:\n%s\n%s", indent, "", field,
+ vq.Qualifier.goString(indent+2, "Qualifier: "),
+ vq.Type.goString(indent+2, "Type: "))
+}
+
+// ArrayType is an array type.
+type ArrayType struct {
+ Dimension AST
+ Element AST
+}
+
+func (at *ArrayType) print(ps *printState) {
+ // Pass the array type down as an inner type so that we print
+ // multi-dimensional arrays correctly.
+ ps.inner = append(ps.inner, at)
+ ps.print(at.Element)
+ if ln := len(ps.inner); ln > 0 {
+ ps.inner = ps.inner[:ln-1]
+ at.printDimension(ps)
+ }
+}
+
+func (at *ArrayType) printInner(ps *printState) {
+ at.printDimension(ps)
+}
+
+// Print the array dimension.
+func (at *ArrayType) printDimension(ps *printState) {
+ space := " "
+ for len(ps.inner) > 0 {
+ // We haven't gotten to the real type yet. Use
+ // parentheses around that type, except that if it is
+ // an array type we print it as a multi-dimensional
+ // array
+ in := ps.inner[len(ps.inner)-1]
+ if twq, ok := in.(*TypeWithQualifiers); ok {
+ in = twq.Base
+ }
+ if _, ok := in.(*ArrayType); ok {
+ if in == ps.inner[len(ps.inner)-1] {
+ space = ""
+ }
+ ps.printOneInner(nil)
+ } else {
+ ps.writeString(" (")
+ ps.printInner(false)
+ ps.writeByte(')')
+ }
+ }
+ ps.writeString(space)
+ ps.writeByte('[')
+ ps.print(at.Dimension)
+ ps.writeByte(']')
+}
+
+func (at *ArrayType) Traverse(fn func(AST) bool) {
+ if fn(at) {
+ at.Dimension.Traverse(fn)
+ at.Element.Traverse(fn)
+ }
+}
+
+func (at *ArrayType) Copy(fn func(AST) AST, skip func(AST) bool) AST {
+ if skip(at) {
+ return nil
+ }
+ dimension := at.Dimension.Copy(fn, skip)
+ element := at.Element.Copy(fn, skip)
+ if dimension == nil && element == nil {
+ return fn(at)
+ }
+ if dimension == nil {
+ dimension = at.Dimension
+ }
+ if element == nil {
+ element = at.Element
+ }
+ at = &ArrayType{Dimension: dimension, Element: element}
+ if r := fn(at); r != nil {
+ return r
+ }
+ return at
+}
+
+func (at *ArrayType) GoString() string {
+ return at.goString(0, "")
+}
+
+func (at *ArrayType) goString(indent int, field string) string {
+ return fmt.Sprintf("%*s%sArrayType:\n%s\n%s", indent, "", field,
+ at.Dimension.goString(indent+2, "Dimension: "),
+ at.Element.goString(indent+2, "Element: "))
+}
+
+// FunctionType is a function type.
+type FunctionType struct {
+ Return AST
+ Args []AST
+
+ // The forLocalName field reports whether this FunctionType
+ // was created for a local name. With the default GNU demangling
+ // output we don't print the return type in that case.
+ ForLocalName bool
+}
+
+func (ft *FunctionType) print(ps *printState) {
+ retType := ft.Return
+ if ft.ForLocalName && (!ps.enclosingParams || !ps.llvmStyle) {
+ retType = nil
+ }
+ if retType != nil {
+ // Pass the return type as an inner type in order to
+ // print the arguments in the right location.
+ ps.inner = append(ps.inner, ft)
+ ps.print(retType)
+ if len(ps.inner) == 0 {
+ // Everything was printed.
+ return
+ }
+ ps.inner = ps.inner[:len(ps.inner)-1]
+ ps.writeByte(' ')
+ }
+ ft.printArgs(ps)
+}
+
+func (ft *FunctionType) printInner(ps *printState) {
+ ft.printArgs(ps)
+}
+
+// printArgs prints the arguments of a function type. It looks at the
+// inner types for spacing.
+func (ft *FunctionType) printArgs(ps *printState) {
+ paren := false
+ space := false
+ for i := len(ps.inner) - 1; i >= 0; i-- {
+ switch ps.inner[i].(type) {
+ case *PointerType, *ReferenceType, *RvalueReferenceType:
+ paren = true
+ case *TypeWithQualifiers, *ComplexType, *ImaginaryType, *PtrMem:
+ space = true
+ paren = true
+ }
+ if paren {
+ break
+ }
+ }
+
+ if paren {
+ if !space && (ps.last != '(' && ps.last != '*') {
+ space = true
+ }
+ if space && ps.last != ' ' {
+ ps.writeByte(' ')
+ }
+ ps.writeByte('(')
+ }
+
+ save := ps.printInner(true)
+
+ if paren {
+ ps.writeByte(')')
+ }
+
+ ps.writeByte('(')
+ if !ft.ForLocalName || ps.enclosingParams {
+ first := true
+ for _, a := range ft.Args {
+ if ps.isEmpty(a) {
+ continue
+ }
+ if !first {
+ ps.writeString(", ")
+ }
+ ps.print(a)
+ first = false
+ }
+ }
+ ps.writeByte(')')
+
+ ps.inner = save
+ ps.printInner(false)
+}
+
+func (ft *FunctionType) Traverse(fn func(AST) bool) {
+ if fn(ft) {
+ if ft.Return != nil {
+ ft.Return.Traverse(fn)
+ }
+ for _, a := range ft.Args {
+ a.Traverse(fn)
+ }
+ }
+}
+
+func (ft *FunctionType) Copy(fn func(AST) AST, skip func(AST) bool) AST {
+ if skip(ft) {
+ return nil
+ }
+ changed := false
+ var ret AST
+ if ft.Return != nil {
+ ret = ft.Return.Copy(fn, skip)
+ if ret == nil {
+ ret = ft.Return
+ } else {
+ changed = true
+ }
+ }
+ args := make([]AST, len(ft.Args))
+ for i, a := range ft.Args {
+ ac := a.Copy(fn, skip)
+ if ac == nil {
+ args[i] = a
+ } else {
+ args[i] = ac
+ changed = true
+ }
+ }
+ if !changed {
+ return fn(ft)
+ }
+ ft = &FunctionType{
+ Return: ret,
+ Args: args,
+ ForLocalName: ft.ForLocalName,
+ }
+ if r := fn(ft); r != nil {
+ return r
+ }
+ return ft
+}
+
+func (ft *FunctionType) GoString() string {
+ return ft.goString(0, "")
+}
+
+func (ft *FunctionType) goString(indent int, field string) string {
+ var forLocalName string
+ if ft.ForLocalName {
+ forLocalName = " ForLocalName: true"
+ }
+ var r string
+ if ft.Return == nil {
+ r = fmt.Sprintf("%*sReturn: nil", indent+2, "")
+ } else {
+ r = ft.Return.goString(indent+2, "Return: ")
+ }
+ var args string
+ if len(ft.Args) == 0 {
+ args = fmt.Sprintf("%*sArgs: nil", indent+2, "")
+ } else {
+ args = fmt.Sprintf("%*sArgs:", indent+2, "")
+ for i, a := range ft.Args {
+ args += "\n"
+ args += a.goString(indent+4, fmt.Sprintf("%d: ", i))
+ }
+ }
+ return fmt.Sprintf("%*s%sFunctionType:%s\n%s\n%s", indent, "", field,
+ forLocalName, r, args)
+}
+
+// FunctionParam is a parameter of a function, used for last-specified
+// return type in a closure.
+type FunctionParam struct {
+ Index int
+}
+
+func (fp *FunctionParam) print(ps *printState) {
+ if fp.Index == 0 {
+ ps.writeString("this")
+ } else if ps.llvmStyle {
+ if fp.Index == 1 {
+ ps.writeString("fp")
+ } else {
+ fmt.Fprintf(&ps.buf, "fp%d", fp.Index-2)
+ }
+ } else {
+ fmt.Fprintf(&ps.buf, "{parm#%d}", fp.Index)
+ }
+}
+
+func (fp *FunctionParam) Traverse(fn func(AST) bool) {
+ fn(fp)
+}
+
+func (fp *FunctionParam) Copy(fn func(AST) AST, skip func(AST) bool) AST {
+ if skip(fp) {
+ return nil
+ }
+ return fn(fp)
+}
+
+func (fp *FunctionParam) GoString() string {
+ return fp.goString(0, "")
+}
+
+func (fp *FunctionParam) goString(indent int, field string) string {
+ return fmt.Sprintf("%*s%sFunctionParam: %d", indent, "", field, fp.Index)
+}
+
+// PtrMem is a pointer-to-member expression.
+type PtrMem struct {
+ Class AST
+ Member AST
+}
+
+func (pm *PtrMem) print(ps *printState) {
+ ps.inner = append(ps.inner, pm)
+ ps.print(pm.Member)
+ if len(ps.inner) > 0 {
+ ps.printOneInner(nil)
+ }
+}
+
+func (pm *PtrMem) printInner(ps *printState) {
+ if ps.last != '(' {
+ ps.writeByte(' ')
+ }
+ ps.print(pm.Class)
+ ps.writeString("::*")
+}
+
+func (pm *PtrMem) Traverse(fn func(AST) bool) {
+ if fn(pm) {
+ pm.Class.Traverse(fn)
+ pm.Member.Traverse(fn)
+ }
+}
+
+func (pm *PtrMem) Copy(fn func(AST) AST, skip func(AST) bool) AST {
+ if skip(pm) {
+ return nil
+ }
+ class := pm.Class.Copy(fn, skip)
+ member := pm.Member.Copy(fn, skip)
+ if class == nil && member == nil {
+ return fn(pm)
+ }
+ if class == nil {
+ class = pm.Class
+ }
+ if member == nil {
+ member = pm.Member
+ }
+ pm = &PtrMem{Class: class, Member: member}
+ if r := fn(pm); r != nil {
+ return r
+ }
+ return pm
+}
+
+func (pm *PtrMem) GoString() string {
+ return pm.goString(0, "")
+}
+
+func (pm *PtrMem) goString(indent int, field string) string {
+ return fmt.Sprintf("%*s%sPtrMem:\n%s\n%s", indent, "", field,
+ pm.Class.goString(indent+2, "Class: "),
+ pm.Member.goString(indent+2, "Member: "))
+}
+
+// FixedType is a fixed numeric type of unknown size.
+type FixedType struct {
+ Base AST
+ Accum bool
+ Sat bool
+}
+
+func (ft *FixedType) print(ps *printState) {
+ if ft.Sat {
+ ps.writeString("_Sat ")
+ }
+ if bt, ok := ft.Base.(*BuiltinType); ok && bt.Name == "int" {
+ // The standard demangler skips printing "int".
+ } else {
+ ps.print(ft.Base)
+ ps.writeByte(' ')
+ }
+ if ft.Accum {
+ ps.writeString("_Accum")
+ } else {
+ ps.writeString("_Fract")
+ }
+}
+
+func (ft *FixedType) Traverse(fn func(AST) bool) {
+ if fn(ft) {
+ ft.Base.Traverse(fn)
+ }
+}
+
+func (ft *FixedType) Copy(fn func(AST) AST, skip func(AST) bool) AST {
+ if skip(ft) {
+ return nil
+ }
+ base := ft.Base.Copy(fn, skip)
+ if base == nil {
+ return fn(ft)
+ }
+ ft = &FixedType{Base: base, Accum: ft.Accum, Sat: ft.Sat}
+ if r := fn(ft); r != nil {
+ return r
+ }
+ return ft
+}
+
+func (ft *FixedType) GoString() string {
+ return ft.goString(0, "")
+}
+
+func (ft *FixedType) goString(indent int, field string) string {
+ return fmt.Sprintf("%*s%sFixedType: Accum: %t; Sat: %t\n%s", indent, "", field,
+ ft.Accum, ft.Sat,
+ ft.Base.goString(indent+2, "Base: "))
+}
+
+// BinaryFP is a binary floating-point type.
+type BinaryFP struct {
+ Bits int
+}
+
+func (bfp *BinaryFP) print(ps *printState) {
+ fmt.Fprintf(&ps.buf, "_Float%d", bfp.Bits)
+}
+
+func (bfp *BinaryFP) Traverse(fn func(AST) bool) {
+ fn(bfp)
+}
+
+func (bfp *BinaryFP) Copy(fn func(AST) AST, skip func(AST) bool) AST {
+ if skip(bfp) {
+ return nil
+ }
+ return fn(bfp)
+}
+
+func (bfp *BinaryFP) GoString() string {
+ return bfp.goString(0, "")
+}
+
+func (bfp *BinaryFP) goString(indent int, field string) string {
+ return fmt.Sprintf("%*s%sBinaryFP: %d", indent, "", field, bfp.Bits)
+}
+
+// VectorType is a vector type.
+type VectorType struct {
+ Dimension AST
+ Base AST
+}
+
+func (vt *VectorType) print(ps *printState) {
+ ps.inner = append(ps.inner, vt)
+ ps.print(vt.Base)
+ if len(ps.inner) > 0 {
+ ps.printOneInner(nil)
+ }
+}
+
+func (vt *VectorType) printInner(ps *printState) {
+ end := byte(')')
+ if ps.llvmStyle {
+ ps.writeString(" vector[")
+ end = ']'
+ } else {
+ ps.writeString(" __vector(")
+ }
+ ps.print(vt.Dimension)
+ ps.writeByte(end)
+}
+
+func (vt *VectorType) Traverse(fn func(AST) bool) {
+ if fn(vt) {
+ vt.Dimension.Traverse(fn)
+ vt.Base.Traverse(fn)
+ }
+}
+
+func (vt *VectorType) Copy(fn func(AST) AST, skip func(AST) bool) AST {
+ if skip(vt) {
+ return nil
+ }
+ dimension := vt.Dimension.Copy(fn, skip)
+ base := vt.Base.Copy(fn, skip)
+ if dimension == nil && base == nil {
+ return fn(vt)
+ }
+ if dimension == nil {
+ dimension = vt.Dimension
+ }
+ if base == nil {
+ base = vt.Base
+ }
+ vt = &VectorType{Dimension: dimension, Base: base}
+ if r := fn(vt); r != nil {
+ return r
+ }
+ return vt
+}
+
+func (vt *VectorType) GoString() string {
+ return vt.goString(0, "")
+}
+
+func (vt *VectorType) goString(indent int, field string) string {
+ return fmt.Sprintf("%*s%sVectorType:\n%s\n%s", indent, "", field,
+ vt.Dimension.goString(indent+2, "Dimension: "),
+ vt.Base.goString(indent+2, "Base: "))
+}
+
+// ElaboratedType is an elaborated struct/union/enum type.
+type ElaboratedType struct {
+ Kind string
+ Type AST
+}
+
+func (et *ElaboratedType) print(ps *printState) {
+ ps.writeString(et.Kind)
+ ps.writeString(" ")
+ et.Type.print(ps)
+}
+
+func (et *ElaboratedType) Traverse(fn func(AST) bool) {
+ if fn(et) {
+ et.Type.Traverse(fn)
+ }
+}
+
+func (et *ElaboratedType) Copy(fn func(AST) AST, skip func(AST) bool) AST {
+ if skip(et) {
+ return nil
+ }
+ typ := et.Type.Copy(fn, skip)
+ if typ == nil {
+ return fn(et)
+ }
+ et = &ElaboratedType{Kind: et.Kind, Type: typ}
+ if r := fn(et); r != nil {
+ return r
+ }
+ return et
+}
+
+func (et *ElaboratedType) GoString() string {
+ return et.goString(0, "")
+}
+
+func (et *ElaboratedType) goString(indent int, field string) string {
+ return fmt.Sprintf("%*s%sElaboratedtype: Kind: %s\n%s", indent, "", field,
+ et.Kind, et.Type.goString(indent+2, "Expr: "))
+}
+
+// Decltype is the decltype operator.
+type Decltype struct {
+ Expr AST
+}
+
+func (dt *Decltype) print(ps *printState) {
+ ps.writeString("decltype")
+ if !ps.llvmStyle {
+ ps.writeString(" ")
+ }
+ ps.writeString("(")
+ ps.print(dt.Expr)
+ ps.writeByte(')')
+}
+
+func (dt *Decltype) Traverse(fn func(AST) bool) {
+ if fn(dt) {
+ dt.Expr.Traverse(fn)
+ }
+}
+
+func (dt *Decltype) Copy(fn func(AST) AST, skip func(AST) bool) AST {
+ if skip(dt) {
+ return nil
+ }
+ expr := dt.Expr.Copy(fn, skip)
+ if expr == nil {
+ return fn(dt)
+ }
+ dt = &Decltype{Expr: expr}
+ if r := fn(dt); r != nil {
+ return r
+ }
+ return dt
+}
+
+func (dt *Decltype) GoString() string {
+ return dt.goString(0, "")
+}
+
+func (dt *Decltype) goString(indent int, field string) string {
+ return fmt.Sprintf("%*s%sDecltype:\n%s", indent, "", field,
+ dt.Expr.goString(indent+2, "Expr: "))
+}
+
+// Operator is an operator.
+type Operator struct {
+ Name string
+}
+
+func (op *Operator) print(ps *printState) {
+ ps.writeString("operator")
+ if isLower(op.Name[0]) {
+ ps.writeByte(' ')
+ }
+ n := op.Name
+ n = strings.TrimSuffix(n, " ")
+ ps.writeString(n)
+}
+
+func (op *Operator) Traverse(fn func(AST) bool) {
+ fn(op)
+}
+
+func (op *Operator) Copy(fn func(AST) AST, skip func(AST) bool) AST {
+ if skip(op) {
+ return nil
+ }
+ return fn(op)
+}
+
+func (op *Operator) GoString() string {
+ return op.goString(0, "")
+}
+
+func (op *Operator) goString(indent int, field string) string {
+ return fmt.Sprintf("%*s%sOperator: %s", indent, "", field, op.Name)
+}
+
+// Constructor is a constructor.
+type Constructor struct {
+ Name AST
+ Base AST // base class of inheriting constructor
+}
+
+func (c *Constructor) print(ps *printState) {
+ ps.print(c.Name)
+ // We don't include the base class in the demangled string.
+}
+
+func (c *Constructor) Traverse(fn func(AST) bool) {
+ if fn(c) {
+ c.Name.Traverse(fn)
+ if c.Base != nil {
+ c.Base.Traverse(fn)
+ }
+ }
+}
+
+func (c *Constructor) Copy(fn func(AST) AST, skip func(AST) bool) AST {
+ if skip(c) {
+ return nil
+ }
+ name := c.Name.Copy(fn, skip)
+ var base AST
+ if c.Base != nil {
+ base = c.Base.Copy(fn, skip)
+ }
+ if name == nil && base == nil {
+ return fn(c)
+ }
+ if name == nil {
+ name = c.Name
+ }
+ if base == nil {
+ base = c.Base
+ }
+ c = &Constructor{Name: name, Base: base}
+ if r := fn(c); r != nil {
+ return r
+ }
+ return c
+}
+
+func (c *Constructor) GoString() string {
+ return c.goString(0, "")
+}
+
+func (c *Constructor) goString(indent int, field string) string {
+ var sb strings.Builder
+ fmt.Fprintf(&sb, "%*s%sConstructor:\n", indent, "", field)
+ if c.Base != nil {
+ fmt.Fprintf(&sb, "%s\n", c.Base.goString(indent+2, "Base: "))
+ }
+ fmt.Fprintf(&sb, "%s", c.Name.goString(indent+2, "Name: "))
+ return sb.String()
+}
+
+// Destructor is a destructor.
+type Destructor struct {
+ Name AST
+}
+
+func (d *Destructor) print(ps *printState) {
+ ps.writeByte('~')
+ ps.print(d.Name)
+}
+
+func (d *Destructor) Traverse(fn func(AST) bool) {
+ if fn(d) {
+ d.Name.Traverse(fn)
+ }
+}
+
+func (d *Destructor) Copy(fn func(AST) AST, skip func(AST) bool) AST {
+ if skip(d) {
+ return nil
+ }
+ name := d.Name.Copy(fn, skip)
+ if name == nil {
+ return fn(d)
+ }
+ d = &Destructor{Name: name}
+ if r := fn(d); r != nil {
+ return r
+ }
+ return d
+}
+
+func (d *Destructor) GoString() string {
+ return d.goString(0, "")
+}
+
+func (d *Destructor) goString(indent int, field string) string {
+ return fmt.Sprintf("%*s%sDestructor:\n%s", indent, "", field, d.Name.goString(indent+2, "Name: "))
+}
+
+// GlobalCDtor is a global constructor or destructor.
+type GlobalCDtor struct {
+ Ctor bool
+ Key AST
+}
+
+func (gcd *GlobalCDtor) print(ps *printState) {
+ ps.writeString("global ")
+ if gcd.Ctor {
+ ps.writeString("constructors")
+ } else {
+ ps.writeString("destructors")
+ }
+ ps.writeString(" keyed to ")
+ ps.print(gcd.Key)
+}
+
+func (gcd *GlobalCDtor) Traverse(fn func(AST) bool) {
+ if fn(gcd) {
+ gcd.Key.Traverse(fn)
+ }
+}
+
+func (gcd *GlobalCDtor) Copy(fn func(AST) AST, skip func(AST) bool) AST {
+ if skip(gcd) {
+ return nil
+ }
+ key := gcd.Key.Copy(fn, skip)
+ if key == nil {
+ return fn(gcd)
+ }
+ gcd = &GlobalCDtor{Ctor: gcd.Ctor, Key: key}
+ if r := fn(gcd); r != nil {
+ return r
+ }
+ return gcd
+}
+
+func (gcd *GlobalCDtor) GoString() string {
+ return gcd.goString(0, "")
+}
+
+func (gcd *GlobalCDtor) goString(indent int, field string) string {
+ return fmt.Sprintf("%*s%sGlobalCDtor: Ctor: %t\n%s", indent, "", field,
+ gcd.Ctor, gcd.Key.goString(indent+2, "Key: "))
+}
+
+// TaggedName is a name with an ABI tag.
+type TaggedName struct {
+ Name AST
+ Tag AST
+}
+
+func (t *TaggedName) print(ps *printState) {
+ ps.print(t.Name)
+ ps.writeString("[abi:")
+ ps.print(t.Tag)
+ ps.writeByte(']')
+}
+
+func (t *TaggedName) Traverse(fn func(AST) bool) {
+ if fn(t) {
+ t.Name.Traverse(fn)
+ t.Tag.Traverse(fn)
+ }
+}
+
+func (t *TaggedName) Copy(fn func(AST) AST, skip func(AST) bool) AST {
+ if skip(t) {
+ return nil
+ }
+ name := t.Name.Copy(fn, skip)
+ tag := t.Tag.Copy(fn, skip)
+ if name == nil && tag == nil {
+ return fn(t)
+ }
+ if name == nil {
+ name = t.Name
+ }
+ if tag == nil {
+ tag = t.Tag
+ }
+ t = &TaggedName{Name: name, Tag: tag}
+ if r := fn(t); r != nil {
+ return r
+ }
+ return t
+}
+
+func (t *TaggedName) GoString() string {
+ return t.goString(0, "")
+}
+
+func (t *TaggedName) goString(indent int, field string) string {
+ return fmt.Sprintf("%*s%sTaggedName:\n%s\n%s", indent, "", field,
+ t.Name.goString(indent+2, "Name: "),
+ t.Tag.goString(indent+2, "Tag: "))
+}
+
+// PackExpansion is a pack expansion. The Pack field may be nil.
+type PackExpansion struct {
+ Base AST
+ Pack *ArgumentPack
+}
+
+func (pe *PackExpansion) print(ps *printState) {
+ // We normally only get here if the simplify function was
+ // unable to locate and expand the pack.
+ if pe.Pack == nil {
+ if ps.llvmStyle {
+ ps.print(pe.Base)
+ } else {
+ parenthesize(ps, pe.Base)
+ ps.writeString("...")
+ }
+ } else {
+ ps.print(pe.Base)
+ }
+}
+
+func (pe *PackExpansion) Traverse(fn func(AST) bool) {
+ if fn(pe) {
+ pe.Base.Traverse(fn)
+ // Don't traverse Template--it points elsewhere in the AST.
+ }
+}
+
+func (pe *PackExpansion) Copy(fn func(AST) AST, skip func(AST) bool) AST {
+ if skip(pe) {
+ return nil
+ }
+ base := pe.Base.Copy(fn, skip)
+ if base == nil {
+ return fn(pe)
+ }
+ pe = &PackExpansion{Base: base, Pack: pe.Pack}
+ if r := fn(pe); r != nil {
+ return r
+ }
+ return pe
+}
+
+func (pe *PackExpansion) GoString() string {
+ return pe.goString(0, "")
+}
+
+func (pe *PackExpansion) goString(indent int, field string) string {
+ return fmt.Sprintf("%*s%sPackExpansion: Pack: %p\n%s", indent, "", field,
+ pe.Pack, pe.Base.goString(indent+2, "Base: "))
+}
+
+// ArgumentPack is an argument pack.
+type ArgumentPack struct {
+ Args []AST
+}
+
+func (ap *ArgumentPack) print(ps *printState) {
+ for i, a := range ap.Args {
+ if i > 0 {
+ ps.writeString(", ")
+ }
+ ps.print(a)
+ }
+}
+
+func (ap *ArgumentPack) Traverse(fn func(AST) bool) {
+ if fn(ap) {
+ for _, a := range ap.Args {
+ a.Traverse(fn)
+ }
+ }
+}
+
+func (ap *ArgumentPack) Copy(fn func(AST) AST, skip func(AST) bool) AST {
+ if skip(ap) {
+ return nil
+ }
+ args := make([]AST, len(ap.Args))
+ changed := false
+ for i, a := range ap.Args {
+ ac := a.Copy(fn, skip)
+ if ac == nil {
+ args[i] = a
+ } else {
+ args[i] = ac
+ changed = true
+ }
+ }
+ if !changed {
+ return fn(ap)
+ }
+ ap = &ArgumentPack{Args: args}
+ if r := fn(ap); r != nil {
+ return r
+ }
+ return ap
+}
+
+func (ap *ArgumentPack) GoString() string {
+ return ap.goString(0, "")
+}
+
+func (ap *ArgumentPack) goString(indent int, field string) string {
+ if len(ap.Args) == 0 {
+ return fmt.Sprintf("%*s%sArgumentPack: nil", indent, "", field)
+ }
+ s := fmt.Sprintf("%*s%sArgumentPack:", indent, "", field)
+ for i, a := range ap.Args {
+ s += "\n"
+ s += a.goString(indent+2, fmt.Sprintf("%d: ", i))
+ }
+ return s
+}
+
+// SizeofPack is the sizeof operator applied to an argument pack.
+type SizeofPack struct {
+ Pack *ArgumentPack
+}
+
+func (sp *SizeofPack) print(ps *printState) {
+ if ps.llvmStyle {
+ ps.writeString("sizeof...(")
+ ps.print(sp.Pack)
+ ps.writeByte(')')
+ } else {
+ ps.writeString(fmt.Sprintf("%d", len(sp.Pack.Args)))
+ }
+}
+
+func (sp *SizeofPack) Traverse(fn func(AST) bool) {
+ fn(sp)
+ // Don't traverse the pack--it points elsewhere in the AST.
+}
+
+func (sp *SizeofPack) Copy(fn func(AST) AST, skip func(AST) bool) AST {
+ if skip(sp) {
+ return nil
+ }
+ sp = &SizeofPack{Pack: sp.Pack}
+ if r := fn(sp); r != nil {
+ return r
+ }
+ return sp
+}
+
+func (sp *SizeofPack) GoString() string {
+ return sp.goString(0, "")
+}
+
+func (sp *SizeofPack) goString(indent int, field string) string {
+ return fmt.Sprintf("%*s%sSizeofPack: Pack: %p", indent, "", field, sp.Pack)
+}
+
+// SizeofArgs is the size of a captured template parameter pack from
+// an alias template.
+type SizeofArgs struct {
+ Args []AST
+}
+
+func (sa *SizeofArgs) print(ps *printState) {
+ c := 0
+ for _, a := range sa.Args {
+ if ap, ok := a.(*ArgumentPack); ok {
+ c += len(ap.Args)
+ } else if el, ok := a.(*ExprList); ok {
+ c += len(el.Exprs)
+ } else {
+ c++
+ }
+ }
+ ps.writeString(fmt.Sprintf("%d", c))
+}
+
+func (sa *SizeofArgs) Traverse(fn func(AST) bool) {
+ if fn(sa) {
+ for _, a := range sa.Args {
+ a.Traverse(fn)
+ }
+ }
+}
+
+func (sa *SizeofArgs) Copy(fn func(AST) AST, skip func(AST) bool) AST {
+ if skip(sa) {
+ return nil
+ }
+ changed := false
+ args := make([]AST, len(sa.Args))
+ for i, a := range sa.Args {
+ ac := a.Copy(fn, skip)
+ if ac == nil {
+ args[i] = a
+ } else {
+ args[i] = ac
+ changed = true
+ }
+ }
+ if !changed {
+ return fn(sa)
+ }
+ sa = &SizeofArgs{Args: args}
+ if r := fn(sa); r != nil {
+ return r
+ }
+ return sa
+}
+
+func (sa *SizeofArgs) GoString() string {
+ return sa.goString(0, "")
+}
+
+func (sa *SizeofArgs) goString(indent int, field string) string {
+ var args string
+ if len(sa.Args) == 0 {
+ args = fmt.Sprintf("%*sArgs: nil", indent+2, "")
+ } else {
+ args = fmt.Sprintf("%*sArgs:", indent+2, "")
+ for i, a := range sa.Args {
+ args += "\n"
+ args += a.goString(indent+4, fmt.Sprintf("%d: ", i))
+ }
+ }
+ return fmt.Sprintf("%*s%sSizeofArgs:\n%s", indent, "", field, args)
+}
+
+// TemplateParamName is the name of a template parameter that the
+// demangler introduced for a lambda that has explicit template
+// parameters. This is a prefix with an index.
+type TemplateParamName struct {
+ Prefix string
+ Index int
+}
+
+func (tpn *TemplateParamName) print(ps *printState) {
+ ps.writeString(tpn.Prefix)
+ if tpn.Index > 0 {
+ ps.writeString(fmt.Sprintf("%d", tpn.Index-1))
+ }
+}
+
+func (tpn *TemplateParamName) Traverse(fn func(AST) bool) {
+ fn(tpn)
+}
+
+func (tpn *TemplateParamName) Copy(fn func(AST) AST, skip func(AST) bool) AST {
+ if skip(tpn) {
+ return nil
+ }
+ return fn(tpn)
+}
+
+func (tpn *TemplateParamName) GoString() string {
+ return tpn.goString(0, "")
+}
+
+func (tpn *TemplateParamName) goString(indent int, field string) string {
+ name := tpn.Prefix
+ if tpn.Index > 0 {
+ name += fmt.Sprintf("%d", tpn.Index-1)
+ }
+ return fmt.Sprintf("%*s%sTemplateParamName: %s", indent, "", field, name)
+}
+
+// TypeTemplateParam is a type template parameter that appears in a
+// lambda with explicit template parameters.
+type TypeTemplateParam struct {
+ Name AST
+}
+
+func (ttp *TypeTemplateParam) print(ps *printState) {
+ ps.writeString("typename ")
+ ps.printInner(false)
+ ps.print(ttp.Name)
+}
+
+func (ttp *TypeTemplateParam) Traverse(fn func(AST) bool) {
+ if fn(ttp) {
+ ttp.Name.Traverse(fn)
+ }
+}
+
+func (ttp *TypeTemplateParam) Copy(fn func(AST) AST, skip func(AST) bool) AST {
+ if skip(ttp) {
+ return nil
+ }
+ name := ttp.Name.Copy(fn, skip)
+ if name == nil {
+ return fn(ttp)
+ }
+ ttp = &TypeTemplateParam{Name: name}
+ if r := fn(ttp); r != nil {
+ return r
+ }
+ return ttp
+}
+
+func (ttp *TypeTemplateParam) GoString() string {
+ return ttp.goString(0, "")
+}
+
+func (ttp *TypeTemplateParam) goString(indent int, field string) string {
+ return fmt.Sprintf("%*s%sTypeTemplateParam:\n%s", indent, "", field,
+ ttp.Name.goString(indent+2, "Name"))
+}
+
+// NonTypeTemplateParam is a non-type template parameter that appears
+// in a lambda with explicit template parameters.
+type NonTypeTemplateParam struct {
+ Name AST
+ Type AST
+}
+
+func (nttp *NonTypeTemplateParam) print(ps *printState) {
+ ps.inner = append(ps.inner, nttp)
+ ps.print(nttp.Type)
+ if len(ps.inner) > 0 {
+ ps.writeByte(' ')
+ ps.print(nttp.Name)
+ ps.inner = ps.inner[:len(ps.inner)-1]
+ }
+}
+
+func (nttp *NonTypeTemplateParam) printInner(ps *printState) {
+ ps.print(nttp.Name)
+}
+
+func (nttp *NonTypeTemplateParam) Traverse(fn func(AST) bool) {
+ if fn(nttp) {
+ nttp.Name.Traverse(fn)
+ nttp.Type.Traverse(fn)
+ }
+}
+
+func (nttp *NonTypeTemplateParam) Copy(fn func(AST) AST, skip func(AST) bool) AST {
+ if skip(nttp) {
+ return nil
+ }
+ name := nttp.Name.Copy(fn, skip)
+ typ := nttp.Type.Copy(fn, skip)
+ if name == nil && typ == nil {
+ return fn(nttp)
+ }
+ if name == nil {
+ name = nttp.Name
+ }
+ if typ == nil {
+ typ = nttp.Type
+ }
+ nttp = &NonTypeTemplateParam{Name: name, Type: typ}
+ if r := fn(nttp); r != nil {
+ return r
+ }
+ return nttp
+}
+
+func (nttp *NonTypeTemplateParam) GoString() string {
+ return nttp.goString(0, "")
+}
+
+func (nttp *NonTypeTemplateParam) goString(indent int, field string) string {
+ return fmt.Sprintf("%*s%sNonTypeTemplateParam:\n%s\n%s", indent, "", field,
+ nttp.Name.goString(indent+2, "Name: "),
+ nttp.Type.goString(indent+2, "Type: "))
+}
+
+// TemplateTemplateParam is a template template parameter that appears
+// in a lambda with explicit template parameters.
+type TemplateTemplateParam struct {
+ Name AST
+ Params []AST
+}
+
+func (ttp *TemplateTemplateParam) print(ps *printState) {
+ ps.writeString("template<")
+ for i, param := range ttp.Params {
+ if i > 0 {
+ ps.writeString(", ")
+ }
+ ps.print(param)
+ }
+ ps.writeString("> typename ")
+ ps.print(ttp.Name)
+}
+
+func (ttp *TemplateTemplateParam) Traverse(fn func(AST) bool) {
+ if fn(ttp) {
+ ttp.Name.Traverse(fn)
+ for _, param := range ttp.Params {
+ param.Traverse(fn)
+ }
+ }
+}
+
+func (ttp *TemplateTemplateParam) Copy(fn func(AST) AST, skip func(AST) bool) AST {
+ if skip(ttp) {
+ return nil
+ }
+
+ changed := false
+
+ name := ttp.Name.Copy(fn, skip)
+ if name == nil {
+ name = ttp.Name
+ } else {
+ changed = true
+ }
+
+ params := make([]AST, len(ttp.Params))
+ for i, p := range ttp.Params {
+ pc := p.Copy(fn, skip)
+ if pc == nil {
+ params[i] = p
+ } else {
+ params[i] = pc
+ changed = true
+ }
+ }
+
+ if !changed {
+ return fn(ttp)
+ }
+
+ ttp = &TemplateTemplateParam{
+ Name: name,
+ Params: params,
+ }
+ if r := fn(ttp); r != nil {
+ return r
+ }
+ return ttp
+}
+
+func (ttp *TemplateTemplateParam) GoString() string {
+ return ttp.goString(0, "")
+}
+
+func (ttp *TemplateTemplateParam) goString(indent int, field string) string {
+ var params strings.Builder
+ fmt.Fprintf(&params, "%*sParams:", indent+2, "")
+ for i, p := range ttp.Params {
+ params.WriteByte('\n')
+ params.WriteString(p.goString(indent+4, fmt.Sprintf("%d: ", i)))
+ }
+ return fmt.Sprintf("%*s%sTemplateTemplateParam:\n%s\n%s", indent, "", field,
+ ttp.Name.goString(indent+2, "Name: "),
+ params.String())
+}
+
+// TemplateParamPack is a template parameter pack that appears in a
+// lambda with explicit template parameters.
+type TemplateParamPack struct {
+ Param AST
+}
+
+func (tpp *TemplateParamPack) print(ps *printState) {
+ holdInner := ps.inner
+ defer func() { ps.inner = holdInner }()
+
+ ps.inner = []AST{tpp}
+ if nttp, ok := tpp.Param.(*NonTypeTemplateParam); ok {
+ ps.print(nttp.Type)
+ } else {
+ ps.print(tpp.Param)
+ }
+ if len(ps.inner) > 0 {
+ ps.writeString("...")
+ }
+}
+
+func (tpp *TemplateParamPack) printInner(ps *printState) {
+ ps.writeString("...")
+ if nttp, ok := tpp.Param.(*NonTypeTemplateParam); ok {
+ ps.print(nttp.Name)
+ }
+}
+
+func (tpp *TemplateParamPack) Traverse(fn func(AST) bool) {
+ if fn(tpp) {
+ tpp.Param.Traverse(fn)
+ }
+}
+
+func (tpp *TemplateParamPack) Copy(fn func(AST) AST, skip func(AST) bool) AST {
+ if skip(tpp) {
+ return nil
+ }
+ param := tpp.Param.Copy(fn, skip)
+ if param == nil {
+ return fn(tpp)
+ }
+ tpp = &TemplateParamPack{Param: param}
+ if r := fn(tpp); r != nil {
+ return r
+ }
+ return tpp
+}
+
+func (tpp *TemplateParamPack) GoString() string {
+ return tpp.goString(0, "")
+}
+
+func (tpp *TemplateParamPack) goString(indent int, field string) string {
+ return fmt.Sprintf("%*s%sTemplateParamPack:\n%s", indent, "", field,
+ tpp.Param.goString(indent+2, "Param: "))
+}
+
+// Cast is a type cast.
+type Cast struct {
+ To AST
+}
+
+func (c *Cast) print(ps *printState) {
+ ps.writeString("operator ")
+ ps.print(c.To)
+}
+
+func (c *Cast) Traverse(fn func(AST) bool) {
+ if fn(c) {
+ c.To.Traverse(fn)
+ }
+}
+
+func (c *Cast) Copy(fn func(AST) AST, skip func(AST) bool) AST {
+ if skip(c) {
+ return nil
+ }
+ to := c.To.Copy(fn, skip)
+ if to == nil {
+ return fn(c)
+ }
+ c = &Cast{To: to}
+ if r := fn(c); r != nil {
+ return r
+ }
+ return c
+}
+
+func (c *Cast) GoString() string {
+ return c.goString(0, "")
+}
+
+func (c *Cast) goString(indent int, field string) string {
+ return fmt.Sprintf("%*s%sCast\n%s", indent, "", field,
+ c.To.goString(indent+2, "To: "))
+}
+
+// The parenthesize function prints the string for val, wrapped in
+// parentheses if necessary.
+func parenthesize(ps *printState, val AST) {
+ paren := false
+ switch v := val.(type) {
+ case *Name, *InitializerList:
+ case *FunctionParam:
+ if ps.llvmStyle {
+ paren = true
+ }
+ case *Qualified:
+ if v.LocalName {
+ paren = true
+ }
+ default:
+ paren = true
+ }
+ if paren {
+ ps.writeByte('(')
+ }
+ ps.print(val)
+ if paren {
+ ps.writeByte(')')
+ }
+}
+
+// Nullary is an operator in an expression with no arguments, such as
+// throw.
+type Nullary struct {
+ Op AST
+}
+
+func (n *Nullary) print(ps *printState) {
+ if op, ok := n.Op.(*Operator); ok {
+ ps.writeString(op.Name)
+ } else {
+ ps.print(n.Op)
+ }
+}
+
+func (n *Nullary) Traverse(fn func(AST) bool) {
+ if fn(n) {
+ n.Op.Traverse(fn)
+ }
+}
+
+func (n *Nullary) Copy(fn func(AST) AST, skip func(AST) bool) AST {
+ if skip(n) {
+ return nil
+ }
+ op := n.Op.Copy(fn, skip)
+ if op == nil {
+ return fn(n)
+ }
+ n = &Nullary{Op: op}
+ if r := fn(n); r != nil {
+ return r
+ }
+ return n
+}
+
+func (n *Nullary) GoString() string {
+ return n.goString(0, "")
+}
+
+func (n *Nullary) goString(indent int, field string) string {
+ return fmt.Sprintf("%*s%sNullary:\n%s", indent, "", field,
+ n.Op.goString(indent+2, "Op: "))
+}
+
+// Unary is a unary operation in an expression.
+type Unary struct {
+ Op AST
+ Expr AST
+ Suffix bool // true for ++ -- when used as postfix
+ SizeofType bool // true for sizeof (type)
+}
+
+func (u *Unary) print(ps *printState) {
+ op, _ := u.Op.(*Operator)
+ expr := u.Expr
+
+ // Don't print the argument list when taking the address of a
+ // function.
+ if !ps.llvmStyle {
+ if op != nil && op.Name == "&" {
+ if t, ok := expr.(*Typed); ok {
+ if _, ok := t.Type.(*FunctionType); ok {
+ expr = t.Name
+ }
+ }
+ }
+ }
+
+ if u.Suffix {
+ parenthesize(ps, expr)
+ }
+
+ if op != nil {
+ ps.writeString(op.Name)
+ if ps.llvmStyle && op.Name == "noexcept" {
+ ps.writeByte(' ')
+ }
+ } else if c, ok := u.Op.(*Cast); ok {
+ ps.writeByte('(')
+ ps.print(c.To)
+ ps.writeByte(')')
+ } else {
+ ps.print(u.Op)
+ }
+
+ if !u.Suffix {
+ isDelete := op != nil && (op.Name == "delete " || op.Name == "delete[] ")
+ if op != nil && op.Name == "::" {
+ // Don't use parentheses after ::.
+ ps.print(expr)
+ } else if u.SizeofType {
+ // Always use parentheses for sizeof argument.
+ ps.writeByte('(')
+ ps.print(expr)
+ ps.writeByte(')')
+ } else if op != nil && op.Name == "__alignof__" {
+ // Always use parentheses for __alignof__ argument.
+ ps.writeByte('(')
+ ps.print(expr)
+ ps.writeByte(')')
+ } else if ps.llvmStyle {
+ if op == nil || (op.Name != `operator"" ` && !isDelete) {
+ ps.writeByte('(')
+ }
+ ps.print(expr)
+ if op == nil || (op.Name != `operator"" ` && !isDelete) {
+ ps.writeByte(')')
+ }
+ } else {
+ parenthesize(ps, expr)
+ }
+ }
+}
+
+func (u *Unary) Traverse(fn func(AST) bool) {
+ if fn(u) {
+ u.Op.Traverse(fn)
+ u.Expr.Traverse(fn)
+ }
+}
+
+func (u *Unary) Copy(fn func(AST) AST, skip func(AST) bool) AST {
+ if skip(u) {
+ return nil
+ }
+ op := u.Op.Copy(fn, skip)
+ expr := u.Expr.Copy(fn, skip)
+ if op == nil && expr == nil {
+ return fn(u)
+ }
+ if op == nil {
+ op = u.Op
+ }
+ if expr == nil {
+ expr = u.Expr
+ }
+ u = &Unary{Op: op, Expr: expr, Suffix: u.Suffix, SizeofType: u.SizeofType}
+ if r := fn(u); r != nil {
+ return r
+ }
+ return u
+}
+
+func (u *Unary) GoString() string {
+ return u.goString(0, "")
+}
+
+func (u *Unary) goString(indent int, field string) string {
+ var s string
+ if u.Suffix {
+ s = " Suffix: true"
+ }
+ if u.SizeofType {
+ s += " SizeofType: true"
+ }
+ return fmt.Sprintf("%*s%sUnary:%s\n%s\n%s", indent, "", field,
+ s, u.Op.goString(indent+2, "Op: "),
+ u.Expr.goString(indent+2, "Expr: "))
+}
+
+// isDesignatedInitializer reports whether x is a designated
+// initializer.
+func isDesignatedInitializer(x AST) bool {
+ switch x := x.(type) {
+ case *Binary:
+ if op, ok := x.Op.(*Operator); ok {
+ if op.Name == "]=" {
+ return true
+ }
+ if op.Name != "=" {
+ return false
+ }
+ if _, ok := x.Left.(*Literal); ok {
+ return false
+ }
+ return true
+ }
+ case *Trinary:
+ if op, ok := x.Op.(*Operator); ok {
+ return op.Name == "[...]="
+ }
+ }
+ return false
+}
+
+// Binary is a binary operation in an expression.
+type Binary struct {
+ Op AST
+ Left AST
+ Right AST
+}
+
+func (b *Binary) print(ps *printState) {
+ op, _ := b.Op.(*Operator)
+
+ if op != nil && strings.Contains(op.Name, "cast") {
+ ps.writeString(op.Name)
+ ps.writeByte('<')
+ ps.print(b.Left)
+ ps.writeString(">(")
+ ps.print(b.Right)
+ ps.writeByte(')')
+ return
+ }
+
+ if isDesignatedInitializer(b) {
+ if op.Name == "=" {
+ ps.writeByte('.')
+ } else {
+ ps.writeByte('[')
+ }
+ ps.print(b.Left)
+ if op.Name == "]=" {
+ ps.writeByte(']')
+ }
+ if isDesignatedInitializer(b.Right) {
+ // Don't add anything between designated
+ // initializer chains.
+ ps.print(b.Right)
+ } else {
+ if ps.llvmStyle {
+ ps.writeString(" = ")
+ ps.print(b.Right)
+ } else {
+ ps.writeByte('=')
+ parenthesize(ps, b.Right)
+ }
+ }
+ return
+ }
+
+ // Use an extra set of parentheses around an expression that
+ // uses the greater-than operator, so that it does not get
+ // confused with the '>' that ends template parameters.
+ if op != nil && op.Name == ">" {
+ ps.writeByte('(')
+ }
+
+ left := b.Left
+
+ skipParens := false
+ skipBothParens := false
+ addSpaces := ps.llvmStyle
+ if ps.llvmStyle && op != nil {
+ switch op.Name {
+ case ".", "->":
+ skipBothParens = true
+ addSpaces = false
+ case "->*":
+ skipParens = true
+ addSpaces = false
+ }
+ }
+
+ // For a function call in an expression, don't print the types
+ // of the arguments unless there is a return type.
+ if op != nil && op.Name == "()" {
+ if ty, ok := b.Left.(*Typed); ok {
+ if ft, ok := ty.Type.(*FunctionType); ok {
+ if ft.Return == nil {
+ left = ty.Name
+ } else {
+ skipParens = true
+ }
+ } else {
+ left = ty.Name
+ }
+ }
+ if ps.llvmStyle {
+ skipParens = true
+ }
+ }
+
+ if skipParens || skipBothParens {
+ ps.print(left)
+ } else if ps.llvmStyle {
+ ps.writeByte('(')
+ ps.print(left)
+ ps.writeByte(')')
+ } else {
+ parenthesize(ps, left)
+ }
+
+ if op != nil && op.Name == "[]" {
+ ps.writeByte('[')
+ ps.print(b.Right)
+ ps.writeByte(']')
+ return
+ }
+
+ if op != nil {
+ if op.Name != "()" {
+ if addSpaces {
+ ps.writeByte(' ')
+ }
+ ps.writeString(op.Name)
+ if addSpaces {
+ ps.writeByte(' ')
+ }
+ }
+ } else {
+ ps.print(b.Op)
+ }
+
+ if skipBothParens {
+ ps.print(b.Right)
+ } else if ps.llvmStyle {
+ ps.writeByte('(')
+ ps.print(b.Right)
+ ps.writeByte(')')
+ } else {
+ parenthesize(ps, b.Right)
+ }
+
+ if op != nil && op.Name == ">" {
+ ps.writeByte(')')
+ }
+}
+
+func (b *Binary) Traverse(fn func(AST) bool) {
+ if fn(b) {
+ b.Op.Traverse(fn)
+ b.Left.Traverse(fn)
+ b.Right.Traverse(fn)
+ }
+}
+
+func (b *Binary) Copy(fn func(AST) AST, skip func(AST) bool) AST {
+ if skip(b) {
+ return nil
+ }
+ op := b.Op.Copy(fn, skip)
+ left := b.Left.Copy(fn, skip)
+ right := b.Right.Copy(fn, skip)
+ if op == nil && left == nil && right == nil {
+ return fn(b)
+ }
+ if op == nil {
+ op = b.Op
+ }
+ if left == nil {
+ left = b.Left
+ }
+ if right == nil {
+ right = b.Right
+ }
+ b = &Binary{Op: op, Left: left, Right: right}
+ if r := fn(b); r != nil {
+ return r
+ }
+ return b
+}
+
+func (b *Binary) GoString() string {
+ return b.goString(0, "")
+}
+
+func (b *Binary) goString(indent int, field string) string {
+ return fmt.Sprintf("%*s%sBinary:\n%s\n%s\n%s", indent, "", field,
+ b.Op.goString(indent+2, "Op: "),
+ b.Left.goString(indent+2, "Left: "),
+ b.Right.goString(indent+2, "Right: "))
+}
+
+// Trinary is the ?: trinary operation in an expression.
+type Trinary struct {
+ Op AST
+ First AST
+ Second AST
+ Third AST
+}
+
+func (t *Trinary) print(ps *printState) {
+ if isDesignatedInitializer(t) {
+ ps.writeByte('[')
+ ps.print(t.First)
+ ps.writeString(" ... ")
+ ps.print(t.Second)
+ ps.writeByte(']')
+ if isDesignatedInitializer(t.Third) {
+ // Don't add anything between designated
+ // initializer chains.
+ ps.print(t.Third)
+ } else {
+ if ps.llvmStyle {
+ ps.writeString(" = ")
+ ps.print(t.Third)
+ } else {
+ ps.writeByte('=')
+ parenthesize(ps, t.Third)
+ }
+ }
+ return
+ }
+
+ parenthesize(ps, t.First)
+ if ps.llvmStyle {
+ ps.writeString(" ? ")
+ } else {
+ ps.writeByte('?')
+ }
+ parenthesize(ps, t.Second)
+ ps.writeString(" : ")
+ parenthesize(ps, t.Third)
+}
+
+func (t *Trinary) Traverse(fn func(AST) bool) {
+ if fn(t) {
+ t.Op.Traverse(fn)
+ t.First.Traverse(fn)
+ t.Second.Traverse(fn)
+ t.Third.Traverse(fn)
+ }
+}
+
+func (t *Trinary) Copy(fn func(AST) AST, skip func(AST) bool) AST {
+ if skip(t) {
+ return nil
+ }
+ op := t.Op.Copy(fn, skip)
+ first := t.First.Copy(fn, skip)
+ second := t.Second.Copy(fn, skip)
+ third := t.Third.Copy(fn, skip)
+ if op == nil && first == nil && second == nil && third == nil {
+ return fn(t)
+ }
+ if op == nil {
+ op = t.Op
+ }
+ if first == nil {
+ first = t.First
+ }
+ if second == nil {
+ second = t.Second
+ }
+ if third == nil {
+ third = t.Third
+ }
+ t = &Trinary{Op: op, First: first, Second: second, Third: third}
+ if r := fn(t); r != nil {
+ return r
+ }
+ return t
+}
+
+func (t *Trinary) GoString() string {
+ return t.goString(0, "")
+}
+
+func (t *Trinary) goString(indent int, field string) string {
+ return fmt.Sprintf("%*s%sTrinary:\n%s\n%s\n%s\n%s", indent, "", field,
+ t.Op.goString(indent+2, "Op: "),
+ t.First.goString(indent+2, "First: "),
+ t.Second.goString(indent+2, "Second: "),
+ t.Third.goString(indent+2, "Third: "))
+}
+
+// Fold is a C++17 fold-expression. Arg2 is nil for a unary operator.
+type Fold struct {
+ Left bool
+ Op AST
+ Arg1 AST
+ Arg2 AST
+}
+
+func (f *Fold) print(ps *printState) {
+ op, _ := f.Op.(*Operator)
+ printOp := func() {
+ if op != nil {
+ if ps.llvmStyle {
+ ps.writeByte(' ')
+ }
+ ps.writeString(op.Name)
+ if ps.llvmStyle {
+ ps.writeByte(' ')
+ }
+ } else {
+ ps.print(f.Op)
+ }
+ }
+ foldParenthesize := func(a AST) {
+ if _, ok := a.(*ArgumentPack); ok || !ps.llvmStyle {
+ parenthesize(ps, a)
+ } else {
+ ps.print(a)
+ }
+ }
+
+ if f.Arg2 == nil {
+ if f.Left {
+ ps.writeString("(...")
+ printOp()
+ foldParenthesize(f.Arg1)
+ ps.writeString(")")
+ } else {
+ ps.writeString("(")
+ foldParenthesize(f.Arg1)
+ printOp()
+ ps.writeString("...)")
+ }
+ } else {
+ ps.writeString("(")
+ foldParenthesize(f.Arg1)
+ printOp()
+ ps.writeString("...")
+ printOp()
+ foldParenthesize(f.Arg2)
+ ps.writeString(")")
+ }
+}
+
+func (f *Fold) Traverse(fn func(AST) bool) {
+ if fn(f) {
+ f.Op.Traverse(fn)
+ f.Arg1.Traverse(fn)
+ if f.Arg2 != nil {
+ f.Arg2.Traverse(fn)
+ }
+ }
+}
+
+func (f *Fold) Copy(fn func(AST) AST, skip func(AST) bool) AST {
+ if skip(f) {
+ return nil
+ }
+ op := f.Op.Copy(fn, skip)
+ arg1 := f.Arg1.Copy(fn, skip)
+ var arg2 AST
+ if f.Arg2 != nil {
+ arg2 = f.Arg2.Copy(fn, skip)
+ }
+ if op == nil && arg1 == nil && arg2 == nil {
+ return fn(f)
+ }
+ if op == nil {
+ op = f.Op
+ }
+ if arg1 == nil {
+ arg1 = f.Arg1
+ }
+ if arg2 == nil {
+ arg2 = f.Arg2
+ }
+ f = &Fold{Left: f.Left, Op: op, Arg1: arg1, Arg2: arg2}
+ if r := fn(f); r != nil {
+ return r
+ }
+ return f
+}
+
+func (f *Fold) GoString() string {
+ return f.goString(0, "")
+}
+
+func (f *Fold) goString(indent int, field string) string {
+ if f.Arg2 == nil {
+ return fmt.Sprintf("%*s%sFold: Left: %t\n%s\n%s", indent, "", field,
+ f.Left, f.Op.goString(indent+2, "Op: "),
+ f.Arg1.goString(indent+2, "Arg1: "))
+ } else {
+ return fmt.Sprintf("%*s%sFold: Left: %t\n%s\n%s\n%s", indent, "", field,
+ f.Left, f.Op.goString(indent+2, "Op: "),
+ f.Arg1.goString(indent+2, "Arg1: "),
+ f.Arg2.goString(indent+2, "Arg2: "))
+ }
+}
+
+// Subobject is a a reference to an offset in an expression. This is
+// used for C++20 manglings of class types used as the type of
+// non-type template arguments.
+//
+// See https://github.com/itanium-cxx-abi/cxx-abi/issues/47.
+type Subobject struct {
+ Type AST
+ SubExpr AST
+ Offset int
+ Selectors []int
+ PastEnd bool
+}
+
+func (so *Subobject) print(ps *printState) {
+ ps.print(so.SubExpr)
+ ps.writeString(".<")
+ ps.print(so.Type)
+ ps.writeString(fmt.Sprintf(" at offset %d>", so.Offset))
+}
+
+func (so *Subobject) Traverse(fn func(AST) bool) {
+ if fn(so) {
+ so.Type.Traverse(fn)
+ so.SubExpr.Traverse(fn)
+ }
+}
+
+func (so *Subobject) Copy(fn func(AST) AST, skip func(AST) bool) AST {
+ if skip(so) {
+ return nil
+ }
+ typ := so.Type.Copy(fn, skip)
+ subExpr := so.SubExpr.Copy(fn, skip)
+ if typ == nil && subExpr == nil {
+ return nil
+ }
+ if typ == nil {
+ typ = so.Type
+ }
+ if subExpr == nil {
+ subExpr = so.SubExpr
+ }
+ so = &Subobject{
+ Type: typ,
+ SubExpr: subExpr,
+ Offset: so.Offset,
+ Selectors: so.Selectors,
+ PastEnd: so.PastEnd,
+ }
+ if r := fn(so); r != nil {
+ return r
+ }
+ return so
+}
+
+func (so *Subobject) GoString() string {
+ return so.goString(0, "")
+}
+
+func (so *Subobject) goString(indent int, field string) string {
+ var selectors string
+ for _, s := range so.Selectors {
+ selectors += fmt.Sprintf(" %d", s)
+ }
+ return fmt.Sprintf("%*s%sSubobject:\n%s\n%s\n%*sOffset: %d\n%*sSelectors:%s\n%*sPastEnd: %t",
+ indent, "", field,
+ so.Type.goString(indent+2, "Type: "),
+ so.SubExpr.goString(indent+2, "SubExpr: "),
+ indent+2, "", so.Offset,
+ indent+2, "", selectors,
+ indent+2, "", so.PastEnd)
+}
+
+// PtrMemCast is a conversion of an expression to a pointer-to-member
+// type. This is used for C++20 manglings of class types used as the
+// type of non-type template arguments.
+//
+// See https://github.com/itanium-cxx-abi/cxx-abi/issues/47.
+type PtrMemCast struct {
+ Type AST
+ Expr AST
+ Offset int
+}
+
+func (pmc *PtrMemCast) print(ps *printState) {
+ ps.writeString("(")
+ ps.print(pmc.Type)
+ ps.writeString(")(")
+ ps.print(pmc.Expr)
+ ps.writeString(")")
+}
+
+func (pmc *PtrMemCast) Traverse(fn func(AST) bool) {
+ if fn(pmc) {
+ pmc.Type.Traverse(fn)
+ pmc.Expr.Traverse(fn)
+ }
+}
+
+func (pmc *PtrMemCast) Copy(fn func(AST) AST, skip func(AST) bool) AST {
+ if skip(pmc) {
+ return nil
+ }
+ typ := pmc.Type.Copy(fn, skip)
+ expr := pmc.Expr.Copy(fn, skip)
+ if typ == nil && expr == nil {
+ return nil
+ }
+ if typ == nil {
+ typ = pmc.Type
+ }
+ if expr == nil {
+ expr = pmc.Expr
+ }
+ pmc = &PtrMemCast{
+ Type: typ,
+ Expr: expr,
+ Offset: pmc.Offset,
+ }
+ if r := fn(pmc); r != nil {
+ return r
+ }
+ return pmc
+}
+
+func (pmc *PtrMemCast) GoString() string {
+ return pmc.goString(0, "")
+}
+
+func (pmc *PtrMemCast) goString(indent int, field string) string {
+ return fmt.Sprintf("%*s%sPtrMemCast:\n%s\n%s\n%*sOffset: %d",
+ indent, "", field,
+ pmc.Type.goString(indent+2, "Type: "),
+ pmc.Expr.goString(indent+2, "Expr: "),
+ indent+2, "", pmc.Offset)
+}
+
+// New is a use of operator new in an expression.
+type New struct {
+ Op AST
+ Place AST
+ Type AST
+ Init AST
+}
+
+func (n *New) print(ps *printState) {
+ if !ps.llvmStyle {
+ // Op doesn't really matter for printing--we always print "new".
+ ps.writeString("new ")
+ } else {
+ op, _ := n.Op.(*Operator)
+ if op != nil {
+ ps.writeString(op.Name)
+ if n.Place == nil {
+ ps.writeByte(' ')
+ }
+ } else {
+ ps.print(n.Op)
+ }
+ }
+ if n.Place != nil {
+ parenthesize(ps, n.Place)
+ ps.writeByte(' ')
+ }
+ ps.print(n.Type)
+ if n.Init != nil {
+ parenthesize(ps, n.Init)
+ }
+}
+
+func (n *New) Traverse(fn func(AST) bool) {
+ if fn(n) {
+ n.Op.Traverse(fn)
+ if n.Place != nil {
+ n.Place.Traverse(fn)
+ }
+ n.Type.Traverse(fn)
+ if n.Init != nil {
+ n.Init.Traverse(fn)
+ }
+ }
+}
+
+func (n *New) Copy(fn func(AST) AST, skip func(AST) bool) AST {
+ if skip(n) {
+ return nil
+ }
+ op := n.Op.Copy(fn, skip)
+ var place AST
+ if n.Place != nil {
+ place = n.Place.Copy(fn, skip)
+ }
+ typ := n.Type.Copy(fn, skip)
+ var ini AST
+ if n.Init != nil {
+ ini = n.Init.Copy(fn, skip)
+ }
+ if op == nil && place == nil && typ == nil && ini == nil {
+ return fn(n)
+ }
+ if op == nil {
+ op = n.Op
+ }
+ if place == nil {
+ place = n.Place
+ }
+ if typ == nil {
+ typ = n.Type
+ }
+ if ini == nil {
+ ini = n.Init
+ }
+ n = &New{Op: op, Place: place, Type: typ, Init: ini}
+ if r := fn(n); r != nil {
+ return r
+ }
+ return n
+}
+
+func (n *New) GoString() string {
+ return n.goString(0, "")
+}
+
+func (n *New) goString(indent int, field string) string {
+ var place string
+ if n.Place == nil {
+ place = fmt.Sprintf("%*sPlace: nil", indent, "")
+ } else {
+ place = n.Place.goString(indent+2, "Place: ")
+ }
+ var ini string
+ if n.Init == nil {
+ ini = fmt.Sprintf("%*sInit: nil", indent, "")
+ } else {
+ ini = n.Init.goString(indent+2, "Init: ")
+ }
+ return fmt.Sprintf("%*s%sNew:\n%s\n%s\n%s\n%s", indent, "", field,
+ n.Op.goString(indent+2, "Op: "), place,
+ n.Type.goString(indent+2, "Type: "), ini)
+}
+
+// Literal is a literal in an expression.
+type Literal struct {
+ Type AST
+ Val string
+ Neg bool
+}
+
+// Suffixes to use for constants of the given integer type.
+var builtinTypeSuffix = map[string]string{
+ "int": "",
+ "unsigned int": "u",
+ "long": "l",
+ "unsigned long": "ul",
+ "long long": "ll",
+ "unsigned long long": "ull",
+}
+
+// Builtin float types.
+var builtinTypeFloat = map[string]bool{
+ "double": true,
+ "long double": true,
+ "float": true,
+ "__float128": true,
+ "half": true,
+}
+
+func (l *Literal) print(ps *printState) {
+ isFloat := false
+ if b, ok := l.Type.(*BuiltinType); ok {
+ if suffix, ok := builtinTypeSuffix[b.Name]; ok {
+ if l.Neg {
+ ps.writeByte('-')
+ }
+ ps.writeString(l.Val)
+ ps.writeString(suffix)
+ return
+ } else if b.Name == "bool" && !l.Neg {
+ switch l.Val {
+ case "0":
+ ps.writeString("false")
+ return
+ case "1":
+ ps.writeString("true")
+ return
+ }
+ } else if b.Name == "decltype(nullptr)" && l.Val == "" {
+ if ps.llvmStyle {
+ ps.writeString("nullptr")
+ } else {
+ ps.print(l.Type)
+ }
+ return
+ } else {
+ isFloat = builtinTypeFloat[b.Name]
+ }
+ }
+
+ ps.writeByte('(')
+ ps.print(l.Type)
+ ps.writeByte(')')
+
+ if isFloat {
+ ps.writeByte('[')
+ }
+ if l.Neg {
+ ps.writeByte('-')
+ }
+ ps.writeString(l.Val)
+ if isFloat {
+ ps.writeByte(']')
+ }
+}
+
+func (l *Literal) Traverse(fn func(AST) bool) {
+ if fn(l) {
+ l.Type.Traverse(fn)
+ }
+}
+
+func (l *Literal) Copy(fn func(AST) AST, skip func(AST) bool) AST {
+ if skip(l) {
+ return nil
+ }
+ typ := l.Type.Copy(fn, skip)
+ if typ == nil {
+ return fn(l)
+ }
+ l = &Literal{Type: typ, Val: l.Val, Neg: l.Neg}
+ if r := fn(l); r != nil {
+ return r
+ }
+ return l
+}
+
+func (l *Literal) GoString() string {
+ return l.goString(0, "")
+}
+
+func (l *Literal) goString(indent int, field string) string {
+ var neg string
+ if l.Neg {
+ neg = " Neg: true"
+ }
+ return fmt.Sprintf("%*s%sLiteral:%s\n%s\n%*sVal: %s", indent, "", field,
+ neg, l.Type.goString(indent+2, "Type: "),
+ indent+2, "", l.Val)
+}
+
+// StringLiteral is a string literal.
+type StringLiteral struct {
+ Type AST
+}
+
+func (sl *StringLiteral) print(ps *printState) {
+ ps.writeString(`"<`)
+ sl.Type.print(ps)
+ ps.writeString(`>"`)
+}
+
+func (sl *StringLiteral) Traverse(fn func(AST) bool) {
+ if fn(sl) {
+ sl.Type.Traverse(fn)
+ }
+}
+
+func (sl *StringLiteral) Copy(fn func(AST) AST, skip func(AST) bool) AST {
+ if skip(sl) {
+ return nil
+ }
+ typ := sl.Type.Copy(fn, skip)
+ if typ == nil {
+ return fn(sl)
+ }
+ sl = &StringLiteral{Type: typ}
+ if r := fn(sl); r != nil {
+ return r
+ }
+ return sl
+}
+
+func (sl *StringLiteral) GoString() string {
+ return sl.goString(0, "")
+}
+
+func (sl *StringLiteral) goString(indent int, field string) string {
+ return fmt.Sprintf("%*s%sStringLiteral:\n%s", indent, "", field,
+ sl.Type.goString(indent+2, ""))
+}
+
+// LambdaExpr is a literal that is a lambda expression.
+type LambdaExpr struct {
+ Type AST
+}
+
+func (le *LambdaExpr) print(ps *printState) {
+ ps.writeString("[]")
+ if cl, ok := le.Type.(*Closure); ok {
+ cl.printTypes(ps)
+ }
+ ps.writeString("{...}")
+}
+
+func (le *LambdaExpr) Traverse(fn func(AST) bool) {
+ if fn(le) {
+ le.Type.Traverse(fn)
+ }
+}
+
+func (le *LambdaExpr) Copy(fn func(AST) AST, skip func(AST) bool) AST {
+ if skip(le) {
+ return nil
+ }
+ typ := le.Type.Copy(fn, skip)
+ if typ == nil {
+ return fn(le)
+ }
+ le = &LambdaExpr{Type: typ}
+ if r := fn(le); r != nil {
+ return r
+ }
+ return le
+}
+
+func (le *LambdaExpr) GoString() string {
+ return le.goString(0, "")
+}
+
+func (le *LambdaExpr) goString(indent int, field string) string {
+ return fmt.Sprintf("%*s%sLambdaExpr:\n%s", indent, "", field,
+ le.Type.goString(indent+2, ""))
+}
+
+// ExprList is a list of expressions, typically arguments to a
+// function call in an expression.
+type ExprList struct {
+ Exprs []AST
+}
+
+func (el *ExprList) print(ps *printState) {
+ for i, e := range el.Exprs {
+ if i > 0 {
+ ps.writeString(", ")
+ }
+ ps.print(e)
+ }
+}
+
+func (el *ExprList) Traverse(fn func(AST) bool) {
+ if fn(el) {
+ for _, e := range el.Exprs {
+ e.Traverse(fn)
+ }
+ }
+}
+
+func (el *ExprList) Copy(fn func(AST) AST, skip func(AST) bool) AST {
+ if skip(el) {
+ return nil
+ }
+ exprs := make([]AST, len(el.Exprs))
+ changed := false
+ for i, e := range el.Exprs {
+ ec := e.Copy(fn, skip)
+ if ec == nil {
+ exprs[i] = e
+ } else {
+ exprs[i] = ec
+ changed = true
+ }
+ }
+ if !changed {
+ return fn(el)
+ }
+ el = &ExprList{Exprs: exprs}
+ if r := fn(el); r != nil {
+ return r
+ }
+ return el
+}
+
+func (el *ExprList) GoString() string {
+ return el.goString(0, "")
+}
+
+func (el *ExprList) goString(indent int, field string) string {
+ if len(el.Exprs) == 0 {
+ return fmt.Sprintf("%*s%sExprList: nil", indent, "", field)
+ }
+ s := fmt.Sprintf("%*s%sExprList:", indent, "", field)
+ for i, e := range el.Exprs {
+ s += "\n"
+ s += e.goString(indent+2, fmt.Sprintf("%d: ", i))
+ }
+ return s
+}
+
+// InitializerList is an initializer list: an optional type with a
+// list of expressions.
+type InitializerList struct {
+ Type AST
+ Exprs AST
+}
+
+func (il *InitializerList) print(ps *printState) {
+ if il.Type != nil {
+ ps.print(il.Type)
+ }
+ ps.writeByte('{')
+ ps.print(il.Exprs)
+ ps.writeByte('}')
+}
+
+func (il *InitializerList) Traverse(fn func(AST) bool) {
+ if fn(il) {
+ if il.Type != nil {
+ il.Type.Traverse(fn)
+ }
+ il.Exprs.Traverse(fn)
+ }
+}
+
+func (il *InitializerList) Copy(fn func(AST) AST, skip func(AST) bool) AST {
+ if skip(il) {
+ return nil
+ }
+ var typ AST
+ if il.Type != nil {
+ typ = il.Type.Copy(fn, skip)
+ }
+ exprs := il.Exprs.Copy(fn, skip)
+ if typ == nil && exprs == nil {
+ return fn(il)
+ }
+ if typ == nil {
+ typ = il.Type
+ }
+ if exprs == nil {
+ exprs = il.Exprs
+ }
+ il = &InitializerList{Type: typ, Exprs: exprs}
+ if r := fn(il); r != nil {
+ return r
+ }
+ return il
+}
+
+func (il *InitializerList) GoString() string {
+ return il.goString(0, "")
+}
+
+func (il *InitializerList) goString(indent int, field string) string {
+ var t string
+ if il.Type == nil {
+ t = fmt.Sprintf("%*sType: nil", indent+2, "")
+ } else {
+ t = il.Type.goString(indent+2, "Type: ")
+ }
+ return fmt.Sprintf("%*s%sInitializerList:\n%s\n%s", indent, "", field,
+ t, il.Exprs.goString(indent+2, "Exprs: "))
+}
+
+// DefaultArg holds a default argument for a local name.
+type DefaultArg struct {
+ Num int
+ Arg AST
+}
+
+func (da *DefaultArg) print(ps *printState) {
+ if !ps.llvmStyle {
+ fmt.Fprintf(&ps.buf, "{default arg#%d}::", da.Num+1)
+ }
+ ps.print(da.Arg)
+}
+
+func (da *DefaultArg) Traverse(fn func(AST) bool) {
+ if fn(da) {
+ da.Arg.Traverse(fn)
+ }
+}
+
+func (da *DefaultArg) Copy(fn func(AST) AST, skip func(AST) bool) AST {
+ if skip(da) {
+ return nil
+ }
+ arg := da.Arg.Copy(fn, skip)
+ if arg == nil {
+ return fn(da)
+ }
+ da = &DefaultArg{Num: da.Num, Arg: arg}
+ if r := fn(da); r != nil {
+ return r
+ }
+ return da
+}
+
+func (da *DefaultArg) GoString() string {
+ return da.goString(0, "")
+}
+
+func (da *DefaultArg) goString(indent int, field string) string {
+ return fmt.Sprintf("%*s%sDefaultArg: Num: %d\n%s", indent, "", field, da.Num,
+ da.Arg.goString(indent+2, "Arg: "))
+}
+
+// Closure is a closure, or lambda expression.
+type Closure struct {
+ TemplateArgs []AST
+ Types []AST
+ Num int
+}
+
+func (cl *Closure) print(ps *printState) {
+ if ps.llvmStyle {
+ if cl.Num == 0 {
+ ps.writeString("'lambda'")
+ } else {
+ ps.writeString(fmt.Sprintf("'lambda%d'", cl.Num-1))
+ }
+ } else {
+ ps.writeString("{lambda")
+ }
+ cl.printTypes(ps)
+ if !ps.llvmStyle {
+ ps.writeString(fmt.Sprintf("#%d}", cl.Num+1))
+ }
+}
+
+func (cl *Closure) printTypes(ps *printState) {
+ if len(cl.TemplateArgs) > 0 {
+ ps.writeString("<")
+ for i, a := range cl.TemplateArgs {
+ if i > 0 {
+ ps.writeString(", ")
+ }
+ ps.print(a)
+ }
+ ps.writeString(">")
+ }
+ ps.writeString("(")
+ for i, t := range cl.Types {
+ if i > 0 {
+ ps.writeString(", ")
+ }
+ ps.print(t)
+ }
+ ps.writeString(")")
+}
+
+func (cl *Closure) Traverse(fn func(AST) bool) {
+ if fn(cl) {
+ for _, a := range cl.TemplateArgs {
+ a.Traverse(fn)
+ }
+ for _, t := range cl.Types {
+ t.Traverse(fn)
+ }
+ }
+}
+
+func (cl *Closure) Copy(fn func(AST) AST, skip func(AST) bool) AST {
+ if skip(cl) {
+ return nil
+ }
+ changed := false
+
+ args := make([]AST, len(cl.TemplateArgs))
+ for i, a := range cl.TemplateArgs {
+ ac := a.Copy(fn, skip)
+ if ac == nil {
+ args[i] = a
+ } else {
+ args[i] = ac
+ changed = true
+ }
+ }
+
+ types := make([]AST, len(cl.Types))
+ for i, t := range cl.Types {
+ tc := t.Copy(fn, skip)
+ if tc == nil {
+ types[i] = t
+ } else {
+ types[i] = tc
+ changed = true
+ }
+ }
+
+ if !changed {
+ return fn(cl)
+ }
+ cl = &Closure{TemplateArgs: args, Types: types, Num: cl.Num}
+ if r := fn(cl); r != nil {
+ return r
+ }
+ return cl
+}
+
+func (cl *Closure) GoString() string {
+ return cl.goString(0, "")
+}
+
+func (cl *Closure) goString(indent int, field string) string {
+ var args string
+ if len(cl.TemplateArgs) == 0 {
+ args = fmt.Sprintf("%*sTemplateArgs: nil", indent+2, "")
+ } else {
+ args = fmt.Sprintf("%*sTemplateArgs:", indent+2, "")
+ for i, a := range cl.TemplateArgs {
+ args += "\n"
+ args += a.goString(indent+4, fmt.Sprintf("%d: ", i))
+ }
+ }
+ var types string
+ if len(cl.Types) == 0 {
+ types = fmt.Sprintf("%*sTypes: nil", indent+2, "")
+ } else {
+ types = fmt.Sprintf("%*sTypes:", indent+2, "")
+ for i, t := range cl.Types {
+ types += "\n"
+ types += t.goString(indent+4, fmt.Sprintf("%d: ", i))
+ }
+ }
+ return fmt.Sprintf("%*s%sClosure: Num: %d\n%s\n%s", indent, "", field,
+ cl.Num, args, types)
+}
+
+// StructuredBindings is a structured binding declaration.
+type StructuredBindings struct {
+ Bindings []AST
+}
+
+func (sb *StructuredBindings) print(ps *printState) {
+ ps.writeString("[")
+ for i, b := range sb.Bindings {
+ if i > 0 {
+ ps.writeString(", ")
+ }
+ b.print(ps)
+ }
+ ps.writeString("]")
+}
+
+func (sb *StructuredBindings) Traverse(fn func(AST) bool) {
+ if fn(sb) {
+ for _, b := range sb.Bindings {
+ b.Traverse(fn)
+ }
+ }
+}
+
+func (sb *StructuredBindings) Copy(fn func(AST) AST, skip func(AST) bool) AST {
+ if skip(sb) {
+ return nil
+ }
+ changed := false
+ bindings := make([]AST, len(sb.Bindings))
+ for i, b := range sb.Bindings {
+ bc := b.Copy(fn, skip)
+ if bc == nil {
+ bindings[i] = b
+ } else {
+ bindings[i] = bc
+ changed = true
+ }
+ }
+ if !changed {
+ return fn(sb)
+ }
+ sb = &StructuredBindings{Bindings: bindings}
+ if r := fn(sb); r != nil {
+ return r
+ }
+ return sb
+}
+
+func (sb *StructuredBindings) GoString() string {
+ return sb.goString(0, "")
+}
+
+func (sb *StructuredBindings) goString(indent int, field string) string {
+ var strb strings.Builder
+ fmt.Fprintf(&strb, "%*s%sStructuredBinding:", indent, "", field)
+ for _, b := range sb.Bindings {
+ strb.WriteByte('\n')
+ strb.WriteString(b.goString(indent+2, ""))
+ }
+ return strb.String()
+}
+
+// UnnamedType is an unnamed type, that just has an index.
+type UnnamedType struct {
+ Num int
+}
+
+func (ut *UnnamedType) print(ps *printState) {
+ if ps.llvmStyle {
+ if ut.Num == 0 {
+ ps.writeString("'unnamed'")
+ } else {
+ ps.writeString(fmt.Sprintf("'unnamed%d'", ut.Num-1))
+ }
+ } else {
+ ps.writeString(fmt.Sprintf("{unnamed type#%d}", ut.Num+1))
+ }
+}
+
+func (ut *UnnamedType) Traverse(fn func(AST) bool) {
+ fn(ut)
+}
+
+func (ut *UnnamedType) Copy(fn func(AST) AST, skip func(AST) bool) AST {
+ if skip(ut) {
+ return nil
+ }
+ return fn(ut)
+}
+
+func (ut *UnnamedType) GoString() string {
+ return ut.goString(0, "")
+}
+
+func (ut *UnnamedType) goString(indent int, field string) string {
+ return fmt.Sprintf("%*s%sUnnamedType: Num: %d", indent, "", field, ut.Num)
+}
+
+// Clone is a clone of a function, with a distinguishing suffix.
+type Clone struct {
+ Base AST
+ Suffix string
+}
+
+func (c *Clone) print(ps *printState) {
+ ps.print(c.Base)
+ if ps.llvmStyle {
+ ps.writeString(" (")
+ ps.writeString(c.Suffix)
+ ps.writeByte(')')
+ } else {
+ ps.writeString(fmt.Sprintf(" [clone %s]", c.Suffix))
+ }
+}
+
+func (c *Clone) Traverse(fn func(AST) bool) {
+ if fn(c) {
+ c.Base.Traverse(fn)
+ }
+}
+
+func (c *Clone) Copy(fn func(AST) AST, skip func(AST) bool) AST {
+ if skip(c) {
+ return nil
+ }
+ base := c.Base.Copy(fn, skip)
+ if base == nil {
+ return fn(c)
+ }
+ c = &Clone{Base: base, Suffix: c.Suffix}
+ if r := fn(c); r != nil {
+ return r
+ }
+ return c
+}
+
+func (c *Clone) GoString() string {
+ return c.goString(0, "")
+}
+
+func (c *Clone) goString(indent int, field string) string {
+ return fmt.Sprintf("%*s%sClone: Suffix: %s\n%s", indent, "", field,
+ c.Suffix, c.Base.goString(indent+2, "Base: "))
+}
+
+// Special is a special symbol, printed as a prefix plus another
+// value.
+type Special struct {
+ Prefix string
+ Val AST
+}
+
+func (s *Special) print(ps *printState) {
+ prefix := s.Prefix
+ if ps.llvmStyle {
+ switch prefix {
+ case "TLS wrapper function for ":
+ prefix = "thread-local wrapper routine for "
+ case "TLS init function for ":
+ prefix = "thread-local initialization routine for "
+ }
+ }
+ ps.writeString(prefix)
+ ps.print(s.Val)
+}
+
+func (s *Special) Traverse(fn func(AST) bool) {
+ if fn(s) {
+ s.Val.Traverse(fn)
+ }
+}
+
+func (s *Special) Copy(fn func(AST) AST, skip func(AST) bool) AST {
+ if skip(s) {
+ return nil
+ }
+ val := s.Val.Copy(fn, skip)
+ if val == nil {
+ return fn(s)
+ }
+ s = &Special{Prefix: s.Prefix, Val: val}
+ if r := fn(s); r != nil {
+ return r
+ }
+ return s
+}
+
+func (s *Special) GoString() string {
+ return s.goString(0, "")
+}
+
+func (s *Special) goString(indent int, field string) string {
+ return fmt.Sprintf("%*s%sSpecial: Prefix: %s\n%s", indent, "", field,
+ s.Prefix, s.Val.goString(indent+2, "Val: "))
+}
+
+// Special2 is like special, but uses two values.
+type Special2 struct {
+ Prefix string
+ Val1 AST
+ Middle string
+ Val2 AST
+}
+
+func (s *Special2) print(ps *printState) {
+ ps.writeString(s.Prefix)
+ ps.print(s.Val1)
+ ps.writeString(s.Middle)
+ ps.print(s.Val2)
+}
+
+func (s *Special2) Traverse(fn func(AST) bool) {
+ if fn(s) {
+ s.Val1.Traverse(fn)
+ s.Val2.Traverse(fn)
+ }
+}
+
+func (s *Special2) Copy(fn func(AST) AST, skip func(AST) bool) AST {
+ if skip(s) {
+ return nil
+ }
+ val1 := s.Val1.Copy(fn, skip)
+ val2 := s.Val2.Copy(fn, skip)
+ if val1 == nil && val2 == nil {
+ return fn(s)
+ }
+ if val1 == nil {
+ val1 = s.Val1
+ }
+ if val2 == nil {
+ val2 = s.Val2
+ }
+ s = &Special2{Prefix: s.Prefix, Val1: val1, Middle: s.Middle, Val2: val2}
+ if r := fn(s); r != nil {
+ return r
+ }
+ return s
+}
+
+func (s *Special2) GoString() string {
+ return s.goString(0, "")
+}
+
+func (s *Special2) goString(indent int, field string) string {
+ return fmt.Sprintf("%*s%sSpecial2: Prefix: %s\n%s\n%*sMiddle: %s\n%s", indent, "", field,
+ s.Prefix, s.Val1.goString(indent+2, "Val1: "),
+ indent+2, "", s.Middle, s.Val2.goString(indent+2, "Val2: "))
+}
+
+// EnableIf is used by clang for an enable_if attribute.
+type EnableIf struct {
+ Type AST
+ Args []AST
+}
+
+func (ei *EnableIf) print(ps *printState) {
+ ps.print(ei.Type)
+ ps.writeString(" [enable_if:")
+ first := true
+ for _, a := range ei.Args {
+ if !first {
+ ps.writeString(", ")
+ }
+ ps.print(a)
+ first = false
+ }
+ ps.writeString("]")
+}
+
+func (ei *EnableIf) Traverse(fn func(AST) bool) {
+ if fn(ei) {
+ ei.Type.Traverse(fn)
+ for _, a := range ei.Args {
+ a.Traverse(fn)
+ }
+ }
+}
+
+func (ei *EnableIf) Copy(fn func(AST) AST, skip func(AST) bool) AST {
+ if skip(ei) {
+ return nil
+ }
+ typ := ei.Type.Copy(fn, skip)
+ argsChanged := false
+ args := make([]AST, len(ei.Args))
+ for i, a := range ei.Args {
+ ac := a.Copy(fn, skip)
+ if ac == nil {
+ args[i] = a
+ } else {
+ args[i] = ac
+ argsChanged = true
+ }
+ }
+ if typ == nil && !argsChanged {
+ return fn(ei)
+ }
+ if typ == nil {
+ typ = ei.Type
+ }
+ ei = &EnableIf{Type: typ, Args: args}
+ if r := fn(ei); r != nil {
+ return r
+ }
+ return ei
+}
+
+func (ei *EnableIf) GoString() string {
+ return ei.goString(0, "")
+}
+
+func (ei *EnableIf) goString(indent int, field string) string {
+ var args string
+ if len(ei.Args) == 0 {
+ args = fmt.Sprintf("%*sArgs: nil", indent+2, "")
+ } else {
+ args = fmt.Sprintf("%*sArgs:", indent+2, "")
+ for i, a := range ei.Args {
+ args += "\n"
+ args += a.goString(indent+4, fmt.Sprintf("%d: ", i))
+ }
+ }
+ return fmt.Sprintf("%*s%sEnableIf:\n%s\n%s", indent, "", field,
+ ei.Type.goString(indent+2, "Type: "), args)
+}
+
+// Print the inner types.
+func (ps *printState) printInner(prefixOnly bool) []AST {
+ var save []AST
+ var psave *[]AST
+ if prefixOnly {
+ psave = &save
+ }
+ for len(ps.inner) > 0 {
+ ps.printOneInner(psave)
+ }
+ return save
+}
+
+// innerPrinter is an interface for types that can print themselves as
+// inner types.
+type innerPrinter interface {
+ printInner(*printState)
+}
+
+// Print the most recent inner type. If save is not nil, only print
+// prefixes.
+func (ps *printState) printOneInner(save *[]AST) {
+ if len(ps.inner) == 0 {
+ panic("printOneInner called with no inner types")
+ }
+ ln := len(ps.inner)
+ a := ps.inner[ln-1]
+ ps.inner = ps.inner[:ln-1]
+
+ if save != nil {
+ if _, ok := a.(*MethodWithQualifiers); ok {
+ *save = append(*save, a)
+ return
+ }
+ }
+
+ if ip, ok := a.(innerPrinter); ok {
+ ip.printInner(ps)
+ } else {
+ ps.print(a)
+ }
+}
+
+// isEmpty returns whether printing a will not print anything.
+func (ps *printState) isEmpty(a AST) bool {
+ switch a := a.(type) {
+ case *ArgumentPack:
+ for _, a := range a.Args {
+ if !ps.isEmpty(a) {
+ return false
+ }
+ }
+ return true
+ case *ExprList:
+ return len(a.Exprs) == 0
+ case *PackExpansion:
+ return a.Pack != nil && ps.isEmpty(a.Base)
+ default:
+ return false
+ }
+}
diff --git a/src/cmd/vendor/github.com/ianlancetaylor/demangle/demangle.go b/src/cmd/vendor/github.com/ianlancetaylor/demangle/demangle.go
new file mode 100644
index 0000000..14e77a6
--- /dev/null
+++ b/src/cmd/vendor/github.com/ianlancetaylor/demangle/demangle.go
@@ -0,0 +1,3362 @@
+// Copyright 2015 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Package demangle defines functions that demangle GCC/LLVM
+// C++ and Rust symbol names.
+// This package recognizes names that were mangled according to the C++ ABI
+// defined at http://codesourcery.com/cxx-abi/ and the Rust ABI
+// defined at
+// https://rust-lang.github.io/rfcs/2603-rust-symbol-name-mangling-v0.html
+//
+// Most programs will want to call Filter or ToString.
+package demangle
+
+import (
+ "errors"
+ "fmt"
+ "strings"
+)
+
+// ErrNotMangledName is returned by CheckedDemangle if the string does
+// not appear to be a C++ symbol name.
+var ErrNotMangledName = errors.New("not a C++ or Rust mangled name")
+
+// Option is the type of demangler options.
+type Option int
+
+const (
+ // The NoParams option disables demangling of function parameters.
+ // It only omits the parameters of the function name being demangled,
+ // not the parameter types of other functions that may be mentioned.
+ // Using the option will speed up the demangler and cause it to
+ // use less memory.
+ NoParams Option = iota
+
+ // The NoTemplateParams option disables demangling of template parameters.
+ // This applies to both C++ and Rust.
+ NoTemplateParams
+
+ // The NoEnclosingParams option disables demangling of the function
+ // parameter types of the enclosing function when demangling a
+ // local name defined within a function.
+ NoEnclosingParams
+
+ // The NoClones option disables inclusion of clone suffixes.
+ // NoParams implies NoClones.
+ NoClones
+
+ // The NoRust option disables demangling of old-style Rust
+ // mangled names, which can be confused with C++ style mangled
+ // names. New style Rust mangled names are still recognized.
+ NoRust
+
+ // The Verbose option turns on more verbose demangling.
+ Verbose
+
+ // LLVMStyle tries to translate an AST to a string in the
+ // style of the LLVM demangler. This does not affect
+ // the parsing of the AST, only the conversion of the AST
+ // to a string.
+ LLVMStyle
+)
+
+// maxLengthShift is how we shift the MaxLength value.
+const maxLengthShift = 16
+
+// maxLengthMask is a mask for the maxLength value.
+const maxLengthMask = 0x1f << maxLengthShift
+
+// MaxLength returns an Option that limits the maximum length of a
+// demangled string. The maximum length is expressed as a power of 2,
+// so a value of 1 limits the returned string to 2 characters, and
+// a value of 16 limits the returned string to 65,536 characters.
+// The value must be between 1 and 30.
+func MaxLength(pow int) Option {
+ if pow <= 0 || pow > 30 {
+ panic("demangle: invalid MaxLength value")
+ }
+ return Option(pow << maxLengthShift)
+}
+
+// isMaxLength reports whether an Option holds a maximum length.
+func isMaxLength(opt Option) bool {
+ return opt&maxLengthMask != 0
+}
+
+// maxLength returns the maximum length stored in an Option.
+func maxLength(opt Option) int {
+ return 1 << ((opt & maxLengthMask) >> maxLengthShift)
+}
+
+// Filter demangles a C++ or Rust symbol name,
+// returning the human-readable C++ or Rust name.
+// If any error occurs during demangling, the input string is returned.
+func Filter(name string, options ...Option) string {
+ ret, err := ToString(name, options...)
+ if err != nil {
+ return name
+ }
+ return ret
+}
+
+// ToString demangles a C++ or Rust symbol name,
+// returning a human-readable C++ or Rust name or an error.
+// If the name does not appear to be a C++ or Rust symbol name at all,
+// the error will be ErrNotMangledName.
+func ToString(name string, options ...Option) (string, error) {
+ if strings.HasPrefix(name, "_R") {
+ return rustToString(name, options)
+ }
+
+ // Check for an old-style Rust mangled name.
+ // It starts with _ZN and ends with "17h" followed by 16 hex digits
+ // followed by "E" followed by an optional suffix starting with "."
+ // (which we ignore).
+ if strings.HasPrefix(name, "_ZN") {
+ rname := name
+ if pos := strings.LastIndex(rname, "E."); pos > 0 {
+ rname = rname[:pos+1]
+ }
+ if strings.HasSuffix(rname, "E") && len(rname) > 23 && rname[len(rname)-20:len(rname)-17] == "17h" {
+ noRust := false
+ for _, o := range options {
+ if o == NoRust {
+ noRust = true
+ break
+ }
+ }
+ if !noRust {
+ s, ok := oldRustToString(rname, options)
+ if ok {
+ return s, nil
+ }
+ }
+ }
+ }
+
+ a, err := ToAST(name, options...)
+ if err != nil {
+ return "", err
+ }
+ return ASTToString(a, options...), nil
+}
+
+// ToAST demangles a C++ symbol name into an abstract syntax tree
+// representing the symbol.
+// If the NoParams option is passed, and the name has a function type,
+// the parameter types are not demangled.
+// If the name does not appear to be a C++ symbol name at all, the
+// error will be ErrNotMangledName.
+// This function does not currently support Rust symbol names.
+func ToAST(name string, options ...Option) (AST, error) {
+ if strings.HasPrefix(name, "_Z") {
+ a, err := doDemangle(name[2:], options...)
+ return a, adjustErr(err, 2)
+ }
+
+ if strings.HasPrefix(name, "___Z") {
+ // clang extensions
+ block := strings.LastIndex(name, "_block_invoke")
+ if block == -1 {
+ return nil, ErrNotMangledName
+ }
+ a, err := doDemangle(name[4:block], options...)
+ if err != nil {
+ return a, adjustErr(err, 4)
+ }
+ name = strings.TrimPrefix(name[block:], "_block_invoke")
+ if len(name) > 0 && name[0] == '_' {
+ name = name[1:]
+ }
+ for len(name) > 0 && isDigit(name[0]) {
+ name = name[1:]
+ }
+ if len(name) > 0 && name[0] != '.' {
+ return nil, errors.New("unparsed characters at end of mangled name")
+ }
+ a = &Special{Prefix: "invocation function for block in ", Val: a}
+ return a, nil
+ }
+
+ const prefix = "_GLOBAL_"
+ if strings.HasPrefix(name, prefix) {
+ // The standard demangler ignores NoParams for global
+ // constructors. We are compatible.
+ i := 0
+ for i < len(options) {
+ if options[i] == NoParams {
+ options = append(options[:i], options[i+1:]...)
+ } else {
+ i++
+ }
+ }
+ a, err := globalCDtorName(name[len(prefix):], options...)
+ return a, adjustErr(err, len(prefix))
+ }
+
+ return nil, ErrNotMangledName
+}
+
+// globalCDtorName demangles a global constructor/destructor symbol name.
+// The parameter is the string following the "_GLOBAL_" prefix.
+func globalCDtorName(name string, options ...Option) (AST, error) {
+ if len(name) < 4 {
+ return nil, ErrNotMangledName
+ }
+ switch name[0] {
+ case '.', '_', '$':
+ default:
+ return nil, ErrNotMangledName
+ }
+
+ var ctor bool
+ switch name[1] {
+ case 'I':
+ ctor = true
+ case 'D':
+ ctor = false
+ default:
+ return nil, ErrNotMangledName
+ }
+
+ if name[2] != '_' {
+ return nil, ErrNotMangledName
+ }
+
+ if !strings.HasPrefix(name[3:], "_Z") {
+ return &GlobalCDtor{Ctor: ctor, Key: &Name{Name: name}}, nil
+ } else {
+ a, err := doDemangle(name[5:], options...)
+ if err != nil {
+ return nil, adjustErr(err, 5)
+ }
+ return &GlobalCDtor{Ctor: ctor, Key: a}, nil
+ }
+}
+
+// The doDemangle function is the entry point into the demangler proper.
+func doDemangle(name string, options ...Option) (ret AST, err error) {
+ // When the demangling routines encounter an error, they panic
+ // with a value of type demangleErr.
+ defer func() {
+ if r := recover(); r != nil {
+ if de, ok := r.(demangleErr); ok {
+ ret = nil
+ err = de
+ return
+ }
+ panic(r)
+ }
+ }()
+
+ params := true
+ clones := true
+ verbose := false
+ for _, o := range options {
+ switch {
+ case o == NoParams:
+ params = false
+ clones = false
+ case o == NoClones:
+ clones = false
+ case o == Verbose:
+ verbose = true
+ case o == NoTemplateParams || o == NoEnclosingParams || o == LLVMStyle || isMaxLength(o):
+ // These are valid options but only affect
+ // printing of the AST.
+ case o == NoRust:
+ // Unimportant here.
+ default:
+ return nil, fmt.Errorf("unrecognized demangler option %v", o)
+ }
+ }
+
+ st := &state{str: name, verbose: verbose}
+ a := st.encoding(params, notForLocalName)
+
+ // Accept a clone suffix.
+ if clones {
+ for len(st.str) > 1 && st.str[0] == '.' && (isLower(st.str[1]) || st.str[1] == '_' || isDigit(st.str[1])) {
+ a = st.cloneSuffix(a)
+ }
+ }
+
+ if clones && len(st.str) > 0 {
+ st.fail("unparsed characters at end of mangled name")
+ }
+
+ return a, nil
+}
+
+// A state holds the current state of demangling a string.
+type state struct {
+ str string // remainder of string to demangle
+ verbose bool // whether to use verbose demangling
+ off int // offset of str within original string
+ subs substitutions // substitutions
+ templates []*Template // templates being processed
+
+ // The number of entries in templates when we started parsing
+ // a lambda, plus 1 so that 0 means not parsing a lambda.
+ lambdaTemplateLevel int
+
+ // Counts of template parameters without template arguments,
+ // for lambdas.
+ typeTemplateParamCount int
+ nonTypeTemplateParamCount int
+ templateTemplateParamCount int
+}
+
+// copy returns a copy of the current state.
+func (st *state) copy() *state {
+ n := new(state)
+ *n = *st
+ return n
+}
+
+// fail panics with demangleErr, to be caught in doDemangle.
+func (st *state) fail(err string) {
+ panic(demangleErr{err: err, off: st.off})
+}
+
+// failEarlier is like fail, but decrements the offset to indicate
+// that the point of failure occurred earlier in the string.
+func (st *state) failEarlier(err string, dec int) {
+ if st.off < dec {
+ panic("internal error")
+ }
+ panic(demangleErr{err: err, off: st.off - dec})
+}
+
+// advance advances the current string offset.
+func (st *state) advance(add int) {
+ if len(st.str) < add {
+ panic("internal error")
+ }
+ st.str = st.str[add:]
+ st.off += add
+}
+
+// checkChar requires that the next character in the string be c, and
+// advances past it.
+func (st *state) checkChar(c byte) {
+ if len(st.str) == 0 || st.str[0] != c {
+ panic("internal error")
+ }
+ st.advance(1)
+}
+
+// A demangleErr is an error at a specific offset in the mangled
+// string.
+type demangleErr struct {
+ err string
+ off int
+}
+
+// Error implements the builtin error interface for demangleErr.
+func (de demangleErr) Error() string {
+ return fmt.Sprintf("%s at %d", de.err, de.off)
+}
+
+// adjustErr adjusts the position of err, if it is a demangleErr,
+// and returns err.
+func adjustErr(err error, adj int) error {
+ if err == nil {
+ return nil
+ }
+ if de, ok := err.(demangleErr); ok {
+ de.off += adj
+ return de
+ }
+ return err
+}
+
+type forLocalNameType int
+
+const (
+ forLocalName forLocalNameType = iota
+ notForLocalName
+)
+
+// encoding parses:
+//
+// encoding ::= <(function) name> <bare-function-type>
+// <(data) name>
+// <special-name>
+func (st *state) encoding(params bool, local forLocalNameType) AST {
+ if len(st.str) < 1 {
+ st.fail("expected encoding")
+ }
+
+ if st.str[0] == 'G' || st.str[0] == 'T' {
+ return st.specialName()
+ }
+
+ a := st.name()
+ a = simplify(a)
+
+ if !params {
+ // Don't demangle the parameters.
+
+ // Strip CV-qualifiers, as they apply to the 'this'
+ // parameter, and are not output by the standard
+ // demangler without parameters.
+ if mwq, ok := a.(*MethodWithQualifiers); ok {
+ a = mwq.Method
+ }
+
+ // If this is a local name, there may be CV-qualifiers
+ // on the name that really apply to the top level, and
+ // therefore must be discarded when discarding
+ // parameters. This can happen when parsing a class
+ // that is local to a function.
+ if q, ok := a.(*Qualified); ok && q.LocalName {
+ p := &q.Name
+ if da, ok := (*p).(*DefaultArg); ok {
+ p = &da.Arg
+ }
+ if mwq, ok := (*p).(*MethodWithQualifiers); ok {
+ *p = mwq.Method
+ }
+ }
+
+ return a
+ }
+
+ if len(st.str) == 0 || st.str[0] == 'E' {
+ // There are no parameters--this is a data symbol, not
+ // a function symbol.
+ return a
+ }
+
+ mwq, _ := a.(*MethodWithQualifiers)
+
+ var findTemplate func(AST) *Template
+ findTemplate = func(check AST) *Template {
+ switch check := check.(type) {
+ case *Template:
+ return check
+ case *Qualified:
+ if check.LocalName {
+ return findTemplate(check.Name)
+ } else if _, ok := check.Name.(*Constructor); ok {
+ return findTemplate(check.Name)
+ }
+ case *MethodWithQualifiers:
+ return findTemplate(check.Method)
+ case *Constructor:
+ if check.Base != nil {
+ return findTemplate(check.Base)
+ }
+ }
+ return nil
+ }
+
+ template := findTemplate(a)
+ var oldLambdaTemplateLevel int
+ if template != nil {
+ st.templates = append(st.templates, template)
+ oldLambdaTemplateLevel = st.lambdaTemplateLevel
+ st.lambdaTemplateLevel = 0
+ }
+
+ // Checking for the enable_if attribute here is what the LLVM
+ // demangler does. This is not very general but perhaps it is
+ // sufficient.
+ const enableIfPrefix = "Ua9enable_ifI"
+ var enableIfArgs []AST
+ if strings.HasPrefix(st.str, enableIfPrefix) {
+ st.advance(len(enableIfPrefix) - 1)
+ enableIfArgs = st.templateArgs()
+ }
+
+ ft := st.bareFunctionType(hasReturnType(a))
+
+ if template != nil {
+ st.templates = st.templates[:len(st.templates)-1]
+ st.lambdaTemplateLevel = oldLambdaTemplateLevel
+ }
+
+ ft = simplify(ft)
+
+ // For a local name, discard the return type, so that it
+ // doesn't get confused with the top level return type.
+ if local == forLocalName {
+ if functype, ok := ft.(*FunctionType); ok {
+ functype.ForLocalName = true
+ }
+ }
+
+ // Any top-level qualifiers belong to the function type.
+ if mwq != nil {
+ a = mwq.Method
+ mwq.Method = ft
+ ft = mwq
+ }
+ if q, ok := a.(*Qualified); ok && q.LocalName {
+ p := &q.Name
+ if da, ok := (*p).(*DefaultArg); ok {
+ p = &da.Arg
+ }
+ if mwq, ok := (*p).(*MethodWithQualifiers); ok {
+ *p = mwq.Method
+ mwq.Method = ft
+ ft = mwq
+ }
+ }
+
+ r := AST(&Typed{Name: a, Type: ft})
+
+ if len(enableIfArgs) > 0 {
+ r = &EnableIf{Type: r, Args: enableIfArgs}
+ }
+
+ return r
+}
+
+// hasReturnType returns whether the mangled form of a will have a
+// return type.
+func hasReturnType(a AST) bool {
+ switch a := a.(type) {
+ case *Qualified:
+ if a.LocalName {
+ return hasReturnType(a.Name)
+ }
+ return false
+ case *Template:
+ return !isCDtorConversion(a.Name)
+ case *TypeWithQualifiers:
+ return hasReturnType(a.Base)
+ case *MethodWithQualifiers:
+ return hasReturnType(a.Method)
+ default:
+ return false
+ }
+}
+
+// isCDtorConversion returns when an AST is a constructor, a
+// destructor, or a conversion operator.
+func isCDtorConversion(a AST) bool {
+ switch a := a.(type) {
+ case *Qualified:
+ return isCDtorConversion(a.Name)
+ case *Constructor, *Destructor, *Cast:
+ return true
+ default:
+ return false
+ }
+}
+
+// taggedName parses:
+//
+// <tagged-name> ::= <name> B <source-name>
+func (st *state) taggedName(a AST) AST {
+ for len(st.str) > 0 && st.str[0] == 'B' {
+ st.advance(1)
+ tag := st.sourceName()
+ a = &TaggedName{Name: a, Tag: tag}
+ }
+ return a
+}
+
+// name parses:
+//
+// <name> ::= <nested-name>
+// ::= <unscoped-name>
+// ::= <unscoped-template-name> <template-args>
+// ::= <local-name>
+//
+// <unscoped-name> ::= <unqualified-name>
+// ::= St <unqualified-name>
+//
+// <unscoped-template-name> ::= <unscoped-name>
+// ::= <substitution>
+func (st *state) name() AST {
+ if len(st.str) < 1 {
+ st.fail("expected name")
+ }
+ switch st.str[0] {
+ case 'N':
+ return st.nestedName()
+ case 'Z':
+ return st.localName()
+ case 'U':
+ a, isCast := st.unqualifiedName()
+ if isCast {
+ st.setTemplate(a, nil)
+ }
+ return a
+ case 'S':
+ if len(st.str) < 2 {
+ st.advance(1)
+ st.fail("expected substitution index")
+ }
+ var a AST
+ isCast := false
+ subst := false
+ if st.str[1] == 't' {
+ st.advance(2)
+ a, isCast = st.unqualifiedName()
+ a = &Qualified{Scope: &Name{Name: "std"}, Name: a, LocalName: false}
+ } else {
+ a = st.substitution(false)
+ subst = true
+ }
+ if len(st.str) > 0 && st.str[0] == 'I' {
+ // This can only happen if we saw
+ // <unscoped-template-name> and are about to see
+ // <template-args>. <unscoped-template-name> is a
+ // substitution candidate if it did not come from a
+ // substitution.
+ if !subst {
+ st.subs.add(a)
+ }
+ args := st.templateArgs()
+ tmpl := &Template{Name: a, Args: args}
+ if isCast {
+ st.setTemplate(a, tmpl)
+ st.clearTemplateArgs(args)
+ isCast = false
+ }
+ a = tmpl
+ }
+ if isCast {
+ st.setTemplate(a, nil)
+ }
+ return a
+
+ default:
+ a, isCast := st.unqualifiedName()
+ if len(st.str) > 0 && st.str[0] == 'I' {
+ st.subs.add(a)
+ args := st.templateArgs()
+ tmpl := &Template{Name: a, Args: args}
+ if isCast {
+ st.setTemplate(a, tmpl)
+ st.clearTemplateArgs(args)
+ isCast = false
+ }
+ a = tmpl
+ }
+ if isCast {
+ st.setTemplate(a, nil)
+ }
+ return a
+ }
+}
+
+// nestedName parses:
+//
+// <nested-name> ::= N [<CV-qualifiers>] [<ref-qualifier>] <prefix> <unqualified-name> E
+// ::= N [<CV-qualifiers>] [<ref-qualifier>] <template-prefix> <template-args> E
+func (st *state) nestedName() AST {
+ st.checkChar('N')
+ q := st.cvQualifiers()
+ r := st.refQualifier()
+ a := st.prefix()
+ if q != nil || r != "" {
+ a = &MethodWithQualifiers{Method: a, Qualifiers: q, RefQualifier: r}
+ }
+ if len(st.str) == 0 || st.str[0] != 'E' {
+ st.fail("expected E after nested name")
+ }
+ st.advance(1)
+ return a
+}
+
+// prefix parses:
+//
+// <prefix> ::= <prefix> <unqualified-name>
+// ::= <template-prefix> <template-args>
+// ::= <template-param>
+// ::= <decltype>
+// ::=
+// ::= <substitution>
+//
+// <template-prefix> ::= <prefix> <(template) unqualified-name>
+// ::= <template-param>
+// ::= <substitution>
+//
+// <decltype> ::= Dt <expression> E
+// ::= DT <expression> E
+func (st *state) prefix() AST {
+ var a AST
+
+ // The last name seen, for a constructor/destructor.
+ var last AST
+
+ getLast := func(a AST) AST {
+ for {
+ if t, ok := a.(*Template); ok {
+ a = t.Name
+ } else if q, ok := a.(*Qualified); ok {
+ a = q.Name
+ } else if t, ok := a.(*TaggedName); ok {
+ a = t.Name
+ } else {
+ return a
+ }
+ }
+ }
+
+ var cast *Cast
+ for {
+ if len(st.str) == 0 {
+ st.fail("expected prefix")
+ }
+ var next AST
+
+ c := st.str[0]
+ if isDigit(c) || isLower(c) || c == 'U' || c == 'L' || (c == 'D' && len(st.str) > 1 && st.str[1] == 'C') {
+ un, isUnCast := st.unqualifiedName()
+ next = un
+ if isUnCast {
+ if tn, ok := un.(*TaggedName); ok {
+ un = tn.Name
+ }
+ cast = un.(*Cast)
+ }
+ } else {
+ switch st.str[0] {
+ case 'C':
+ inheriting := false
+ st.advance(1)
+ if len(st.str) > 0 && st.str[0] == 'I' {
+ inheriting = true
+ st.advance(1)
+ }
+ if len(st.str) < 1 {
+ st.fail("expected constructor type")
+ }
+ if last == nil {
+ st.fail("constructor before name is seen")
+ }
+ st.advance(1)
+ var base AST
+ if inheriting {
+ base = st.demangleType(false)
+ }
+ next = &Constructor{
+ Name: getLast(last),
+ Base: base,
+ }
+ if len(st.str) > 0 && st.str[0] == 'B' {
+ next = st.taggedName(next)
+ }
+ case 'D':
+ if len(st.str) > 1 && (st.str[1] == 'T' || st.str[1] == 't') {
+ next = st.demangleType(false)
+ } else {
+ if len(st.str) < 2 {
+ st.fail("expected destructor type")
+ }
+ if last == nil {
+ st.fail("destructor before name is seen")
+ }
+ st.advance(2)
+ next = &Destructor{Name: getLast(last)}
+ if len(st.str) > 0 && st.str[0] == 'B' {
+ next = st.taggedName(next)
+ }
+ }
+ case 'S':
+ next = st.substitution(true)
+ case 'I':
+ if a == nil {
+ st.fail("unexpected template arguments")
+ }
+ var args []AST
+ args = st.templateArgs()
+ tmpl := &Template{Name: a, Args: args}
+ if cast != nil {
+ st.setTemplate(cast, tmpl)
+ st.clearTemplateArgs(args)
+ cast = nil
+ }
+ a = nil
+ next = tmpl
+ case 'T':
+ next = st.templateParam()
+ case 'E':
+ if a == nil {
+ st.fail("expected prefix")
+ }
+ if cast != nil {
+ var toTmpl *Template
+ if castTempl, ok := cast.To.(*Template); ok {
+ toTmpl = castTempl
+ }
+ st.setTemplate(cast, toTmpl)
+ }
+ return a
+ case 'M':
+ if a == nil {
+ st.fail("unexpected lambda initializer")
+ }
+ // This is the initializer scope for a
+ // lambda. We don't need to record
+ // it. The normal code will treat the
+ // variable has a type scope, which
+ // gives appropriate output.
+ st.advance(1)
+ continue
+ case 'J':
+ // It appears that in some cases clang
+ // can emit a J for a template arg
+ // without the expected I. I don't
+ // know when this happens, but I've
+ // seen it in some large C++ programs.
+ if a == nil {
+ st.fail("unexpected template arguments")
+ }
+ var args []AST
+ for len(st.str) == 0 || st.str[0] != 'E' {
+ arg := st.templateArg()
+ args = append(args, arg)
+ }
+ st.advance(1)
+ tmpl := &Template{Name: a, Args: args}
+ if cast != nil {
+ st.setTemplate(cast, tmpl)
+ st.clearTemplateArgs(args)
+ cast = nil
+ }
+ a = nil
+ next = tmpl
+ default:
+ st.fail("unrecognized letter in prefix")
+ }
+ }
+ last = next
+ if a == nil {
+ a = next
+ } else {
+ a = &Qualified{Scope: a, Name: next, LocalName: false}
+ }
+
+ if c != 'S' && (len(st.str) == 0 || st.str[0] != 'E') {
+ st.subs.add(a)
+ }
+ }
+}
+
+// unqualifiedName parses:
+//
+// <unqualified-name> ::= <operator-name>
+// ::= <ctor-dtor-name>
+// ::= <source-name>
+// ::= <local-source-name>
+//
+// <local-source-name> ::= L <source-name> <discriminator>
+func (st *state) unqualifiedName() (r AST, isCast bool) {
+ if len(st.str) < 1 {
+ st.fail("expected unqualified name")
+ }
+ var a AST
+ isCast = false
+ c := st.str[0]
+ if isDigit(c) {
+ a = st.sourceName()
+ } else if isLower(c) {
+ a, _ = st.operatorName(false)
+ if _, ok := a.(*Cast); ok {
+ isCast = true
+ }
+ if op, ok := a.(*Operator); ok && op.Name == `operator"" ` {
+ n := st.sourceName()
+ a = &Unary{Op: op, Expr: n, Suffix: false, SizeofType: false}
+ }
+ } else if c == 'D' && len(st.str) > 1 && st.str[1] == 'C' {
+ var bindings []AST
+ st.advance(2)
+ for {
+ binding := st.sourceName()
+ bindings = append(bindings, binding)
+ if len(st.str) > 0 && st.str[0] == 'E' {
+ st.advance(1)
+ break
+ }
+ }
+ a = &StructuredBindings{Bindings: bindings}
+ } else {
+ switch c {
+ case 'C', 'D':
+ st.fail("constructor/destructor not in nested name")
+ case 'L':
+ st.advance(1)
+ a = st.sourceName()
+ a = st.discriminator(a)
+ case 'U':
+ if len(st.str) < 2 {
+ st.advance(1)
+ st.fail("expected closure or unnamed type")
+ }
+ c := st.str[1]
+ switch c {
+ case 'b':
+ st.advance(2)
+ st.compactNumber()
+ a = &Name{Name: "'block-literal'"}
+ case 'l':
+ a = st.closureTypeName()
+ case 't':
+ a = st.unnamedTypeName()
+ default:
+ st.advance(1)
+ st.fail("expected closure or unnamed type")
+ }
+ default:
+ st.fail("expected unqualified name")
+ }
+ }
+
+ if len(st.str) > 0 && st.str[0] == 'B' {
+ a = st.taggedName(a)
+ }
+
+ return a, isCast
+}
+
+// sourceName parses:
+//
+// <source-name> ::= <(positive length) number> <identifier>
+// identifier ::= <(unqualified source code identifier)>
+func (st *state) sourceName() AST {
+ val := st.number()
+ if val <= 0 {
+ st.fail("expected positive number")
+ }
+ if len(st.str) < val {
+ st.fail("not enough characters for identifier")
+ }
+ id := st.str[:val]
+ st.advance(val)
+
+ // Look for GCC encoding of anonymous namespace, and make it
+ // more friendly.
+ const anonPrefix = "_GLOBAL_"
+ if strings.HasPrefix(id, anonPrefix) && len(id) > len(anonPrefix)+2 {
+ c1 := id[len(anonPrefix)]
+ c2 := id[len(anonPrefix)+1]
+ if (c1 == '.' || c1 == '_' || c1 == '$') && c2 == 'N' {
+ id = "(anonymous namespace)"
+ }
+ }
+
+ n := &Name{Name: id}
+ return n
+}
+
+// number parses:
+//
+// number ::= [n] <(non-negative decimal integer)>
+func (st *state) number() int {
+ neg := false
+ if len(st.str) > 0 && st.str[0] == 'n' {
+ neg = true
+ st.advance(1)
+ }
+ if len(st.str) == 0 || !isDigit(st.str[0]) {
+ st.fail("missing number")
+ }
+ val := 0
+ for len(st.str) > 0 && isDigit(st.str[0]) {
+ // Number picked to ensure we can't overflow with 32-bit int.
+ // Any very large number here is bogus.
+ if val >= 0x80000000/10-10 {
+ st.fail("numeric overflow")
+ }
+ val = val*10 + int(st.str[0]-'0')
+ st.advance(1)
+ }
+ if neg {
+ val = -val
+ }
+ return val
+}
+
+// seqID parses:
+//
+// <seq-id> ::= <0-9A-Z>+
+//
+// We expect this to be followed by an underscore.
+func (st *state) seqID(eofOK bool) int {
+ if len(st.str) > 0 && st.str[0] == '_' {
+ st.advance(1)
+ return 0
+ }
+ id := 0
+ for {
+ if len(st.str) == 0 {
+ if eofOK {
+ return id + 1
+ }
+ st.fail("missing end to sequence ID")
+ }
+ // Don't overflow a 32-bit int.
+ if id >= 0x80000000/36-36 {
+ st.fail("sequence ID overflow")
+ }
+ c := st.str[0]
+ if c == '_' {
+ st.advance(1)
+ return id + 1
+ }
+ if isDigit(c) {
+ id = id*36 + int(c-'0')
+ } else if isUpper(c) {
+ id = id*36 + int(c-'A') + 10
+ } else {
+ st.fail("invalid character in sequence ID")
+ }
+ st.advance(1)
+ }
+}
+
+// An operator is the demangled name, and the number of arguments it
+// takes in an expression.
+type operator struct {
+ name string
+ args int
+}
+
+// The operators map maps the mangled operator names to information
+// about them.
+var operators = map[string]operator{
+ "aN": {"&=", 2},
+ "aS": {"=", 2},
+ "aa": {"&&", 2},
+ "ad": {"&", 1},
+ "an": {"&", 2},
+ "at": {"alignof ", 1},
+ "aw": {"co_await ", 1},
+ "az": {"alignof ", 1},
+ "cc": {"const_cast", 2},
+ "cl": {"()", 2},
+ // cp is not in the ABI but is used by clang "when the call
+ // would use ADL except for being parenthesized."
+ "cp": {"()", 2},
+ "cm": {",", 2},
+ "co": {"~", 1},
+ "dV": {"/=", 2},
+ "dX": {"[...]=", 3},
+ "da": {"delete[] ", 1},
+ "dc": {"dynamic_cast", 2},
+ "de": {"*", 1},
+ "di": {"=", 2},
+ "dl": {"delete ", 1},
+ "ds": {".*", 2},
+ "dt": {".", 2},
+ "dv": {"/", 2},
+ "dx": {"]=", 2},
+ "eO": {"^=", 2},
+ "eo": {"^", 2},
+ "eq": {"==", 2},
+ "fl": {"...", 2},
+ "fr": {"...", 2},
+ "fL": {"...", 3},
+ "fR": {"...", 3},
+ "ge": {">=", 2},
+ "gs": {"::", 1},
+ "gt": {">", 2},
+ "ix": {"[]", 2},
+ "lS": {"<<=", 2},
+ "le": {"<=", 2},
+ "li": {`operator"" `, 1},
+ "ls": {"<<", 2},
+ "lt": {"<", 2},
+ "mI": {"-=", 2},
+ "mL": {"*=", 2},
+ "mi": {"-", 2},
+ "ml": {"*", 2},
+ "mm": {"--", 1},
+ "na": {"new[]", 3},
+ "ne": {"!=", 2},
+ "ng": {"-", 1},
+ "nt": {"!", 1},
+ "nw": {"new", 3},
+ "nx": {"noexcept", 1},
+ "oR": {"|=", 2},
+ "oo": {"||", 2},
+ "or": {"|", 2},
+ "pL": {"+=", 2},
+ "pl": {"+", 2},
+ "pm": {"->*", 2},
+ "pp": {"++", 1},
+ "ps": {"+", 1},
+ "pt": {"->", 2},
+ "qu": {"?", 3},
+ "rM": {"%=", 2},
+ "rS": {">>=", 2},
+ "rc": {"reinterpret_cast", 2},
+ "rm": {"%", 2},
+ "rs": {">>", 2},
+ "sP": {"sizeof...", 1},
+ "sZ": {"sizeof...", 1},
+ "sc": {"static_cast", 2},
+ "ss": {"<=>", 2},
+ "st": {"sizeof ", 1},
+ "sz": {"sizeof ", 1},
+ "tr": {"throw", 0},
+ "tw": {"throw ", 1},
+}
+
+// operatorName parses:
+//
+// operator_name ::= many different two character encodings.
+// ::= cv <type>
+// ::= v <digit> <source-name>
+//
+// We need to know whether we are in an expression because it affects
+// how we handle template parameters in the type of a cast operator.
+func (st *state) operatorName(inExpression bool) (AST, int) {
+ if len(st.str) < 2 {
+ st.fail("missing operator code")
+ }
+ code := st.str[:2]
+ st.advance(2)
+ if code[0] == 'v' && isDigit(code[1]) {
+ name := st.sourceName()
+ return &Operator{Name: name.(*Name).Name}, int(code[1] - '0')
+ } else if code == "cv" {
+ // Push a nil on templates to indicate that template
+ // parameters will have their template filled in
+ // later.
+ if !inExpression {
+ st.templates = append(st.templates, nil)
+ }
+
+ t := st.demangleType(!inExpression)
+
+ if !inExpression {
+ st.templates = st.templates[:len(st.templates)-1]
+ }
+
+ return &Cast{To: t}, 1
+ } else if op, ok := operators[code]; ok {
+ return &Operator{Name: op.name}, op.args
+ } else {
+ st.failEarlier("unrecognized operator code", 2)
+ panic("not reached")
+ }
+}
+
+// localName parses:
+//
+// <local-name> ::= Z <(function) encoding> E <(entity) name> [<discriminator>]
+// ::= Z <(function) encoding> E s [<discriminator>]
+// ::= Z <(function) encoding> E d [<parameter> number>] _ <entity name>
+func (st *state) localName() AST {
+ st.checkChar('Z')
+ fn := st.encoding(true, forLocalName)
+ if len(st.str) == 0 || st.str[0] != 'E' {
+ st.fail("expected E after local name")
+ }
+ st.advance(1)
+ if len(st.str) > 0 && st.str[0] == 's' {
+ st.advance(1)
+ var n AST = &Name{Name: "string literal"}
+ n = st.discriminator(n)
+ return &Qualified{Scope: fn, Name: n, LocalName: true}
+ } else {
+ num := -1
+ if len(st.str) > 0 && st.str[0] == 'd' {
+ // Default argument scope.
+ st.advance(1)
+ num = st.compactNumber()
+ }
+ n := st.name()
+ n = st.discriminator(n)
+ if num >= 0 {
+ n = &DefaultArg{Num: num, Arg: n}
+ }
+ return &Qualified{Scope: fn, Name: n, LocalName: true}
+ }
+}
+
+// Parse a Java resource special-name.
+func (st *state) javaResource() AST {
+ off := st.off
+ ln := st.number()
+ if ln <= 1 {
+ st.failEarlier("java resource length less than 1", st.off-off)
+ }
+ if len(st.str) == 0 || st.str[0] != '_' {
+ st.fail("expected _ after number")
+ }
+ st.advance(1)
+ ln--
+ if len(st.str) < ln {
+ st.fail("not enough characters for java resource length")
+ }
+ str := st.str[:ln]
+ final := ""
+ st.advance(ln)
+ for i := 0; i < len(str); i++ {
+ if str[i] != '$' {
+ final += string(str[i])
+ } else {
+ if len(str) <= i+1 {
+ st.failEarlier("java resource escape at end of string", 1)
+ }
+ i++
+ r, ok := map[byte]string{
+ 'S': "/",
+ '_': ".",
+ '$': "$",
+ }[str[i]]
+ if !ok {
+ st.failEarlier("unrecognized java resource escape", ln-i-1)
+ }
+ final += r
+ }
+ }
+ return &Special{Prefix: "java resource ", Val: &Name{Name: final}}
+}
+
+// specialName parses:
+//
+// <special-name> ::= TV <type>
+// ::= TT <type>
+// ::= TI <type>
+// ::= TS <type>
+// ::= TA <template-arg>
+// ::= GV <(object) name>
+// ::= T <call-offset> <(base) encoding>
+// ::= Tc <call-offset> <call-offset> <(base) encoding>
+// g++ extensions:
+// ::= TC <type> <(offset) number> _ <(base) type>
+// ::= TF <type>
+// ::= TJ <type>
+// ::= GR <name>
+// ::= GA <encoding>
+// ::= Gr <resource name>
+// ::= GTt <encoding>
+// ::= GTn <encoding>
+func (st *state) specialName() AST {
+ if st.str[0] == 'T' {
+ st.advance(1)
+ if len(st.str) == 0 {
+ st.fail("expected special name code")
+ }
+ c := st.str[0]
+ st.advance(1)
+ switch c {
+ case 'V':
+ t := st.demangleType(false)
+ return &Special{Prefix: "vtable for ", Val: t}
+ case 'T':
+ t := st.demangleType(false)
+ return &Special{Prefix: "VTT for ", Val: t}
+ case 'I':
+ t := st.demangleType(false)
+ return &Special{Prefix: "typeinfo for ", Val: t}
+ case 'S':
+ t := st.demangleType(false)
+ return &Special{Prefix: "typeinfo name for ", Val: t}
+ case 'A':
+ t := st.templateArg()
+ return &Special{Prefix: "template parameter object for ", Val: t}
+ case 'h':
+ st.callOffset('h')
+ v := st.encoding(true, notForLocalName)
+ return &Special{Prefix: "non-virtual thunk to ", Val: v}
+ case 'v':
+ st.callOffset('v')
+ v := st.encoding(true, notForLocalName)
+ return &Special{Prefix: "virtual thunk to ", Val: v}
+ case 'c':
+ st.callOffset(0)
+ st.callOffset(0)
+ v := st.encoding(true, notForLocalName)
+ return &Special{Prefix: "covariant return thunk to ", Val: v}
+ case 'C':
+ derived := st.demangleType(false)
+ off := st.off
+ offset := st.number()
+ if offset < 0 {
+ st.failEarlier("expected positive offset", st.off-off)
+ }
+ if len(st.str) == 0 || st.str[0] != '_' {
+ st.fail("expected _ after number")
+ }
+ st.advance(1)
+ base := st.demangleType(false)
+ return &Special2{Prefix: "construction vtable for ", Val1: base, Middle: "-in-", Val2: derived}
+ case 'F':
+ t := st.demangleType(false)
+ return &Special{Prefix: "typeinfo fn for ", Val: t}
+ case 'J':
+ t := st.demangleType(false)
+ return &Special{Prefix: "java Class for ", Val: t}
+ case 'H':
+ n := st.name()
+ return &Special{Prefix: "TLS init function for ", Val: n}
+ case 'W':
+ n := st.name()
+ return &Special{Prefix: "TLS wrapper function for ", Val: n}
+ default:
+ st.fail("unrecognized special T name code")
+ panic("not reached")
+ }
+ } else {
+ st.checkChar('G')
+ if len(st.str) == 0 {
+ st.fail("expected special name code")
+ }
+ c := st.str[0]
+ st.advance(1)
+ switch c {
+ case 'V':
+ n := st.name()
+ return &Special{Prefix: "guard variable for ", Val: n}
+ case 'R':
+ n := st.name()
+ st.seqID(true)
+ return &Special{Prefix: "reference temporary for ", Val: n}
+ case 'A':
+ v := st.encoding(true, notForLocalName)
+ return &Special{Prefix: "hidden alias for ", Val: v}
+ case 'T':
+ if len(st.str) == 0 {
+ st.fail("expected special GT name code")
+ }
+ c := st.str[0]
+ st.advance(1)
+ v := st.encoding(true, notForLocalName)
+ switch c {
+ case 'n':
+ return &Special{Prefix: "non-transaction clone for ", Val: v}
+ default:
+ // The proposal is that different
+ // letters stand for different types
+ // of transactional cloning. Treat
+ // them all the same for now.
+ fallthrough
+ case 't':
+ return &Special{Prefix: "transaction clone for ", Val: v}
+ }
+ case 'r':
+ return st.javaResource()
+ default:
+ st.fail("unrecognized special G name code")
+ panic("not reached")
+ }
+ }
+}
+
+// callOffset parses:
+//
+// <call-offset> ::= h <nv-offset> _
+// ::= v <v-offset> _
+//
+// <nv-offset> ::= <(offset) number>
+//
+// <v-offset> ::= <(offset) number> _ <(virtual offset) number>
+//
+// The c parameter, if not 0, is a character we just read which is the
+// start of the <call-offset>.
+//
+// We don't display the offset information anywhere.
+func (st *state) callOffset(c byte) {
+ if c == 0 {
+ if len(st.str) == 0 {
+ st.fail("missing call offset")
+ }
+ c = st.str[0]
+ st.advance(1)
+ }
+ switch c {
+ case 'h':
+ st.number()
+ case 'v':
+ st.number()
+ if len(st.str) == 0 || st.str[0] != '_' {
+ st.fail("expected _ after number")
+ }
+ st.advance(1)
+ st.number()
+ default:
+ st.failEarlier("unrecognized call offset code", 1)
+ }
+ if len(st.str) == 0 || st.str[0] != '_' {
+ st.fail("expected _ after call offset")
+ }
+ st.advance(1)
+}
+
+// builtinTypes maps the type letter to the type name.
+var builtinTypes = map[byte]string{
+ 'a': "signed char",
+ 'b': "bool",
+ 'c': "char",
+ 'd': "double",
+ 'e': "long double",
+ 'f': "float",
+ 'g': "__float128",
+ 'h': "unsigned char",
+ 'i': "int",
+ 'j': "unsigned int",
+ 'l': "long",
+ 'm': "unsigned long",
+ 'n': "__int128",
+ 'o': "unsigned __int128",
+ 's': "short",
+ 't': "unsigned short",
+ 'v': "void",
+ 'w': "wchar_t",
+ 'x': "long long",
+ 'y': "unsigned long long",
+ 'z': "...",
+}
+
+// demangleType parses:
+//
+// <type> ::= <builtin-type>
+// ::= <function-type>
+// ::= <class-enum-type>
+// ::= <array-type>
+// ::= <pointer-to-member-type>
+// ::= <template-param>
+// ::= <template-template-param> <template-args>
+// ::= <substitution>
+// ::= <CV-qualifiers> <type>
+// ::= P <type>
+// ::= R <type>
+// ::= O <type> (C++0x)
+// ::= C <type>
+// ::= G <type>
+// ::= U <source-name> <type>
+//
+// <builtin-type> ::= various one letter codes
+// ::= u <source-name>
+func (st *state) demangleType(isCast bool) AST {
+ if len(st.str) == 0 {
+ st.fail("expected type")
+ }
+
+ addSubst := true
+
+ q := st.cvQualifiers()
+ if q != nil {
+ if len(st.str) == 0 {
+ st.fail("expected type")
+ }
+
+ // CV-qualifiers before a function type apply to
+ // 'this', so avoid adding the unqualified function
+ // type to the substitution list.
+ if st.str[0] == 'F' {
+ addSubst = false
+ }
+ }
+
+ var ret AST
+
+ // Use correct substitution for a template parameter.
+ var sub AST
+
+ if btype, ok := builtinTypes[st.str[0]]; ok {
+ ret = &BuiltinType{Name: btype}
+ st.advance(1)
+ if q != nil {
+ ret = &TypeWithQualifiers{Base: ret, Qualifiers: q}
+ st.subs.add(ret)
+ }
+ return ret
+ }
+ c := st.str[0]
+ switch c {
+ case 'u':
+ st.advance(1)
+ ret = st.sourceName()
+ case 'F':
+ ret = st.functionType()
+ case 'N', 'Z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
+ ret = st.name()
+ case 'A':
+ ret = st.arrayType(isCast)
+ case 'M':
+ ret = st.pointerToMemberType(isCast)
+ case 'T':
+ if len(st.str) > 1 && (st.str[1] == 's' || st.str[1] == 'u' || st.str[1] == 'e') {
+ c = st.str[1]
+ st.advance(2)
+ ret = st.name()
+ var kind string
+ switch c {
+ case 's':
+ kind = "struct"
+ case 'u':
+ kind = "union"
+ case 'e':
+ kind = "enum"
+ }
+ ret = &ElaboratedType{Kind: kind, Type: ret}
+ break
+ }
+
+ ret = st.templateParam()
+ if len(st.str) > 0 && st.str[0] == 'I' {
+ // See the function comment to explain this.
+ if !isCast {
+ st.subs.add(ret)
+ args := st.templateArgs()
+ ret = &Template{Name: ret, Args: args}
+ } else {
+ ret = st.demangleCastTemplateArgs(ret, true)
+ }
+ }
+ case 'S':
+ // If this is a special substitution, then it
+ // is the start of <class-enum-type>.
+ var c2 byte
+ if len(st.str) > 1 {
+ c2 = st.str[1]
+ }
+ if isDigit(c2) || c2 == '_' || isUpper(c2) {
+ ret = st.substitution(false)
+ if len(st.str) == 0 || st.str[0] != 'I' {
+ addSubst = false
+ } else {
+ // See the function comment to explain this.
+ if _, ok := ret.(*TemplateParam); !ok || !isCast {
+ args := st.templateArgs()
+ ret = &Template{Name: ret, Args: args}
+ } else {
+ next := st.demangleCastTemplateArgs(ret, false)
+ if next == ret {
+ addSubst = false
+ }
+ ret = next
+ }
+ }
+ } else {
+ ret = st.name()
+ // This substitution is not itself a
+ // substitution candidate, unless template
+ // arguments were added.
+ if ret == subAST[c2] || ret == verboseAST[c2] {
+ addSubst = false
+ }
+ }
+ case 'O', 'P', 'R', 'C', 'G':
+ st.advance(1)
+ t := st.demangleType(isCast)
+ switch c {
+ case 'O':
+ ret = &RvalueReferenceType{Base: t}
+ case 'P':
+ ret = &PointerType{Base: t}
+ case 'R':
+ ret = &ReferenceType{Base: t}
+ case 'C':
+ ret = &ComplexType{Base: t}
+ case 'G':
+ ret = &ImaginaryType{Base: t}
+ }
+ case 'U':
+ if len(st.str) < 2 {
+ st.fail("expected source name or unnamed type")
+ }
+ switch st.str[1] {
+ case 'l':
+ ret = st.closureTypeName()
+ addSubst = false
+ case 't':
+ ret = st.unnamedTypeName()
+ addSubst = false
+ default:
+ st.advance(1)
+ n := st.sourceName()
+ if len(st.str) > 0 && st.str[0] == 'I' {
+ args := st.templateArgs()
+ n = &Template{Name: n, Args: args}
+ }
+ t := st.demangleType(isCast)
+ ret = &VendorQualifier{Qualifier: n, Type: t}
+ }
+ case 'D':
+ st.advance(1)
+ if len(st.str) == 0 {
+ st.fail("expected D code for type")
+ }
+ addSubst = false
+ c2 := st.str[0]
+ st.advance(1)
+ switch c2 {
+ case 'T', 't':
+ // decltype(expression)
+ ret = st.expression()
+ if len(st.str) == 0 || st.str[0] != 'E' {
+ st.fail("expected E after expression in type")
+ }
+ st.advance(1)
+ ret = &Decltype{Expr: ret}
+ addSubst = true
+
+ case 'p':
+ t := st.demangleType(isCast)
+ pack := st.findArgumentPack(t)
+ ret = &PackExpansion{Base: t, Pack: pack}
+ addSubst = true
+
+ case 'a':
+ ret = &Name{Name: "auto"}
+ case 'c':
+ ret = &Name{Name: "decltype(auto)"}
+
+ case 'f':
+ ret = &BuiltinType{Name: "decimal32"}
+ case 'd':
+ ret = &BuiltinType{Name: "decimal64"}
+ case 'e':
+ ret = &BuiltinType{Name: "decimal128"}
+ case 'h':
+ ret = &BuiltinType{Name: "half"}
+ case 'u':
+ ret = &BuiltinType{Name: "char8_t"}
+ case 's':
+ ret = &BuiltinType{Name: "char16_t"}
+ case 'i':
+ ret = &BuiltinType{Name: "char32_t"}
+ case 'n':
+ ret = &BuiltinType{Name: "decltype(nullptr)"}
+
+ case 'F':
+ accum := false
+ bits := 0
+ if len(st.str) > 0 && isDigit(st.str[0]) {
+ accum = true
+ bits = st.number()
+ }
+ if len(st.str) > 0 && st.str[0] == '_' {
+ if bits == 0 {
+ st.fail("expected non-zero number of bits")
+ }
+ st.advance(1)
+ ret = &BinaryFP{Bits: bits}
+ } else {
+ base := st.demangleType(isCast)
+ if len(st.str) > 0 && isDigit(st.str[0]) {
+ // We don't care about the bits.
+ st.number()
+ }
+ sat := false
+ if len(st.str) > 0 {
+ if st.str[0] == 's' {
+ sat = true
+ }
+ st.advance(1)
+ }
+ ret = &FixedType{Base: base, Accum: accum, Sat: sat}
+ }
+
+ case 'v':
+ ret = st.vectorType(isCast)
+ addSubst = true
+
+ default:
+ st.fail("unrecognized D code in type")
+ }
+
+ default:
+ st.fail("unrecognized type code")
+ }
+
+ if addSubst {
+ if sub != nil {
+ st.subs.add(sub)
+ } else {
+ st.subs.add(ret)
+ }
+ }
+
+ if q != nil {
+ if _, ok := ret.(*FunctionType); ok {
+ ret = &MethodWithQualifiers{Method: ret, Qualifiers: q, RefQualifier: ""}
+ } else if mwq, ok := ret.(*MethodWithQualifiers); ok {
+ // Merge adjacent qualifiers. This case
+ // happens with a function with a trailing
+ // ref-qualifier.
+ mwq.Qualifiers = mergeQualifiers(q, mwq.Qualifiers)
+ } else {
+ // Merge adjacent qualifiers. This case
+ // happens with multi-dimensional array types.
+ if qsub, ok := ret.(*TypeWithQualifiers); ok {
+ q = mergeQualifiers(q, qsub.Qualifiers)
+ ret = qsub.Base
+ }
+ ret = &TypeWithQualifiers{Base: ret, Qualifiers: q}
+ }
+ st.subs.add(ret)
+ }
+
+ return ret
+}
+
+// demangleCastTemplateArgs is for a rather hideous parse. When we
+// see a template-param followed by a template-args, we need to decide
+// whether we have a template-param or a template-template-param.
+// Normally it is template-template-param, meaning that we pick up the
+// template arguments here. But, if we are parsing the type for a
+// cast operator, then the only way this can be template-template-param
+// is if there is another set of template-args immediately after this
+// set. That would look like this:
+//
+// <nested-name>
+// -> <template-prefix> <template-args>
+// -> <prefix> <template-unqualified-name> <template-args>
+// -> <unqualified-name> <template-unqualified-name> <template-args>
+// -> <source-name> <template-unqualified-name> <template-args>
+// -> <source-name> <operator-name> <template-args>
+// -> <source-name> cv <type> <template-args>
+// -> <source-name> cv <template-template-param> <template-args> <template-args>
+//
+// Otherwise, we have this derivation:
+//
+// <nested-name>
+// -> <template-prefix> <template-args>
+// -> <prefix> <template-unqualified-name> <template-args>
+// -> <unqualified-name> <template-unqualified-name> <template-args>
+// -> <source-name> <template-unqualified-name> <template-args>
+// -> <source-name> <operator-name> <template-args>
+// -> <source-name> cv <type> <template-args>
+// -> <source-name> cv <template-param> <template-args>
+//
+// in which the template-args are actually part of the prefix. For
+// the special case where this arises, demangleType is called with
+// isCast as true. This function is then responsible for checking
+// whether we see <template-param> <template-args> but there is not
+// another following <template-args>. In that case, we reset the
+// parse and just return the <template-param>.
+func (st *state) demangleCastTemplateArgs(tp AST, addSubst bool) AST {
+ save := st.copy()
+
+ var args []AST
+ failed := false
+ func() {
+ defer func() {
+ if r := recover(); r != nil {
+ if _, ok := r.(demangleErr); ok {
+ failed = true
+ } else {
+ panic(r)
+ }
+ }
+ }()
+
+ args = st.templateArgs()
+ }()
+
+ if !failed && len(st.str) > 0 && st.str[0] == 'I' {
+ if addSubst {
+ st.subs.add(tp)
+ }
+ return &Template{Name: tp, Args: args}
+ }
+ // Reset back to before we started reading the template arguments.
+ // They will be read again by st.prefix.
+ *st = *save
+ return tp
+}
+
+// mergeQualifiers merges two qualifier lists into one.
+func mergeQualifiers(q1AST, q2AST AST) AST {
+ if q1AST == nil {
+ return q2AST
+ }
+ if q2AST == nil {
+ return q1AST
+ }
+ q1 := q1AST.(*Qualifiers)
+ m := make(map[string]bool)
+ for _, qualAST := range q1.Qualifiers {
+ qual := qualAST.(*Qualifier)
+ if len(qual.Exprs) == 0 {
+ m[qual.Name] = true
+ }
+ }
+ rq := q1.Qualifiers
+ for _, qualAST := range q2AST.(*Qualifiers).Qualifiers {
+ qual := qualAST.(*Qualifier)
+ if len(qual.Exprs) > 0 {
+ rq = append(rq, qualAST)
+ } else if !m[qual.Name] {
+ rq = append(rq, qualAST)
+ m[qual.Name] = true
+ }
+ }
+ q1.Qualifiers = rq
+ return q1
+}
+
+// qualifiers maps from the character used in the mangled name to the
+// string to print.
+var qualifiers = map[byte]string{
+ 'r': "restrict",
+ 'V': "volatile",
+ 'K': "const",
+}
+
+// cvQualifiers parses:
+//
+// <CV-qualifiers> ::= [r] [V] [K]
+func (st *state) cvQualifiers() AST {
+ var q []AST
+qualLoop:
+ for len(st.str) > 0 {
+ if qv, ok := qualifiers[st.str[0]]; ok {
+ qual := &Qualifier{Name: qv}
+ q = append([]AST{qual}, q...)
+ st.advance(1)
+ } else if len(st.str) > 1 && st.str[0] == 'D' {
+ var qual AST
+ switch st.str[1] {
+ case 'x':
+ qual = &Qualifier{Name: "transaction_safe"}
+ st.advance(2)
+ case 'o':
+ qual = &Qualifier{Name: "noexcept"}
+ st.advance(2)
+ case 'O':
+ st.advance(2)
+ expr := st.expression()
+ if len(st.str) == 0 || st.str[0] != 'E' {
+ st.fail("expected E after computed noexcept expression")
+ }
+ st.advance(1)
+ qual = &Qualifier{Name: "noexcept", Exprs: []AST{expr}}
+ case 'w':
+ st.advance(2)
+ parmlist := st.parmlist()
+ if len(st.str) == 0 || st.str[0] != 'E' {
+ st.fail("expected E after throw parameter list")
+ }
+ st.advance(1)
+ qual = &Qualifier{Name: "throw", Exprs: parmlist}
+ default:
+ break qualLoop
+ }
+ q = append([]AST{qual}, q...)
+ } else {
+ break
+ }
+ }
+ if len(q) == 0 {
+ return nil
+ }
+ return &Qualifiers{Qualifiers: q}
+}
+
+// refQualifier parses:
+//
+// <ref-qualifier> ::= R
+// ::= O
+func (st *state) refQualifier() string {
+ if len(st.str) > 0 {
+ switch st.str[0] {
+ case 'R':
+ st.advance(1)
+ return "&"
+ case 'O':
+ st.advance(1)
+ return "&&"
+ }
+ }
+ return ""
+}
+
+// parmlist parses:
+//
+// <type>+
+func (st *state) parmlist() []AST {
+ var ret []AST
+ for {
+ if len(st.str) < 1 {
+ break
+ }
+ if st.str[0] == 'E' || st.str[0] == '.' {
+ break
+ }
+ if (st.str[0] == 'R' || st.str[0] == 'O') && len(st.str) > 1 && st.str[1] == 'E' {
+ // This is a function ref-qualifier.
+ break
+ }
+ ptype := st.demangleType(false)
+ ret = append(ret, ptype)
+ }
+
+ // There should always be at least one type. A function that
+ // takes no arguments will have a single parameter type
+ // "void".
+ if len(ret) == 0 {
+ st.fail("expected at least one type in type list")
+ }
+
+ // Omit a single parameter type void.
+ if len(ret) == 1 {
+ if bt, ok := ret[0].(*BuiltinType); ok && bt.Name == "void" {
+ ret = nil
+ }
+ }
+
+ return ret
+}
+
+// functionType parses:
+//
+// <function-type> ::= F [Y] <bare-function-type> [<ref-qualifier>] E
+func (st *state) functionType() AST {
+ st.checkChar('F')
+ if len(st.str) > 0 && st.str[0] == 'Y' {
+ // Function has C linkage. We don't print this.
+ st.advance(1)
+ }
+ ret := st.bareFunctionType(true)
+ r := st.refQualifier()
+ if r != "" {
+ ret = &MethodWithQualifiers{Method: ret, Qualifiers: nil, RefQualifier: r}
+ }
+ if len(st.str) == 0 || st.str[0] != 'E' {
+ st.fail("expected E after function type")
+ }
+ st.advance(1)
+ return ret
+}
+
+// bareFunctionType parses:
+//
+// <bare-function-type> ::= [J]<type>+
+func (st *state) bareFunctionType(hasReturnType bool) AST {
+ if len(st.str) > 0 && st.str[0] == 'J' {
+ hasReturnType = true
+ st.advance(1)
+ }
+ var returnType AST
+ if hasReturnType {
+ returnType = st.demangleType(false)
+ }
+ types := st.parmlist()
+ return &FunctionType{
+ Return: returnType,
+ Args: types,
+ ForLocalName: false, // may be set later in encoding
+ }
+}
+
+// arrayType parses:
+//
+// <array-type> ::= A <(positive dimension) number> _ <(element) type>
+// ::= A [<(dimension) expression>] _ <(element) type>
+func (st *state) arrayType(isCast bool) AST {
+ st.checkChar('A')
+
+ if len(st.str) == 0 {
+ st.fail("missing array dimension")
+ }
+
+ var dim AST
+ if st.str[0] == '_' {
+ dim = &Name{Name: ""}
+ } else if isDigit(st.str[0]) {
+ i := 1
+ for len(st.str) > i && isDigit(st.str[i]) {
+ i++
+ }
+ dim = &Name{Name: st.str[:i]}
+ st.advance(i)
+ } else {
+ dim = st.expression()
+ }
+
+ if len(st.str) == 0 || st.str[0] != '_' {
+ st.fail("expected _ after dimension")
+ }
+ st.advance(1)
+
+ t := st.demangleType(isCast)
+
+ arr := &ArrayType{Dimension: dim, Element: t}
+
+ // Qualifiers on the element of an array type go on the whole
+ // array type.
+ if q, ok := arr.Element.(*TypeWithQualifiers); ok {
+ return &TypeWithQualifiers{Base: &ArrayType{Dimension: dim, Element: q.Base}, Qualifiers: q.Qualifiers}
+ }
+
+ return arr
+}
+
+// vectorType parses:
+//
+// <vector-type> ::= Dv <number> _ <type>
+// ::= Dv _ <expression> _ <type>
+func (st *state) vectorType(isCast bool) AST {
+ if len(st.str) == 0 {
+ st.fail("expected vector dimension")
+ }
+
+ var dim AST
+ if st.str[0] == '_' {
+ st.advance(1)
+ dim = st.expression()
+ } else {
+ num := st.number()
+ dim = &Name{Name: fmt.Sprintf("%d", num)}
+ }
+
+ if len(st.str) == 0 || st.str[0] != '_' {
+ st.fail("expected _ after vector dimension")
+ }
+ st.advance(1)
+
+ t := st.demangleType(isCast)
+
+ return &VectorType{Dimension: dim, Base: t}
+}
+
+// pointerToMemberType parses:
+//
+// <pointer-to-member-type> ::= M <(class) type> <(member) type>
+func (st *state) pointerToMemberType(isCast bool) AST {
+ st.checkChar('M')
+ cl := st.demangleType(false)
+
+ // The ABI says, "The type of a non-static member function is
+ // considered to be different, for the purposes of
+ // substitution, from the type of a namespace-scope or static
+ // member function whose type appears similar. The types of
+ // two non-static member functions are considered to be
+ // different, for the purposes of substitution, if the
+ // functions are members of different classes. In other words,
+ // for the purposes of substitution, the class of which the
+ // function is a member is considered part of the type of
+ // function."
+ //
+ // For a pointer to member function, this call to demangleType
+ // will end up adding a (possibly qualified) non-member
+ // function type to the substitution table, which is not
+ // correct; however, the member function type will never be
+ // used in a substitution, so putting the wrong type in the
+ // substitution table is harmless.
+ mem := st.demangleType(isCast)
+ return &PtrMem{Class: cl, Member: mem}
+}
+
+// compactNumber parses:
+//
+// <non-negative number> _
+func (st *state) compactNumber() int {
+ if len(st.str) == 0 {
+ st.fail("missing index")
+ }
+ if st.str[0] == '_' {
+ st.advance(1)
+ return 0
+ } else if st.str[0] == 'n' {
+ st.fail("unexpected negative number")
+ }
+ n := st.number()
+ if len(st.str) == 0 || st.str[0] != '_' {
+ st.fail("missing underscore after number")
+ }
+ st.advance(1)
+ return n + 1
+}
+
+// templateParam parses:
+//
+// <template-param> ::= T_
+// ::= T <(parameter-2 non-negative) number> _
+// ::= TL <level-1> __
+// ::= TL <level-1> _ <parameter-2 non-negative number> _
+//
+// When a template parameter is a substitution candidate, any
+// reference to that substitution refers to the template parameter
+// with the same index in the currently active template, not to
+// whatever the template parameter would be expanded to here. We sort
+// this out in substitution and simplify.
+func (st *state) templateParam() AST {
+ off := st.off
+ st.checkChar('T')
+
+ level := 0
+ if len(st.str) > 0 && st.str[0] == 'L' {
+ st.advance(1)
+ level = st.compactNumber()
+ }
+
+ n := st.compactNumber()
+
+ if level >= len(st.templates) {
+ if st.lambdaTemplateLevel > 0 && level == st.lambdaTemplateLevel-1 {
+ // Lambda auto params are mangled as template params.
+ // See https://gcc.gnu.org/PR78252.
+ return &LambdaAuto{Index: n}
+ }
+ st.failEarlier(fmt.Sprintf("template parameter is not in scope of template (level %d >= %d)", level, len(st.templates)), st.off-off)
+ }
+
+ template := st.templates[level]
+
+ if template == nil {
+ // We are parsing a cast operator. If the cast is
+ // itself a template, then this is a forward
+ // reference. Fill it in later.
+ return &TemplateParam{Index: n, Template: nil}
+ }
+
+ if n >= len(template.Args) {
+ if st.lambdaTemplateLevel > 0 && level == st.lambdaTemplateLevel-1 {
+ // Lambda auto params are mangled as template params.
+ // See https://gcc.gnu.org/PR78252.
+ return &LambdaAuto{Index: n}
+ }
+ st.failEarlier(fmt.Sprintf("template index out of range (%d >= %d)", n, len(template.Args)), st.off-off)
+ }
+
+ return &TemplateParam{Index: n, Template: template}
+}
+
+// setTemplate sets the Template field of any TemplateParam's in a.
+// This handles the forward referencing template parameters found in
+// cast operators.
+func (st *state) setTemplate(a AST, tmpl *Template) {
+ var seen []AST
+ a.Traverse(func(a AST) bool {
+ switch a := a.(type) {
+ case *TemplateParam:
+ if a.Template != nil {
+ if tmpl != nil {
+ st.fail("duplicate template parameters")
+ }
+ return false
+ }
+ if tmpl == nil {
+ st.fail("cast template parameter not in scope of template")
+ }
+ if a.Index >= len(tmpl.Args) {
+ st.fail(fmt.Sprintf("cast template index out of range (%d >= %d)", a.Index, len(tmpl.Args)))
+ }
+ a.Template = tmpl
+ return false
+ case *Closure:
+ // There are no template params in closure types.
+ // https://gcc.gnu.org/PR78252.
+ return false
+ default:
+ for _, v := range seen {
+ if v == a {
+ return false
+ }
+ }
+ seen = append(seen, a)
+ return true
+ }
+ })
+}
+
+// clearTemplateArgs gives an error for any unset Template field in
+// args. This handles erroneous cases where a cast operator with a
+// forward referenced template is in the scope of another cast
+// operator.
+func (st *state) clearTemplateArgs(args []AST) {
+ for _, a := range args {
+ st.setTemplate(a, nil)
+ }
+}
+
+// templateArgs parses:
+//
+// <template-args> ::= I <template-arg>+ E
+func (st *state) templateArgs() []AST {
+ if len(st.str) == 0 || (st.str[0] != 'I' && st.str[0] != 'J') {
+ panic("internal error")
+ }
+ st.advance(1)
+
+ var ret []AST
+ for len(st.str) == 0 || st.str[0] != 'E' {
+ arg := st.templateArg()
+ ret = append(ret, arg)
+ }
+ st.advance(1)
+ return ret
+}
+
+// templateArg parses:
+//
+// <template-arg> ::= <type>
+// ::= X <expression> E
+// ::= <expr-primary>
+func (st *state) templateArg() AST {
+ if len(st.str) == 0 {
+ st.fail("missing template argument")
+ }
+ switch st.str[0] {
+ case 'X':
+ st.advance(1)
+ expr := st.expression()
+ if len(st.str) == 0 || st.str[0] != 'E' {
+ st.fail("missing end of expression")
+ }
+ st.advance(1)
+ return expr
+
+ case 'L':
+ return st.exprPrimary()
+
+ case 'I', 'J':
+ args := st.templateArgs()
+ return &ArgumentPack{Args: args}
+
+ default:
+ return st.demangleType(false)
+ }
+}
+
+// exprList parses a sequence of expressions up to a terminating character.
+func (st *state) exprList(stop byte) AST {
+ if len(st.str) > 0 && st.str[0] == stop {
+ st.advance(1)
+ return &ExprList{Exprs: nil}
+ }
+
+ var exprs []AST
+ for {
+ e := st.expression()
+ exprs = append(exprs, e)
+ if len(st.str) > 0 && st.str[0] == stop {
+ st.advance(1)
+ break
+ }
+ }
+ return &ExprList{Exprs: exprs}
+}
+
+// expression parses:
+//
+// <expression> ::= <(unary) operator-name> <expression>
+// ::= <(binary) operator-name> <expression> <expression>
+// ::= <(trinary) operator-name> <expression> <expression> <expression>
+// ::= pp_ <expression>
+// ::= mm_ <expression>
+// ::= cl <expression>+ E
+// ::= cl <expression>+ E
+// ::= cv <type> <expression>
+// ::= cv <type> _ <expression>* E
+// ::= tl <type> <braced-expression>* E
+// ::= il <braced-expression>* E
+// ::= [gs] nw <expression>* _ <type> E
+// ::= [gs] nw <expression>* _ <type> <initializer>
+// ::= [gs] na <expression>* _ <type> E
+// ::= [gs] na <expression>* _ <type> <initializer>
+// ::= [gs] dl <expression>
+// ::= [gs] da <expression>
+// ::= dc <type> <expression>
+// ::= sc <type> <expression>
+// ::= cc <type> <expression>
+// ::= mc <parameter type> <expr> [<offset number>] E
+// ::= rc <type> <expression>
+// ::= ti <type>
+// ::= te <expression>
+// ::= so <referent type> <expr> [<offset number>] <union-selector>* [p] E
+// ::= st <type>
+// ::= sz <expression>
+// ::= at <type>
+// ::= az <expression>
+// ::= nx <expression>
+// ::= <template-param>
+// ::= <function-param>
+// ::= dt <expression> <unresolved-name>
+// ::= pt <expression> <unresolved-name>
+// ::= ds <expression> <expression>
+// ::= sZ <template-param>
+// ::= sZ <function-param>
+// ::= sP <template-arg>* E
+// ::= sp <expression>
+// ::= fl <binary operator-name> <expression>
+// ::= fr <binary operator-name> <expression>
+// ::= fL <binary operator-name> <expression> <expression>
+// ::= fR <binary operator-name> <expression> <expression>
+// ::= tw <expression>
+// ::= tr
+// ::= u <source-name> <template-arg>* E
+// ::= <unresolved-name>
+// ::= <expr-primary>
+//
+// <function-param> ::= fp <CV-qualifiers> _
+// ::= fp <CV-qualifiers> <number>
+// ::= fL <number> p <CV-qualifiers> _
+// ::= fL <number> p <CV-qualifiers> <number>
+// ::= fpT
+//
+// <braced-expression> ::= <expression>
+// ::= di <field source-name> <braced-expression>
+// ::= dx <index expression> <braced-expression>
+// ::= dX <range begin expression> <range end expression> <braced-expression>
+func (st *state) expression() AST {
+ if len(st.str) == 0 {
+ st.fail("expected expression")
+ }
+ if st.str[0] == 'L' {
+ return st.exprPrimary()
+ } else if st.str[0] == 'T' {
+ return st.templateParam()
+ } else if st.str[0] == 's' && len(st.str) > 1 && st.str[1] == 'o' {
+ st.advance(2)
+ return st.subobject()
+ } else if st.str[0] == 's' && len(st.str) > 1 && st.str[1] == 'r' {
+ return st.unresolvedName()
+ } else if st.str[0] == 's' && len(st.str) > 1 && st.str[1] == 'p' {
+ st.advance(2)
+ e := st.expression()
+ pack := st.findArgumentPack(e)
+ return &PackExpansion{Base: e, Pack: pack}
+ } else if st.str[0] == 's' && len(st.str) > 1 && st.str[1] == 'Z' {
+ st.advance(2)
+ off := st.off
+ e := st.expression()
+ ap := st.findArgumentPack(e)
+ if ap == nil {
+ st.failEarlier("missing argument pack", st.off-off)
+ }
+ return &SizeofPack{Pack: ap}
+ } else if st.str[0] == 's' && len(st.str) > 1 && st.str[1] == 'P' {
+ st.advance(2)
+ var args []AST
+ for len(st.str) == 0 || st.str[0] != 'E' {
+ arg := st.templateArg()
+ args = append(args, arg)
+ }
+ st.advance(1)
+ return &SizeofArgs{Args: args}
+ } else if st.str[0] == 'f' && len(st.str) > 1 && st.str[1] == 'p' {
+ st.advance(2)
+ if len(st.str) > 0 && st.str[0] == 'T' {
+ st.advance(1)
+ return &FunctionParam{Index: 0}
+ } else {
+ // We can see qualifiers here, but we don't
+ // include them in the demangled string.
+ st.cvQualifiers()
+ index := st.compactNumber()
+ return &FunctionParam{Index: index + 1}
+ }
+ } else if st.str[0] == 'f' && len(st.str) > 2 && st.str[1] == 'L' && isDigit(st.str[2]) {
+ st.advance(2)
+ // We don't include the scope count in the demangled string.
+ st.number()
+ if len(st.str) == 0 || st.str[0] != 'p' {
+ st.fail("expected p after function parameter scope count")
+ }
+ st.advance(1)
+ // We can see qualifiers here, but we don't include them
+ // in the demangled string.
+ st.cvQualifiers()
+ index := st.compactNumber()
+ return &FunctionParam{Index: index + 1}
+ } else if st.str[0] == 'm' && len(st.str) > 1 && st.str[1] == 'c' {
+ st.advance(2)
+ typ := st.demangleType(false)
+ expr := st.expression()
+ offset := 0
+ if len(st.str) > 0 && (st.str[0] == 'n' || isDigit(st.str[0])) {
+ offset = st.number()
+ }
+ if len(st.str) == 0 || st.str[0] != 'E' {
+ st.fail("expected E after pointer-to-member conversion")
+ }
+ st.advance(1)
+ return &PtrMemCast{
+ Type: typ,
+ Expr: expr,
+ Offset: offset,
+ }
+ } else if isDigit(st.str[0]) || (st.str[0] == 'o' && len(st.str) > 1 && st.str[1] == 'n') {
+ if st.str[0] == 'o' {
+ // Skip operator function ID.
+ st.advance(2)
+ }
+ n, _ := st.unqualifiedName()
+ if len(st.str) > 0 && st.str[0] == 'I' {
+ args := st.templateArgs()
+ n = &Template{Name: n, Args: args}
+ }
+ return n
+ } else if (st.str[0] == 'i' || st.str[0] == 't') && len(st.str) > 1 && st.str[1] == 'l' {
+ // Brace-enclosed initializer list.
+ c := st.str[0]
+ st.advance(2)
+ var t AST
+ if c == 't' {
+ t = st.demangleType(false)
+ }
+ exprs := st.exprList('E')
+ return &InitializerList{Type: t, Exprs: exprs}
+ } else if st.str[0] == 's' && len(st.str) > 1 && st.str[1] == 't' {
+ o, _ := st.operatorName(true)
+ t := st.demangleType(false)
+ return &Unary{Op: o, Expr: t, Suffix: false, SizeofType: true}
+ } else if st.str[0] == 'u' {
+ st.advance(1)
+ name := st.sourceName()
+ // Special case __uuidof followed by type or
+ // expression, as used by LLVM.
+ if n, ok := name.(*Name); ok && n.Name == "__uuidof" {
+ if len(st.str) < 2 {
+ st.fail("missing uuidof argument")
+ }
+ var operand AST
+ if st.str[0] == 't' {
+ st.advance(1)
+ operand = st.demangleType(false)
+ } else if st.str[0] == 'z' {
+ st.advance(1)
+ operand = st.expression()
+ }
+ if operand != nil {
+ return &Binary{
+ Op: &Operator{Name: "()"},
+ Left: name,
+ Right: &ExprList{
+ Exprs: []AST{operand},
+ },
+ }
+ }
+ }
+ var args []AST
+ for {
+ if len(st.str) == 0 {
+ st.fail("missing argument in vendor extended expressoin")
+ }
+ if st.str[0] == 'E' {
+ st.advance(1)
+ break
+ }
+ arg := st.templateArg()
+ args = append(args, arg)
+ }
+ return &Binary{
+ Op: &Operator{Name: "()"},
+ Left: name,
+ Right: &ExprList{Exprs: args},
+ }
+ } else {
+ if len(st.str) < 2 {
+ st.fail("missing operator code")
+ }
+ code := st.str[:2]
+ o, args := st.operatorName(true)
+ switch args {
+ case 0:
+ return &Nullary{Op: o}
+
+ case 1:
+ suffix := false
+ if code == "pp" || code == "mm" {
+ if len(st.str) > 0 && st.str[0] == '_' {
+ st.advance(1)
+ } else {
+ suffix = true
+ }
+ }
+ var operand AST
+ if _, ok := o.(*Cast); ok && len(st.str) > 0 && st.str[0] == '_' {
+ st.advance(1)
+ operand = st.exprList('E')
+ } else {
+ operand = st.expression()
+ }
+ return &Unary{Op: o, Expr: operand, Suffix: suffix, SizeofType: false}
+
+ case 2:
+ var left, right AST
+ if code == "sc" || code == "dc" || code == "cc" || code == "rc" {
+ left = st.demangleType(false)
+ } else if code[0] == 'f' {
+ left, _ = st.operatorName(true)
+ right = st.expression()
+ return &Fold{Left: code[1] == 'l', Op: left, Arg1: right, Arg2: nil}
+ } else if code == "di" {
+ left, _ = st.unqualifiedName()
+ } else {
+ left = st.expression()
+ }
+ if code == "cl" || code == "cp" {
+ right = st.exprList('E')
+ } else if code == "dt" || code == "pt" {
+ right = st.unresolvedName()
+ if len(st.str) > 0 && st.str[0] == 'I' {
+ args := st.templateArgs()
+ right = &Template{Name: right, Args: args}
+ }
+ } else {
+ right = st.expression()
+ }
+ return &Binary{Op: o, Left: left, Right: right}
+
+ case 3:
+ if code[0] == 'n' {
+ if code[1] != 'w' && code[1] != 'a' {
+ panic("internal error")
+ }
+ place := st.exprList('_')
+ if place.(*ExprList).Exprs == nil {
+ place = nil
+ }
+ t := st.demangleType(false)
+ var ini AST
+ if len(st.str) > 0 && st.str[0] == 'E' {
+ st.advance(1)
+ } else if len(st.str) > 1 && st.str[0] == 'p' && st.str[1] == 'i' {
+ // Parenthesized initializer.
+ st.advance(2)
+ ini = st.exprList('E')
+ } else if len(st.str) > 1 && st.str[0] == 'i' && st.str[1] == 'l' {
+ // Initializer list.
+ ini = st.expression()
+ } else {
+ st.fail("unrecognized new initializer")
+ }
+ return &New{Op: o, Place: place, Type: t, Init: ini}
+ } else if code[0] == 'f' {
+ first, _ := st.operatorName(true)
+ second := st.expression()
+ third := st.expression()
+ return &Fold{Left: code[1] == 'L', Op: first, Arg1: second, Arg2: third}
+ } else {
+ first := st.expression()
+ second := st.expression()
+ third := st.expression()
+ return &Trinary{Op: o, First: first, Second: second, Third: third}
+ }
+
+ default:
+ st.fail(fmt.Sprintf("unsupported number of operator arguments: %d", args))
+ panic("not reached")
+ }
+ }
+}
+
+// subobject parses:
+//
+// <expression> ::= so <referent type> <expr> [<offset number>] <union-selector>* [p] E
+// <union-selector> ::= _ [<number>]
+func (st *state) subobject() AST {
+ typ := st.demangleType(false)
+ expr := st.expression()
+ offset := 0
+ if len(st.str) > 0 && (st.str[0] == 'n' || isDigit(st.str[0])) {
+ offset = st.number()
+ }
+ var selectors []int
+ for len(st.str) > 0 && st.str[0] == '_' {
+ st.advance(1)
+ selector := 0
+ if len(st.str) > 0 && (st.str[0] == 'n' || isDigit(st.str[0])) {
+ selector = st.number()
+ }
+ selectors = append(selectors, selector)
+ }
+ pastEnd := false
+ if len(st.str) > 0 && st.str[0] == 'p' {
+ st.advance(1)
+ pastEnd = true
+ }
+ if len(st.str) == 0 || st.str[0] != 'E' {
+ st.fail("expected E after subobject")
+ }
+ st.advance(1)
+ return &Subobject{
+ Type: typ,
+ SubExpr: expr,
+ Offset: offset,
+ Selectors: selectors,
+ PastEnd: pastEnd,
+ }
+}
+
+// unresolvedName parses:
+//
+// <unresolved-name> ::= [gs] <base-unresolved-name>
+// ::= sr <unresolved-type> <base-unresolved-name>
+// ::= srN <unresolved-type> <unresolved-qualifier-level>+ E <base-unresolved-name>
+// ::= [gs] sr <unresolved-qualifier-level>+ E <base-unresolved-name>
+func (st *state) unresolvedName() AST {
+ if len(st.str) >= 2 && st.str[:2] == "gs" {
+ st.advance(2)
+ n := st.unresolvedName()
+ return &Unary{
+ Op: &Operator{Name: "::"},
+ Expr: n,
+ Suffix: false,
+ SizeofType: false,
+ }
+ } else if len(st.str) >= 2 && st.str[:2] == "sr" {
+ st.advance(2)
+ if len(st.str) == 0 {
+ st.fail("expected unresolved type")
+ }
+ switch st.str[0] {
+ case 'T', 'D', 'S':
+ t := st.demangleType(false)
+ n := st.baseUnresolvedName()
+ n = &Qualified{Scope: t, Name: n, LocalName: false}
+ if len(st.str) > 0 && st.str[0] == 'I' {
+ args := st.templateArgs()
+ n = &Template{Name: n, Args: args}
+ st.subs.add(n)
+ }
+ return n
+ default:
+ var s AST
+ if st.str[0] == 'N' {
+ st.advance(1)
+ s = st.demangleType(false)
+ }
+ for len(st.str) == 0 || st.str[0] != 'E' {
+ // GCC does not seem to follow the ABI here.
+ // It can emit type/name without an 'E'.
+ if s != nil && len(st.str) > 0 && !isDigit(st.str[0]) {
+ if q, ok := s.(*Qualified); ok {
+ a := q.Scope
+ if t, ok := a.(*Template); ok {
+ st.subs.add(t.Name)
+ st.subs.add(t)
+ } else {
+ st.subs.add(a)
+ }
+ return s
+ }
+ }
+ n := st.sourceName()
+ if len(st.str) > 0 && st.str[0] == 'I' {
+ st.subs.add(n)
+ args := st.templateArgs()
+ n = &Template{Name: n, Args: args}
+ }
+ if s == nil {
+ s = n
+ } else {
+ s = &Qualified{Scope: s, Name: n, LocalName: false}
+ }
+ st.subs.add(s)
+ }
+ if s == nil {
+ st.fail("missing scope in unresolved name")
+ }
+ st.advance(1)
+ n := st.baseUnresolvedName()
+ return &Qualified{Scope: s, Name: n, LocalName: false}
+ }
+ } else {
+ return st.baseUnresolvedName()
+ }
+}
+
+// baseUnresolvedName parses:
+//
+// <base-unresolved-name> ::= <simple-id>
+// ::= on <operator-name>
+// ::= on <operator-name> <template-args>
+// ::= dn <destructor-name>
+//
+// <simple-id> ::= <source-name> [ <template-args> ]
+func (st *state) baseUnresolvedName() AST {
+ var n AST
+ if len(st.str) >= 2 && st.str[:2] == "on" {
+ st.advance(2)
+ n, _ = st.operatorName(true)
+ } else if len(st.str) >= 2 && st.str[:2] == "dn" {
+ st.advance(2)
+ if len(st.str) > 0 && isDigit(st.str[0]) {
+ n = st.sourceName()
+ } else {
+ n = st.demangleType(false)
+ }
+ n = &Destructor{Name: n}
+ } else if len(st.str) > 0 && isDigit(st.str[0]) {
+ n = st.sourceName()
+ } else {
+ // GCC seems to not follow the ABI here: it can have
+ // an operator name without on.
+ // See https://gcc.gnu.org/PR70182.
+ n, _ = st.operatorName(true)
+ }
+ if len(st.str) > 0 && st.str[0] == 'I' {
+ args := st.templateArgs()
+ n = &Template{Name: n, Args: args}
+ }
+ return n
+}
+
+// exprPrimary parses:
+//
+// <expr-primary> ::= L <type> <(value) number> E
+// ::= L <type> <(value) float> E
+// ::= L <mangled-name> E
+func (st *state) exprPrimary() AST {
+ st.checkChar('L')
+ if len(st.str) == 0 {
+ st.fail("expected primary expression")
+
+ }
+
+ // Check for 'Z' here because g++ incorrectly omitted the
+ // underscore until -fabi-version=3.
+ var ret AST
+ if st.str[0] == '_' || st.str[0] == 'Z' {
+ if st.str[0] == '_' {
+ st.advance(1)
+ }
+ if len(st.str) == 0 || st.str[0] != 'Z' {
+ st.fail("expected mangled name")
+ }
+ st.advance(1)
+ ret = st.encoding(true, notForLocalName)
+ } else {
+ t := st.demangleType(false)
+
+ isArrayType := func(typ AST) bool {
+ if twq, ok := typ.(*TypeWithQualifiers); ok {
+ typ = twq.Base
+ }
+ _, ok := typ.(*ArrayType)
+ return ok
+ }
+
+ neg := false
+ if len(st.str) > 0 && st.str[0] == 'n' {
+ neg = true
+ st.advance(1)
+ }
+ if len(st.str) > 0 && st.str[0] == 'E' {
+ if bt, ok := t.(*BuiltinType); ok && bt.Name == "decltype(nullptr)" {
+ // A nullptr should not have a value.
+ // We accept one if present because GCC
+ // used to generate one.
+ // https://gcc.gnu.org/PR91979.
+ } else if cl, ok := t.(*Closure); ok {
+ // A closure doesn't have a value.
+ st.advance(1)
+ return &LambdaExpr{Type: cl}
+ } else if isArrayType(t) {
+ st.advance(1)
+ return &StringLiteral{Type: t}
+ } else {
+ st.fail("missing literal value")
+ }
+ }
+ i := 0
+ for len(st.str) > i && st.str[i] != 'E' {
+ i++
+ }
+ val := st.str[:i]
+ st.advance(i)
+ ret = &Literal{Type: t, Val: val, Neg: neg}
+ }
+ if len(st.str) == 0 || st.str[0] != 'E' {
+ st.fail("expected E after literal")
+ }
+ st.advance(1)
+ return ret
+}
+
+// discriminator parses:
+//
+// <discriminator> ::= _ <(non-negative) number> (when number < 10)
+// __ <(non-negative) number> _ (when number >= 10)
+func (st *state) discriminator(a AST) AST {
+ if len(st.str) == 0 || st.str[0] != '_' {
+ // clang can generate a discriminator at the end of
+ // the string with no underscore.
+ for i := 0; i < len(st.str); i++ {
+ if !isDigit(st.str[i]) {
+ return a
+ }
+ }
+ // Skip the trailing digits.
+ st.advance(len(st.str))
+ return a
+ }
+ off := st.off
+ st.advance(1)
+ trailingUnderscore := false
+ if len(st.str) > 0 && st.str[0] == '_' {
+ st.advance(1)
+ trailingUnderscore = true
+ }
+ d := st.number()
+ if d < 0 {
+ st.failEarlier("invalid negative discriminator", st.off-off)
+ }
+ if trailingUnderscore && d >= 10 {
+ if len(st.str) == 0 || st.str[0] != '_' {
+ st.fail("expected _ after discriminator >= 10")
+ }
+ st.advance(1)
+ }
+ // We don't currently print out the discriminator, so we don't
+ // save it.
+ return a
+}
+
+// closureTypeName parses:
+//
+// <closure-type-name> ::= Ul <lambda-sig> E [ <nonnegative number> ] _
+// <lambda-sig> ::= <parameter type>+
+func (st *state) closureTypeName() AST {
+ st.checkChar('U')
+ st.checkChar('l')
+
+ oldLambdaTemplateLevel := st.lambdaTemplateLevel
+ st.lambdaTemplateLevel = len(st.templates) + 1
+
+ var templateArgs []AST
+ var template *Template
+ for len(st.str) > 1 && st.str[0] == 'T' {
+ arg, templateVal := st.templateParamDecl()
+ if arg == nil {
+ break
+ }
+ templateArgs = append(templateArgs, arg)
+ if template == nil {
+ template = &Template{
+ Name: &Name{Name: "lambda"},
+ }
+ st.templates = append(st.templates, template)
+ }
+ template.Args = append(template.Args, templateVal)
+ }
+
+ types := st.parmlist()
+
+ st.lambdaTemplateLevel = oldLambdaTemplateLevel
+
+ if template != nil {
+ st.templates = st.templates[:len(st.templates)-1]
+ }
+
+ if len(st.str) == 0 || st.str[0] != 'E' {
+ st.fail("expected E after closure type name")
+ }
+ st.advance(1)
+ num := st.compactNumber()
+ return &Closure{TemplateArgs: templateArgs, Types: types, Num: num}
+}
+
+// templateParamDecl parses:
+//
+// <template-param-decl> ::= Ty # type parameter
+// ::= Tn <type> # non-type parameter
+// ::= Tt <template-param-decl>* E # template parameter
+// ::= Tp <template-param-decl> # parameter pack
+//
+// Returns the new AST to include in the AST we are building and the
+// new AST to add to the list of template parameters.
+//
+// Returns nil, nil if not looking at a template-param-decl.
+func (st *state) templateParamDecl() (AST, AST) {
+ if len(st.str) < 2 || st.str[0] != 'T' {
+ return nil, nil
+ }
+ mk := func(prefix string, p *int) AST {
+ idx := *p
+ (*p)++
+ return &TemplateParamName{
+ Prefix: prefix,
+ Index: idx,
+ }
+ }
+ switch st.str[1] {
+ case 'y':
+ st.advance(2)
+ name := mk("$T", &st.typeTemplateParamCount)
+ tp := &TypeTemplateParam{
+ Name: name,
+ }
+ return tp, name
+ case 'n':
+ st.advance(2)
+ name := mk("$N", &st.nonTypeTemplateParamCount)
+ typ := st.demangleType(false)
+ tp := &NonTypeTemplateParam{
+ Name: name,
+ Type: typ,
+ }
+ return tp, name
+ case 't':
+ st.advance(2)
+ name := mk("$TT", &st.templateTemplateParamCount)
+ var params []AST
+ var template *Template
+ for {
+ if len(st.str) == 0 {
+ st.fail("expected closure template parameter")
+ }
+ if st.str[0] == 'E' {
+ st.advance(1)
+ break
+ }
+ off := st.off
+ param, templateVal := st.templateParamDecl()
+ if param == nil {
+ st.failEarlier("expected closure template parameter", st.off-off)
+ }
+ params = append(params, param)
+ if template == nil {
+ template = &Template{
+ Name: &Name{Name: "template_template"},
+ }
+ st.templates = append(st.templates, template)
+ }
+ template.Args = append(template.Args, templateVal)
+ }
+ if template != nil {
+ st.templates = st.templates[:len(st.templates)-1]
+ }
+ tp := &TemplateTemplateParam{
+ Name: name,
+ Params: params,
+ }
+ return tp, name
+ case 'p':
+ st.advance(2)
+ off := st.off
+ param, templateVal := st.templateParamDecl()
+ if param == nil {
+ st.failEarlier("expected lambda template parameter", st.off-off)
+ }
+ return &TemplateParamPack{Param: param}, templateVal
+ default:
+ return nil, nil
+ }
+}
+
+// unnamedTypeName parses:
+//
+// <unnamed-type-name> ::= Ut [ <nonnegative number> ] _
+func (st *state) unnamedTypeName() AST {
+ st.checkChar('U')
+ st.checkChar('t')
+ num := st.compactNumber()
+ ret := &UnnamedType{Num: num}
+ st.subs.add(ret)
+ return ret
+}
+
+// Recognize a clone suffix. These are not part of the mangling API,
+// but are added by GCC when cloning functions.
+func (st *state) cloneSuffix(a AST) AST {
+ i := 0
+ if len(st.str) > 1 && st.str[0] == '.' && (isLower(st.str[1]) || isDigit(st.str[1]) || st.str[1] == '_') {
+ i += 2
+ for len(st.str) > i && (isLower(st.str[i]) || isDigit(st.str[i]) || st.str[i] == '_') {
+ i++
+ }
+ }
+ for len(st.str) > i+1 && st.str[i] == '.' && isDigit(st.str[i+1]) {
+ i += 2
+ for len(st.str) > i && isDigit(st.str[i]) {
+ i++
+ }
+ }
+ suffix := st.str[:i]
+ st.advance(i)
+ return &Clone{Base: a, Suffix: suffix}
+}
+
+// substitutions is the list of substitution candidates that may
+// appear later in the string.
+type substitutions []AST
+
+// add adds a new substitution candidate.
+func (subs *substitutions) add(a AST) {
+ *subs = append(*subs, a)
+}
+
+// subAST maps standard substitution codes to the corresponding AST.
+var subAST = map[byte]AST{
+ 't': &Name{Name: "std"},
+ 'a': &Qualified{Scope: &Name{Name: "std"}, Name: &Name{Name: "allocator"}},
+ 'b': &Qualified{Scope: &Name{Name: "std"}, Name: &Name{Name: "basic_string"}},
+ 's': &Qualified{Scope: &Name{Name: "std"}, Name: &Name{Name: "string"}},
+ 'i': &Qualified{Scope: &Name{Name: "std"}, Name: &Name{Name: "istream"}},
+ 'o': &Qualified{Scope: &Name{Name: "std"}, Name: &Name{Name: "ostream"}},
+ 'd': &Qualified{Scope: &Name{Name: "std"}, Name: &Name{Name: "iostream"}},
+}
+
+// verboseAST maps standard substitution codes to the long form of the
+// corresponding AST. We use this when the Verbose option is used, to
+// match the standard demangler.
+var verboseAST = map[byte]AST{
+ 't': &Name{Name: "std"},
+ 'a': &Qualified{Scope: &Name{Name: "std"}, Name: &Name{Name: "allocator"}},
+ 'b': &Qualified{Scope: &Name{Name: "std"}, Name: &Name{Name: "basic_string"}},
+
+ // std::basic_string<char, std::char_traits<char>, std::allocator<char> >
+ 's': &Template{
+ Name: &Qualified{Scope: &Name{Name: "std"}, Name: &Name{Name: "basic_string"}},
+ Args: []AST{
+ &BuiltinType{Name: "char"},
+ &Template{
+ Name: &Qualified{Scope: &Name{Name: "std"}, Name: &Name{Name: "char_traits"}},
+ Args: []AST{&BuiltinType{Name: "char"}}},
+ &Template{
+ Name: &Qualified{Scope: &Name{Name: "std"}, Name: &Name{Name: "allocator"}},
+ Args: []AST{&BuiltinType{Name: "char"}}}}},
+ // std::basic_istream<char, std::char_traits<char> >
+ 'i': &Template{
+ Name: &Qualified{Scope: &Name{Name: "std"}, Name: &Name{Name: "basic_istream"}},
+ Args: []AST{
+ &BuiltinType{Name: "char"},
+ &Template{
+ Name: &Qualified{Scope: &Name{Name: "std"}, Name: &Name{Name: "char_traits"}},
+ Args: []AST{&BuiltinType{Name: "char"}}}}},
+ // std::basic_ostream<char, std::char_traits<char> >
+ 'o': &Template{
+ Name: &Qualified{Scope: &Name{Name: "std"}, Name: &Name{Name: "basic_ostream"}},
+ Args: []AST{
+ &BuiltinType{Name: "char"},
+ &Template{
+ Name: &Qualified{Scope: &Name{Name: "std"}, Name: &Name{Name: "char_traits"}},
+ Args: []AST{&BuiltinType{Name: "char"}}}}},
+ // std::basic_iostream<char, std::char_traits<char> >
+ 'd': &Template{
+ Name: &Qualified{Scope: &Name{Name: "std"}, Name: &Name{Name: "basic_iostream"}},
+ Args: []AST{
+ &BuiltinType{Name: "char"},
+ &Template{
+ Name: &Qualified{Scope: &Name{Name: "std"}, Name: &Name{Name: "char_traits"}},
+ Args: []AST{&BuiltinType{Name: "char"}}}}},
+}
+
+// substitution parses:
+//
+// <substitution> ::= S <seq-id> _
+// ::= S_
+// ::= St
+// ::= Sa
+// ::= Sb
+// ::= Ss
+// ::= Si
+// ::= So
+// ::= Sd
+func (st *state) substitution(forPrefix bool) AST {
+ st.checkChar('S')
+ if len(st.str) == 0 {
+ st.fail("missing substitution index")
+ }
+ c := st.str[0]
+ off := st.off
+ if c == '_' || isDigit(c) || isUpper(c) {
+ id := st.seqID(false)
+ if id >= len(st.subs) {
+ st.failEarlier(fmt.Sprintf("substitution index out of range (%d >= %d)", id, len(st.subs)), st.off-off)
+ }
+
+ ret := st.subs[id]
+
+ // We need to update any references to template
+ // parameters to refer to the currently active
+ // template.
+
+ // When copying a Typed we may need to adjust
+ // the templates.
+ copyTemplates := st.templates
+ var oldLambdaTemplateLevel []int
+
+ // pushTemplate is called from skip, popTemplate from copy.
+ pushTemplate := func(template *Template) {
+ copyTemplates = append(copyTemplates, template)
+ oldLambdaTemplateLevel = append(oldLambdaTemplateLevel, st.lambdaTemplateLevel)
+ st.lambdaTemplateLevel = 0
+ }
+ popTemplate := func() {
+ copyTemplates = copyTemplates[:len(copyTemplates)-1]
+ st.lambdaTemplateLevel = oldLambdaTemplateLevel[len(oldLambdaTemplateLevel)-1]
+ oldLambdaTemplateLevel = oldLambdaTemplateLevel[:len(oldLambdaTemplateLevel)-1]
+ }
+
+ copy := func(a AST) AST {
+ var index int
+ switch a := a.(type) {
+ case *Typed:
+ // Remove the template added in skip.
+ if _, ok := a.Name.(*Template); ok {
+ popTemplate()
+ }
+ return nil
+ case *Closure:
+ // Undo the save in skip.
+ st.lambdaTemplateLevel = oldLambdaTemplateLevel[len(oldLambdaTemplateLevel)-1]
+ oldLambdaTemplateLevel = oldLambdaTemplateLevel[:len(oldLambdaTemplateLevel)-1]
+ return nil
+ case *TemplateParam:
+ index = a.Index
+ case *LambdaAuto:
+ // A lambda auto parameter is represented
+ // as a template parameter, so we may have
+ // to change back when substituting.
+ index = a.Index
+ default:
+ return nil
+ }
+ if st.lambdaTemplateLevel > 0 {
+ if _, ok := a.(*LambdaAuto); ok {
+ return nil
+ }
+ return &LambdaAuto{Index: index}
+ }
+ var template *Template
+ if len(copyTemplates) > 0 {
+ template = copyTemplates[len(copyTemplates)-1]
+ } else if rt, ok := ret.(*Template); ok {
+ // At least with clang we can see a template
+ // to start, and sometimes we need to refer
+ // to it. There is probably something wrong
+ // here.
+ template = rt
+ } else {
+ st.failEarlier("substituted template parameter not in scope of template", st.off-off)
+ }
+ if template == nil {
+ // This template parameter is within
+ // the scope of a cast operator.
+ return &TemplateParam{Index: index, Template: nil}
+ }
+
+ if index >= len(template.Args) {
+ st.failEarlier(fmt.Sprintf("substituted template index out of range (%d >= %d)", index, len(template.Args)), st.off-off)
+ }
+
+ return &TemplateParam{Index: index, Template: template}
+ }
+ var seen []AST
+ skip := func(a AST) bool {
+ switch a := a.(type) {
+ case *Typed:
+ if template, ok := a.Name.(*Template); ok {
+ // This template is removed in copy.
+ pushTemplate(template)
+ }
+ return false
+ case *Closure:
+ // This is undone in copy.
+ oldLambdaTemplateLevel = append(oldLambdaTemplateLevel, st.lambdaTemplateLevel)
+ st.lambdaTemplateLevel = len(copyTemplates) + 1
+ return false
+ case *TemplateParam, *LambdaAuto:
+ return false
+ }
+ for _, v := range seen {
+ if v == a {
+ return true
+ }
+ }
+ seen = append(seen, a)
+ return false
+ }
+
+ if c := ret.Copy(copy, skip); c != nil {
+ return c
+ }
+
+ return ret
+ } else {
+ st.advance(1)
+ m := subAST
+ if st.verbose {
+ m = verboseAST
+ }
+ // For compatibility with the standard demangler, use
+ // a longer name for a constructor or destructor.
+ if forPrefix && len(st.str) > 0 && (st.str[0] == 'C' || st.str[0] == 'D') {
+ m = verboseAST
+ }
+ a, ok := m[c]
+ if !ok {
+ st.failEarlier("unrecognized substitution code", 1)
+ }
+
+ if len(st.str) > 0 && st.str[0] == 'B' {
+ a = st.taggedName(a)
+ st.subs.add(a)
+ }
+
+ return a
+ }
+}
+
+// isDigit returns whetner c is a digit for demangling purposes.
+func isDigit(c byte) bool {
+ return c >= '0' && c <= '9'
+}
+
+// isUpper returns whether c is an upper case letter for demangling purposes.
+func isUpper(c byte) bool {
+ return c >= 'A' && c <= 'Z'
+}
+
+// isLower returns whether c is a lower case letter for demangling purposes.
+func isLower(c byte) bool {
+ return c >= 'a' && c <= 'z'
+}
+
+// simplify replaces template parameters with their expansions, and
+// merges qualifiers.
+func simplify(a AST) AST {
+ var seen []AST
+ skip := func(a AST) bool {
+ for _, v := range seen {
+ if v == a {
+ return true
+ }
+ }
+ seen = append(seen, a)
+ return false
+ }
+ if r := a.Copy(simplifyOne, skip); r != nil {
+ return r
+ }
+ return a
+}
+
+// simplifyOne simplifies a single AST. It returns nil if there is
+// nothing to do.
+func simplifyOne(a AST) AST {
+ switch a := a.(type) {
+ case *TemplateParam:
+ if a.Template != nil && a.Index < len(a.Template.Args) {
+ return a.Template.Args[a.Index]
+ }
+ case *MethodWithQualifiers:
+ if m, ok := a.Method.(*MethodWithQualifiers); ok {
+ ref := a.RefQualifier
+ if ref == "" {
+ ref = m.RefQualifier
+ } else if m.RefQualifier != "" {
+ if ref == "&" || m.RefQualifier == "&" {
+ ref = "&"
+ }
+ }
+ return &MethodWithQualifiers{Method: m.Method, Qualifiers: mergeQualifiers(a.Qualifiers, m.Qualifiers), RefQualifier: ref}
+ }
+ if t, ok := a.Method.(*TypeWithQualifiers); ok {
+ return &MethodWithQualifiers{Method: t.Base, Qualifiers: mergeQualifiers(a.Qualifiers, t.Qualifiers), RefQualifier: a.RefQualifier}
+ }
+ case *TypeWithQualifiers:
+ if ft, ok := a.Base.(*FunctionType); ok {
+ return &MethodWithQualifiers{Method: ft, Qualifiers: a.Qualifiers, RefQualifier: ""}
+ }
+ if t, ok := a.Base.(*TypeWithQualifiers); ok {
+ return &TypeWithQualifiers{Base: t.Base, Qualifiers: mergeQualifiers(a.Qualifiers, t.Qualifiers)}
+ }
+ if m, ok := a.Base.(*MethodWithQualifiers); ok {
+ return &MethodWithQualifiers{Method: m.Method, Qualifiers: mergeQualifiers(a.Qualifiers, m.Qualifiers), RefQualifier: m.RefQualifier}
+ }
+ case *ReferenceType:
+ if rt, ok := a.Base.(*ReferenceType); ok {
+ return rt
+ }
+ if rrt, ok := a.Base.(*RvalueReferenceType); ok {
+ return &ReferenceType{Base: rrt.Base}
+ }
+ case *RvalueReferenceType:
+ if rrt, ok := a.Base.(*RvalueReferenceType); ok {
+ return rrt
+ }
+ if rt, ok := a.Base.(*ReferenceType); ok {
+ return rt
+ }
+ case *ArrayType:
+ // Qualifiers on the element of an array type
+ // go on the whole array type.
+ if q, ok := a.Element.(*TypeWithQualifiers); ok {
+ return &TypeWithQualifiers{
+ Base: &ArrayType{Dimension: a.Dimension, Element: q.Base},
+ Qualifiers: q.Qualifiers,
+ }
+ }
+ case *PackExpansion:
+ // Expand the pack and replace it with a list of
+ // expressions.
+ if a.Pack != nil {
+ exprs := make([]AST, len(a.Pack.Args))
+ for i, arg := range a.Pack.Args {
+ copy := func(sub AST) AST {
+ // Replace the ArgumentPack
+ // with a specific argument.
+ if sub == a.Pack {
+ return arg
+ }
+ // Copy everything else.
+ return nil
+ }
+
+ var seen []AST
+ skip := func(sub AST) bool {
+ // Don't traverse into another
+ // pack expansion.
+ if _, ok := sub.(*PackExpansion); ok {
+ return true
+ }
+ for _, v := range seen {
+ if v == sub {
+ return true
+ }
+ }
+ seen = append(seen, sub)
+ return false
+ }
+
+ b := a.Base.Copy(copy, skip)
+ if b == nil {
+ b = a.Base
+ }
+ exprs[i] = simplify(b)
+ }
+ return &ExprList{Exprs: exprs}
+ }
+ }
+ return nil
+}
+
+// findArgumentPack walks the AST looking for the argument pack for a
+// pack expansion. We find it via a template parameter.
+func (st *state) findArgumentPack(a AST) *ArgumentPack {
+ var seen []AST
+ var ret *ArgumentPack
+ a.Traverse(func(a AST) bool {
+ if ret != nil {
+ return false
+ }
+ switch a := a.(type) {
+ case *TemplateParam:
+ if a.Template == nil || a.Index >= len(a.Template.Args) {
+ return true
+ }
+ if pack, ok := a.Template.Args[a.Index].(*ArgumentPack); ok {
+ ret = pack
+ return false
+ }
+ case *PackExpansion, *Closure, *Name:
+ return false
+ case *TaggedName, *Operator, *BuiltinType, *FunctionParam:
+ return false
+ case *UnnamedType, *FixedType, *DefaultArg:
+ return false
+ }
+ for _, v := range seen {
+ if v == a {
+ return false
+ }
+ }
+ seen = append(seen, a)
+ return true
+ })
+ return ret
+}
diff --git a/src/cmd/vendor/github.com/ianlancetaylor/demangle/rust.go b/src/cmd/vendor/github.com/ianlancetaylor/demangle/rust.go
new file mode 100644
index 0000000..f3d2d33
--- /dev/null
+++ b/src/cmd/vendor/github.com/ianlancetaylor/demangle/rust.go
@@ -0,0 +1,1165 @@
+// Copyright 2021 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package demangle
+
+import (
+ "fmt"
+ "math"
+ "math/bits"
+ "strings"
+ "unicode/utf8"
+)
+
+// rustToString demangles a Rust symbol.
+func rustToString(name string, options []Option) (ret string, err error) {
+ if !strings.HasPrefix(name, "_R") {
+ return "", ErrNotMangledName
+ }
+
+ // When the demangling routines encounter an error, they panic
+ // with a value of type demangleErr.
+ defer func() {
+ if r := recover(); r != nil {
+ if de, ok := r.(demangleErr); ok {
+ ret = ""
+ err = de
+ return
+ }
+ panic(r)
+ }
+ }()
+
+ suffix := ""
+ dot := strings.Index(name, ".")
+ if dot >= 0 {
+ suffix = name[dot:]
+ name = name[:dot]
+ }
+
+ name = name[2:]
+ rst := &rustState{orig: name, str: name}
+
+ for _, o := range options {
+ if o == NoTemplateParams {
+ rst.noGenericArgs = true
+ } else if isMaxLength(o) {
+ rst.max = maxLength(o)
+ }
+ }
+
+ rst.symbolName()
+
+ if len(rst.str) > 0 {
+ rst.fail("unparsed characters at end of mangled name")
+ }
+
+ if suffix != "" {
+ llvmStyle := false
+ for _, o := range options {
+ if o == LLVMStyle {
+ llvmStyle = true
+ break
+ }
+ }
+ if llvmStyle {
+ rst.skip = false
+ rst.writeString(" (")
+ rst.writeString(suffix)
+ rst.writeByte(')')
+ }
+ }
+
+ s := rst.buf.String()
+ if rst.max > 0 && len(s) > rst.max {
+ s = s[:rst.max]
+ }
+ return s, nil
+}
+
+// A rustState holds the current state of demangling a Rust string.
+type rustState struct {
+ orig string // the original string being demangled
+ str string // remainder of string to demangle
+ off int // offset of str within original string
+ buf strings.Builder // demangled string being built
+ skip bool // don't print, just skip
+ lifetimes int64 // number of bound lifetimes
+ last byte // last byte written to buffer
+ noGenericArgs bool // don't demangle generic arguments
+ max int // maximum output length
+}
+
+// fail panics with demangleErr, to be caught in rustToString.
+func (rst *rustState) fail(err string) {
+ panic(demangleErr{err: err, off: rst.off})
+}
+
+// advance advances the current string offset.
+func (rst *rustState) advance(add int) {
+ if len(rst.str) < add {
+ panic("internal error")
+ }
+ rst.str = rst.str[add:]
+ rst.off += add
+}
+
+// checkChar requires that the next character in the string be c,
+// and advances past it.
+func (rst *rustState) checkChar(c byte) {
+ if len(rst.str) == 0 || rst.str[0] != c {
+ rst.fail("expected " + string(c))
+ }
+ rst.advance(1)
+}
+
+// writeByte writes a byte to the buffer.
+func (rst *rustState) writeByte(c byte) {
+ if rst.skip {
+ return
+ }
+ if rst.max > 0 && rst.buf.Len() > rst.max {
+ rst.skip = true
+ return
+ }
+ rst.last = c
+ rst.buf.WriteByte(c)
+}
+
+// writeString writes a string to the buffer.
+func (rst *rustState) writeString(s string) {
+ if rst.skip {
+ return
+ }
+ if rst.max > 0 && rst.buf.Len() > rst.max {
+ rst.skip = true
+ return
+ }
+ if len(s) > 0 {
+ rst.last = s[len(s)-1]
+ rst.buf.WriteString(s)
+ }
+}
+
+// symbolName parses:
+//
+// <symbol-name> = "_R" [<decimal-number>] <path> [<instantiating-crate>]
+// <instantiating-crate> = <path>
+//
+// We've already skipped the "_R".
+func (rst *rustState) symbolName() {
+ if len(rst.str) < 1 {
+ rst.fail("expected symbol-name")
+ }
+
+ if isDigit(rst.str[0]) {
+ rst.fail("unsupported Rust encoding version")
+ }
+
+ rst.path(true)
+
+ if len(rst.str) > 0 {
+ rst.skip = true
+ rst.path(false)
+ }
+}
+
+// path parses:
+//
+// <path> = "C" <identifier> // crate root
+// | "M" <impl-path> <type> // <T> (inherent impl)
+// | "X" <impl-path> <type> <path> // <T as Trait> (trait impl)
+// | "Y" <type> <path> // <T as Trait> (trait definition)
+// | "N" <namespace> <path> <identifier> // ...::ident (nested path)
+// | "I" <path> {<generic-arg>} "E" // ...<T, U> (generic args)
+// | <backref>
+// <namespace> = "C" // closure
+// | "S" // shim
+// | <A-Z> // other special namespaces
+// | <a-z> // internal namespaces
+//
+// needsSeparator is true if we need to write out :: for a generic;
+// it is passed as false if we are in the middle of a type.
+func (rst *rustState) path(needsSeparator bool) {
+ if len(rst.str) < 1 {
+ rst.fail("expected path")
+ }
+ switch c := rst.str[0]; c {
+ case 'C':
+ rst.advance(1)
+ _, ident := rst.identifier()
+ rst.writeString(ident)
+ case 'M', 'X':
+ rst.advance(1)
+ rst.implPath()
+ rst.writeByte('<')
+ rst.demangleType()
+ if c == 'X' {
+ rst.writeString(" as ")
+ rst.path(false)
+ }
+ rst.writeByte('>')
+ case 'Y':
+ rst.advance(1)
+ rst.writeByte('<')
+ rst.demangleType()
+ rst.writeString(" as ")
+ rst.path(false)
+ rst.writeByte('>')
+ case 'N':
+ rst.advance(1)
+
+ if len(rst.str) < 1 {
+ rst.fail("expected namespace")
+ }
+ ns := rst.str[0]
+ switch {
+ case ns >= 'a' && ns <= 'z':
+ case ns >= 'A' && ns <= 'Z':
+ default:
+ rst.fail("invalid namespace character")
+ }
+ rst.advance(1)
+
+ rst.path(needsSeparator)
+
+ dis, ident := rst.identifier()
+
+ if ns >= 'A' && ns <= 'Z' {
+ rst.writeString("::{")
+ switch ns {
+ case 'C':
+ rst.writeString("closure")
+ case 'S':
+ rst.writeString("shim")
+ default:
+ rst.writeByte(ns)
+ }
+ if len(ident) > 0 {
+ rst.writeByte(':')
+ rst.writeString(ident)
+ }
+ if !rst.skip {
+ fmt.Fprintf(&rst.buf, "#%d}", dis)
+ rst.last = '}'
+ }
+ } else {
+ rst.writeString("::")
+ rst.writeString(ident)
+ }
+ case 'I':
+ rst.advance(1)
+ rst.path(needsSeparator)
+ if needsSeparator {
+ rst.writeString("::")
+ }
+ rst.writeByte('<')
+ rst.genericArgs()
+ rst.writeByte('>')
+ rst.checkChar('E')
+ case 'B':
+ rst.backref(func() { rst.path(needsSeparator) })
+ default:
+ rst.fail("unrecognized letter in path")
+ }
+}
+
+// implPath parses:
+//
+// <impl-path> = [<disambiguator>] <path>
+func (rst *rustState) implPath() {
+ // This path is not part of the demangled string.
+ hold := rst.skip
+ rst.skip = true
+ defer func() {
+ rst.skip = hold
+ }()
+
+ rst.disambiguator()
+ rst.path(false)
+}
+
+// identifier parses:
+//
+// <identifier> = [<disambiguator>] <undisambiguated-identifier>
+//
+// It returns the disambiguator and the identifier.
+func (rst *rustState) identifier() (int64, string) {
+ dis := rst.disambiguator()
+ ident, _ := rst.undisambiguatedIdentifier()
+ return dis, ident
+}
+
+// disambiguator parses an optional:
+//
+// <disambiguator> = "s" <base-62-number>
+func (rst *rustState) disambiguator() int64 {
+ if len(rst.str) == 0 || rst.str[0] != 's' {
+ return 0
+ }
+ rst.advance(1)
+ return rst.base62Number() + 1
+}
+
+// undisambiguatedIdentifier parses:
+//
+// <undisambiguated-identifier> = ["u"] <decimal-number> ["_"] <bytes>
+func (rst *rustState) undisambiguatedIdentifier() (id string, isPunycode bool) {
+ isPunycode = false
+ if len(rst.str) > 0 && rst.str[0] == 'u' {
+ rst.advance(1)
+ isPunycode = true
+ }
+
+ val := rst.decimalNumber()
+
+ if len(rst.str) > 0 && rst.str[0] == '_' {
+ rst.advance(1)
+ }
+
+ if len(rst.str) < val {
+ rst.fail("not enough characters for identifier")
+ }
+ id = rst.str[:val]
+ rst.advance(val)
+
+ for i := 0; i < len(id); i++ {
+ c := id[i]
+ switch {
+ case c >= '0' && c <= '9':
+ case c >= 'A' && c <= 'Z':
+ case c >= 'a' && c <= 'z':
+ case c == '_':
+ default:
+ rst.fail("invalid character in identifier")
+ }
+ }
+
+ if isPunycode {
+ id = rst.expandPunycode(id)
+ }
+
+ return id, isPunycode
+}
+
+// expandPunycode decodes the Rust version of punycode.
+// This algorithm is taken from RFC 3492 section 6.2.
+func (rst *rustState) expandPunycode(s string) string {
+ const (
+ base = 36
+ tmin = 1
+ tmax = 26
+ skew = 38
+ damp = 700
+ initialBias = 72
+ initialN = 128
+ )
+
+ var (
+ output []rune
+ encoding string
+ )
+ idx := strings.LastIndex(s, "_")
+ if idx >= 0 {
+ output = []rune(s[:idx])
+ encoding = s[idx+1:]
+ } else {
+ encoding = s
+ }
+
+ i := 0
+ n := initialN
+ bias := initialBias
+
+ pos := 0
+ for pos < len(encoding) {
+ oldI := i
+ w := 1
+ for k := base; ; k += base {
+ if pos == len(encoding) {
+ rst.fail("unterminated punycode")
+ }
+
+ var digit byte
+ d := encoding[pos]
+ pos++
+ switch {
+ case '0' <= d && d <= '9':
+ digit = d - '0' + 26
+ case 'A' <= d && d <= 'Z':
+ digit = d - 'A'
+ case 'a' <= d && d <= 'z':
+ digit = d - 'a'
+ default:
+ rst.fail("invalid punycode digit")
+ }
+
+ i += int(digit) * w
+ if i < 0 {
+ rst.fail("punycode number overflow")
+ }
+
+ var t int
+ if k <= bias {
+ t = tmin
+ } else if k > bias+tmax {
+ t = tmax
+ } else {
+ t = k - bias
+ }
+
+ if int(digit) < t {
+ break
+ }
+
+ if w >= math.MaxInt32/base {
+ rst.fail("punycode number overflow")
+ }
+ w *= base - t
+ }
+
+ delta := i - oldI
+ numPoints := len(output) + 1
+ firstTime := oldI == 0
+ if firstTime {
+ delta /= damp
+ } else {
+ delta /= 2
+ }
+ delta += delta / numPoints
+ k := 0
+ for delta > ((base-tmin)*tmax)/2 {
+ delta /= base - tmin
+ k += base
+ }
+ bias = k + ((base-tmin+1)*delta)/(delta+skew)
+
+ n += i / (len(output) + 1)
+ if n > utf8.MaxRune {
+ rst.fail("punycode rune overflow")
+ } else if !utf8.ValidRune(rune(n)) {
+ rst.fail("punycode invalid code point")
+ }
+ i %= len(output) + 1
+ output = append(output, 0)
+ copy(output[i+1:], output[i:])
+ output[i] = rune(n)
+ i++
+ }
+
+ return string(output)
+}
+
+// genericArgs prints a list of generic arguments, without angle brackets.
+func (rst *rustState) genericArgs() {
+ if rst.noGenericArgs {
+ hold := rst.skip
+ rst.skip = true
+ defer func() {
+ rst.skip = hold
+ }()
+ }
+
+ first := true
+ for len(rst.str) > 0 && rst.str[0] != 'E' {
+ if first {
+ first = false
+ } else {
+ rst.writeString(", ")
+ }
+ rst.genericArg()
+ }
+}
+
+// genericArg parses:
+//
+// <generic-arg> = <lifetime>
+// | <type>
+// | "K" <const> // forward-compat for const generics
+// <lifetime> = "L" <base-62-number>
+func (rst *rustState) genericArg() {
+ if len(rst.str) < 1 {
+ rst.fail("expected generic-arg")
+ }
+ if rst.str[0] == 'L' {
+ rst.advance(1)
+ rst.writeLifetime(rst.base62Number())
+ } else if rst.str[0] == 'K' {
+ rst.advance(1)
+ rst.demangleConst()
+ } else {
+ rst.demangleType()
+ }
+}
+
+// binder parses an optional:
+//
+// <binder> = "G" <base-62-number>
+func (rst *rustState) binder() {
+ if len(rst.str) < 1 || rst.str[0] != 'G' {
+ return
+ }
+ rst.advance(1)
+
+ binderLifetimes := rst.base62Number() + 1
+
+ // Every bound lifetime should be referenced later.
+ if binderLifetimes >= int64(len(rst.str))-rst.lifetimes {
+ rst.fail("binder lifetimes overflow")
+ }
+
+ rst.writeString("for<")
+ for i := int64(0); i < binderLifetimes; i++ {
+ if i > 0 {
+ rst.writeString(", ")
+ }
+ rst.lifetimes++
+ rst.writeLifetime(1)
+ }
+ rst.writeString("> ")
+}
+
+// demangleType parses:
+//
+// <type> = <basic-type>
+// | <path> // named type
+// | "A" <type> <const> // [T; N]
+// | "S" <type> // [T]
+// | "T" {<type>} "E" // (T1, T2, T3, ...)
+// | "R" [<lifetime>] <type> // &T
+// | "Q" [<lifetime>] <type> // &mut T
+// | "P" <type> // *const T
+// | "O" <type> // *mut T
+// | "F" <fn-sig> // fn(...) -> ...
+// | "D" <dyn-bounds> <lifetime> // dyn Trait<Assoc = X> + Send + 'a
+// | <backref>
+func (rst *rustState) demangleType() {
+ if len(rst.str) < 1 {
+ rst.fail("expected type")
+ }
+ c := rst.str[0]
+ if c >= 'a' && c <= 'z' {
+ rst.basicType()
+ return
+ }
+ switch c {
+ case 'C', 'M', 'X', 'Y', 'N', 'I':
+ rst.path(false)
+ case 'A', 'S':
+ rst.advance(1)
+ rst.writeByte('[')
+ rst.demangleType()
+ if c == 'A' {
+ rst.writeString("; ")
+ rst.demangleConst()
+ }
+ rst.writeByte(']')
+ case 'T':
+ rst.advance(1)
+ rst.writeByte('(')
+ c := 0
+ for len(rst.str) > 0 && rst.str[0] != 'E' {
+ if c > 0 {
+ rst.writeString(", ")
+ }
+ c++
+ rst.demangleType()
+ }
+ if c == 1 {
+ rst.writeByte(',')
+ }
+ rst.writeByte(')')
+ rst.checkChar('E')
+ case 'R', 'Q':
+ rst.advance(1)
+ rst.writeByte('&')
+ if len(rst.str) > 0 && rst.str[0] == 'L' {
+ rst.advance(1)
+ if lifetime := rst.base62Number(); lifetime > 0 {
+ rst.writeLifetime(lifetime)
+ rst.writeByte(' ')
+ }
+ }
+ if c == 'Q' {
+ rst.writeString("mut ")
+ }
+ rst.demangleType()
+ case 'P':
+ rst.advance(1)
+ rst.writeString("*const ")
+ rst.demangleType()
+ case 'O':
+ rst.advance(1)
+ rst.writeString("*mut ")
+ rst.demangleType()
+ case 'F':
+ rst.advance(1)
+ hold := rst.lifetimes
+ rst.fnSig()
+ rst.lifetimes = hold
+ case 'D':
+ rst.advance(1)
+ hold := rst.lifetimes
+ rst.dynBounds()
+ rst.lifetimes = hold
+ if len(rst.str) == 0 || rst.str[0] != 'L' {
+ rst.fail("expected L")
+ }
+ rst.advance(1)
+ if lifetime := rst.base62Number(); lifetime > 0 {
+ if rst.last != ' ' {
+ rst.writeByte(' ')
+ }
+ rst.writeString("+ ")
+ rst.writeLifetime(lifetime)
+ }
+ case 'B':
+ rst.backref(rst.demangleType)
+ default:
+ rst.fail("unrecognized character in type")
+ }
+}
+
+var rustBasicTypes = map[byte]string{
+ 'a': "i8",
+ 'b': "bool",
+ 'c': "char",
+ 'd': "f64",
+ 'e': "str",
+ 'f': "f32",
+ 'h': "u8",
+ 'i': "isize",
+ 'j': "usize",
+ 'l': "i32",
+ 'm': "u32",
+ 'n': "i128",
+ 'o': "u128",
+ 'p': "_",
+ 's': "i16",
+ 't': "u16",
+ 'u': "()",
+ 'v': "...",
+ 'x': "i64",
+ 'y': "u64",
+ 'z': "!",
+}
+
+// basicType parses:
+//
+// <basic-type>
+func (rst *rustState) basicType() {
+ if len(rst.str) < 1 {
+ rst.fail("expected basic type")
+ }
+ str, ok := rustBasicTypes[rst.str[0]]
+ if !ok {
+ rst.fail("unrecognized basic type character")
+ }
+ rst.advance(1)
+ rst.writeString(str)
+}
+
+// fnSig parses:
+//
+// <fn-sig> = [<binder>] ["U"] ["K" <abi>] {<type>} "E" <type>
+// <abi> = "C"
+// | <undisambiguated-identifier>
+func (rst *rustState) fnSig() {
+ rst.binder()
+ if len(rst.str) > 0 && rst.str[0] == 'U' {
+ rst.advance(1)
+ rst.writeString("unsafe ")
+ }
+ if len(rst.str) > 0 && rst.str[0] == 'K' {
+ rst.advance(1)
+ if len(rst.str) > 0 && rst.str[0] == 'C' {
+ rst.advance(1)
+ rst.writeString(`extern "C" `)
+ } else {
+ rst.writeString(`extern "`)
+ id, isPunycode := rst.undisambiguatedIdentifier()
+ if isPunycode {
+ rst.fail("punycode used in ABI string")
+ }
+ id = strings.ReplaceAll(id, "_", "-")
+ rst.writeString(id)
+ rst.writeString(`" `)
+ }
+ }
+ rst.writeString("fn(")
+ first := true
+ for len(rst.str) > 0 && rst.str[0] != 'E' {
+ if first {
+ first = false
+ } else {
+ rst.writeString(", ")
+ }
+ rst.demangleType()
+ }
+ rst.checkChar('E')
+ rst.writeByte(')')
+ if len(rst.str) > 0 && rst.str[0] == 'u' {
+ rst.advance(1)
+ } else {
+ rst.writeString(" -> ")
+ rst.demangleType()
+ }
+}
+
+// dynBounds parses:
+//
+// <dyn-bounds> = [<binder>] {<dyn-trait>} "E"
+func (rst *rustState) dynBounds() {
+ rst.writeString("dyn ")
+ rst.binder()
+ first := true
+ for len(rst.str) > 0 && rst.str[0] != 'E' {
+ if first {
+ first = false
+ } else {
+ rst.writeString(" + ")
+ }
+ rst.dynTrait()
+ }
+ rst.checkChar('E')
+}
+
+// dynTrait parses:
+//
+// <dyn-trait> = <path> {<dyn-trait-assoc-binding>}
+// <dyn-trait-assoc-binding> = "p" <undisambiguated-identifier> <type>
+func (rst *rustState) dynTrait() {
+ started := rst.pathStartGenerics()
+ for len(rst.str) > 0 && rst.str[0] == 'p' {
+ rst.advance(1)
+ if started {
+ rst.writeString(", ")
+ } else {
+ rst.writeByte('<')
+ started = true
+ }
+ id, _ := rst.undisambiguatedIdentifier()
+ rst.writeString(id)
+ rst.writeString(" = ")
+ rst.demangleType()
+ }
+ if started {
+ rst.writeByte('>')
+ }
+}
+
+// pathStartGenerics is like path but if it sees an I to start generic
+// arguments it won't close them. It reports whether it started generics.
+func (rst *rustState) pathStartGenerics() bool {
+ if len(rst.str) < 1 {
+ rst.fail("expected path")
+ }
+ switch rst.str[0] {
+ case 'I':
+ rst.advance(1)
+ rst.path(false)
+ rst.writeByte('<')
+ rst.genericArgs()
+ rst.checkChar('E')
+ return true
+ case 'B':
+ var started bool
+ rst.backref(func() { started = rst.pathStartGenerics() })
+ return started
+ default:
+ rst.path(false)
+ return false
+ }
+}
+
+// writeLifetime writes out a lifetime binding.
+func (rst *rustState) writeLifetime(lifetime int64) {
+ rst.writeByte('\'')
+ if lifetime == 0 {
+ rst.writeByte('_')
+ return
+ }
+ depth := rst.lifetimes - lifetime
+ if depth < 0 {
+ rst.fail("invalid lifetime")
+ } else if depth < 26 {
+ rst.writeByte('a' + byte(depth))
+ } else {
+ rst.writeByte('z')
+ if !rst.skip {
+ fmt.Fprintf(&rst.buf, "%d", depth-26+1)
+ rst.last = '0'
+ }
+ }
+}
+
+// demangleConst parses:
+//
+// <const> = <type> <const-data>
+// | "p" // placeholder, shown as _
+// | <backref>
+// <const-data> = ["n"] {<hex-digit>} "_"
+func (rst *rustState) demangleConst() {
+ if len(rst.str) < 1 {
+ rst.fail("expected constant")
+ }
+
+ if rst.str[0] == 'B' {
+ rst.backref(rst.demangleConst)
+ return
+ }
+
+ if rst.str[0] == 'p' {
+ rst.advance(1)
+ rst.writeByte('_')
+ return
+ }
+
+ typ := rst.str[0]
+
+ const (
+ invalid = iota
+ signedInt
+ unsignedInt
+ boolean
+ character
+ )
+
+ var kind int
+ switch typ {
+ case 'a', 's', 'l', 'x', 'n', 'i':
+ kind = signedInt
+ case 'h', 't', 'm', 'y', 'o', 'j':
+ kind = unsignedInt
+ case 'b':
+ kind = boolean
+ case 'c':
+ kind = character
+ default:
+ rst.fail("unrecognized constant type")
+ }
+
+ rst.advance(1)
+
+ if kind == signedInt && len(rst.str) > 0 && rst.str[0] == 'n' {
+ rst.advance(1)
+ rst.writeByte('-')
+ }
+
+ start := rst.str
+ digits := 0
+ val := uint64(0)
+digitLoop:
+ for len(rst.str) > 0 {
+ c := rst.str[0]
+ var digit uint64
+ switch {
+ case c >= '0' && c <= '9':
+ digit = uint64(c - '0')
+ case c >= 'a' && c <= 'f':
+ digit = uint64(c - 'a' + 10)
+ case c == '_':
+ rst.advance(1)
+ break digitLoop
+ default:
+ rst.fail("expected hex digit or _")
+ }
+ rst.advance(1)
+ if val == 0 && digit == 0 && (len(rst.str) == 0 || rst.str[0] != '_') {
+ rst.fail("invalid leading 0 in constant")
+ }
+ val *= 16
+ val += digit
+ digits++
+ }
+
+ if digits == 0 {
+ rst.fail("expected constant")
+ }
+
+ switch kind {
+ case signedInt, unsignedInt:
+ if digits > 16 {
+ // Value too big, just write out the string.
+ rst.writeString("0x")
+ rst.writeString(start[:digits])
+ } else {
+ if !rst.skip {
+ fmt.Fprintf(&rst.buf, "%d", val)
+ rst.last = '0'
+ }
+ }
+ case boolean:
+ if digits > 1 {
+ rst.fail("boolean value too large")
+ } else if val == 0 {
+ rst.writeString("false")
+ } else if val == 1 {
+ rst.writeString("true")
+ } else {
+ rst.fail("invalid boolean value")
+ }
+ case character:
+ if digits > 6 {
+ rst.fail("character value too large")
+ }
+ rst.writeByte('\'')
+ if val == '\t' {
+ rst.writeString(`\t`)
+ } else if val == '\r' {
+ rst.writeString(`\r`)
+ } else if val == '\n' {
+ rst.writeString(`\n`)
+ } else if val == '\\' {
+ rst.writeString(`\\`)
+ } else if val == '\'' {
+ rst.writeString(`\'`)
+ } else if val >= ' ' && val <= '~' {
+ // printable ASCII character
+ rst.writeByte(byte(val))
+ } else {
+ if !rst.skip {
+ fmt.Fprintf(&rst.buf, `\u{%x}`, val)
+ rst.last = '}'
+ }
+ }
+ rst.writeByte('\'')
+ default:
+ panic("internal error")
+ }
+}
+
+// base62Number parses:
+//
+// <base-62-number> = {<0-9a-zA-Z>} "_"
+func (rst *rustState) base62Number() int64 {
+ if len(rst.str) > 0 && rst.str[0] == '_' {
+ rst.advance(1)
+ return 0
+ }
+ val := int64(0)
+ for len(rst.str) > 0 {
+ c := rst.str[0]
+ rst.advance(1)
+ if c == '_' {
+ return val + 1
+ }
+ val *= 62
+ if c >= '0' && c <= '9' {
+ val += int64(c - '0')
+ } else if c >= 'a' && c <= 'z' {
+ val += int64(c - 'a' + 10)
+ } else if c >= 'A' && c <= 'Z' {
+ val += int64(c - 'A' + 36)
+ } else {
+ rst.fail("invalid digit in base 62 number")
+ }
+ }
+ rst.fail("expected _ after base 62 number")
+ return 0
+}
+
+// backref parses:
+//
+// <backref> = "B" <base-62-number>
+func (rst *rustState) backref(demangle func()) {
+ backoff := rst.off
+
+ rst.checkChar('B')
+ idx64 := rst.base62Number()
+
+ if rst.skip {
+ return
+ }
+ if rst.max > 0 && rst.buf.Len() > rst.max {
+ return
+ }
+
+ idx := int(idx64)
+ if int64(idx) != idx64 {
+ rst.fail("backref index overflow")
+ }
+ if idx < 0 || idx >= backoff {
+ rst.fail("invalid backref index")
+ }
+
+ holdStr := rst.str
+ holdOff := rst.off
+ rst.str = rst.orig[idx:backoff]
+ rst.off = idx
+ defer func() {
+ rst.str = holdStr
+ rst.off = holdOff
+ }()
+
+ demangle()
+}
+
+func (rst *rustState) decimalNumber() int {
+ if len(rst.str) == 0 {
+ rst.fail("expected number")
+ }
+
+ val := 0
+ for len(rst.str) > 0 && isDigit(rst.str[0]) {
+ add := int(rst.str[0] - '0')
+ if val >= math.MaxInt32/10-add {
+ rst.fail("decimal number overflow")
+ }
+ val *= 10
+ val += add
+ rst.advance(1)
+ }
+ return val
+}
+
+// oldRustToString demangles a Rust symbol using the old demangling.
+// The second result reports whether this is a valid Rust mangled name.
+func oldRustToString(name string, options []Option) (string, bool) {
+ max := 0
+ for _, o := range options {
+ if isMaxLength(o) {
+ max = maxLength(o)
+ }
+ }
+
+ // We know that the string starts with _ZN.
+ name = name[3:]
+
+ hexDigit := func(c byte) (byte, bool) {
+ switch {
+ case c >= '0' && c <= '9':
+ return c - '0', true
+ case c >= 'a' && c <= 'f':
+ return c - 'a' + 10, true
+ default:
+ return 0, false
+ }
+ }
+
+ // We know that the strings end with "17h" followed by 16 characters
+ // followed by "E". We check that the 16 characters are all hex digits.
+ // Also the hex digits must contain at least 5 distinct digits.
+ seen := uint16(0)
+ for i := len(name) - 17; i < len(name)-1; i++ {
+ digit, ok := hexDigit(name[i])
+ if !ok {
+ return "", false
+ }
+ seen |= 1 << digit
+ }
+ if bits.OnesCount16(seen) < 5 {
+ return "", false
+ }
+ name = name[:len(name)-20]
+
+ // The name is a sequence of length-preceded identifiers.
+ var sb strings.Builder
+ for len(name) > 0 {
+ if max > 0 && sb.Len() > max {
+ break
+ }
+
+ if !isDigit(name[0]) {
+ return "", false
+ }
+
+ val := 0
+ for len(name) > 0 && isDigit(name[0]) {
+ add := int(name[0] - '0')
+ if val >= math.MaxInt32/10-add {
+ return "", false
+ }
+ val *= 10
+ val += add
+ name = name[1:]
+ }
+
+ // An optional trailing underscore can separate the
+ // length from the identifier.
+ if len(name) > 0 && name[0] == '_' {
+ name = name[1:]
+ val--
+ }
+
+ if len(name) < val {
+ return "", false
+ }
+
+ id := name[:val]
+ name = name[val:]
+
+ if sb.Len() > 0 {
+ sb.WriteString("::")
+ }
+
+ // Ignore leading underscores preceding escape sequences.
+ if strings.HasPrefix(id, "_$") {
+ id = id[1:]
+ }
+
+ // The identifier can have escape sequences.
+ escape:
+ for len(id) > 0 {
+ switch c := id[0]; c {
+ case '$':
+ codes := map[string]byte{
+ "SP": '@',
+ "BP": '*',
+ "RF": '&',
+ "LT": '<',
+ "GT": '>',
+ "LP": '(',
+ "RP": ')',
+ }
+
+ valid := true
+ if len(id) > 2 && id[1] == 'C' && id[2] == '$' {
+ sb.WriteByte(',')
+ id = id[3:]
+ } else if len(id) > 4 && id[1] == 'u' && id[4] == '$' {
+ dig1, ok1 := hexDigit(id[2])
+ dig2, ok2 := hexDigit(id[3])
+ val := (dig1 << 4) | dig2
+ if !ok1 || !ok2 || dig1 > 7 || val < ' ' {
+ valid = false
+ } else {
+ sb.WriteByte(val)
+ id = id[5:]
+ }
+ } else if len(id) > 3 && id[3] == '$' {
+ if code, ok := codes[id[1:3]]; !ok {
+ valid = false
+ } else {
+ sb.WriteByte(code)
+ id = id[4:]
+ }
+ } else {
+ valid = false
+ }
+ if !valid {
+ sb.WriteString(id)
+ break escape
+ }
+ case '.':
+ if strings.HasPrefix(id, "..") {
+ sb.WriteString("::")
+ id = id[2:]
+ } else {
+ sb.WriteByte(c)
+ id = id[1:]
+ }
+ default:
+ sb.WriteByte(c)
+ id = id[1:]
+ }
+ }
+ }
+
+ s := sb.String()
+ if max > 0 && len(s) > max {
+ s = s[:max]
+ }
+ return s, true
+}