// 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 types

import (
	
	
	. 
	
)

// ----------------------------------------------------------------------------
// API

// A Struct represents a struct type.
type Struct struct {
	fields []*Var   // fields != nil indicates the struct is set up (possibly with len(fields) == 0)
	tags   []string // field tags; nil if there are no tags
}

// NewStruct returns a new struct with the given fields and corresponding field tags.
// If a field with index i has a tag, tags[i] must be that tag, but len(tags) may be
// only as long as required to hold the tag with the largest index i. Consequently,
// if no field has a tag, tags may be nil.
func ( []*Var,  []string) *Struct {
	var  objset
	for ,  := range  {
		if .name != "_" && .insert() != nil {
			panic("multiple fields with the same name")
		}
	}
	if len() > len() {
		panic("more tags than fields")
	}
	 := &Struct{fields: , tags: }
	.markComplete()
	return 
}

// NumFields returns the number of fields in the struct (including blank and embedded fields).
func ( *Struct) () int { return len(.fields) }

// Field returns the i'th field for 0 <= i < NumFields().
func ( *Struct) ( int) *Var { return .fields[] }

// Tag returns the i'th field tag for 0 <= i < NumFields().
func ( *Struct) ( int) string {
	if  < len(.tags) {
		return .tags[]
	}
	return ""
}

func ( *Struct) () Type { return  }
func ( *Struct) () string   { return TypeString(, nil) }

// ----------------------------------------------------------------------------
// Implementation

func ( *Struct) () {
	if .fields == nil {
		.fields = make([]*Var, 0)
	}
}

func ( *Checker) ( *Struct,  *ast.StructType) {
	 := .Fields
	if  == nil {
		.markComplete()
		return
	}

	// struct fields and tags
	var  []*Var
	var  []string

	// for double-declaration checks
	var  objset

	// current field typ and tag
	var  Type
	var  string
	 := func( *ast.Ident,  bool) {
		if  != "" &&  == nil {
			 = make([]string, len())
		}
		if  != nil {
			 = append(, )
		}

		 := .Pos()
		 := .Name
		 := NewField(, .pkg, , , )
		// spec: "Within a struct, non-blank field names must be unique."
		if  == "_" || .declareInSet(&, , ) {
			 = append(, )
			.recordDef(, )
		}
	}

	// addInvalid adds an embedded field of invalid type to the struct for
	// fields with errors; this keeps the number of struct fields in sync
	// with the source as long as the fields are _ or have different names
	// (go.dev/issue/25627).
	 := func( *ast.Ident) {
		 = Typ[Invalid]
		 = ""
		(, true)
	}

	for ,  := range .List {
		 = .varType(.Type)
		 = .tag(.Tag)
		if len(.Names) > 0 {
			// named fields
			for ,  := range .Names {
				(, false)
			}
		} else {
			// embedded field
			// spec: "An embedded type must be specified as a type name T or as a
			// pointer to a non-interface type name *T, and T itself may not be a
			// pointer type."
			 := .Type.Pos() // position of type, for errors
			 := embeddedFieldIdent(.Type)
			if  == nil {
				.errorf(.Type, InvalidSyntaxTree, "embedded field type %s has no name", .Type)
				 = ast.NewIdent("_")
				.NamePos = 
				()
				continue
			}
			(, true) // struct{p.T} field has position of T

			// Because we have a name, typ must be of the form T or *T, where T is the name
			// of a (named or alias) type, and t (= deref(typ)) must be the type of T.
			// We must delay this check to the end because we don't want to instantiate
			// (via under(t)) a possibly incomplete type.

			// for use in the closure below
			 := 
			 := .Type

			.later(func() {
				,  := deref()
				switch u := under().(type) {
				case *Basic:
					if !isValid() {
						// error was reported before
						return
					}
					// unsafe.Pointer is treated like a regular pointer
					if .kind == UnsafePointer {
						.error(, InvalidPtrEmbed, "embedded field type cannot be unsafe.Pointer")
					}
				case *Pointer:
					.error(, InvalidPtrEmbed, "embedded field type cannot be a pointer")
				case *Interface:
					if isTypeParam() {
						// The error code here is inconsistent with other error codes for
						// invalid embedding, because this restriction may be relaxed in the
						// future, and so it did not warrant a new error code.
						.error(, MisplacedTypeParam, "embedded field type cannot be a (pointer to a) type parameter")
						break
					}
					if  {
						.error(, InvalidPtrEmbed, "embedded field type cannot be a pointer to an interface")
					}
				}
			}).describef(, "check embedded type %s", )
		}
	}

	.fields = 
	.tags = 
	.markComplete()
}

func embeddedFieldIdent( ast.Expr) *ast.Ident {
	switch e := .(type) {
	case *ast.Ident:
		return 
	case *ast.StarExpr:
		// *T is valid, but **T is not
		if ,  := .X.(*ast.StarExpr); ! {
			return (.X)
		}
	case *ast.SelectorExpr:
		return .Sel
	case *ast.IndexExpr:
		return (.X)
	case *ast.IndexListExpr:
		return (.X)
	}
	return nil // invalid embedded field
}

func ( *Checker) ( *objset,  token.Pos,  Object) bool {
	if  := .insert();  != nil {
		 := .newError(DuplicateDecl)
		.addf(atPos(), "%s redeclared", .Name())
		.addAltDecl()
		.report()
		return false
	}
	return true
}

func ( *Checker) ( *ast.BasicLit) string {
	if  != nil {
		if .Kind == token.STRING {
			if ,  := strconv.Unquote(.Value);  == nil {
				return 
			}
		}
		.errorf(, InvalidSyntaxTree, "incorrect tag syntax: %q", .Value)
	}
	return ""
}