// Copyright 2024 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 typechecking of literals.

package types2

import (
	"cmd/compile/internal/syntax"
	. "internal/types/errors"
	"strings"
)

// langCompat reports an error if the representation of a numeric
// literal is not compatible with the current language version.
func (check *Checker) langCompat(lit *syntax.BasicLit) {
	s := lit.Value
	if len(s) <= 2 || check.allowVersion(go1_13) {
		return
	}
	// len(s) > 2
	if strings.Contains(s, "_") {
		check.versionErrorf(lit, go1_13, "underscore in numeric literal")
		return
	}
	if s[0] != '0' {
		return
	}
	radix := s[1]
	if radix == 'b' || radix == 'B' {
		check.versionErrorf(lit, go1_13, "binary literal")
		return
	}
	if radix == 'o' || radix == 'O' {
		check.versionErrorf(lit, go1_13, "0o/0O-style octal literal")
		return
	}
	if lit.Kind != syntax.IntLit && (radix == 'x' || radix == 'X') {
		check.versionErrorf(lit, go1_13, "hexadecimal floating-point literal")
	}
}

func (check *Checker) basicLit(x *operand, e *syntax.BasicLit) {
	switch e.Kind {
	case syntax.IntLit, syntax.FloatLit, syntax.ImagLit:
		check.langCompat(e)
		// The max. mantissa precision for untyped numeric values
		// is 512 bits, or 4048 bits for each of the two integer
		// parts of a fraction for floating-point numbers that are
		// represented accurately in the go/constant package.
		// Constant literals that are longer than this many bits
		// are not meaningful; and excessively long constants may
		// consume a lot of space and time for a useless conversion.
		// Cap constant length with a generous upper limit that also
		// allows for separators between all digits.
		const limit = 10000
		if len(e.Value) > limit {
			check.errorf(e, InvalidConstVal, "excessively long constant: %s... (%d chars)", e.Value[:10], len(e.Value))
			x.mode = invalid
			return
		}
	}
	x.setConst(e.Kind, e.Value)
	if x.mode == invalid {
		// The parser already establishes syntactic correctness.
		// If we reach here it's because of number under-/overflow.
		// TODO(gri) setConst (and in turn the go/constant package)
		// should return an error describing the issue.
		check.errorf(e, InvalidConstVal, "malformed constant: %s", e.Value)
		x.mode = invalid
		return
	}
	// Ensure that integer values don't overflow (go.dev/issue/54280).
	x.expr = e // make sure that check.overflow below has an error position
	check.overflow(x, opPos(x.expr))
}

func (check *Checker) funcLit(x *operand, e *syntax.FuncLit) {
	if sig, ok := check.typ(e.Type).(*Signature); ok {
		// Set the Scope's extent to the complete "func (...) {...}"
		// so that Scope.Innermost works correctly.
		sig.scope.pos = e.Pos()
		sig.scope.end = endPos(e)
		if !check.conf.IgnoreFuncBodies && e.Body != nil {
			// Anonymous functions are considered part of the
			// init expression/func declaration which contains
			// them: use existing package-level declaration info.
			decl := check.decl // capture for use in closure below
			iota := check.iota // capture for use in closure below (go.dev/issue/22345)
			// Don't type-check right away because the function may
			// be part of a type definition to which the function
			// body refers. Instead, type-check as soon as possible,
			// but before the enclosing scope contents changes (go.dev/issue/22992).
			check.later(func() {
				check.funcBody(decl, "<function literal>", sig, e.Body, iota)
			}).describef(e, "func literal")
		}
		x.mode = value
		x.typ = sig
	} else {
		check.errorf(e, InvalidSyntaxTree, "invalid function literal %v", e)
		x.mode = invalid
	}
}

