package types
import (
)
func ( *Checker) ( *declInfo, string, *Signature, *ast.BlockStmt, constant.Value) {
if .conf.IgnoreFuncBodies {
panic("function body not ignored")
}
if trace {
.trace(.Pos(), "--- %s: %s", , )
defer func() {
.trace(.End(), "--- <end>")
}()
}
.scope.pos = .Pos()
.scope.end = .End()
defer func( environment, int) {
.environment =
.indent =
}(.environment, .indent)
.environment = environment{
decl: ,
scope: .scope,
iota: ,
sig: ,
}
.indent = 0
.stmtList(0, .List)
if .hasLabel {
.labels()
}
if .results.Len() > 0 && !.isTerminating(, "") {
.error(atPos(.Rbrace), _MissingReturn, "missing return")
}
.usage(.scope)
}
func ( *Checker) ( *Scope) {
var []*Var
for , := range .elems {
= resolve(, )
if , := .(*Var); != nil && !.used {
= append(, )
}
}
sort.Slice(, func(, int) bool {
return [].pos < [].pos
})
for , := range {
.softErrorf(, _UnusedVar, "%s declared but not used", .name)
}
for , := range .children {
if !.isFunc {
.()
}
}
}
type stmtContext uint
const (
breakOk stmtContext = 1 << iota
continueOk
fallthroughOk
finalSwitchCase
)
func ( *Checker) ( ast.Stmt) {
if != nil {
.stmt(0, )
}
}
func ( []ast.Stmt) []ast.Stmt {
for := len(); > 0; -- {
if , := [-1].(*ast.EmptyStmt); ! {
return [:]
}
}
return nil
}
func ( *Checker) ( stmtContext, []ast.Stmt) {
:= &fallthroughOk != 0
:= &^ fallthroughOk
= trimTrailingEmptyStmts()
for , := range {
:=
if && +1 == len() {
|= fallthroughOk
}
.stmt(, )
}
}
func ( *Checker) ( []ast.Stmt) {
var ast.Stmt
for , := range {
var ast.Stmt
switch c := .(type) {
case *ast.CaseClause:
if len(.List) == 0 {
=
}
case *ast.CommClause:
if .Comm == nil {
=
}
default:
.invalidAST(, "case/communication clause expected")
}
if != nil {
if != nil {
.errorf(, _DuplicateDefault, "multiple defaults (first at %s)", .fset.Position(.Pos()))
} else {
=
}
}
}
}
func ( *Checker) ( ast.Node, string) {
:= NewScope(.scope, .Pos(), .End(), )
.recordScope(, )
.scope =
}
func ( *Checker) () {
.scope = .scope.Parent()
}
func ( token.Token) token.Token {
if token.ADD_ASSIGN <= && <= token.AND_NOT_ASSIGN {
return + (token.ADD - token.ADD_ASSIGN)
}
return token.ILLEGAL
}
func ( *Checker) ( string, *ast.CallExpr) {
var operand
var string
var errorCode
switch .rawExpr(&, , nil, false) {
case conversion:
= "requires function call, not conversion"
= _InvalidDefer
if == "go" {
= _InvalidGo
}
case expression:
= "discards result of"
= _UnusedResults
case statement:
return
default:
unreachable()
}
.errorf(&, , "%s %s %s", , , &)
}
func ( constant.Value) any {
if == nil {
return nil
}
switch .Kind() {
case constant.Int:
if , := constant.Int64Val(); {
return
}
if , := constant.Uint64Val(); {
return
}
case constant.Float:
if , := constant.Float64Val(); {
return
}
case constant.String:
return constant.StringVal()
}
return nil
}
type (
valueMap map[any][]valueType
valueType struct {
pos token.Pos
typ Type
}
)
func ( *Checker) ( *operand, []ast.Expr, valueMap) {
:
for , := range {
var operand
.expr(&, )
if .mode == invalid || .mode == invalid {
continue
}
.convertUntyped(&, .typ)
if .mode == invalid {
continue
}
:=
.comparison(&, , token.EQL, true)
if .mode == invalid {
continue
}
if .mode != constant_ {
continue
}
if := goVal(.val); != nil {
for , := range [] {
if Identical(.typ, .typ) {
.errorf(&, _DuplicateCase, "duplicate case %s in expression switch", &)
.error(atPos(.pos), _DuplicateCase, "\tprevious case")
continue
}
}
[] = append([], valueType{.Pos(), .typ})
}
}
}
func ( *Checker) ( ast.Expr) bool {
if , := unparen().(*ast.Ident); != nil {
, := .lookup(.Name).(*Nil)
return
}
return false
}
func ( *Checker) ( *operand, []ast.Expr, map[Type]ast.Expr) ( Type) {
var operand
:
for , := range {
if .isNil() {
= nil
.expr(&, )
} else {
= .varType()
if == Typ[Invalid] {
continue
}
}
for , := range {
if == nil && == nil || != nil && != nil && Identical(, ) {
:= "nil"
if != nil {
= TypeString(, .qualifier)
}
.errorf(, _DuplicateCase, "duplicate case %s in type switch", )
.error(, _DuplicateCase, "\tprevious case")
continue
}
}
[] =
if != nil && != nil {
.typeAssertion(, , , true)
}
}
return
}
func ( *Checker) ( stmtContext, ast.Stmt) {
if debug {
defer func( *Scope) {
if := recover(); != nil {
panic()
}
assert( == .scope)
}(.scope)
}
defer .processDelayed(len(.delayed))
:= &^ (fallthroughOk | finalSwitchCase)
switch s := .(type) {
case *ast.BadStmt, *ast.EmptyStmt:
case *ast.DeclStmt:
.declStmt(.Decl)
case *ast.LabeledStmt:
.hasLabel = true
.(, .Stmt)
case *ast.ExprStmt:
var operand
:= .rawExpr(&, .X, nil, false)
var string
var errorCode
switch .mode {
default:
if == statement {
return
}
= "is not used"
= _UnusedExpr
case builtin:
= "must be called"
= _UncalledBuiltin
case typexpr:
= "is not an expression"
= _NotAnExpr
}
.errorf(&, , "%s %s", &, )
case *ast.SendStmt:
var , operand
.expr(&, .Chan)
.expr(&, .Value)
if .mode == invalid || .mode == invalid {
return
}
:= coreType(.typ)
if == nil {
.invalidOp(inNode(, .Arrow), _InvalidSend, "cannot send to %s: no core type", &)
return
}
, := .(*Chan)
if == nil {
.invalidOp(inNode(, .Arrow), _InvalidSend, "cannot send to non-channel %s", &)
return
}
if .dir == RecvOnly {
.invalidOp(inNode(, .Arrow), _InvalidSend, "cannot send to receive-only channel %s", &)
return
}
.assignment(&, .elem, "send")
case *ast.IncDecStmt:
var token.Token
switch .Tok {
case token.INC:
= token.ADD
case token.DEC:
= token.SUB
default:
.invalidAST(inNode(, .TokPos), "unknown inc/dec operation %s", .Tok)
return
}
var operand
.expr(&, .X)
if .mode == invalid {
return
}
if !allNumeric(.typ) {
.invalidOp(.X, _NonNumericIncDec, "%s%s (non-numeric type %s)", .X, .Tok, .typ)
return
}
:= &ast.BasicLit{ValuePos: .X.Pos(), Kind: token.INT, Value: "1"}
.binary(&, nil, .X, , , .TokPos)
if .mode == invalid {
return
}
.assignVar(.X, &)
case *ast.AssignStmt:
switch .Tok {
case token.ASSIGN, token.DEFINE:
if len(.Lhs) == 0 {
.invalidAST(, "missing lhs in assignment")
return
}
if .Tok == token.DEFINE {
.shortVarDecl(inNode(, .TokPos), .Lhs, .Rhs)
} else {
.assignVars(.Lhs, .Rhs)
}
default:
if len(.Lhs) != 1 || len(.Rhs) != 1 {
.errorf(inNode(, .TokPos), _MultiValAssignOp, "assignment operation %s requires single-valued expressions", .Tok)
return
}
:= assignOp(.Tok)
if == token.ILLEGAL {
.invalidAST(atPos(.TokPos), "unknown assignment operation %s", .Tok)
return
}
var operand
.binary(&, nil, .Lhs[0], .Rhs[0], , .TokPos)
if .mode == invalid {
return
}
.assignVar(.Lhs[0], &)
}
case *ast.GoStmt:
.suspendedCall("go", .Call)
case *ast.DeferStmt:
.suspendedCall("defer", .Call)
case *ast.ReturnStmt:
:= .sig.results
if len(.Results) == 0 && .Len() > 0 && .vars[0].name != "" {
for , := range .vars {
if := .lookup(.name); != nil && != {
.errorf(, _OutOfScopeResult, "result parameter %s not in scope at return", .name)
.errorf(, _OutOfScopeResult, "\tinner declaration of %s", )
}
}
} else {
var []*Var
if .Len() > 0 {
= .vars
}
.initVars(, .Results, )
}
case *ast.BranchStmt:
if .Label != nil {
.hasLabel = true
return
}
switch .Tok {
case token.BREAK:
if &breakOk == 0 {
.error(, _MisplacedBreak, "break not in for, switch, or select statement")
}
case token.CONTINUE:
if &continueOk == 0 {
.error(, _MisplacedContinue, "continue not in for statement")
}
case token.FALLTHROUGH:
if &fallthroughOk == 0 {
:= "fallthrough statement out of place"
:= _MisplacedFallthrough
if &finalSwitchCase != 0 {
= "cannot fallthrough final case in switch"
}
.error(, , )
}
default:
.invalidAST(, "branch statement: %s", .Tok)
}
case *ast.BlockStmt:
.openScope(, "block")
defer .closeScope()
.stmtList(, .List)
case *ast.IfStmt:
.openScope(, "if")
defer .closeScope()
.simpleStmt(.Init)
var operand
.expr(&, .Cond)
if .mode != invalid && !allBoolean(.typ) {
.error(.Cond, _InvalidCond, "non-boolean condition in if statement")
}
.(, .Body)
switch .Else.(type) {
case nil, *ast.BadStmt:
case *ast.IfStmt, *ast.BlockStmt:
.(, .Else)
default:
.invalidAST(.Else, "invalid else branch in if statement")
}
case *ast.SwitchStmt:
|= breakOk
.openScope(, "switch")
defer .closeScope()
.simpleStmt(.Init)
var operand
if .Tag != nil {
.expr(&, .Tag)
.assignment(&, nil, "switch expression")
if .mode != invalid && !Comparable(.typ) && !hasNil(.typ) {
.errorf(&, _InvalidExprSwitch, "cannot switch on %s (%s is not comparable)", &, .typ)
.mode = invalid
}
} else {
.mode = constant_
.typ = Typ[Bool]
.val = constant.MakeBool(true)
.expr = &ast.Ident{NamePos: .Body.Lbrace, Name: "true"}
}
.multipleDefaults(.Body.List)
:= make(valueMap)
for , := range .Body.List {
, := .(*ast.CaseClause)
if == nil {
.invalidAST(, "incorrect expression switch case")
continue
}
.caseValues(&, .List, )
.openScope(, "case")
:=
if +1 < len(.Body.List) {
|= fallthroughOk
} else {
|= finalSwitchCase
}
.stmtList(, .Body)
.closeScope()
}
case *ast.TypeSwitchStmt:
|= breakOk
.openScope(, "type switch")
defer .closeScope()
.simpleStmt(.Init)
var *ast.Ident
var ast.Expr
switch guard := .Assign.(type) {
case *ast.ExprStmt:
= .X
case *ast.AssignStmt:
if len(.Lhs) != 1 || .Tok != token.DEFINE || len(.Rhs) != 1 {
.invalidAST(, "incorrect form of type switch guard")
return
}
, _ = .Lhs[0].(*ast.Ident)
if == nil {
.invalidAST(, "incorrect form of type switch guard")
return
}
if .Name == "_" {
.softErrorf(, _NoNewVar, "no new variable on left side of :=")
= nil
} else {
.recordDef(, nil)
}
= .Rhs[0]
default:
.invalidAST(, "incorrect form of type switch guard")
return
}
, := .(*ast.TypeAssertExpr)
if == nil || .Type != nil {
.invalidAST(, "incorrect form of type switch guard")
return
}
var operand
.expr(&, .X)
if .mode == invalid {
return
}
var *operand
if isTypeParam(.typ) {
.errorf(&, _InvalidTypeSwitch, "cannot use type switch on type parameter value %s", &)
} else {
if , := under(.typ).(*Interface); {
= &
} else {
.errorf(&, _InvalidTypeSwitch, "%s is not an interface", &)
}
}
.multipleDefaults(.Body.List)
var []*Var
:= make(map[Type]ast.Expr)
for , := range .Body.List {
, := .(*ast.CaseClause)
if == nil {
.invalidAST(, "incorrect type switch case")
continue
}
:= .caseTypes(, .List, )
.openScope(, "case")
if != nil {
if len(.List) != 1 || == nil {
= .typ
}
:= NewVar(.Pos(), .pkg, .Name, )
:= .Pos() + token.Pos(len("default"))
if := len(.List); > 0 {
= .List[-1].End()
}
.declare(.scope, nil, , )
.recordImplicit(, )
= append(, )
}
.stmtList(, .Body)
.closeScope()
}
if != nil {
var bool
for , := range {
if .used {
= true
}
.used = true
}
if ! {
.softErrorf(, _UnusedVar, "%s declared but not used", .Name)
}
}
case *ast.SelectStmt:
|= breakOk
.multipleDefaults(.Body.List)
for , := range .Body.List {
, := .(*ast.CommClause)
if == nil {
continue
}
:= false
var ast.Expr
switch s := .Comm.(type) {
case nil, *ast.SendStmt:
= true
case *ast.AssignStmt:
if len(.Rhs) == 1 {
= .Rhs[0]
}
case *ast.ExprStmt:
= .X
}
if != nil {
if , := unparen().(*ast.UnaryExpr); != nil && .Op == token.ARROW {
= true
}
}
if ! {
.error(.Comm, _InvalidSelectCase, "select case must be send or receive (possibly with assignment)")
continue
}
.openScope(, "case")
if .Comm != nil {
.(, .Comm)
}
.stmtList(, .Body)
.closeScope()
}
case *ast.ForStmt:
|= breakOk | continueOk
.openScope(, "for")
defer .closeScope()
.simpleStmt(.Init)
if .Cond != nil {
var operand
.expr(&, .Cond)
if .mode != invalid && !allBoolean(.typ) {
.error(.Cond, _InvalidCond, "non-boolean condition in for statement")
}
}
.simpleStmt(.Post)
if , := .Post.(*ast.AssignStmt); != nil && .Tok == token.DEFINE {
.softErrorf(, _InvalidPostDecl, "cannot declare in post statement")
.use(.Lhs...)
}
.(, .Body)
case *ast.RangeStmt:
|= breakOk | continueOk
var operand
.expr(&, .X)
var , Type
if .mode != invalid {
var string
:= coreType(.typ)
switch t := .(type) {
case nil:
= .sprintf("%s has no core type", .typ)
case *Chan:
if .Value != nil {
.softErrorf(.Value, _InvalidIterVar, "range over %s permits only one iteration variable", &)
}
if .dir == SendOnly {
= "receive from send-only channel"
}
}
, = rangeKeyVal()
if == nil || != "" {
if == "" {
.softErrorf(&, _InvalidRangeExpr, "cannot range over %s", &)
} else {
.softErrorf(&, _InvalidRangeExpr, "cannot range over %s (%s)", &, )
}
}
}
.openScope(, "range")
defer .closeScope()
:= [2]ast.Expr{.Key, .Value}
:= [2]Type{, }
if .Tok == token.DEFINE {
var []*Var
for , := range {
if == nil {
continue
}
var *Var
if , := .(*ast.Ident); != nil {
:= .Name
= NewVar(.Pos(), .pkg, , nil)
.recordDef(, )
if != "_" {
= append(, )
}
} else {
.invalidAST(, "cannot declare %s", )
= NewVar(.Pos(), .pkg, "_", nil)
}
if := []; != nil {
.mode = value
.expr =
.typ =
.initVar(, &, "range clause")
} else {
.typ = Typ[Invalid]
.used = true
}
}
if len() > 0 {
:= .Body.Pos()
for , := range {
.declare(.scope, nil , , )
}
} else {
.error(inNode(, .TokPos), _NoNewVar, "no new variables on left side of :=")
}
} else {
for , := range {
if == nil {
continue
}
if := []; != nil {
.mode = value
.expr =
.typ =
.assignVar(, &)
}
}
}
.(, .Body)
default:
.invalidAST(, "invalid statement")
}
}
func ( Type) (, Type) {
switch typ := arrayPtrDeref().(type) {
case *Basic:
if isString() {
return Typ[Int], universeRune
}
case *Array:
return Typ[Int], .elem
case *Slice:
return Typ[Int], .elem
case *Map:
return .key, .elem
case *Chan:
return .elem, Typ[Invalid]
}
return
}