// Copyright 2013 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.

// This file implements printing of types.

package types

import (
	
	
)

// A Qualifier controls how named package-level objects are printed in
// calls to TypeString, ObjectString, and SelectionString.
//
// These three formatting routines call the Qualifier for each
// package-level object O, and if the Qualifier returns a non-empty
// string p, the object is printed in the form p.O.
// If it returns an empty string, only the object name O is printed.
//
// Using a nil Qualifier is equivalent to using (*Package).Path: the
// object is qualified by the import path, e.g., "encoding/json.Marshal".
//
type Qualifier func(*Package) string

// RelativeTo returns a Qualifier that fully qualifies members of
// all packages other than pkg.
func ( *Package) Qualifier {
	if  == nil {
		return nil
	}
	return func( *Package) string {
		if  ==  {
			return "" // same package; unqualified
		}
		return .Path()
	}
}

// If gcCompatibilityMode is set, printing of types is modified
// to match the representation of some types in the gc compiler:
//
//	- byte and rune lose their alias name and simply stand for
//	  uint8 and int32 respectively
//	- embedded interfaces get flattened (the embedding info is lost,
//	  and certain recursive interface types cannot be printed anymore)
//
// This makes it easier to compare packages computed with the type-
// checker vs packages imported from gc export data.
//
// Caution: This flag affects all uses of WriteType, globally.
// It is only provided for testing in conjunction with
// gc-generated data.
//
// This flag is exported in the x/tools/go/types package. We don't
// need it at the moment in the std repo and so we don't export it
// anymore. We should eventually try to remove it altogether.
// TODO(gri) remove this
var gcCompatibilityMode bool

// TypeString returns the string representation of typ.
// The Qualifier controls the printing of
// package-level objects, and may be nil.
func ( Type,  Qualifier) string {
	var  bytes.Buffer
	WriteType(&, , )
	return .String()
}

// WriteType writes the string representation of typ to buf.
// The Qualifier controls the printing of
// package-level objects, and may be nil.
func ( *bytes.Buffer,  Type,  Qualifier) {
	writeType(, , , make([]Type, 0, 8))
}

func writeType( *bytes.Buffer,  Type,  Qualifier,  []Type) {
	// Theoretically, this is a quadratic lookup algorithm, but in
	// practice deeply nested composite types with unnamed component
	// types are uncommon. This code is likely more efficient than
	// using a map.
	for ,  := range  {
		if  ==  {
			fmt.Fprintf(, "○%T", ) // cycle to typ
			return
		}
	}
	 = append(, )

	switch t := .(type) {
	case nil:
		.WriteString("<nil>")

	case *Basic:
		if .kind == UnsafePointer {
			.WriteString("unsafe.")
		}
		if gcCompatibilityMode {
			// forget the alias names
			switch .kind {
			case Byte:
				 = Typ[Uint8]
			case Rune:
				 = Typ[Int32]
			}
		}
		.WriteString(.name)

	case *Array:
		fmt.Fprintf(, "[%d]", .len)
		(, .elem, , )

	case *Slice:
		.WriteString("[]")
		(, .elem, , )

	case *Struct:
		.WriteString("struct{")
		for ,  := range .fields {
			if  > 0 {
				.WriteString("; ")
			}
			if !.embedded {
				.WriteString(.name)
				.WriteByte(' ')
			}
			(, .typ, , )
			if  := .Tag();  != "" {
				fmt.Fprintf(, " %q", )
			}
		}
		.WriteByte('}')

	case *Pointer:
		.WriteByte('*')
		(, .base, , )

	case *Tuple:
		writeTuple(, , false, , )

	case *Signature:
		.WriteString("func")
		writeSignature(, , , )

	case *Interface:
		// We write the source-level methods and embedded types rather
		// than the actual method set since resolved method signatures
		// may have non-printable cycles if parameters have embedded
		// interface types that (directly or indirectly) embed the
		// current interface. For instance, consider the result type
		// of m:
		//
		//     type T interface{
		//         m() interface{ T }
		//     }
		//
		.WriteString("interface{")
		 := true
		if gcCompatibilityMode {
			// print flattened interface
			// (useful to compare against gc-generated interfaces)
			for ,  := range .allMethods {
				if  > 0 {
					.WriteString("; ")
				}
				.WriteString(.name)
				writeSignature(, .typ.(*Signature), , )
				 = false
			}
		} else {
			// print explicit interface methods and embedded types
			for ,  := range .methods {
				if  > 0 {
					.WriteString("; ")
				}
				.WriteString(.name)
				writeSignature(, .typ.(*Signature), , )
				 = false
			}
			for ,  := range .embeddeds {
				if  > 0 || len(.methods) > 0 {
					.WriteString("; ")
				}
				(, , , )
				 = false
			}
		}
		if .allMethods == nil || len(.methods) > len(.allMethods) {
			if ! {
				.WriteByte(' ')
			}
			.WriteString("/* incomplete */")
		}
		.WriteByte('}')

	case *Map:
		.WriteString("map[")
		(, .key, , )
		.WriteByte(']')
		(, .elem, , )

	case *Chan:
		var  string
		var  bool
		switch .dir {
		case SendRecv:
			 = "chan "
			// chan (<-chan T) requires parentheses
			if ,  := .elem.(*Chan);  != nil && .dir == RecvOnly {
				 = true
			}
		case SendOnly:
			 = "chan<- "
		case RecvOnly:
			 = "<-chan "
		default:
			panic("unreachable")
		}
		.WriteString()
		if  {
			.WriteByte('(')
		}
		(, .elem, , )
		if  {
			.WriteByte(')')
		}

	case *Named:
		 := "<Named w/o object>"
		if  := .obj;  != nil {
			if .pkg != nil {
				writePackage(, .pkg, )
			}
			// TODO(gri): function-local named types should be displayed
			// differently from named types at package level to avoid
			// ambiguity.
			 = .name
		}
		.WriteString()

	default:
		// For externally defined implementations of Type.
		.WriteString(.String())
	}
}

func writeTuple( *bytes.Buffer,  *Tuple,  bool,  Qualifier,  []Type) {
	.WriteByte('(')
	if  != nil {
		for ,  := range .vars {
			if  > 0 {
				.WriteString(", ")
			}
			if .name != "" {
				.WriteString(.name)
				.WriteByte(' ')
			}
			 := .typ
			if  &&  == len(.vars)-1 {
				if ,  := .(*Slice);  {
					.WriteString("...")
					 = .elem
				} else {
					// special case:
					// append(s, "foo"...) leads to signature func([]byte, string...)
					if ,  := .Underlying().(*Basic); ! || .kind != String {
						panic("internal error: string type expected")
					}
					writeType(, , , )
					.WriteString("...")
					continue
				}
			}
			writeType(, , , )
		}
	}
	.WriteByte(')')
}

// WriteSignature writes the representation of the signature sig to buf,
// without a leading "func" keyword.
// The Qualifier controls the printing of
// package-level objects, and may be nil.
func ( *bytes.Buffer,  *Signature,  Qualifier) {
	writeSignature(, , , make([]Type, 0, 8))
}

func writeSignature( *bytes.Buffer,  *Signature,  Qualifier,  []Type) {
	writeTuple(, .params, .variadic, , )

	 := .results.Len()
	if  == 0 {
		// no result
		return
	}

	.WriteByte(' ')
	if  == 1 && .results.vars[0].name == "" {
		// single unnamed result
		writeType(, .results.vars[0].typ, , )
		return
	}

	// multiple or named result(s)
	writeTuple(, .results, false, , )
}