func (check *Checker) compositeLit(x *operand, e *syntax.CompositeLit, hint Type) {
	var typ, base Type
	var isElem bool // true if composite literal is an element of an enclosing composite literal

	switch {
	case e.Type != nil:
		// composite literal type present - use it
		// [...]T array types may only appear with composite literals.
		// Check for them here so we don't have to handle ... in general.
		if atyp, _ := e.Type.(*syntax.ArrayType); atyp != nil && isdddArray(atyp) {
			// We have an "open" [...]T array type.
			// Create a new ArrayType with unknown length (-1)
			// and finish setting it up after analyzing the literal.
			typ = &Array{len: -1, elem: check.varType(atyp.Elem)}
			base = typ
			break
		}
		typ = check.typ(e.Type)
		base = typ

	case hint != nil:
		// no composite literal type present - use hint (element type of enclosing type)
		typ = hint
		base = typ
		// *T implies &T{}
		u, _ := commonUnder(base, nil)
		if b, ok := deref(u); ok {
			base = b
		}
		isElem = true

	default:
		// TODO(gri) provide better error messages depending on context
		check.error(e, UntypedLit, "missing type in composite literal")
		// continue with invalid type so that elements are "used" (go.dev/issue/69092)
		typ = Typ[Invalid]
		base = typ
	}

	switch u, _ := commonUnder(base, nil); utyp := u.(type) {
	case *Struct:
		// Prevent crash if the struct referred to is not yet set up.
		// See analogous comment for *Array.
		if utyp.fields == nil {
			check.error(e, InvalidTypeCycle, "invalid recursive type")
			x.mode = invalid
			return
		}
		if len(e.ElemList) == 0 {
			break
		}
		// Convention for error messages on invalid struct literals:
		// we mention the struct type only if it clarifies the error
		// (e.g., a duplicate field error doesn't need the struct type).
		fields := utyp.fields
		if _, ok := e.ElemList[0].(*syntax.KeyValueExpr); ok {
			// all elements must have keys
			visited := make([]bool, len(fields))
			for _, e := range e.ElemList {
				kv, _ := e.(*syntax.KeyValueExpr)
				if kv == nil {
					check.error(e, MixedStructLit, "mixture of field:value and value elements in struct literal")
					continue
				}
				key, _ := kv.Key.(*syntax.Name)
				// do all possible checks early (before exiting due to errors)
				// so we don't drop information on the floor
				check.expr(nil, x, kv.Value)
				if key == nil {
					check.errorf(kv, InvalidLitField, "invalid field name %s in struct literal", kv.Key)
					continue
				}
				i := fieldIndex(fields, check.pkg, key.Value, false)
				if i < 0 {
					var alt Object
					if j := fieldIndex(fields, check.pkg, key.Value, true); j >= 0 {
						alt = fields[j]
					}
					msg := check.lookupError(base, key.Value, alt, true)
					check.error(kv.Key, MissingLitField, msg)
					continue
				}
				fld := fields[i]
				check.recordUse(key, fld)
				etyp := fld.typ
				check.assignment(x, etyp, "struct literal")
				// 0 <= i < len(fields)
				if visited[i] {
					check.errorf(kv, DuplicateLitField, "duplicate field name %s in struct literal", key.Value)
					continue
				}
				visited[i] = true
			}
		} else {
			// no element must have a key
			for i, e := range e.ElemList {
				if kv, _ := e.(*syntax.KeyValueExpr); kv != nil {
					check.error(kv, MixedStructLit, "mixture of field:value and value elements in struct literal")
					continue
				}
				check.expr(nil, x, e)
				if i >= len(fields) {
					check.errorf(x, InvalidStructLit, "too many values in struct literal of type %s", base)
					break // cannot continue
				}
				// i < len(fields)
				fld := fields[i]
				if !fld.Exported() && fld.pkg != check.pkg {
					check.errorf(x, UnexportedLitField, "implicit assignment to unexported field %s in struct literal of type %s", fld.name, base)
					continue
				}
				etyp := fld.typ
				check.assignment(x, etyp, "struct literal")
			}
			if len(e.ElemList) < len(fields) {
				check.errorf(inNode(e, e.Rbrace), InvalidStructLit, "too few values in struct literal of type %s", base)
				// ok to continue
			}
		}

	case *Array:
		// Prevent crash if the array referred to is not yet set up. Was go.dev/issue/18643.
		// This is a stop-gap solution. Should use Checker.objPath to report entire
		// path starting with earliest declaration in the source. TODO(gri) fix this.
		if utyp.elem == nil {
			check.error(e, InvalidTypeCycle, "invalid recursive type")
			x.mode = invalid
			return
		}
		n := check.indexedElts(e.ElemList, utyp.elem, utyp.len)
		// If we have an array of unknown length (usually [...]T arrays, but also
		// arrays [n]T where n is invalid) set the length now that we know it and
		// record the type for the array (usually done by check.typ which is not
		// called for [...]T). We handle [...]T arrays and arrays with invalid
		// length the same here because it makes sense to "guess" the length for
		// the latter if we have a composite literal; e.g. for [n]int{1, 2, 3}
		// where n is invalid for some reason, it seems fair to assume it should
		// be 3 (see also Checked.arrayLength and go.dev/issue/27346).
		if utyp.len < 0 {
			utyp.len = n
			// e.Type is missing if we have a composite literal element
			// that is itself a composite literal with omitted type. In
			// that case there is nothing to record (there is no type in
			// the source at that point).
			if e.Type != nil {
				check.recordTypeAndValue(e.Type, typexpr, utyp, nil)
			}
		}

	case *Slice:
		// Prevent crash if the slice referred to is not yet set up.
		// See analogous comment for *Array.
		if utyp.elem == nil {
			check.error(e, InvalidTypeCycle, "invalid recursive type")
			x.mode = invalid
			return
		}
		check.indexedElts(e.ElemList, utyp.elem, -1)

	case *Map:
		// Prevent crash if the map referred to is not yet set up.
		// See analogous comment for *Array.
		if utyp.key == nil || utyp.elem == nil {
			check.error(e, InvalidTypeCycle, "invalid recursive type")
			x.mode = invalid
			return
		}
		// If the map key type is an interface (but not a type parameter),
		// the type of a constant key must be considered when checking for
		// duplicates.
		keyIsInterface := isNonTypeParamInterface(utyp.key)
		visited := make(map[any][]Type, len(e.ElemList))
		for _, e := range e.ElemList {
			kv, _ := e.(*syntax.KeyValueExpr)
			if kv == nil {
				check.error(e, MissingLitKey, "missing key in map literal")
				continue
			}
			check.exprWithHint(x, kv.Key, utyp.key)
			check.assignment(x, utyp.key, "map literal")
			if x.mode == invalid {
				continue
			}
			if x.mode == constant_ {
				duplicate := false
				xkey := keyVal(x.val)
				if keyIsInterface {
					for _, vtyp := range visited[xkey] {
						if Identical(vtyp, x.typ) {
							duplicate = true
							break
						}
					}
					visited[xkey] = append(visited[xkey], x.typ)
				} else {
					_, duplicate = visited[xkey]
					visited[xkey] = nil
				}
				if duplicate {
					check.errorf(x, DuplicateLitKey, "duplicate key %s in map literal", x.val)
					continue
				}
			}
			check.exprWithHint(x, kv.Value, utyp.elem)
			check.assignment(x, utyp.elem, "map literal")
		}

	default:
		// when "using" all elements unpack KeyValueExpr
		// explicitly because check.use doesn't accept them
		for _, e := range e.ElemList {
			if kv, _ := e.(*syntax.KeyValueExpr); kv != nil {
				// Ideally, we should also "use" kv.Key but we can't know
				// if it's an externally defined struct key or not. Going
				// forward anyway can lead to other errors. Give up instead.
				e = kv.Value
			}
			check.use(e)
		}
		// if utyp is invalid, an error was reported before
		if isValid(utyp) {
			var qualifier string
			if isElem {
				qualifier = " element"
			}
			var cause string
			if utyp == nil {
				cause = " (no common underlying type)"
			}
			check.errorf(e, InvalidLit, "invalid composite literal%s type %s%s", qualifier, typ, cause)
			x.mode = invalid
			return
		}
	}

	x.mode = value
	x.typ = typ
}

