// Copyright 2013 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 call and selector expressions.package typesimport (.)// funcInst type-checks a function instantiation.// The incoming x must be a generic function.// If ix != nil, it provides some or all of the type arguments (ix.Indices).// If target != nil, it may be used to infer missing type arguments of x, if any.// At least one of T or ix must be provided.//// There are two modes of operation://// 1. If infer == true, funcInst infers missing type arguments as needed and// instantiates the function x. The returned results are nil.//// 2. If infer == false and inst provides all type arguments, funcInst// instantiates the function x. The returned results are nil.// If inst doesn't provide enough type arguments, funcInst returns the// available arguments and the corresponding expression list; x remains// unchanged.//// If an error (other than a version error) occurs in any case, it is reported// and x.mode is set to invalid.func ( *Checker) ( *target, token.Pos, *operand, *typeparams.IndexExpr, bool) ([]Type, []ast.Expr) {assert( != nil || != nil)varpositionerif != nil { = inNode(.Orig, .Lbrack) .expr = .Orig// if we don't have an index expression, keep the existing expression of x } else { = atPos() } := !.verifyVersionf(, go1_18, "function instantiation")// targs and xlist are the type arguments and corresponding type expressions, or nil.var []Typevar []ast.Exprif != nil { = .Indices = .typeList()if == nil { .mode = invalidreturnnil, nil }assert(len() == len()) }// Check the number of type arguments (got) vs number of type parameters (want). // Note that x is a function value, not a type expression, so we don't need to // call under below. := .typ.(*Signature) , := len(), .TypeParams().Len()if > {// Providing too many type arguments is always an error. .errorf(.Indices[-1], WrongTypeArgCount, "got %d type arguments but want %d", , ) .mode = invalidreturnnil, nil }if < {if ! {return , }// If the uninstantiated or partially instantiated function x is used in // an assignment (tsig != nil), infer missing type arguments by treating // the assignment // // var tvar tsig = x // // like a call g(tvar) of the synthetic generic function g // // func g[type_parameters_of_x](func_type_of_x) //var []*operandvar []*Varvarboolif != nil && .tparams != nil {if ! && !.allowVersion(, go1_21) {if != nil { .versionErrorf(, go1_21, "partially instantiated function in assignment") } else { .versionErrorf(, go1_21, "implicitly instantiated function in assignment") } } := NewSignatureType(nil, nil, nil, .params, .results, .variadic) = []*Var{NewVar(.Pos(), .pkg, "", )}// The type of the argument operand is tsig, which is the type of the LHS in an assignment // or the result type in a return statement. Create a pseudo-expression for that operand // that makes sense when reported in error messages from infer, below. := ast.NewIdent(.desc) .NamePos = .Pos() // correct position = []*operand{{mode: value, expr: , typ: .sig}} = true }// Rename type parameters to avoid problems with recursive instantiations. // Note that NewTuple(params...) below is (*Tuple)(nil) if len(params) == 0, as desired. , := .renameTParams(, .TypeParams().list(), NewTuple(...)) := .newError(CannotInferTypeArgs) = .infer(atPos(), , , .(*Tuple), , , )if == nil {if !.empty() { .report() } .mode = invalidreturnnil, nil } = len() }assert( == )// instantiate function signature = .instantiateSignature(.Pos(), .expr, , , ) .typ = .mode = valuereturnnil, nil}func ( *Checker) ( token.Pos, ast.Expr, *Signature, []Type, []ast.Expr) ( *Signature) {assert( != nil)assert(len() == .TypeParams().Len())if .conf._Trace { .trace(, "-- instantiating signature %s with %s", , ) .indent++deferfunc() { .indent-- .trace(, "=> %s (under = %s)", , .Underlying()) }() } := .instance(, , , nil, .context()).(*Signature)assert(.TypeParams().Len() == 0) // signature is not generic anymore .recordInstance(, , )assert(len() <= len())// verify instantiation lazily (was go.dev/issue/50450) .later(func() { := .TypeParams().list()if , := .verify(, , , .context()); != nil {// best position for error reporting := if < len() { = [].Pos() } .softErrorf(atPos(), InvalidTypeArg, "%s", ) } else { .mono.recordInstance(.pkg, , , , ) } }).describef(atPos(), "verify instantiation")return}func ( *Checker) ( *operand, *ast.CallExpr) exprKind { := typeparams.UnpackIndexExpr(.Fun)if != nil {if .indexExpr(, ) {// Delay function instantiation to argument checking, // where we combine type and value arguments for type // inference.assert(.mode == value) } else { = nil } .expr = .Fun .record() } else { .exprOrType(, .Fun, true) }// x.typ may be genericswitch .mode {caseinvalid: .use(.Args...) .expr = returnstatementcasetypexpr:// conversion .nonGeneric(nil, )if .mode == invalid {returnconversion } := .typ .mode = invalidswitch := len(.Args); {case0: .errorf(inNode(, .Rparen), WrongArgCount, "missing argument in conversion to %s", )case1: .expr(nil, , .Args[0])if .mode != invalid {ifhasDots() { .errorf(.Args[0], BadDotDotDotSyntax, "invalid use of ... in conversion to %s", )break }if , := under().(*Interface); != nil && !isTypeParam() {if !.IsMethodSet() { .errorf(, MisplacedConstraintIface, "cannot use interface %s in conversion (contains specific type constraints or is comparable)", )break } } .conversion(, ) }default: .use(.Args...) .errorf(.Args[-1], WrongArgCount, "too many arguments in conversion to %s", ) } .expr = returnconversioncasebuiltin:// no need to check for non-genericity here := .idif !.builtin(, , ) { .mode = invalid } .expr = // a non-constant result implies a function callif .mode != invalid && .mode != constant_ { .hasCallOrRecv = true }returnpredeclaredFuncs[].kind }// ordinary function/method call // signature may be generic := .mode == cgofunc// a type parameter may be "called" if all types have the same signature , := coreType(.typ).(*Signature)if == nil { .errorf(, InvalidCall, invalidOp+"cannot call non-function %s", ) .mode = invalid .expr = returnstatement }// Capture wasGeneric before sig is potentially instantiated below. := .TypeParams().Len() > 0// evaluate type arguments, if anyvar []ast.Exprvar []Typeif != nil { = .Indices = .typeList()if == nil { .use(.Args...) .mode = invalid .expr = returnstatement }assert(len() == len())// check number of type arguments (got) vs number of type parameters (want) , := len(), .TypeParams().Len()if > { .errorf([], WrongTypeArgCount, "got %d type arguments but want %d", , ) .use(.Args...) .mode = invalid .expr = returnstatement }// If sig is generic and all type arguments are provided, preempt function // argument type inference by explicitly instantiating the signature. This // ensures that we record accurate type information for sig, even if there // is an error checking its arguments (for example, if an incorrect number // of arguments is supplied).if == && > 0 { .verifyVersionf(atPos(.Lbrack), go1_18, "function instantiation") = .instantiateSignature(.Pos(), .Orig, , , )// targs have been consumed; proceed with checking arguments of the // non-generic signature. = nil = nil } }// evaluate arguments , , := .genericExprList(.Args) = .arguments(, , , , , , )if && .TypeParams().Len() == 0 {// Update the recorded type of call.Fun to its instantiated type. .recordTypeAndValue(.Fun, value, , nil) }// determine resultswitch .results.Len() {case0: .mode = novaluecase1:if { .mode = commaerr } else { .mode = value } .typ = .results.vars[0].typ// unpack tupledefault: .mode = value .typ = .results } .expr = .hasCallOrRecv = true// if type inference failed, a parameterized result must be invalidated // (operands cannot have a parameterized type)if .mode == value && .TypeParams().Len() > 0 && isParameterized(.TypeParams().list(), .typ) { .mode = invalid }returnstatement}// exprList evaluates a list of expressions and returns the corresponding operands.// A single-element expression list may evaluate to multiple operands.func ( *Checker) ( []ast.Expr) ( []*operand) {if := len(); == 1 { , _ = .multiExpr([0], false) } elseif > 1 {// multiple (possibly invalid) values = make([]*operand, )for , := range {varoperand .expr(nil, &, ) [] = & } }return}// genericExprList is like exprList but result operands may be uninstantiated or partially// instantiated generic functions (where constraint information is insufficient to infer// the missing type arguments) for Go 1.21 and later.// For each non-generic or uninstantiated generic operand, the corresponding targsList and// xlistList elements do not exist (targsList and xlistList are nil) or the elements are nil.// For each partially instantiated generic function operand, the corresponding targsList and// xlistList elements are the operand's partial type arguments and type expression lists.func ( *Checker) ( []ast.Expr) ( []*operand, [][]Type, [][]ast.Expr) {ifdebug {deferfunc() {// targsList and xlistList must have matching lengthsassert(len() == len())// type arguments must only exist for partially instantiated functionsfor , := range {if < len() {if := len([]); > 0 {// x must be a partially instantiated functionassert( < .typ.(*Signature).TypeParams().Len()) } } } }() }// Before Go 1.21, uninstantiated or partially instantiated argument functions are // nor permitted. Checker.funcInst must infer missing type arguments in that case. := true// for -lang < go1.21 := len()if > 0 && .allowVersion([0], go1_21) { = false }if == 1 {// single value (possibly a partially instantiated function), or a multi-valued expression := [0]varoperandif := typeparams.UnpackIndexExpr(); != nil && .indexExpr(&, ) {// x is a generic function. , := .funcInst(nil, .Pos(), &, , )if != nil {// x was not instantiated: collect the (partial) type arguments. = [][]Type{} = [][]ast.Expr{}// Update x.expr so that we can record the partially instantiated function. .expr = .Orig } else {// x was instantiated: we must record it here because we didn't // use the usual expression evaluators. .record(&) } = []*operand{&} } else {// x is not a function instantiation (it may still be a generic function). .rawExpr(nil, &, , nil, true) .exclude(&, 1<<novalue|1<<builtin|1<<typexpr)if , := .typ.(*Tuple); && .mode != invalid {// x is a function call returning multiple values; it cannot be generic. = make([]*operand, .Len())for , := range .vars { [] = &operand{mode: value, expr: , typ: .typ} } } else {// x is exactly one value (possibly invalid or uninstantiated generic function). = []*operand{&} } } } elseif > 1 {// multiple values = make([]*operand, ) = make([][]Type, ) = make([][]ast.Expr, )for , := range {varoperandif := typeparams.UnpackIndexExpr(); != nil && .indexExpr(&, ) {// x is a generic function. , := .funcInst(nil, .Pos(), &, , )if != nil {// x was not instantiated: collect the (partial) type arguments. [] = [] = // Update x.expr so that we can record the partially instantiated function. .expr = .Orig } else {// x was instantiated: we must record it here because we didn't // use the usual expression evaluators. .record(&) } } else {// x is exactly one value (possibly invalid or uninstantiated generic function). .genericExpr(&, ) } [] = & } }return}// arguments type-checks arguments passed to a function call with the given signature.// The function and its arguments may be generic, and possibly partially instantiated.// targs and xlist are the function's type arguments (and corresponding expressions).// args are the function arguments. If an argument args[i] is a partially instantiated// generic function, atargs[i] and atxlist[i] are the corresponding type arguments// (and corresponding expressions).// If the callee is variadic, arguments adjusts its signature to match the provided// arguments. The type parameters and arguments of the callee and all its arguments// are used together to infer any missing type arguments, and the callee and argument// functions are instantiated as necessary.// The result signature is the (possibly adjusted and instantiated) function signature.// If an error occurred, the result signature is the incoming sig.func ( *Checker) ( *ast.CallExpr, *Signature, []Type, []ast.Expr, []*operand, [][]Type, [][]ast.Expr) ( *Signature) { = // Function call argument/parameter count requirements // // | standard call | dotdotdot call | // --------------+------------------+----------------+ // standard func | nargs == npars | invalid | // --------------+------------------+----------------+ // variadic func | nargs >= npars-1 | nargs == npars | // --------------+------------------+----------------+ := len() := .params.Len() := hasDots()// set up parameters := .params// adjusted for variadic functions (may be nil for empty parameter lists!) := false// indicates if sigParams is different from sig.paramsif .variadic {if {// variadic_func(a, b, c...)iflen(.Args) == 1 && > 1 {// f()... is not permitted if f() is multi-valued .errorf(inNode(, .Ellipsis), InvalidDotDotDot, "cannot use ... with %d-valued %s", , .Args[0])return } } else {// variadic_func(a, b, c)if >= -1 {// Create custom parameters for arguments: keep // the first npars-1 parameters and add one for // each argument mapping to the ... parameter. := make([]*Var, -1) // npars > 0 for variadic functionscopy(, .params.vars) := .params.vars[-1] := .typ.(*Slice).elemforlen() < { = append(, NewParam(.pos, .pkg, .name, )) } = NewTuple(...) // possibly nil! = true = } else {// nargs < npars-1 -- // for correct error message below } } } else {if {// standard_func(a, b, c...) .errorf(inNode(, .Ellipsis), NonVariadicDotDotDot, "cannot use ... in call to non-variadic %s", .Fun)return }// standard_func(a, b, c) }// check argument countif != {varpositioner = := "not enough"if > { = [].expr// report at first extra argument = "too many" } else { = atPos(.Rparen) // report at closing ) }// take care of empty parameter lists represented by nil tuplesvar []*Varif .params != nil { = .params.vars } := .newError(WrongArgCount) .addf(, "%s arguments in call to %s", , .Fun) .addf(noposn, "have %s", .typesSummary(operandTypes(), false)) .addf(noposn, "want %s", .typesSummary(varTypes(), .variadic)) .report()return }// collect type parameters of callee and generic function argumentsvar []*TypeParam// collect type parameters of callee := .TypeParams().Len()if > 0 {if !.allowVersion(, go1_18) {switch .Fun.(type) {case *ast.IndexExpr, *ast.IndexListExpr: := typeparams.UnpackIndexExpr(.Fun) .versionErrorf(inNode(.Fun, .Lbrack), go1_18, "function instantiation")default: .versionErrorf(inNode(, .Lparen), go1_18, "implicit function instantiation") } }// rename type parameters to avoid problems with recursive callsvarType , = .renameTParams(.Pos(), .TypeParams().list(), ) = .(*Tuple)// make sure targs and tparams have the same lengthforlen() < len() { = append(, nil) } }assert(len() == len())// collect type parameters from generic function argumentsvar []int// indices of generic function argumentsifenableReverseTypeInference {for , := range {// generic arguments cannot have a defined (*Named) type - no need for underlying type belowif , := .typ.(*Signature); != nil && .TypeParams().Len() > 0 {// The argument type is a generic function signature. This type is // pointer-identical with (it's copied from) the type of the generic // function argument and thus the function object. // Before we change the type (type parameter renaming, below), make // a clone of it as otherwise we implicitly modify the object's type // (go.dev/issues/63260). = clone()// Rename type parameters for cases like f(g, g); this gives each // generic function argument a unique type identity (go.dev/issues/59956). // TODO(gri) Consider only doing this if a function argument appears // multiple times, which is rare (possible optimization). , := .renameTParams(.Pos(), .TypeParams().list(), ) = .(*Signature) .tparams = &TypeParamList{} // renameTParams doesn't touch associated type parameters .typ = // new type identity for the function argument = append(, ...)// add partial list of type arguments, if anyif < len() { = append(, []...) }// make sure targs and tparams have the same lengthforlen() < len() { = append(, nil) } = append(, ) } } }assert(len() == len())// at the moment we only support implicit instantiations of argument functions _ = len() > 0 && .verifyVersionf([[0]], go1_21, "implicitly instantiated function as argument")// tparams holds the type parameters of the callee and generic function arguments, if any: // the first n type parameters belong to the callee, followed by mi type parameters for each // of the generic function arguments, where mi = args[i].typ.(*Signature).TypeParams().Len().// infer missing type arguments of callee and function argumentsiflen() > 0 { := .newError(CannotInferTypeArgs) = .infer(, , , , , false, )if == nil {// TODO(gri) If infer inferred the first targs[:n], consider instantiating // the call signature for better error messages/gopls behavior. // Perhaps instantiate as much as we can, also for arguments. // This will require changes to how infer returns its results.if !.empty() { .errorf(.posn(), CannotInferTypeArgs, "in call to %s, %s", .Fun, .msg()) }return }// update result signature: instantiate if neededif > 0 { = .instantiateSignature(.Pos(), .Fun, , [:], )// If the callee's parameter list was adjusted we need to update (instantiate) // it separately. Otherwise we can simply use the result signature's parameter // list.if { = .subst(.Pos(), , makeSubstMap([:], [:]), nil, .context()).(*Tuple) } else { = .params } }// compute argument signatures: instantiate if needed := for , := range { := [] := .typ.(*Signature) := + .TypeParams().Len()// targs[j:k] are the inferred type arguments for asig .typ = .instantiateSignature(.Pos(), .expr, , [:], nil) // TODO(gri) provide xlist if possible (partial instantiations) .record() // record here because we didn't use the usual expr evaluators = } }// check argumentsiflen() > 0 { := .sprintf("argument to %s", .Fun)for , := range { .assignment(, .vars[].typ, ) } }return}var cgoPrefixes = [...]string{"_Ciconst_","_Cfconst_","_Csconst_","_Ctype_","_Cvar_", // actually a pointer to the var"_Cfpvar_fp_","_Cfunc_","_Cmacro_", // function to evaluate the expanded expression}func ( *Checker) ( *operand, *ast.SelectorExpr, *TypeName, bool) {// these must be declared before the "goto Error" statementsvar (Object []intbool ) := .Sel.Name// If the identifier refers to a package, handle everything here // so we don't need a "package" mode for operands: package names // can only appear in qualified identifiers which are mapped to // selector expressions.if , := .X.(*ast.Ident); { := .lookup(.Name)if , := .(*PkgName); != nil {assert(.pkg == .pkg) .recordUse(, ) .used = true := .importedvarObject := valueif .cgo {// cgo special cases C.malloc: it's // rewritten to _CMalloc and does not // support two-result calls.if == "malloc" { = "_CMalloc" } else { = cgofunc }for , := rangecgoPrefixes {// cgo objects are part of the current package (in file // _cgo_gotypes.go). Use regular lookup. _, = .scope.LookupParent(+, .pos)if != nil {break } }if == nil { .errorf(.Sel, UndeclaredImportedName, "undefined: %s", ast.Expr()) // cast to ast.Expr to silence vetgoto } .objDecl(, nil) } else { = .scope.Lookup()if == nil {if !.fake { .errorf(.Sel, UndeclaredImportedName, "undefined: %s", ast.Expr()) }goto }if !.Exported() { .errorf(.Sel, UnexportedName, "name %s not exported by package %s", , .name)// ok to continue } } .recordUse(.Sel, )// Simplified version of the code for *ast.Idents: // - imported objects are always fully initializedswitch exp := .(type) {case *Const:assert(.Val() != nil) .mode = constant_ .typ = .typ .val = .valcase *TypeName: .mode = typexpr .typ = .typcase *Var: .mode = variable .typ = .typif .cgo && strings.HasPrefix(.name, "_Cvar_") { .typ = .typ.(*Pointer).base }case *Func: .mode = .typ = .typif .cgo && strings.HasPrefix(.name, "_Cmacro_") { .mode = value .typ = .typ.(*Signature).results.vars[0].typ }case *Builtin: .mode = builtin .typ = .typ .id = .iddefault: .dump("%v: unexpected object %v", .Sel.Pos(), )panic("unreachable") } .expr = return } } .exprOrType(, .X, false)switch .mode {casetypexpr:// don't crash for "type T T.x" (was go.dev/issue/51509)if != nil && .typ == .typ { .cycleError([]Object{}, 0)goto }casebuiltin:// types2 uses the position of '.' for the error .errorf(.Sel, UncalledBuiltin, "cannot select on %s", )gotocaseinvalid:goto }// Avoid crashing when checking an invalid selector in a method declaration // (i.e., where def is not set): // // type S[T any] struct{} // type V = S[any] // func (fs *S[T]) M(x V.M) {} // // All codepaths below return a non-type expression. If we get here while // expecting a type expression, it is an error. // // See go.dev/issue/57522 for more details. // // TODO(rfindley): We should do better by refusing to check selectors in all cases where // x.typ is incomplete.if { .errorf(.Sel, NotAType, "%s is not a type", ast.Expr())goto } , , = lookupFieldOrMethod(.typ, .mode == variable, .pkg, , false)if == nil {// Don't report another error if the underlying type was invalid (go.dev/issue/49541).if !isValid(under(.typ)) {goto }if != nil {// TODO(gri) should provide actual type where the conflict happens .errorf(.Sel, AmbiguousSelector, "ambiguous selector %s.%s", .expr, )goto }if {if .mode == typexpr { .errorf(.Sel, InvalidMethodExpr, "invalid method expression %s.%s (needs pointer receiver (*%s).%s)", .typ, , .typ, ) } else { .errorf(.Sel, InvalidMethodExpr, "cannot call pointer method %s on %s", , .typ) }goto }varstringifisInterfacePtr(.typ) { = .interfacePtrError(.typ) } else { , , := lookupFieldOrMethod(.typ, .mode == variable, .pkg, , true) = .lookupError(.typ, , , false) } .errorf(.Sel, MissingFieldOrMethod, "%s.%s undefined (%s)", .expr, , )goto }// methods may not have a fully set up signature yetif , := .(*Func); != nil { .objDecl(, nil) }if .mode == typexpr {// method expression , := .(*Func)if == nil { .errorf(.Sel, MissingFieldOrMethod, "%s.%s undefined (type %s has no method %s)", .expr, , .typ, )goto } .recordSelection(, MethodExpr, .typ, , , ) := .typ.(*Signature)if .recv == nil { .error(, InvalidDeclCycle, "illegal cycle in method declaration")goto }// the receiver type becomes the type of the first function // argument of the method expression's function typevar []*Varif .params != nil { = .params.vars }// Be consistent about named/unnamed parameters. This is not needed // for type-checking, but the newly constructed signature may appear // in an error message and then have mixed named/unnamed parameters. // (An alternative would be to not print parameter names in errors, // but it's useful to see them; this is cheap and method expressions // are rare.) := ""iflen() > 0 && [0].name != "" {// name needed = .recv.nameif == "" { = "_" } } = append([]*Var{NewVar(.recv.pos, .recv.pkg, , .typ)}, ...) .mode = value .typ = &Signature{tparams: .tparams,params: NewTuple(...),results: .results,variadic: .variadic, } .addDeclDep() } else {// regular selectorswitch obj := .(type) {case *Var: .recordSelection(, FieldVal, .typ, , , )if .mode == variable || { .mode = variable } else { .mode = value } .typ = .typcase *Func:// TODO(gri) If we needed to take into account the receiver's // addressability, should we report the type &(x.typ) instead? .recordSelection(, MethodVal, .typ, , , )// TODO(gri) The verification pass below is disabled for now because // method sets don't match method lookup in some cases. // For instance, if we made a copy above when creating a // custom method for a parameterized received type, the // method set method doesn't match (no copy there). There /// may be other situations. := trueif ! && debug {// Verify that LookupFieldOrMethod and MethodSet.Lookup agree. // TODO(gri) This only works because we call LookupFieldOrMethod // _before_ calling NewMethodSet: LookupFieldOrMethod completes // any incomplete interfaces so they are available to NewMethodSet // (which assumes that interfaces have been completed already). := .typif .mode == variable {// If typ is not an (unnamed) pointer or an interface, // use *typ instead, because the method set of *typ // includes the methods of typ. // Variables are addressable, so we can always take their // address.if , := .(*Pointer); ! && !IsInterface() { = &Pointer{base: } } }// If we created a synthetic pointer type above, we will throw // away the method set computed here after use. // TODO(gri) Method set computation should probably always compute // both, the value and the pointer receiver method set and represent // them in a single structure. // TODO(gri) Consider also using a method set cache for the lifetime // of checker once we rely on MethodSet lookup instead of individual // lookup. := NewMethodSet()if := .Lookup(.pkg, ); == nil || .obj != { .dump("%v: (%s).%v -> %s", .Pos(), , .name, ) .dump("%s\n", )// Caution: MethodSets are supposed to be used externally // only (after all interface types were completed). It's // now possible that we get here incorrectly. Not urgent // to fix since we only run this code in debug mode. // TODO(gri) fix this eventually.panic("method sets and lookup don't agree") } } .mode = value// remove receiver := *.typ.(*Signature) .recv = nil .typ = & .addDeclDep()default:panic("unreachable") } }// everything went well .expr = return: .mode = invalid .expr = }// use type-checks each argument.// Useful to make sure expressions are evaluated// (and variables are "used") in the presence of// other errors. Arguments may be nil.// Reports if all arguments evaluated without error.func ( *Checker) ( ...ast.Expr) bool { return .useN(, false) }// useLHS is like use, but doesn't "use" top-level identifiers.// It should be called instead of use if the arguments are// expressions on the lhs of an assignment.func ( *Checker) ( ...ast.Expr) bool { return .useN(, true) }func ( *Checker) ( []ast.Expr, bool) bool { := truefor , := range {if !.use1(, ) { = false } }return}func ( *Checker) ( ast.Expr, bool) bool {varoperand .mode = value// anything but invalidswitch n := ast.Unparen().(type) {casenil:// nothing to docase *ast.Ident:// don't report an error evaluating blankif .Name == "_" {break }// If the lhs is an identifier denoting a variable v, this assignment // is not a 'use' of v. Remember current value of v.used and restore // after evaluating the lhs via check.rawExpr.var *Varvarboolif {if , := .scope.LookupParent(.Name, nopos); != nil {// It's ok to mark non-local variables, but ignore variables // from other packages to avoid potential race conditions with // dot-imported variables.if , := .(*Var); != nil && .pkg == .pkg { = = .used } } } .exprOrType(&, , true)if != nil { .used = // restore v.used }default: .rawExpr(nil, &, , nil, true) }return .mode != invalid}
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.