// 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"
"strconv"
"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,
scopes: 1,
}
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
// The scopes field is used to avoid unnecessary parentheses
// around expressions that use > (or >>). It is incremented if
// we output a parenthesis or something else that means that >
// or >> won't be treated as ending a template. It starts out
// as 1, and is set to 0 when we start writing template
// arguments. We add parentheses around expressions using > if
// scopes is 0. The effect is that an expression with > gets
// parentheses if used as a template argument that is not
// inside some other set of parentheses.
scopes int
// lambdaTemplateArgs is true if we are printing the template
// arguments to a lambda with explicit template parameters.
// In that case template parameters are printed without names.
lambdaTemplateArgs bool
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]
}
// printList prints a list of AST values separated by commas,
// optionally skipping some.
func (ps *printState) printList(args []AST, skip func(AST) bool) {
first := true
for _, a := range args {
if skip != nil && skip(a) {
continue
}
if !first {
ps.writeString(", ")
}
needsParen := false
if ps.llvmStyle {
if p, ok := a.(hasPrec); ok {
if p.prec() >= precComma {
needsParen = true
}
}
}
if needsParen {
ps.startScope('(')
}
ps.print(a)
if needsParen {
ps.endScope(')')
}
first = false
}
}
// startScope starts a scope. This is used to decide whether we need
// to parenthesize an expression using > or >>.
func (ps *printState) startScope(b byte) {
ps.scopes++
ps.writeByte(b)
}
// endScope closes a scope.
func (ps *printState) endScope(b byte) {
ps.scopes--
ps.writeByte(b)
}
// precedence is used for operator precedence. This is used to avoid
// unnecessary parentheses when printing expressions in the LLVM style.
type precedence int
// The precedence values, in order from high to low.
const (
precPrimary precedence = iota
precPostfix
precUnary
precCast
precPtrMem
precMul
precAdd
precShift
precSpaceship
precRel
precEqual
precAnd
precXor
precOr
precLogicalAnd
precLogicalOr
precCond
precAssign
precComma
precDefault
)
// hasPrec matches the AST nodes that have a prec method that returns
// the node's precedence.
type hasPrec interface {
prec() precedence
}
// 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)
}
func (n *Name) prec() precedence {
return precPrimary
}
// 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: "))
}
func (q *Qualified) prec() precedence {
return precPrimary
}
// 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(' ')
}
scopes := ps.scopes
ps.scopes = 0
ps.writeByte('<')
ps.printList(t.Args, ps.isEmpty)
if ps.last == '>' && !ps.llvmStyle {
// Avoid syntactic ambiguity in old versions of C++.
ps.writeByte(' ')
}
ps.writeByte('>')
ps.scopes = scopes
}
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)
}
// TemplateParamQualifiedArg is used when the mangled name includes
// both the template parameter declaration and the template argument.
// See https://github.com/itanium-cxx-abi/cxx-abi/issues/47.
type TemplateParamQualifiedArg struct {
Param AST
Arg AST
}
func (tpqa *TemplateParamQualifiedArg) print(ps *printState) {
// We only demangle the actual template argument.
// That is what the LLVM demangler does.
// The parameter disambiguates the argument,
// but is hopefully not required by a human reader.
ps.print(tpqa.Arg)
}
func (tpqa *TemplateParamQualifiedArg) Traverse(fn func(AST) bool) {
if fn(tpqa) {
tpqa.Param.Traverse(fn)
tpqa.Arg.Traverse(fn)
}
}
func (tpqa *TemplateParamQualifiedArg) Copy(fn func(AST) AST, skip func(AST) bool) AST {
if skip(tpqa) {
return nil
}
param := tpqa.Param.Copy(fn, skip)
arg := tpqa.Arg.Copy(fn, skip)
if param == nil && arg == nil {
return fn(tpqa)
}
if param == nil {
param = tpqa.Param
}
if arg == nil {
arg = tpqa.Arg
}
tpqa = &TemplateParamQualifiedArg{Param: param, Arg: arg}
if r := fn(tpqa); r != nil {
return r
}
return tpqa
}
func (tpqa *TemplateParamQualifiedArg) GoString() string {
return tpqa.goString(0, "")
}
func (tpqa *TemplateParamQualifiedArg) goString(indent int, field string) string {
return fmt.Sprintf("%*s%sTemplateParamQualifiedArg:\n%s\n%s", indent, "", field,
tpqa.Param.goString(indent+2, "Param: "),
tpqa.Arg.goString(indent+2, "Arg: "))
}
// 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.startScope('(')
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.endScope(')')
}
}
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)
// In implausible situations like a throw qualified by a throw,
// we may have printed the qualifier already,
// so check the length of ps.inner again.
if len(ps.inner) > 0 {
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)
}
func (bt *BuiltinType) prec() precedence {
return precPrimary
}
// 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, ""))
}
// SuffixType is an type with an arbitrary suffix.
type SuffixType struct {
Base AST
Suffix string
}
func (st *SuffixType) print(ps *printState) {
printBase(ps, st, st.Base)
}
func (st *SuffixType) printInner(ps *printState) {
ps.writeByte(' ')
ps.writeString(st.Suffix)
}
func (st *SuffixType) Traverse(fn func(AST) bool) {
if fn(st) {
st.Base.Traverse(fn)
}
}
func (st *SuffixType) Copy(fn func(AST) AST, skip func(AST) bool) AST {
if skip(st) {
return nil
}
base := st.Base.Copy(fn, skip)
if base == nil {
return fn(st)
}
st = &SuffixType{Base: base, Suffix: st.Suffix}
if r := fn(st); r != nil {
return r
}
return st
}
func (st *SuffixType) GoString() string {
return st.goString(0, "")
}
func (st *SuffixType) goString(indent int, field string) string {
return fmt.Sprintf("%*s%sSuffixType: %s\n%s", indent, "", field,
st.Suffix, st.Base.goString(indent+2, "Base: "))
}
// TransformedType is a builtin type with a template argument.
type TransformedType struct {
Name string
Base AST
}
func (tt *TransformedType) print(ps *printState) {
ps.writeString(tt.Name)
ps.startScope('(')
ps.print(tt.Base)
ps.endScope(')')
}
func (tt *TransformedType) Traverse(fn func(AST) bool) {
if fn(tt) {
tt.Base.Traverse(fn)
}
}
func (tt *TransformedType) Copy(fn func(AST) AST, skip func(AST) bool) AST {
if skip(tt) {
return nil
}
base := tt.Base.Copy(fn, skip)
if base == nil {
return fn(tt)
}
tt = &TransformedType{Name: tt.Name, Base: base}
if r := fn(tt); r != nil {
return r
}
return tt
}
func (tt *TransformedType) GoString() string {
return tt.goString(0, "")
}
func (tt *TransformedType) goString(indent int, field string) string {
return fmt.Sprintf("%*s%sTransformedType: %s\n%s", indent, "", field,
tt.Name, tt.Base.goString(indent+2, "Base: "))
}
// 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.writeByte(' ')
ps.startScope('(')
ps.printInner(false)
ps.endScope(')')
}
}
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.startScope('(')
}
save := ps.printInner(true)
if paren {
ps.endScope(')')
}
ps.startScope('(')
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.endScope(')')
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)
}
func (fp *FunctionParam) prec() precedence {
return precPrimary
}
// 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
Suffix string
}
func (bfp *BinaryFP) print(ps *printState) {
fmt.Fprintf(&ps.buf, "_Float%d%s", bfp.Bits, bfp.Suffix)
}
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: Bits: %d Suffix: %s", indent, "", field, bfp.Bits, bfp.Suffix)
}
// BitIntType is the C++23 _BitInt(N) type.
type BitIntType struct {
Size AST
Signed bool
}
func (bt *BitIntType) print(ps *printState) {
if !bt.Signed {
ps.writeString("unsigned ")
}
ps.writeString("_BitInt")
ps.startScope('(')
ps.print(bt.Size)
ps.endScope(')')
}
func (bt *BitIntType) Traverse(fn func(AST) bool) {
if fn(bt) {
bt.Size.Traverse(fn)
}
}
func (bt *BitIntType) Copy(fn func(AST) AST, skip func(AST) bool) AST {
if skip(bt) {
return nil
}
size := bt.Size.Copy(fn, skip)
if size == nil {
return fn(bt)
}
bt = &BitIntType{Size: size, Signed: bt.Signed}
if r := fn(bt); r != nil {
return r
}
return bt
}
func (bt *BitIntType) GoString() string {
return bt.goString(0, "")
}
func (bt *BitIntType) goString(indent int, field string) string {
return fmt.Sprintf("%*s%sBitIntType: Signed: %t\n%s", indent, "", field,
bt.Signed,
bt.Size.goString(indent+2, "Size: "))
}
// 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.startScope('(')
ps.print(dt.Expr)
ps.endScope(')')
}
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
precedence precedence
}
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)
}
func (op *Operator) prec() precedence {
return op.precedence
}
// 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.startScope('(')
ps.print(sp.Pack)
ps.endScope(')')
} 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 ps.llvmStyle {
if tpn.Index > 0 {
ps.writeString(fmt.Sprintf("%d", tpn.Index-1))
}
} else {
ps.writeString(fmt.Sprintf("%d", tpn.Index))
}
}
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 {
return fmt.Sprintf("%*s%sTemplateParamName: %s%d", indent, "", field, tpn.Prefix, tpn.Index)
}
// 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")
if ps.llvmStyle {
ps.writeByte(' ')
}
ps.printInner(false)
if ps.llvmStyle || !ps.lambdaTemplateArgs {
if !ps.llvmStyle {
ps.writeByte(' ')
}
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 {
if ps.llvmStyle || !ps.lambdaTemplateArgs {
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
Constraint AST
}
func (ttp *TemplateTemplateParam) print(ps *printState) {
scopes := ps.scopes
ps.scopes = 0
lambdaTemplateArgs := ps.lambdaTemplateArgs
ps.lambdaTemplateArgs = true
inner := ps.inner
ps.inner = nil
ps.writeString("template<")
ps.printList(ttp.Params, nil)
ps.writeString("> ")
if ps.llvmStyle {
ps.writeString("typename")
} else {
ps.writeString("class")
}
ps.scopes = scopes
ps.lambdaTemplateArgs = lambdaTemplateArgs
ps.inner = inner
ps.printInner(false)
if ps.llvmStyle || !ps.lambdaTemplateArgs {
ps.writeByte(' ')
ps.print(ttp.Name)
}
if ttp.Constraint != nil {
ps.writeString(" requires ")
ps.print(ttp.Constraint)
}
}
func (ttp *TemplateTemplateParam) Traverse(fn func(AST) bool) {
if fn(ttp) {
ttp.Name.Traverse(fn)
for _, param := range ttp.Params {
param.Traverse(fn)
}
if ttp.Constraint != nil {
ttp.Constraint.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
}
}
var constraint AST
if ttp.Constraint != nil {
constraint = ttp.Constraint.Copy(fn, skip)
if constraint == nil {
constraint = ttp.Constraint
} else {
changed = true
}
}
if !changed {
return fn(ttp)
}
ttp = &TemplateTemplateParam{
Name: name,
Params: params,
Constraint: constraint,
}
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(¶ms, "%*sParams:", indent+2, "")
for i, p := range ttp.Params {
params.WriteByte('\n')
params.WriteString(p.goString(indent+4, fmt.Sprintf("%d: ", i)))
}
var constraint string
if ttp.Constraint == nil {
constraint = fmt.Sprintf("%*sConstraint: nil", indent+2, "")
} else {
constraint = ttp.Constraint.goString(indent+2, "Constraint: ")
}
return fmt.Sprintf("%*s%sTemplateTemplateParam:\n%s\n%s\n%s", indent, "", field,
ttp.Name.goString(indent+2, "Name: "),
params.String(),
constraint)
}
// ConstrainedTypeTemplateParam is a constrained template type
// parameter declaration.
type ConstrainedTypeTemplateParam struct {
Name AST
Constraint AST
}
func (cttp *ConstrainedTypeTemplateParam) print(ps *printState) {
ps.inner = append(ps.inner, cttp)
ps.print(cttp.Constraint)
if len(ps.inner) > 0 {
ps.writeByte(' ')
ps.print(cttp.Name)
ps.inner = ps.inner[:len(ps.inner)-1]
}
}
func (cttp *ConstrainedTypeTemplateParam) printInner(ps *printState) {
ps.print(cttp.Name)
}
func (cttp *ConstrainedTypeTemplateParam) Traverse(fn func(AST) bool) {
if fn(cttp) {
cttp.Name.Traverse(fn)
cttp.Constraint.Traverse(fn)
}
}
func (cttp *ConstrainedTypeTemplateParam) Copy(fn func(AST) AST, skip func(AST) bool) AST {
if skip(cttp) {
return nil
}
name := cttp.Name.Copy(fn, skip)
constraint := cttp.Constraint.Copy(fn, skip)
if name == nil && constraint == nil {
return fn(cttp)
}
if name == nil {
name = cttp.Name
}
if constraint == nil {
constraint = cttp.Constraint
}
cttp = &ConstrainedTypeTemplateParam{Name: name, Constraint: constraint}
if r := fn(cttp); r != nil {
return r
}
return cttp
}
func (cttp *ConstrainedTypeTemplateParam) GoString() string {
return cttp.goString(0, "")
}
func (cttp *ConstrainedTypeTemplateParam) goString(indent int, field string) string {
return fmt.Sprintf("%*s%sConstrainedTypeTemplateParam\n%s\n%s", indent, "", field,
cttp.Name.goString(indent+2, "Name: "),
cttp.Constraint.goString(indent+2, "Constraint: "))
}
// 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}
nttp, isNTTP := tpp.Param.(*NonTypeTemplateParam)
if isNTTP {
ps.print(nttp.Type)
} else {
ps.print(tpp.Param)
}
if len(ps.inner) > 0 {
ps.writeString("...")
if isNTTP && (ps.llvmStyle || !ps.lambdaTemplateArgs) {
ps.writeByte(' ')
ps.print(nttp.Name)
}
}
}
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: "))
}
func (c *Cast) prec() precedence {
return precCast
}
// 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.startScope('(')
}
ps.print(val)
if paren {
ps.endScope(')')
}
}
// 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 {
if ps.llvmStyle {
wantParens := true
opPrec := precUnary
if op != nil {
opPrec = op.precedence
}
if p, ok := expr.(hasPrec); ok {
if p.prec() < opPrec {
wantParens = false
}
}
if wantParens {
ps.startScope('(')
}
ps.print(expr)
if wantParens {
ps.endScope(')')
}
} else {
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.startScope('(')
ps.print(c.To)
ps.endScope(')')
} else {
ps.print(u.Op)
}
if !u.Suffix {
alwaysParens := u.SizeofType || (op != nil && (op.Name == "__alignof__" || op.Name == "noexcept"))
if op != nil && op.Name == "::" {
// Don't use parentheses after ::.
ps.print(expr)
} else if alwaysParens {
ps.startScope('(')
ps.print(expr)
ps.endScope(')')
} else if ps.llvmStyle {
var wantParens bool
switch {
case op == nil:
wantParens = true
case op.Name == `operator"" `:
wantParens = false
case op.Name == "&":
wantParens = false
case op.Name == "delete" || op.Name == "delete[]":
wantParens = false
case op.Name == "alignof ":
wantParens = true
case op.Name == "sizeof ":
wantParens = true
case op.Name == "typeid ":
wantParens = true
default:
wantParens = true
if p, ok := expr.(hasPrec); ok {
if p.prec() < op.precedence {
wantParens = false
}
}
}
if wantParens {
ps.startScope('(')
}
ps.print(expr)
if wantParens {
ps.endScope(')')
}
} 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: "))
}
func (u *Unary) prec() precedence {
if p, ok := u.Op.(hasPrec); ok {
return p.prec()
}
return precDefault
}
// 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)
scopes := ps.scopes
ps.scopes = 0
ps.writeByte('<')
ps.print(b.Left)
ps.writeString(">")
ps.scopes = scopes
ps.startScope('(')
ps.print(b.Right)
ps.endScope(')')
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.
needsOuterParen := op != nil && (op.Name == ">" || op.Name == ">>")
if ps.llvmStyle && ps.scopes > 0 {
needsOuterParen = false
}
if needsOuterParen {
ps.startScope('(')
}
left := b.Left
skipParens := false
addSpaces := ps.llvmStyle
if ps.llvmStyle && op != nil {
switch op.Name {
case ".", "->", "->*":
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 && strings.HasPrefix(op.Name, "()") {
if ty, ok := b.Left.(*Typed); ok {
if ft, ok := ty.Type.(*FunctionType); ok {
if ft.Return == nil {
left = ty.Name
} else {
if op.Name != "() " {
skipParens = true
}
}
} else {
left = ty.Name
}
}
if ps.llvmStyle && op.Name != "() " {
skipParens = true
}
}
if skipParens {
ps.print(left)
} else if ps.llvmStyle {
prec := precPrimary
if p, ok := left.(hasPrec); ok {
prec = p.prec()
}
needsParen := op.Name == "() "
if prec > b.prec() {
needsParen = true
}
if needsParen {
ps.startScope('(')
}
ps.print(left)
if needsParen {
ps.endScope(')')
}
} else {
parenthesize(ps, left)
}
if op != nil && op.Name == "[]" {
ps.writeByte('[')
ps.print(b.Right)
ps.writeByte(']')
return
}
if op != nil {
if !strings.HasPrefix(op.Name, "()") {
if addSpaces && op.Name != "," {
ps.writeByte(' ')
}
ps.writeString(op.Name)
if addSpaces {
ps.writeByte(' ')
}
}
} else {
ps.print(b.Op)
}
if ps.llvmStyle {
prec := precPrimary
if p, ok := b.Right.(hasPrec); ok {
prec = p.prec()
}
needsParen := false
if prec >= b.prec() {
needsParen = true
}
if needsParen {
ps.startScope('(')
}
ps.print(b.Right)
if needsParen {
ps.endScope(')')
}
} else {
parenthesize(ps, b.Right)
}
if needsOuterParen {
ps.endScope(')')
}
}
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: "))
}
func (b *Binary) prec() precedence {
if p, ok := b.Op.(hasPrec); ok {
return p.prec()
}
return precDefault
}
// 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
}
if ps.llvmStyle {
wantParens := true
opPrec := precPrimary
if op, ok := t.Op.(*Operator); ok {
opPrec = op.precedence
}
if p, ok := t.First.(hasPrec); ok {
if p.prec() < opPrec {
wantParens = false
}
}
if wantParens {
ps.startScope('(')
}
ps.print(t.First)
if wantParens {
ps.endScope(')')
}
} else {
parenthesize(ps, t.First)
}
if ps.llvmStyle {
ps.writeString(" ? ")
} else {
ps.writeByte('?')
}
if ps.llvmStyle {
wantParens := true
if p, ok := t.Second.(hasPrec); ok {
if p.prec() < precDefault {
wantParens = false
}
}
if wantParens {
ps.startScope('(')
}
ps.print(t.Second)
if wantParens {
ps.endScope(')')
}
} else {
parenthesize(ps, t.Second)
}
ps.writeString(" : ")
if ps.llvmStyle {
wantParens := true
if p, ok := t.Third.(hasPrec); ok {
if p.prec() < precAssign {
wantParens = false
}
}
if wantParens {
ps.startScope('(')
}
ps.print(t.Third)
if wantParens {
ps.endScope(')')
}
} else {
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 ps.llvmStyle {
prec := precDefault
if p, ok := a.(hasPrec); ok {
prec = p.prec()
}
needsParen := false
if prec > precCast {
needsParen = true
}
if needsParen {
ps.startScope('(')
}
ps.print(a)
if needsParen {
ps.endScope(')')
}
} else {
parenthesize(ps, a)
}
}
if f.Arg2 == nil {
if f.Left {
ps.startScope('(')
ps.writeString("...")
printOp()
foldParenthesize(f.Arg1)
ps.endScope(')')
} else {
ps.startScope('(')
foldParenthesize(f.Arg1)
printOp()
ps.writeString("...")
ps.endScope(')')
}
} else {
ps.startScope('(')
foldParenthesize(f.Arg1)
printOp()
ps.writeString("...")
printOp()
foldParenthesize(f.Arg2)
ps.endScope(')')
}
}
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.startScope('(')
ps.print(pmc.Type)
ps.writeString(")(")
ps.print(pmc.Expr)
ps.endScope(')')
}
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 == "" || l.Val == "0") {
if ps.llvmStyle {
ps.writeString("nullptr")
} else {
ps.print(l.Type)
}
return
} else {
isFloat = builtinTypeFloat[b.Name]
}
}
ps.startScope('(')
ps.print(l.Type)
ps.endScope(')')
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)
}
func (l *Literal) prec() precedence {
return precPrimary
}
// 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) {
ps.printList(el.Exprs, nil)
}
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
}
func (el *ExprList) prec() precedence {
return precComma
}
// 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 {
if array, ok := il.Type.(*ArrayType); ok {
if builtin, ok := array.Element.(*BuiltinType); ok && builtin.Name == "char" {
if il.printAsString(ps) {
return
}
}
}
}
if il.Type != nil {
ps.print(il.Type)
}
ps.writeByte('{')
ps.print(il.Exprs)
ps.writeByte('}')
}
// printAsString tries to print a char array initializer as a string.
// It reports whether it succeeded.
func (il *InitializerList) printAsString(ps *printState) bool {
exprList, ok := il.Exprs.(*ExprList)
if !ok {
return false
}
var sb strings.Builder
sb.WriteByte('"')
inNumericEscape := false
for _, e := range exprList.Exprs {
lit, ok := e.(*Literal)
if !ok {
return false
}
if lit.Neg {
return false
}
val, err := strconv.Atoi(lit.Val)
if err != nil || val > 255 {
return false
}
// If we just added a numeric escape,
// make sure it doesn't get accidentally extended
// (numeric escapes here are C++ syntax, not Go syntax).
if inNumericEscape {
if (val >= '0' && val <= '9') ||
(val >= 'a' && val <= 'f') ||
(val >= 'A' && val <= 'F') {
sb.WriteString(`""`)
}
}
inNumericEscape = false
switch val {
case '\a':
sb.WriteString(`\a`)
case '\b':
sb.WriteString(`\b`)
case '\f':
sb.WriteString(`\f`)
case '\n':
sb.WriteString(`\n`)
case '\r':
sb.WriteString(`\r`)
case '\t':
sb.WriteString(`\t`)
case '\v':
sb.WriteString(`\v`)
case '"':
sb.WriteString(`\"`)
case '\\':
sb.WriteString(`\\`)
default:
if val < 32 || val == 127 {
const hex = "0123456789ABCDEF"
sb.WriteByte('\\')
if val > 7 {
sb.WriteByte('x')
}
if val >= 16 {
sb.WriteByte(hex[val>>4])
}
sb.WriteByte(hex[val&0xf])
inNumericEscape = true
break
}
sb.WriteByte(byte(val))
}
}
sb.WriteByte('"')
ps.writeString(sb.String())
return true
}
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
TemplateArgsConstraint AST
Types []AST
Num int
CallConstraint AST
}
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 {
scopes := ps.scopes
ps.scopes = 0
ps.writeString("<")
ps.printList(cl.TemplateArgs, nil)
ps.writeString(">")
ps.scopes = scopes
}
if cl.TemplateArgsConstraint != nil {
ps.writeString(" requires ")
ps.print(cl.TemplateArgsConstraint)
ps.writeByte(' ')
}
ps.startScope('(')
ps.printList(cl.Types, nil)
ps.endScope(')')
if cl.CallConstraint != nil {
ps.writeString(" requires ")
ps.print(cl.CallConstraint)
}
}
func (cl *Closure) Traverse(fn func(AST) bool) {
if fn(cl) {
for _, a := range cl.TemplateArgs {
a.Traverse(fn)
}
if cl.TemplateArgsConstraint != nil {
cl.TemplateArgsConstraint.Traverse(fn)
}
for _, t := range cl.Types {
t.Traverse(fn)
}
if cl.CallConstraint != nil {
cl.CallConstraint.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
}
}
var templateArgsConstraint AST
if cl.TemplateArgsConstraint != nil {
templateArgsConstraint = cl.TemplateArgsConstraint.Copy(fn, skip)
if templateArgsConstraint == nil {
templateArgsConstraint = cl.TemplateArgsConstraint
} else {
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
}
}
var callConstraint AST
if cl.CallConstraint != nil {
callConstraint = cl.CallConstraint.Copy(fn, skip)
if callConstraint == nil {
callConstraint = cl.CallConstraint
} else {
changed = true
}
}
if !changed {
return fn(cl)
}
cl = &Closure{
TemplateArgs: args,
TemplateArgsConstraint: templateArgsConstraint,
Types: types,
Num: cl.Num,
CallConstraint: callConstraint,
}
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 strings.Builder
if len(cl.TemplateArgs) == 0 {
fmt.Fprintf(&args, "%*sTemplateArgs: nil", indent+2, "")
} else {
fmt.Fprintf(&args, "%*sTemplateArgs:", indent+2, "")
for i, a := range cl.TemplateArgs {
args.WriteByte('\n')
args.WriteString(a.goString(indent+4, fmt.Sprintf("%d: ", i)))
}
}
var templateArgsConstraint string
if cl.TemplateArgsConstraint != nil {
templateArgsConstraint = "\n" + cl.TemplateArgsConstraint.goString(indent+2, "TemplateArgsConstraint: ")
}
var types strings.Builder
if len(cl.Types) == 0 {
fmt.Fprintf(&types, "%*sTypes: nil", indent+2, "")
} else {
fmt.Fprintf(&types, "%*sTypes:", indent+2, "")
for i, t := range cl.Types {
types.WriteByte('\n')
types.WriteString(t.goString(indent+4, fmt.Sprintf("%d: ", i)))
}
}
var callConstraint string
if cl.CallConstraint != nil {
callConstraint = "\n" + cl.CallConstraint.goString(indent+2, "CallConstraint: ")
}
return fmt.Sprintf("%*s%sClosure: Num: %d\n%s\n%s%s%s", indent, "", field,
cl.Num, args.String(), templateArgsConstraint, types.String(),
callConstraint)
}
// StructuredBindings is a structured binding declaration.
type StructuredBindings struct {
Bindings []AST
}
func (sb *StructuredBindings) print(ps *printState) {
ps.writeString("[")
ps.printList(sb.Bindings, nil)
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.writeByte(' ')
ps.startScope('(')
ps.writeString(c.Suffix)
ps.endScope(')')
} 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:")
ps.printList(ei.Args, nil)
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)
}
// ModuleName is a C++20 module.
type ModuleName struct {
Parent AST
Name AST
IsPartition bool
}
func (mn *ModuleName) print(ps *printState) {
if mn.Parent != nil {
ps.print(mn.Parent)
}
if mn.IsPartition {
ps.writeByte(':')
} else if mn.Parent != nil {
ps.writeByte('.')
}
ps.print(mn.Name)
}
func (mn *ModuleName) Traverse(fn func(AST) bool) {
if fn(mn) {
if mn.Parent != nil {
mn.Parent.Traverse(fn)
}
mn.Name.Traverse(fn)
}
}
func (mn *ModuleName) Copy(fn func(AST) AST, skip func(AST) bool) AST {
if skip(mn) {
return nil
}
var parent AST
if mn.Parent != nil {
parent = mn.Parent.Copy(fn, skip)
}
name := mn.Name.Copy(fn, skip)
if parent == nil && name == nil {
return fn(mn)
}
if parent == nil {
parent = mn.Parent
}
if name == nil {
name = mn.Name
}
mn = &ModuleName{Parent: parent, Name: name, IsPartition: mn.IsPartition}
if r := fn(mn); r != nil {
return r
}
return mn
}
func (mn *ModuleName) GoString() string {
return mn.goString(0, "")
}
func (mn *ModuleName) goString(indent int, field string) string {
var parent string
if mn.Parent == nil {
parent = fmt.Sprintf("%*sParent: nil", indent+2, "")
} else {
parent = mn.Parent.goString(indent+2, "Parent: ")
}
return fmt.Sprintf("%*s%sModuleName: IsPartition: %t\n%s\n%s", indent, "", field,
mn.IsPartition, parent,
mn.Name.goString(indent+2, "Name: "))
}
// ModuleEntity is a name inside a module.
type ModuleEntity struct {
Module AST
Name AST
}
func (me *ModuleEntity) print(ps *printState) {
ps.print(me.Name)
ps.writeByte('@')
ps.print(me.Module)
}
func (me *ModuleEntity) Traverse(fn func(AST) bool) {
if fn(me) {
me.Module.Traverse(fn)
me.Name.Traverse(fn)
}
}
func (me *ModuleEntity) Copy(fn func(AST) AST, skip func(AST) bool) AST {
if skip(me) {
return nil
}
module := me.Module.Copy(fn, skip)
name := me.Name.Copy(fn, skip)
if module == nil && name == nil {
return fn(me)
}
if module == nil {
module = me.Module
}
if name == nil {
name = me.Name
}
me = &ModuleEntity{Module: module, Name: name}
if r := fn(me); r != nil {
return r
}
return me
}
func (me *ModuleEntity) GoString() string {
return me.goString(0, "")
}
func (me *ModuleEntity) goString(indent int, field string) string {
return fmt.Sprintf("%*s%sModuleEntity:\n%s\n%s", indent, "", field,
me.Module.goString(indent+2, "Module: "),
me.Name.goString(indent+2, "Name: "))
}
// Friend is a member like friend name.
type Friend struct {
Name AST
}
func (f *Friend) print(ps *printState) {
if ps.llvmStyle {
ps.writeString("friend ")
}
ps.print(f.Name)
if !ps.llvmStyle {
ps.writeString("[friend]")
}
}
func (f *Friend) Traverse(fn func(AST) bool) {
if fn(f) {
f.Name.Traverse(fn)
}
}
func (f *Friend) Copy(fn func(AST) AST, skip func(AST) bool) AST {
if skip(f) {
return nil
}
name := f.Name.Copy(fn, skip)
if name == nil {
return fn(f)
}
f = &Friend{Name: name}
if r := fn(f); r != nil {
return r
}
return f
}
func (f *Friend) GoString() string {
return f.goString(0, "")
}
func (f *Friend) goString(indent int, field string) string {
return fmt.Sprintf("%*s%sFriend:\n%s", indent, "", field,
f.Name.goString(indent+2, "Name: "))
}
// Constraint represents an AST with a constraint.
// ForTemplateArgs is true if this constraint was found at the
// end of a list of template arguments. We need that because
// in LLVM style we don't print constraints of that sort.
type Constraint struct {
Name AST
Requires AST
ForTemplateArgs bool
}
func (c *Constraint) print(ps *printState) {
ps.print(c.Name)
// For LLVM style skip the constraint on a template.
if ps.llvmStyle && c.ForTemplateArgs {
return
}
ps.writeString(" requires ")
ps.print(c.Requires)
}
func (c *Constraint) Traverse(fn func(AST) bool) {
if fn(c) {
c.Name.Traverse(fn)
c.Requires.Traverse(fn)
}
}
func (c *Constraint) Copy(fn func(AST) AST, skip func(AST) bool) AST {
if skip(c) {
return nil
}
name := c.Name.Copy(fn, skip)
requires := c.Requires.Copy(fn, skip)
if name == nil && requires == nil {
return fn(c)
}
if name == nil {
name = c.Name
}
if requires == nil {
requires = c.Requires
}
c = &Constraint{
Name: name,
Requires: requires,
ForTemplateArgs: c.ForTemplateArgs,
}
if r := fn(c); r != nil {
return r
}
return c
}
func (c *Constraint) GoString() string {
return c.goString(0, "")
}
func (c *Constraint) goString(indent int, field string) string {
return fmt.Sprintf("%*s%sConstraint: ForTemplateArgs: %t\n%s\n%s", indent, "", field,
c.ForTemplateArgs,
c.Name.goString(indent+2, "Name: "),
c.Requires.goString(indent+2, "Requires: "))
}
// RequiresExpr is a C++20 requires expression.
type RequiresExpr struct {
Params []AST
Requirements []AST
}
func (re *RequiresExpr) print(ps *printState) {
ps.writeString("requires")
if len(re.Params) > 0 {
ps.writeByte(' ')
ps.startScope('(')
ps.printList(re.Params, nil)
ps.endScope(')')
}
ps.writeByte(' ')
ps.startScope('{')
for _, req := range re.Requirements {
ps.print(req)
}
ps.writeByte(' ')
ps.endScope('}')
}
func (re *RequiresExpr) Traverse(fn func(AST) bool) {
if fn(re) {
for _, p := range re.Params {
p.Traverse(fn)
}
for _, r := range re.Requirements {
r.Traverse(fn)
}
}
}
func (re *RequiresExpr) Copy(fn func(AST) AST, skip func(AST) bool) AST {
if skip(re) {
return nil
}
changed := false
var params []AST
if len(re.Params) > 0 {
params = make([]AST, len(re.Params))
for i, p := range re.Params {
pc := p.Copy(fn, skip)
if pc == nil {
params[i] = p
} else {
params[i] = pc
changed = true
}
}
}
requirements := make([]AST, len(re.Requirements))
for i, r := range re.Requirements {
rc := r.Copy(fn, skip)
if rc == nil {
requirements[i] = r
} else {
requirements[i] = rc
changed = true
}
}
if !changed {
return fn(re)
}
re = &RequiresExpr{Params: params, Requirements: requirements}
if r := fn(re); r != nil {
return r
}
return re
}
func (re *RequiresExpr) GoString() string {
return re.goString(0, "")
}
func (re *RequiresExpr) goString(indent int, field string) string {
var params strings.Builder
if len(re.Params) == 0 {
fmt.Fprintf(¶ms, "%*sParams: nil", indent+2, "")
} else {
fmt.Fprintf(¶ms, "%*sParams:", indent+2, "")
for i, p := range re.Params {
params.WriteByte('\n')
params.WriteString(p.goString(indent+4, fmt.Sprintf("%d: ", i)))
}
}
var requirements strings.Builder
fmt.Fprintf(&requirements, "%*sRequirements:", indent+2, "")
for i, r := range re.Requirements {
requirements.WriteByte('\n')
requirements.WriteString(r.goString(indent+4, fmt.Sprintf("%d: ", i)))
}
return fmt.Sprintf("%*s%sRequirements:\n%s\n%s", indent, "", field,
params.String(), requirements.String())
}
// ExprRequirement is a simple requirement in a requires expression.
// This is an arbitrary expression.
type ExprRequirement struct {
Expr AST
Noexcept bool
TypeReq AST
}
func (er *ExprRequirement) print(ps *printState) {
ps.writeByte(' ')
if er.Noexcept || er.TypeReq != nil {
ps.startScope('{')
}
ps.print(er.Expr)
if er.Noexcept || er.TypeReq != nil {
ps.endScope('}')
}
if er.Noexcept {
ps.writeString(" noexcept")
}
if er.TypeReq != nil {
ps.writeString(" -> ")
ps.print(er.TypeReq)
}
ps.writeByte(';')
}
func (er *ExprRequirement) Traverse(fn func(AST) bool) {
if fn(er) {
er.Expr.Traverse(fn)
if er.TypeReq != nil {
er.TypeReq.Traverse(fn)
}
}
}
func (er *ExprRequirement) Copy(fn func(AST) AST, skip func(AST) bool) AST {
if skip(er) {
return nil
}
expr := er.Expr.Copy(fn, skip)
var typeReq AST
if er.TypeReq != nil {
typeReq = er.TypeReq.Copy(fn, skip)
}
if expr == nil && typeReq == nil {
return fn(er)
}
if expr == nil {
expr = er.Expr
}
if typeReq == nil {
typeReq = er.TypeReq
}
er = &ExprRequirement{Expr: expr, TypeReq: typeReq}
if r := fn(er); r != nil {
return r
}
return er
}
func (er *ExprRequirement) GoString() string {
return er.goString(0, "")
}
func (er *ExprRequirement) goString(indent int, field string) string {
var typeReq string
if er.TypeReq != nil {
typeReq = "\n" + er.TypeReq.goString(indent+2, "TypeReq: ")
}
return fmt.Sprintf("%*s%sExprRequirement: Noexcept: %t\n%s%s", indent, "", field,
er.Noexcept,
er.Expr.goString(indent+2, "Expr: "),
typeReq)
}
// TypeRequirement is a type requirement in a requires expression.
type TypeRequirement struct {
Type AST
}
func (tr *TypeRequirement) print(ps *printState) {
ps.writeString(" typename ")
ps.print(tr.Type)
ps.writeByte(';')
}
func (tr *TypeRequirement) Traverse(fn func(AST) bool) {
if fn(tr) {
tr.Type.Traverse(fn)
}
}
func (tr *TypeRequirement) Copy(fn func(AST) AST, skip func(AST) bool) AST {
if skip(tr) {
return nil
}
typ := tr.Type.Copy(fn, skip)
if typ == nil {
return fn(tr)
}
tr = &TypeRequirement{Type: typ}
if r := fn(tr); r != nil {
return r
}
return tr
}
func (tr *TypeRequirement) GoString() string {
return tr.goString(0, "")
}
func (tr *TypeRequirement) goString(indent int, field string) string {
return fmt.Sprintf("%*s%sTypeRequirement:\n%s", indent, "", field,
tr.Type.goString(indent+2, ""))
}
// NestedRequirement is a nested requirement in a requires expression.
type NestedRequirement struct {
Constraint AST
}
func (nr *NestedRequirement) print(ps *printState) {
ps.writeString(" requires ")
ps.print(nr.Constraint)
ps.writeByte(';')
}
func (nr *NestedRequirement) Traverse(fn func(AST) bool) {
if fn(nr) {
nr.Constraint.Traverse(fn)
}
}
func (nr *NestedRequirement) Copy(fn func(AST) AST, skip func(AST) bool) AST {
if skip(nr) {
return nil
}
constraint := nr.Constraint.Copy(fn, skip)
if constraint == nil {
return fn(nr)
}
nr = &NestedRequirement{Constraint: constraint}
if r := fn(nr); r != nil {
return r
}
return nr
}
func (nr *NestedRequirement) GoString() string {
return nr.goString(0, "")
}
func (nr *NestedRequirement) goString(indent int, field string) string {
return fmt.Sprintf("%*s%sNestedRequirement:\n%s", indent, "", field,
nr.Constraint.goString(indent+2, ""))
}
// ExplicitObjectParameter represents a C++23 explicit object parameter.
type ExplicitObjectParameter struct {
Base AST
}
func (eop *ExplicitObjectParameter) print(ps *printState) {
ps.writeString("this ")
ps.print(eop.Base)
}
func (eop *ExplicitObjectParameter) Traverse(fn func(AST) bool) {
if fn(eop) {
eop.Base.Traverse(fn)
}
}
func (eop *ExplicitObjectParameter) Copy(fn func(AST) AST, skip func(AST) bool) AST {
if skip(eop) {
return nil
}
base := eop.Base.Copy(fn, skip)
if base == nil {
return fn(eop)
}
eop = &ExplicitObjectParameter{Base: base}
if r := fn(eop); r != nil {
return r
}
return eop
}
func (eop *ExplicitObjectParameter) GoString() string {
return eop.goString(0, "")
}
func (eop *ExplicitObjectParameter) goString(indent int, field string) string {
return fmt.Sprintf("%*s%sExplicitObjectParameter:\n%s", indent, "", field,
eop.Base.goString(indent+2, ""))
}
// 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
}
}
// 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: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
parsingConstraint bool // whether parsing a constraint expression
// 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, explicitObjectParameter := 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)
}
case *Constraint:
return findTemplate(check.Name)
}
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
enableIfConstraint AST
)
if strings.HasPrefix(st.str, enableIfPrefix) {
st.advance(len(enableIfPrefix) - 1)
enableIfArgs, enableIfConstraint = st.templateArgs()
}
ft := st.bareFunctionType(hasReturnType(a), explicitObjectParameter)
var constraint AST
if len(st.str) > 0 && st.str[0] == 'Q' {
constraint = st.constraintExpr()
}
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
}
}
// Any top-level constraint belongs on the function type.
var forTemplateArgs bool
if c, ok := a.(*Constraint); ok && constraint == nil {
a = c.Name
constraint = c.Requires
forTemplateArgs = c.ForTemplateArgs
}
r := AST(&Typed{Name: a, Type: ft})
if len(enableIfArgs) > 0 {
r = &EnableIf{Type: r, Args: enableIfArgs}
r = st.maybeAddConstraint(r, enableIfConstraint)
}
if constraint != nil {
r = &Constraint{
Name: r,
Requires: constraint,
ForTemplateArgs: forTemplateArgs,
}
}
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)
case *Constraint:
return hasReturnType(a.Name)
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>
//
// Besides the name, this returns whether it saw the code indicating
// a C++23 explicit object parameter.
func (st *state) name() (AST, bool) {
if len(st.str) < 1 {
st.fail("expected name")
}
var module AST
switch st.str[0] {
case 'N':
return st.nestedName()
case 'Z':
return st.localName()
case 'U':
a, isCast := st.unqualifiedName(nil)
if isCast {
st.setTemplate(a, nil)
}
return a, false
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(nil)
a = &Qualified{Scope: &Name{Name: "std"}, Name: a, LocalName: false}
} else {
a = st.substitution(false)
if mn, ok := a.(*ModuleName); ok {
module = mn
break
}
subst = true
}
var constraint AST
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)
}
var args []AST
args, constraint = 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)
}
a = st.maybeAddConstraint(a, constraint)
return a, false
}
a, isCast := st.unqualifiedName(module)
var constraint AST
if len(st.str) > 0 && st.str[0] == 'I' {
st.subs.add(a)
var args []AST
args, constraint = 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)
}
a = st.maybeAddConstraint(a, constraint)
return a, false
}
// nestedName parses:
//
// <nested-name> ::= N [<CV-qualifiers>] [<ref-qualifier>] <prefix> <unqualified-name> E
// ::= N [<CV-qualifiers>] [<ref-qualifier>] <template-prefix> <template-args> E
//
// Besides the name, this returns whether it saw the code indicating
// a C++23 explicit object parameter.
func (st *state) nestedName() (AST, bool) {
st.checkChar('N')
var q AST
var r string
explicitObjectParameter := false
if len(st.str) > 0 && st.str[0] == 'H' {
st.advance(1)
explicitObjectParameter = true
} else {
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, explicitObjectParameter
}
// 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
var module 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 == 'F' || c == 'W' || (c == 'D' && len(st.str) > 1 && st.str[1] == 'C') {
un, isUnCast := st.unqualifiedName(module)
next = un
module = nil
if isUnCast {
if cast != nil {
st.fail("cast in scope of cast")
}
if m, ok := un.(*ModuleEntity); ok {
un = m.Name
}
for {
tn, ok := un.(*TaggedName)
if !ok {
break
}
un = tn.Name
}
if f, ok := un.(*Friend); ok {
un = f.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")
}
switch st.str[0] {
// 0 is not used.
case '1', '2', '3', '4', '5':
default:
st.fail("unknown constructor type")
}
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")
}
switch st.str[1] {
// 3 is not used.
case '0', '1', '2', '4', '5':
default:
st.fail("unknown destructor type")
}
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)
if mn, ok := next.(*ModuleName); ok {
module = mn
next = nil
}
case 'I':
if a == nil {
st.fail("unexpected template arguments")
}
args, constraint := st.templateArgs()
tmpl := &Template{Name: a, Args: args}
if cast != nil {
st.setTemplate(cast, tmpl)
st.clearTemplateArgs(args)
cast = nil
}
a = nil
next = st.maybeAddConstraint(tmpl, constraint)
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(nil)
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")
}
}
if next == nil {
continue
}
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(module AST) (r AST, isCast bool) {
module = st.moduleName(module)
if len(st.str) < 1 {
st.fail("expected unqualified name")
}
friend := false
if len(st.str) > 0 && st.str[0] == 'F' {
st.advance(1)
friend = true
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 module != nil {
a = &ModuleEntity{Module: module, Name: a}
}
if len(st.str) > 0 && st.str[0] == 'B' {
a = st.taggedName(a)
}
if friend {
a = &Friend{Name: 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
}
// moduleName parses:
//
// <module-name> ::= <module-subname>
// ::= <module-name> <module-subname>
// ::= <substitution> # passed in by caller
// <module-subname> ::= W <source-name>
// ::= W P <source-name>
//
// The module name is optional. If it is not present, this returns the parent.
func (st *state) moduleName(parent AST) AST {
ret := parent
for len(st.str) > 0 && st.str[0] == 'W' {
st.advance(1)
isPartition := false
if len(st.str) > 0 && st.str[0] == 'P' {
st.advance(1)
isPartition = true
}
name := st.sourceName()
ret = &ModuleName{
Parent: ret,
Name: name,
IsPartition: isPartition,
}
st.subs.add(ret)
}
return ret
}
// 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
prec precedence
}
// The operators map maps the mangled operator names to information
// about them.
var operators = map[string]operator{
"aN": {"&=", 2, precAssign},
"aS": {"=", 2, precAssign},
"aa": {"&&", 2, precLogicalAnd},
"ad": {"&", 1, precUnary},
"an": {"&", 2, precAnd},
"at": {"alignof ", 1, precUnary},
"aw": {"co_await ", 1, precPrimary},
"az": {"alignof ", 1, precUnary},
"cc": {"const_cast", 2, precPostfix},
"cl": {"()", 2, precPostfix},
// cp is not in the ABI but is used by clang "when the call
// would use ADL except for being parenthesized."
// The trailing space indicates this to the printer.
"cp": {"() ", 2, precPostfix},
"cm": {",", 2, precComma},
"co": {"~", 1, precUnary},
"dV": {"/=", 2, precAssign},
"dX": {"[...]=", 3, precAssign},
"da": {"delete[] ", 1, precUnary},
"dc": {"dynamic_cast", 2, precPostfix},
"de": {"*", 1, precUnary},
"di": {"=", 2, precAssign},
"dl": {"delete ", 1, precUnary},
"ds": {".*", 2, precPtrMem},
"dt": {".", 2, precPostfix},
"dv": {"/", 2, precAssign},
"dx": {"]=", 2, precAssign},
"eO": {"^=", 2, precAssign},
"eo": {"^", 2, precXor},
"eq": {"==", 2, precEqual},
"fl": {"...", 2, precPrimary},
"fr": {"...", 2, precPrimary},
"fL": {"...", 3, precPrimary},
"fR": {"...", 3, precPrimary},
"ge": {">=", 2, precRel},
"gs": {"::", 1, precUnary},
"gt": {">", 2, precRel},
"ix": {"[]", 2, precPostfix},
"lS": {"<<=", 2, precAssign},
"le": {"<=", 2, precRel},
"li": {`operator"" `, 1, precUnary},
"ls": {"<<", 2, precShift},
"lt": {"<", 2, precRel},
"mI": {"-=", 2, precAssign},
"mL": {"*=", 2, precAssign},
"mi": {"-", 2, precAdd},
"ml": {"*", 2, precMul},
"mm": {"--", 1, precPostfix},
"na": {"new[]", 3, precUnary},
"ne": {"!=", 2, precEqual},
"ng": {"-", 1, precUnary},
"nt": {"!", 1, precUnary},
"nw": {"new", 3, precUnary},
"nx": {"noexcept", 1, precUnary},
"oR": {"|=", 2, precAssign},
"oo": {"||", 2, precLogicalOr},
"or": {"|", 2, precOr},
"pL": {"+=", 2, precAssign},
"pl": {"+", 2, precAdd},
"pm": {"->*", 2, precPtrMem},
"pp": {"++", 1, precPostfix},
"ps": {"+", 1, precUnary},
"pt": {"->", 2, precPostfix},
"qu": {"?", 3, precCond},
"rM": {"%=", 2, precAssign},
"rS": {">>=", 2, precAssign},
"rc": {"reinterpret_cast", 2, precPostfix},
"rm": {"%", 2, precMul},
"rs": {">>", 2, precShift},
"sP": {"sizeof...", 1, precUnary},
"sZ": {"sizeof...", 1, precUnary},
"sc": {"static_cast", 2, precPostfix},
"ss": {"<=>", 2, precSpaceship},
"st": {"sizeof ", 1, precUnary},
"sz": {"sizeof ", 1, precUnary},
"te": {"typeid ", 1, precPostfix},
"ti": {"typeid ", 1, precPostfix},
"tr": {"throw", 0, precPrimary},
"tw": {"throw ", 1, precUnary},
}
// 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, precedence: op.prec}, 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>
//
// Besides the name, this returns whether it saw the code indicating
// a C++23 explicit object parameter.
func (st *state) localName() (AST, bool) {
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}, false
} else {
num := -1
if len(st.str) > 0 && st.str[0] == 'd' {
// Default argument scope.
st.advance(1)
num = st.compactNumber()
}
n, explicitObjectParameter := st.name()
n = st.discriminator(n)
if num >= 0 {
n = &DefaultArg{Num: num, Arg: n}
}
return &Qualified{Scope: fn, Name: n, LocalName: true}, explicitObjectParameter
}
}
// 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>
// ::= GI <module name>
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(nil)
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()
case 'I':
module := st.moduleName(nil)
if module == nil {
st.fail("expected module after GI")
}
return &Special{Prefix: "initializer for module ", Val: module}
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()
if len(st.str) > 0 && st.str[0] == 'I' {
st.advance(1)
base := st.demangleType(false)
if len(st.str) == 0 || st.str[0] != 'E' {
st.fail("expected E after transformed type")
}
st.advance(1)
ret = &TransformedType{Name: ret.(*Name).Name, Base: base}
}
case 'F':
ret = st.functionType()
case 'N', 'W', '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)
ret = st.template(ret)
} 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 _, ok := ret.(*ModuleName); ok {
ret, _ = st.unqualifiedName(ret)
st.subs.add(ret)
}
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 {
ret = st.template(ret)
} 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' {
n = st.template(n)
}
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)
fixedPrefix := func(c byte) (string, bool) {
switch c {
case 's':
return "short ", true
case 't':
return "unsigned short ", true
case 'i':
return "", true
case 'j':
return "unsigned ", true
case 'l':
return "long ", true
case 'm':
return "unsigned long ", true
default:
return "", false
}
}
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, Suffix: ""}
} else if len(st.str) > 0 && st.str[0] == 'x' {
if bits == 0 {
st.fail("expected non-zero number of bits")
}
st.advance(1)
ret = &BinaryFP{Bits: bits, Suffix: "x"}
} else if len(st.str) > 0 && st.str[0] == 'b' {
if bits != 16 {
st.fail("expected bits to be 16 for std::bfloat16_t")
}
st.advance(1)
ret = &BuiltinType{Name: "std::bfloat16_t"}
} 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
case 'B', 'U':
signed := c2 == 'B'
var size AST
if len(st.str) > 0 && isDigit(st.str[0]) {
bits := st.number()
size = &Name{Name: fmt.Sprintf("%d", bits)}
} else {
size = st.expression()
}
if len(st.str) == 0 || st.str[0] != '_' {
st.fail("expected _ after _BitInt size")
}
st.advance(1)
ret = &BitIntType{Size: size, Signed: signed}
addSubst = true
case 'k':
constraint, _ := st.name()
ret = &SuffixType{
Base: constraint,
Suffix: "auto",
}
case 'K':
constraint, _ := st.name()
ret = &SuffixType{
Base: constraint,
Suffix: "decltype(auto)",
}
case 'A', 'R':
if len(st.str) == 0 {
st.fail("missing D{A,R} code in type")
}
prefix, ok := fixedPrefix(st.str[0])
if !ok {
st.fail("unrecognized D{A,R} code in type")
}
st.advance(1)
switch c2 {
case 'A':
ret = &BuiltinType{Name: prefix + "_Accum"}
case 'R':
ret = &BuiltinType{Name: prefix + "_Fract"}
default:
panic("internal error")
}
case 'S':
if len(st.str) < 3 {
st.fail("missing DS code in type")
}
if st.str[0] != 'D' {
st.fail("unrecognized DS code in type")
}
var suffix string
switch st.str[1] {
case 'A':
suffix = "_Accum"
case 'R':
suffix = "_Fract"
default:
st.fail("unrecognized DSD code in type")
}
prefix, ok := fixedPrefix(st.str[2])
if !ok {
st.fail("unrecognized DSD{A,R} code in type")
}
st.advance(3)
ret = &BuiltinType{Name: "_Sat " + prefix + suffix}
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
constraint AST
)
failed := false
func() {
defer func() {
if r := recover(); r != nil {
if _, ok := r.(demangleErr); ok {
failed = true
} else {
panic(r)
}
}
}()
args, constraint = st.templateArgs()
}()
if !failed && len(st.str) > 0 && st.str[0] == 'I' {
if addSubst {
st.subs.add(tp)
}
ret := &Template{Name: tp, Args: args}
return st.maybeAddConstraint(ret, constraint)
}
// 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(false)
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(explicitObjectParameter bool) []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
}
if st.str[0] == 'Q' {
// This is a requires clause.
break
}
ptype := st.demangleType(false)
if len(ret) == 0 && explicitObjectParameter {
ptype = &ExplicitObjectParameter{Base: ptype}
}
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, false)
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, explicitObjectParameter 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(explicitObjectParameter)
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
str := st.str
st.checkChar('T')
level := 0
if len(st.str) > 0 && st.str[0] == 'L' {
st.advance(1)
level = st.compactNumber()
}
n := st.compactNumber()
// We don't try to substitute template parameters in a
// constraint expression.
if st.parsingConstraint {
return &Name{Name: str[:st.off-1-off]}
}
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) {
seen := make(map[AST]bool)
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:
if seen[a] {
return false
}
seen[a] = true
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
//
// This returns the template arguments and an optional constraint.
func (st *state) templateArgs() ([]AST, AST) {
if len(st.str) == 0 || (st.str[0] != 'I' && st.str[0] != 'J') {
panic("internal error")
}
st.advance(1)
var (
ret []AST
constraint AST
)
for len(st.str) == 0 || st.str[0] != 'E' {
arg := st.templateArg(ret)
ret = append(ret, arg)
if len(st.str) > 0 && st.str[0] == 'Q' {
constraint = st.constraintExpr()
if len(st.str) == 0 || st.str[0] != 'E' {
st.fail("expected end of template arguments after constraint")
}
}
}
st.advance(1)
return ret, constraint
}
// templateArg parses:
//
// <template-arg> ::= <type>
// ::= X <expression> E
// ::= <expr-primary>
// ::= J <template-arg>* E
// ::= LZ <encoding> E
// ::= <template-param-decl> <template-arg>
func (st *state) templateArg(prev []AST) 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, constraint := st.templateArgs()
ret := &ArgumentPack{Args: args}
return st.maybeAddConstraint(ret, constraint)
case 'T':
var arg byte
if len(st.str) > 1 {
arg = st.str[1]
}
switch arg {
case 'y', 'n', 't', 'p', 'k':
off := st.off
// Apparently template references in the
// template parameter refer to previous
// arguments in the same template.
template := &Template{Args: prev}
st.templates = append(st.templates, template)
param, _ := st.templateParamDecl()
st.templates = st.templates[:len(st.templates)-1]
if param == nil {
st.failEarlier("expected template parameter as template argument", st.off-off)
}
arg := st.templateArg(nil)
return &TemplateParamQualifiedArg{Param: param, Arg: arg}
}
return st.demangleType(false)
default:
return st.demangleType(false)
}
}
// maybeAddConstraint adds a constraint to an AST, if constraint is not nil.
// This is only called with a constraint found at the end of
// template arguments.
func (st *state) maybeAddConstraint(a, constraint AST) AST {
if constraint == nil {
return a
}
return &Constraint{
Name: a,
Requires: constraint,
ForTemplateArgs: true,
}
}
// template parses template arguments into a Template with a given name.
func (st *state) template(name AST) AST {
args, constraint := st.templateArgs()
ret := &Template{Name: name, Args: args}
return st.maybeAddConstraint(ret, constraint)
}
// 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(nil)
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, isCast := st.unqualifiedName(nil)
if isCast {
st.setTemplate(n, nil)
}
if len(st.str) > 0 && st.str[0] == 'I' {
n = st.template(n)
}
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(nil)
args = append(args, arg)
}
return &Binary{
Op: &Operator{Name: "()"},
Left: name,
Right: &ExprList{Exprs: args},
}
} else if st.str[0] == 'r' && len(st.str) > 1 && (st.str[1] == 'q' || st.str[1] == 'Q') {
return st.requiresExpr()
} 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(nil)
} else {
left = st.expression()
}
if code == "cl" || code == "cp" {
right = st.exprList('E')
} else if code == "dt" || code == "pt" {
if len(st.str) > 0 && st.str[0] == 'L' {
right = st.exprPrimary()
} else {
right = st.unresolvedName()
if len(st.str) > 0 && st.str[0] == 'I' {
right = st.template(right)
}
}
} 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' {
n = st.template(n)
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)
n = st.template(n)
}
if s == nil {
s = n
} else {
s = &Qualified{Scope: s, Name: n, LocalName: false}
}
}
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' {
n = st.template(n)
}
return n
}
// requiresExpr parses:
//
// <expression> ::= rQ <bare-function-type> _ <requirement>+ E
// ::= rq <requirement>+ E
// <requirement> ::= X <expression> [N] [R <type-constraint>]
// ::= T <type>
// ::= Q <constraint-expression>
func (st *state) requiresExpr() AST {
st.checkChar('r')
if len(st.str) == 0 || (st.str[0] != 'q' && st.str[0] != 'Q') {
st.fail("expected q or Q in requires clause in expression")
}
kind := st.str[0]
st.advance(1)
var params []AST
if kind == 'Q' {
for len(st.str) > 0 && st.str[0] != '_' {
typ := st.demangleType(false)
params = append(params, typ)
}
if len(st.str) == 0 {
st.fail("expected requirement parameter")
}
st.advance(1)
}
var requirements []AST
for len(st.str) > 0 && st.str[0] != 'E' {
var req AST
switch st.str[0] {
case 'X':
st.advance(1)
expr := st.expression()
var noexcept bool
if len(st.str) > 0 && st.str[0] == 'N' {
st.advance(1)
noexcept = true
}
var typeReq AST
if len(st.str) > 0 && st.str[0] == 'R' {
st.advance(1)
typeReq, _ = st.name()
}
req = &ExprRequirement{
Expr: expr,
Noexcept: noexcept,
TypeReq: typeReq,
}
case 'T':
st.advance(1)
typ := st.demangleType(false)
req = &TypeRequirement{Type: typ}
case 'Q':
st.advance(1)
// We parse a regular expression rather than a
// constraint expression.
expr := st.expression()
req = &NestedRequirement{Constraint: expr}
default:
st.fail("unrecognized requirement code")
}
requirements = append(requirements, req)
}
if len(st.str) == 0 || st.str[0] != 'E' {
st.fail("expected E after requirements")
}
st.advance(1)
return &RequiresExpr{
Params: params,
Requirements: requirements,
}
}
// 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)
}
var templateArgsConstraint AST
if len(st.str) > 0 && st.str[0] == 'Q' {
templateArgsConstraint = st.constraintExpr()
}
types := st.parmlist(false)
st.lambdaTemplateLevel = oldLambdaTemplateLevel
if template != nil {
st.templates = st.templates[:len(st.templates)-1]
}
var callConstraint AST
if len(st.str) > 0 && st.str[0] == 'Q' {
callConstraint = st.constraintExpr()
}
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,
TemplateArgsConstraint: templateArgsConstraint,
Types: types,
Num: num,
CallConstraint: callConstraint,
}
}
// templateParamDecl parses:
//
// <template-param-decl> ::= Ty # type parameter
// ::= Tk <concept name> [<template-args>] # constrained 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 'k':
// We don't track enclosing template parameter levels.
// Don't try to demangle template parameter substitutions
// in constraints.
hold := st.parsingConstraint
st.parsingConstraint = true
defer func() { st.parsingConstraint = hold }()
st.advance(2)
constraint, _ := st.name()
name := mk("$T", &st.typeTemplateParamCount)
tp := &ConstrainedTypeTemplateParam{
Name: name,
Constraint: constraint,
}
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
var constraint AST
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 len(st.str) > 0 && st.str[0] == 'Q' {
// A list of template template
// parameters can have a constraint.
constraint = st.constraintExpr()
if len(st.str) == 0 || st.str[0] != 'E' {
st.fail("expected end of template template parameters after constraint")
}
}
}
if template != nil {
st.templates = st.templates[:len(st.templates)-1]
}
tp := &TemplateTemplateParam{
Name: name,
Params: params,
Constraint: constraint,
}
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
}
// constraintExpr parses a constraint expression. This is just a
// regular expression, but template parameters are handled specially.
func (st *state) constraintExpr() AST {
st.checkChar('Q')
hold := st.parsingConstraint
st.parsingConstraint = true
defer func() { st.parsingConstraint = hold }()
return st.expression()
}
// 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
case *PackExpansion:
// Find the possibly-new argument pack.
pack := st.findArgumentPack(a.Base)
if pack != a.Pack {
return &PackExpansion{
Base: a.Base,
Pack: pack,
}
}
return nil
default:
return nil
}
if st.parsingConstraint {
// We don't try to substitute template
// parameters in a constraint expression.
return &Name{Name: fmt.Sprintf("T%d", index)}
}
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}
}
seen := make(map[AST]bool)
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
}
if seen[a] {
return true
}
seen[a] = true
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 {
seen := make(map[AST]bool)
skip := func(a AST) bool {
if seen[a] {
return true
}
seen[a] = true
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
}
seen := make(map[AST]bool)
skip := func(sub AST) bool {
// Don't traverse into another
// pack expansion.
if _, ok := sub.(*PackExpansion); ok {
return true
}
if seen[sub] {
return true
}
seen[sub] = true
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 {
seen := make(map[AST]bool)
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
}
if seen[a] {
return false
}
seen[a] = true
return true
})
return ret
}
// 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
}