// indexedElts checks the elements (elts) of an array or slice composite literal
// against the literal's element type (typ), and the element indices against
// the literal length if known (length >= 0). It returns the length of the
// literal (maximum index value + 1).
func (check *Checker) indexedElts(elts []syntax.Expr, typ Type, length int64) int64 {
	visited := make(map[int64]bool, len(elts))
	var index, max int64
	for _, e := range elts {
		// determine and check index
		validIndex := false
		eval := e
		if kv, _ := e.(*syntax.KeyValueExpr); kv != nil {
			if typ, i := check.index(kv.Key, length); isValid(typ) {
				if i >= 0 {
					index = i
					validIndex = true
				} else {
					check.errorf(e, InvalidLitIndex, "index %s must be integer constant", kv.Key)
				}
			}
			eval = kv.Value
		} else if length >= 0 && index >= length {
			check.errorf(e, OversizeArrayLit, "index %d is out of bounds (>= %d)", index, length)
		} else {
			validIndex = true
		}

		// if we have a valid index, check for duplicate entries
		if validIndex {
			if visited[index] {
				check.errorf(e, DuplicateLitKey, "duplicate index %d in array or slice literal", index)
			}
			visited[index] = true
		}
		index++
		if index > max {
			max = index
		}

		// check element against composite literal element type
		var x operand
		check.exprWithHint(&x, eval, typ)
		check.assignment(&x, typ, "array or slice literal")
	}
	return max
}
