// Copyright 2012 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 statements.package typesimport (.)func ( *Checker) ( *declInfo, string, *Signature, *ast.BlockStmt, constant.Value) {if .conf.IgnoreFuncBodies {panic("function body not ignored") }if .conf._Trace { .trace(.Pos(), "-- %s: %s", , ) }// save/restore current environment and set up function environment // (and use 0 indentation at function start)deferfunc( 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") }// spec: "Implementation restriction: A compiler may make it illegal to // declare a variable inside a function body if the variable is never used." .usage(.scope)}func ( *Checker) ( *Scope) {var []*Varfor , := range .elems { = resolve(, )if , := .(*Var); != nil && !.used { = append(, ) } }sort.Slice(, func(, int) bool {returncmpPos([].pos, [].pos) < 0 })for , := range { .softErrorf(, UnusedVar, "declared and not used: %s", .name) }for , := range .children {// Don't go inside function literal scopes a second time; // they are handled explicitly by funcBody.if !.isFunc { .() } }}// stmtContext is a bitset describing which// control-flow statements are permissible,// and provides additional context information// for better error messages.type stmtContext uintconst (// permissible control-flow statements breakOk stmtContext = 1 << iota continueOk fallthroughOk// additional context information finalSwitchCase inTypeSwitch)func ( *Checker) ( ast.Stmt) {if != nil { .stmt(0, ) }}func trimTrailingEmptyStmts( []ast.Stmt) []ast.Stmt {for := len(); > 0; -- {if , := [-1].(*ast.EmptyStmt); ! {return [:] } }returnnil}func ( *Checker) ( stmtContext, []ast.Stmt) { := &fallthroughOk != 0 := &^ fallthroughOk = trimTrailingEmptyStmts() // trailing empty statements are "invisible" to fallthrough analysisfor , := range { := if && +1 == len() { |= fallthroughOk } .stmt(, ) }}func ( *Checker) ( []ast.Stmt) {varast.Stmtfor , := range {varast.Stmtswitch c := .(type) {case *ast.CaseClause:iflen(.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 assignOp( token.Token) token.Token {// token_test.go verifies the token ordering this function relies oniftoken.ADD_ASSIGN <= && <= token.AND_NOT_ASSIGN {return + (token.ADD - token.ADD_ASSIGN) }returntoken.ILLEGAL}func ( *Checker) ( string, *ast.CallExpr) {varoperandvarstringvarCodeswitch .rawExpr(nil, &, , nil, false) {caseconversion: = "requires function call, not conversion" = InvalidDeferif == "go" { = InvalidGo }caseexpression: = "discards result of" = UnusedResultscasestatement:returndefault:panic("unreachable") } .errorf(&, , "%s %s %s", , , &)}// goVal returns the Go value for val, or nil.func goVal( constant.Value) any {// val should exist, but be conservative and checkif == nil {returnnil }// Match implementation restriction of other compilers. // gc only checks duplicates for integer, floating-point // and string values, so only create Go values for these // types.switch .Kind() {caseconstant.Int:if , := constant.Int64Val(); {return }if , := constant.Uint64Val(); {return }caseconstant.Float:if , := constant.Float64Val(); {return }caseconstant.String:returnconstant.StringVal() }returnnil}// A valueMap maps a case value (of a basic Go type) to a list of positions// where the same case value appeared, together with the corresponding case// types.// Since two case values may have the same "underlying" value but different// types we need to also check the value's types (e.g., byte(1) vs myByte(1))// when the switch expression is of interface type.type ( valueMap map[any][]valueType// underlying Go value -> valueType valueType struct { pos token.Pos typ Type })func ( *Checker) ( *operand, []ast.Expr, valueMap) {:for , := range {varoperand .expr(nil, &, )if .mode == invalid || .mode == invalid {continue } .convertUntyped(&, .typ)if .mode == invalid {continue }// Order matters: By comparing v against x, error positions are at the case values. := // keep original v unchanged .comparison(&, , token.EQL, true)if .mode == invalid {continue }if .mode != constant_ {continue// we're done }// look for duplicate valuesif := goVal(.val); != nil {// look for duplicate types for a given value // (quadratic algorithm, but these lists tend to be very short)for , := range [] {ifIdentical(.typ, .typ) { := .newError(DuplicateCase) .addf(&, "duplicate case %s in expression switch", &) .addf(atPos(.pos), "previous case") .report()continue } } [] = append([], valueType{.Pos(), .typ}) } }}// isNil reports whether the expression e denotes the predeclared value nil.func ( *Checker) ( ast.Expr) bool {// The only way to express the nil value is by literally writing nil (possibly in parentheses).if , := ast.Unparen().(*ast.Ident); != nil { , := .lookup(.Name).(*Nil)return }returnfalse}// caseTypes typechecks the type expressions of a type case, checks for duplicate types// using the seen map, and verifies that each type is valid with respect to the type of// the operand x in the type switch clause. If the type switch expression is invalid, x// must be nil. The result is the type of the last type expression; it is nil if the// expression denotes the predeclared nil.func ( *Checker) ( *operand, []ast.Expr, map[Type]ast.Expr) ( Type) {varoperand:for , := range {// The spec allows the value nil instead of a type.if .isNil() { = nil .expr(nil, &, ) // run e through expr so we get the usual Info recordings } else { = .varType()if !isValid() {continue } }// look for duplicate types // (quadratic algorithm, but type switches tend to be reasonably small)for , := range {if == nil && == nil || != nil && != nil && Identical(, ) {// talk about "case" rather than "type" because of nil case := "nil"if != nil { = TypeString(, .qualifier) } := .newError(DuplicateCase) .addf(, "duplicate case %s in type switch", ) .addf(, "previous case") .report()continue } } [] = if != nil && != nil { .typeAssertion(, , , true) } }return}// TODO(gri) Once we are certain that typeHash is correct in all situations, use this version of caseTypes instead.// (Currently it may be possible that different types have identical names and import paths due to ImporterFrom.)//// func (check *Checker) caseTypes(x *operand, xtyp *Interface, types []ast.Expr, seen map[string]ast.Expr) (T Type) {// var dummy operand// L:// for _, e := range types {// // The spec allows the value nil instead of a type.// var hash string// if check.isNil(e) {// check.expr(nil, &dummy, e) // run e through expr so we get the usual Info recordings// T = nil// hash = "<nil>" // avoid collision with a type named nil// } else {// T = check.varType(e)// if !isValid(T) {// continue L// }// hash = typeHash(T, nil)// }// // look for duplicate types// if other := seen[hash]; other != nil {// // talk about "case" rather than "type" because of nil case// Ts := "nil"// if T != nil {// Ts = TypeString(T, check.qualifier)// }// err := check.newError(_DuplicateCase)// err.addf(e, "duplicate case %s in type switch", Ts)// err.addf(other, "previous case")// err.report()// continue L// }// seen[hash] = e// if T != nil {// check.typeAssertion(e.Pos(), x, xtyp, T)// }// }// return// }// stmt typechecks statement s.func ( *Checker) ( stmtContext, ast.Stmt) {// statements must end with the same top scope as they started withifdebug {deferfunc( *Scope) {// don't check if code is panickingif := recover(); != nil {panic() }assert( == .scope) }(.scope) }// process collected function literals before scope changesdefer .processDelayed(len(.delayed))// reset context for statements of inner blocks := &^ (fallthroughOk | finalSwitchCase | inTypeSwitch)switch s := .(type) {case *ast.BadStmt, *ast.EmptyStmt:// ignorecase *ast.DeclStmt: .declStmt(.Decl)case *ast.LabeledStmt: .hasLabel = true .(, .Stmt)case *ast.ExprStmt:// spec: "With the exception of specific built-in functions, // function and method calls and receive operations can appear // in statement context. Such statements may be parenthesized."varoperand := .rawExpr(nil, &, .X, nil, false)varstringvarCodeswitch .mode {default:if == statement {return } = "is not used" = UnusedExprcasebuiltin: = "must be called" = UncalledBuiltincasetypexpr: = "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 } := coreType(.typ)if == nil { .errorf(inNode(, .Arrow), InvalidSend, invalidOp+"cannot send to %s: no core type", &)return } , := .(*Chan)if == nil { .errorf(inNode(, .Arrow), InvalidSend, invalidOp+"cannot send to non-channel %s", &)return }if .dir == RecvOnly { .errorf(inNode(, .Arrow), InvalidSend, invalidOp+"cannot send to receive-only channel %s", &)return } .assignment(&, .elem, "send")case *ast.IncDecStmt:vartoken.Tokenswitch .Tok {casetoken.INC: = token.ADDcasetoken.DEC: = token.SUBdefault: .errorf(inNode(, .TokPos), InvalidSyntaxTree, "unknown inc/dec operation %s", .Tok)return }varoperand .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"} // use x's position .binary(&, nil, .X, , , .TokPos)if .mode == invalid {return } .assignVar(.X, nil, &, "assignment")case *ast.AssignStmt:switch .Tok {casetoken.ASSIGN, token.DEFINE:iflen(.Lhs) == 0 { .error(, InvalidSyntaxTree, "missing lhs in assignment")return }if .Tok == token.DEFINE { .shortVarDecl(inNode(, .TokPos), .Lhs, .Rhs) } else {// regular assignment .assignVars(.Lhs, .Rhs) }default:// assignment operationsiflen(.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 }varoperand .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// Return with implicit results allowed for function with named results. // (If one is named, all are named.)iflen(.Results) == 0 && .Len() > 0 && .vars[0].name != "" {// spec: "Implementation restriction: A compiler may disallow an empty expression // list in a "return" statement if a different entity (constant, type, or variable) // with the same name as a result parameter is in scope at the place of the return."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()// ok to continue } } } else {var []*Varif .Len() > 0 { = .vars } .initVars(, .Results, ) }case *ast.BranchStmt:if .Label != nil { .hasLabel = truereturn// checked in 2nd pass (check.labels) }switch .Tok {casetoken.BREAK:if &breakOk == 0 { .error(, MisplacedBreak, "break not in for, switch, or select statement") }casetoken.CONTINUE:if &continueOk == 0 { .error(, MisplacedContinue, "continue not in for statement") }casetoken.FALLTHROUGH:if &fallthroughOk == 0 {varstringswitch {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)varoperand .expr(nil, &, .Cond)if .mode != invalid && !allBoolean(.typ) { .error(.Cond, InvalidCond, "non-boolean condition in if statement") } .(, .Body)// The parser produces a correct AST but if it was modified // elsewhere the else branch may be invalid. Check again.switch .Else.(type) {casenil, *ast.BadStmt:// valid or error already reportedcase *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)varoperandif .Tag != nil { .expr(nil, &, .Tag)// By checking assignment of x to an invisible temporary // (as a compiler would), we get all the relevant checks. .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 {// spec: "A missing switch expression is // equivalent to the boolean value true." .mode = constant_ .typ = Typ[Bool] .val = constant.MakeBool(true) .expr = &ast.Ident{NamePos: .Body.Lbrace, Name: "true"} } .multipleDefaults(.Body.List) := make(valueMap) // map of seen case values to positions and typesfor , := 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)// A type switch guard must be of the form: // // TypeSwitchGuard = [ identifier ":=" ] PrimaryExpr "." "(" "type" ")" . // // The parser is checking syntactic correctness; // remaining syntactic errors are considered AST errors here. // TODO(gri) better factoring of error handling (invalid ASTs) //var *ast.Ident// lhs identifier or nilvarast.Exprswitch guard := .Assign.(type) {case *ast.ExprStmt: = .Xcase *ast.AssignStmt:iflen(.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 == "_" {// _ := x.(type) is an invalid short variable declaration .softErrorf(, NoNewVar, "no new variable on left side of :=") = nil// avoid declared and not used error below } else { .recordDef(, nil) // lhs variable is implicitly declared in each cause clause } = .Rhs[0]default: .error(, InvalidSyntaxTree, "incorrect form of type switch guard")return }// rhs must be of the form: expr.(type) and expr must be an ordinary interface , := .(*ast.TypeAssertExpr)if == nil || .Type != nil { .error(, InvalidSyntaxTree, "incorrect form of type switch guard")return }var *operand// switch expression against which cases are compared against; nil if invalid {varoperand .expr(nil, &, .X)if .mode != invalid {ifisTypeParam(.typ) { .errorf(&, InvalidTypeSwitch, "cannot use type switch on type parameter value %s", &) } elseifIsInterface(.typ) { = & } else { .errorf(&, InvalidTypeSwitch, "%s is not an interface", &) } } } .multipleDefaults(.Body.List)var []*Var// list of implicitly declared lhs variables := make(map[Type]ast.Expr) // map of seen types to positionsfor , := range .Body.List { , := .(*ast.CaseClause)if == nil { .error(, InvalidSyntaxTree, "incorrect type switch case")continue }// Check each type in this type switch case. := .caseTypes(, .List, ) .openScope(, "case")// If lhs exists, declare a corresponding variable in the case-local scope.if != nil {// spec: "The TypeSwitchGuard may include a short variable declaration. // When that form is used, the variable is declared at the beginning of // the implicit block in each clause. In clauses with a case listing // exactly one type, the variable has that type; otherwise, the variable // has the type of the expression in the TypeSwitchGuard."iflen(.List) != 1 || == nil { = Typ[Invalid]if != nil { = .typ } } := NewVar(.Pos(), .pkg, .Name, ) := .Pos() + token.Pos(len("default")) // for default clause (len(List) == 0)if := len(.List); > 0 { = .List[-1].End() } .declare(.scope, nil, , ) .recordImplicit(, )// For the "declared and not used" error, all lhs variables act as // one; i.e., if any one of them is 'used', all of them are 'used'. // Collect them for later analysis. = append(, ) } .stmtList(, .Body) .closeScope() }// If lhs exists, we must have at least one lhs variable that was used.if != nil {varboolfor , := range {if .used { = true } .used = true// avoid usage error when checking entire function }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// error reported before }// clause.Comm must be a SendStmt, RecvStmt, or default case := falsevarast.Expr// rhs of RecvStmt, or nilswitch s := .Comm.(type) {casenil, *ast.SendStmt: = truecase *ast.AssignStmt:iflen(.Rhs) == 1 { = .Rhs[0] }case *ast.ExprStmt: = .X }// if present, rhs must be a receive operationif != 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 {varoperand .expr(nil, &, .Cond)if .mode != invalid && !allBoolean(.typ) { .error(.Cond, InvalidCond, "non-boolean condition in for statement") } } .simpleStmt(.Post)// spec: "The init statement may be a short variable // declaration, but the post statement must not."if , := .Post.(*ast.AssignStmt); != nil && .Tok == token.DEFINE { .softErrorf(, InvalidPostDecl, "cannot declare in post statement")// Don't call useLHS here because we want to use the lhs in // this erroneous statement so that we don't get errors about // these lhs variables being declared and not used. .use(.Lhs...) // avoid follow-up errors } .(, .Body)case *ast.RangeStmt: |= breakOk | continueOk .rangeStmt(, )default: .error(, InvalidSyntaxTree, "invalid statement") }}func ( *Checker) ( stmtContext, *ast.RangeStmt) {// Convert go/ast form to local variables.type = ast.Exprtype = ast.Ident := func( *) string { return .Name } , := .Key, .Valuevarast.Expr = nil// (used only in types2 fork) := .Tok == token.DEFINE := .X := inNode(, .TokPos)// Everything from here on is shared between cmd/compile/internal/types2 and go/types.// check expression to iterate overvaroperand .expr(nil, &, )// determine key/value typesvar , Typeif .mode != invalid {// Ranging over a type parameter is permitted if it has a core type. , , , := rangeKeyVal(.typ, func( goVersion) bool {return .allowVersion(.expr, ) })switch {case ! && != "": .softErrorf(&, InvalidRangeExpr, "cannot range over %s: %s", &, )case !: .softErrorf(&, InvalidRangeExpr, "cannot range over %s", &)case == nil && != nil: .softErrorf(, InvalidIterVar, "range over %s permits no iteration variables", &)case == nil && != nil: .softErrorf(, InvalidIterVar, "range over %s permits only one iteration variable", &)case != nil: .softErrorf(, InvalidIterVar, "range clause permits at most two iteration variables") } , = , }// Open the for-statement block scope now, after the range clause. // Iteration variables declared with := need to go in this scope (was go.dev/issue/51437). .openScope(, "range")defer .closeScope()// check assignment to/declaration of iteration variables // (irregular assignment, cannot easily map to existing assignment checks)// lhs expressions and initialization value (rhs) types := [2]{, } // sKey, sValue may be nil := [2]Type{, } // key, val may be nil := isInteger(.typ)if {// short variable declarationvar []*Varfor , := range {if == nil {continue }// determine lhs variablevar *Varif , := .(*); != nil {// declare new variable := () = NewVar(.Pos(), .pkg, , nil) .recordDef(, )// _ variables don't count as new variablesif != "_" { = append(, ) } } else { .errorf(, InvalidSyntaxTree, "cannot declare %s", ) = NewVar(.Pos(), .pkg, "_", nil) // dummy variable }assert(.typ == nil)// initialize lhs iteration variable, if any := []if == nil || == Typ[Invalid] {// typ == Typ[Invalid] can happen if allowVersion fails. .typ = Typ[Invalid] .used = true// don't complain about unused variablecontinue }if {assert( == 0) // at most one iteration variable (rhs[1] == nil or Typ[Invalid] for rangeOverInt) .initVar(, &, "range clause") } else {varoperand .mode = value .expr = // we don't have a better rhs expression to use here .typ = .initVar(, &, "assignment") // error is on variable, use "assignment" not "range clause" }assert(.typ != nil) }// declare variablesiflen() > 0 { := .Body.Pos()for , := range { .declare(.scope, nil/* recordDef already called */, , ) } } else { .error(, NoNewVar, "no new variables on left side of :=") } } elseif != nil/* lhs[0] != nil */ {// ordinary assignmentfor , := range {if == nil {continue }// assign to lhs iteration variable, if any := []if == nil || == Typ[Invalid] {continue }if {assert( == 0) // at most one iteration variable (rhs[1] == nil or Typ[Invalid] for rangeOverInt) .assignVar(, nil, &, "range clause")// If the assignment succeeded, if x was untyped before, it now // has a type inferred via the assignment. It must be an integer. // (go.dev/issues/67027)if .mode != invalid && !isInteger(.typ) { .softErrorf(, InvalidRangeExpr, "cannot use iteration variable of type %s", .typ) } } else {varoperand .mode = value .expr = // we don't have a better rhs expression to use here .typ = .assignVar(, nil, &, "assignment") // error is on variable, use "assignment" not "range clause" } } } elseif {// If we don't have any iteration variables, we still need to // check that a (possibly untyped) integer range expression x // is valid. // We do this by checking the assignment _ = x. This ensures // that an untyped x can be converted to a value of its default // type (rune or int). .assignment(&, nil, "range clause") } .stmt(, .Body)}// rangeKeyVal returns the key and value type produced by a range clause// over an expression of type typ.// If allowVersion != nil, it is used to check the required language version.// If the range clause is not permitted, rangeKeyVal returns ok = false.// When ok = false, rangeKeyVal may also return a reason in cause.func rangeKeyVal( Type, func(goVersion) bool) (, Type, string, bool) { := func( string) (Type, Type, string, bool) {returnTyp[Invalid], Typ[Invalid], , false } := func( Type) *Signature { , := coreType().(*Signature)return } := switch typ := arrayPtrDeref(coreType()).(type) {casenil:return ("no core type")case *Basic:ifisString() {returnTyp[Int], universeRune, "", true// use 'rune' name }ifisInteger() {if != nil && !(go1_22) {return ("requires go1.22 or later") }return , nil, "", true }case *Array:returnTyp[Int], .elem, "", truecase *Slice:returnTyp[Int], .elem, "", truecase *Map:return .key, .elem, "", truecase *Chan:if .dir == SendOnly {return ("receive from send-only channel") }return .elem, nil, "", truecase *Signature:if !buildcfg.Experiment.RangeFunc && != nil && !(go1_23) {return ("requires go1.23 or later") }assert(.Recv() == nil)switch {case .Params().Len() != 1:return ("func must be func(yield func(...) bool): wrong argument count")case (.Params().At(0).Type()) == nil:return ("func must be func(yield func(...) bool): argument is not func")case .Results().Len() != 0:return ("func must be func(yield func(...) bool): unexpected results") } := (.Params().At(0).Type())assert(.Recv() == nil)switch {case .Params().Len() > 2:return ("func must be func(yield func(...) bool): yield func has too many parameters")case .Results().Len() != 1 || !isBoolean(.Results().At(0).Type()):return ("func must be func(yield func(...) bool): yield func does not return bool") }if .Params().Len() >= 1 { = .Params().At(0).Type() }if .Params().Len() >= 2 { = .Params().At(1).Type() }return , , "", true }return}
The pages are generated with Goldsv0.7.0-preview. (GOOS=linux GOARCH=amd64)
Golds is a Go 101 project developed by Tapir Liu.
PR and bug reports are welcome and can be submitted to the issue list.
Please follow @zigo_101 (reachable from the left QR code) to get the latest news of Golds.