package types
import (
.
)
func ( *Checker) ( *declInfo, string, *Signature, *ast.BlockStmt, constant.Value) {
if .conf.IgnoreFuncBodies {
panic("function body not ignored")
}
if .conf._Trace {
.trace(.Pos(), "-- %s: %s", , )
}
defer func( environment, int) {
.environment =
.indent =
}(.environment, .indent)
.environment = environment{
decl: ,
scope: .scope,
version: .version,
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) {
:= func( VarKind) bool {
return !( == RecvVar || == ParamVar || == ResultVar)
}
var []*Var
for , := range .elems {
= resolve(, )
if , := .(*Var); != nil && (.kind) && !.usedVars[] {
= append(, )
}
}
slices.SortFunc(, func(, *Var) int {
return cmpPos(.pos, .pos)
})
for , := range {
.softErrorf(, UnusedVar, "declared and not used: %s", .name)
}
for , := range .children {
if !.isFunc {
.()
}
}
}
type stmtContext uint
const (
breakOk stmtContext = 1 << iota
continueOk
fallthroughOk
finalSwitchCase
inTypeSwitch
)
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:
.error(, InvalidSyntaxTree, "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 Code
switch .rawExpr(nil, &, , nil, false) {
case conversion:
= "requires function call, not conversion"
= InvalidDefer
if == "go" {
= InvalidGo
}
case expression:
= "discards result of"
= UnusedResults
case statement:
return
default:
panic("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(nil, &, )
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) {
:= .newError(DuplicateCase)
.addf(&, "duplicate case %s in expression switch", &)
.addf(atPos(.pos), "previous case")
.report()
continue
}
}
[] = append([], valueType{.Pos(), .typ})
}
}
}
func ( *Checker) ( ast.Expr) bool {
if , := ast.Unparen().(*ast.Ident); != nil {
, := .lookup(.Name).(*Nil)
return
}
return false
}
func ( *Checker) ( *operand, []ast.Expr, map[Type]ast.Expr) Type {
var Type
var operand
:
for , := range {
if .isNil() {
= nil
.expr(nil, &, )
} else {
= .varType()
if !isValid() {
continue
}
}
for , := range {
if == nil && == nil || != nil && != nil && Identical(, ) {
:= "nil"
if != nil {
= TypeString(, .qualifier)
}
:= .newError(DuplicateCase)
.addf(, "duplicate case %s in type switch", )
.addf(, "previous case")
.report()
continue
}
}
[] =
if != nil && != nil {
.typeAssertion(, , , true)
}
}
if len() != 1 || == nil {
= Typ[Invalid]
if != nil {
= .typ
}
}
assert( != nil)
return
}
func ( *Checker) ( *operand, *Interface, []ast.Expr, map[string]ast.Expr) Type {
var Type
var operand
:
for , := range {
var string
if .isNil() {
.expr(nil, &, )
= nil
= "<nil>"
} else {
= .varType()
if !isValid() {
continue
}
panic("enable typeHash(T, nil)")
}
if := []; != nil {
:= "nil"
if != nil {
= TypeString(, .qualifier)
}
:= .newError(DuplicateCase)
.addf(, "duplicate case %s in type switch", )
.addf(, "previous case")
.report()
continue
}
[] =
if != nil {
.typeAssertion(, , , true)
}
}
if len() != 1 || == nil {
= Typ[Invalid]
if != nil {
= .typ
}
}
assert( != nil)
return
}
func ( *Checker) ( stmtContext, ast.Stmt) {
if debug {
defer func( *Scope) {
if := recover(); != nil {
panic()
}
assert( == .scope)
}(.scope)
}
defer .processDelayed(len(.delayed))
:= &^ (fallthroughOk | finalSwitchCase | inTypeSwitch)
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(nil, &, .X, nil, false)
var string
var Code
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(nil, &, .Chan)
.expr(nil, &, .Value)
if .mode == invalid || .mode == invalid {
return
}
if := .chanElem(inNode(, .Arrow), &, false); != nil {
.assignment(&, , "send")
}
case *ast.IncDecStmt:
var token.Token
switch .Tok {
case token.INC:
= token.ADD
case token.DEC:
= token.SUB
default:
.errorf(inNode(, .TokPos), InvalidSyntaxTree, "unknown inc/dec operation %s", .Tok)
return
}
var operand
.expr(nil, &, .X)
if .mode == invalid {
return
}
if !allNumeric(.typ) {
.errorf(.X, NonNumericIncDec, invalidOp+"%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, nil, &, "assignment")
case *ast.AssignStmt:
switch .Tok {
case token.ASSIGN, token.DEFINE:
if len(.Lhs) == 0 {
.error(, InvalidSyntaxTree, "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 {
.errorf(atPos(.TokPos), InvalidSyntaxTree, "unknown assignment operation %s", .Tok)
return
}
var operand
.binary(&, nil, .Lhs[0], .Rhs[0], , .TokPos)
if .mode == invalid {
return
}
.assignVar(.Lhs[0], nil, &, "assignment")
}
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 && != {
:= .newError(OutOfScopeResult)
.addf(, "result parameter %s not in scope at return", .name)
.addf(, "inner declaration of %s", )
.report()
}
}
} 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 {
var string
switch {
case &finalSwitchCase != 0:
= "cannot fallthrough final case in switch"
case &inTypeSwitch != 0:
= "cannot fallthrough in type switch"
default:
= "fallthrough statement out of place"
}
.error(, MisplacedFallthrough, )
}
default:
.errorf(, InvalidSyntaxTree, "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(nil, &, .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:
.error(.Else, InvalidSyntaxTree, "invalid else branch in if statement")
}
case *ast.SwitchStmt:
|= breakOk
.openScope(, "switch")
defer .closeScope()
.simpleStmt(.Init)
var operand
if .Tag != nil {
.expr(nil, &, .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 {
.error(, InvalidSyntaxTree, "incorrect expression switch case")
continue
}
.caseValues(&, .List, )
.openScope(, "case")
:=
if +1 < len(.Body.List) {
|= fallthroughOk
} else {
|= finalSwitchCase
}
.stmtList(, .Body)
.closeScope()
}
case *ast.TypeSwitchStmt:
|= breakOk | inTypeSwitch
.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 {
.error(, InvalidSyntaxTree, "incorrect form of type switch guard")
return
}
, _ = .Lhs[0].(*ast.Ident)
if == nil {
.error(, InvalidSyntaxTree, "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:
.error(, InvalidSyntaxTree, "incorrect form of type switch guard")
return
}
, := .(*ast.TypeAssertExpr)
if == nil || .Type != nil {
.error(, InvalidSyntaxTree, "incorrect form of type switch guard")
return
}
var *operand
{
var operand
.expr(nil, &, .X)
if .mode != invalid {
if isTypeParam(.typ) {
.errorf(&, InvalidTypeSwitch, "cannot use type switch on type parameter value %s", &)
} else if IsInterface(.typ) {
= &
} 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 {
.error(, InvalidSyntaxTree, "incorrect type switch case")
continue
}
:= .caseTypes(, .List, )
.openScope(, "case")
if != nil {
:= newVar(LocalVar, .Pos(), .pkg, .Name, )
.declare(.scope, nil, , .Colon)
.recordImplicit(, )
= append(, )
}
.stmtList(, .Body)
.closeScope()
}
if != nil {
var bool
for , := range {
if .usedVars[] {
= true
}
.usedVars[] = true
}
if ! {
.softErrorf(, UnusedVar, "%s declared and 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 , := ast.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(nil, &, .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
.rangeStmt(, , inNode(, .TokPos), .Key, .Value, nil, .X, .Tok == token.DEFINE)
default:
.error(, InvalidSyntaxTree, "invalid statement")
}
}