github.com/rsc/go@v0.0.0-20150416155037-e040fd465409/src/go/types/stmt.go (about)

     1  // Copyright 2012 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  // This file implements typechecking of statements.
     6  
     7  package types
     8  
     9  import (
    10  	"fmt"
    11  	"go/ast"
    12  	"go/exact"
    13  	"go/token"
    14  )
    15  
    16  func (check *Checker) funcBody(decl *declInfo, name string, sig *Signature, body *ast.BlockStmt) {
    17  	if trace {
    18  		if name == "" {
    19  			name = "<function literal>"
    20  		}
    21  		fmt.Printf("--- %s: %s {\n", name, sig)
    22  		defer fmt.Println("--- <end>")
    23  	}
    24  
    25  	// save/restore current context and setup function context
    26  	// (and use 0 indentation at function start)
    27  	defer func(ctxt context, indent int) {
    28  		check.context = ctxt
    29  		check.indent = indent
    30  	}(check.context, check.indent)
    31  	check.context = context{
    32  		decl:  decl,
    33  		scope: sig.scope,
    34  		sig:   sig,
    35  	}
    36  	check.indent = 0
    37  
    38  	check.stmtList(0, body.List)
    39  
    40  	if check.hasLabel {
    41  		check.labels(body)
    42  	}
    43  
    44  	if sig.results.Len() > 0 && !check.isTerminating(body, "") {
    45  		check.error(body.Rbrace, "missing return")
    46  	}
    47  
    48  	// spec: "Implementation restriction: A compiler may make it illegal to
    49  	// declare a variable inside a function body if the variable is never used."
    50  	// (One could check each scope after use, but that distributes this check
    51  	// over several places because CloseScope is not always called explicitly.)
    52  	check.usage(sig.scope)
    53  }
    54  
    55  func (check *Checker) usage(scope *Scope) {
    56  	for _, obj := range scope.elems {
    57  		if v, _ := obj.(*Var); v != nil && !v.used {
    58  			check.softErrorf(v.pos, "%s declared but not used", v.name)
    59  		}
    60  	}
    61  	for _, scope := range scope.children {
    62  		check.usage(scope)
    63  	}
    64  }
    65  
    66  // stmtContext is a bitset describing which
    67  // control-flow statements are permissible.
    68  type stmtContext uint
    69  
    70  const (
    71  	breakOk stmtContext = 1 << iota
    72  	continueOk
    73  	fallthroughOk
    74  )
    75  
    76  func (check *Checker) simpleStmt(s ast.Stmt) {
    77  	if s != nil {
    78  		check.stmt(0, s)
    79  	}
    80  }
    81  
    82  func (check *Checker) stmtList(ctxt stmtContext, list []ast.Stmt) {
    83  	ok := ctxt&fallthroughOk != 0
    84  	inner := ctxt &^ fallthroughOk
    85  	for i, s := range list {
    86  		inner := inner
    87  		if ok && i+1 == len(list) {
    88  			inner |= fallthroughOk
    89  		}
    90  		check.stmt(inner, s)
    91  	}
    92  }
    93  
    94  func (check *Checker) multipleDefaults(list []ast.Stmt) {
    95  	var first ast.Stmt
    96  	for _, s := range list {
    97  		var d ast.Stmt
    98  		switch c := s.(type) {
    99  		case *ast.CaseClause:
   100  			if len(c.List) == 0 {
   101  				d = s
   102  			}
   103  		case *ast.CommClause:
   104  			if c.Comm == nil {
   105  				d = s
   106  			}
   107  		default:
   108  			check.invalidAST(s.Pos(), "case/communication clause expected")
   109  		}
   110  		if d != nil {
   111  			if first != nil {
   112  				check.errorf(d.Pos(), "multiple defaults (first at %s)", first.Pos())
   113  			} else {
   114  				first = d
   115  			}
   116  		}
   117  	}
   118  }
   119  
   120  func (check *Checker) openScope(s ast.Stmt, comment string) {
   121  	scope := NewScope(check.scope, comment)
   122  	check.recordScope(s, scope)
   123  	check.scope = scope
   124  }
   125  
   126  func (check *Checker) closeScope() {
   127  	check.scope = check.scope.Parent()
   128  }
   129  
   130  func assignOp(op token.Token) token.Token {
   131  	// token_test.go verifies the token ordering this function relies on
   132  	if token.ADD_ASSIGN <= op && op <= token.AND_NOT_ASSIGN {
   133  		return op + (token.ADD - token.ADD_ASSIGN)
   134  	}
   135  	return token.ILLEGAL
   136  }
   137  
   138  func (check *Checker) suspendedCall(keyword string, call *ast.CallExpr) {
   139  	var x operand
   140  	var msg string
   141  	switch check.rawExpr(&x, call, nil) {
   142  	case conversion:
   143  		msg = "requires function call, not conversion"
   144  	case expression:
   145  		msg = "discards result of"
   146  	case statement:
   147  		return
   148  	default:
   149  		unreachable()
   150  	}
   151  	check.errorf(x.pos(), "%s %s %s", keyword, msg, &x)
   152  }
   153  
   154  func (check *Checker) caseValues(x operand /* copy argument (not *operand!) */, values []ast.Expr) {
   155  	// No duplicate checking for now. See issue 4524.
   156  	for _, e := range values {
   157  		var y operand
   158  		check.expr(&y, e)
   159  		if y.mode == invalid {
   160  			return
   161  		}
   162  		// TODO(gri) The convertUntyped call pair below appears in other places. Factor!
   163  		// Order matters: By comparing y against x, error positions are at the case values.
   164  		check.convertUntyped(&y, x.typ)
   165  		if y.mode == invalid {
   166  			return
   167  		}
   168  		check.convertUntyped(&x, y.typ)
   169  		if x.mode == invalid {
   170  			return
   171  		}
   172  		check.comparison(&y, &x, token.EQL)
   173  	}
   174  }
   175  
   176  func (check *Checker) caseTypes(x *operand, xtyp *Interface, types []ast.Expr, seen map[Type]token.Pos) (T Type) {
   177  L:
   178  	for _, e := range types {
   179  		T = check.typOrNil(e)
   180  		if T == Typ[Invalid] {
   181  			continue
   182  		}
   183  		// complain about duplicate types
   184  		// TODO(gri) use a type hash to avoid quadratic algorithm
   185  		for t, pos := range seen {
   186  			if T == nil && t == nil || T != nil && t != nil && Identical(T, t) {
   187  				// talk about "case" rather than "type" because of nil case
   188  				check.error(e.Pos(), "duplicate case in type switch")
   189  				check.errorf(pos, "\tprevious case %s", T) // secondary error, \t indented
   190  				continue L
   191  			}
   192  		}
   193  		seen[T] = e.Pos()
   194  		if T != nil {
   195  			check.typeAssertion(e.Pos(), x, xtyp, T)
   196  		}
   197  	}
   198  	return
   199  }
   200  
   201  // stmt typechecks statement s.
   202  func (check *Checker) stmt(ctxt stmtContext, s ast.Stmt) {
   203  	// statements cannot use iota in general
   204  	// (constant declarations set it explicitly)
   205  	assert(check.iota == nil)
   206  
   207  	// statements must end with the same top scope as they started with
   208  	if debug {
   209  		defer func(scope *Scope) {
   210  			// don't check if code is panicking
   211  			if p := recover(); p != nil {
   212  				panic(p)
   213  			}
   214  			assert(scope == check.scope)
   215  		}(check.scope)
   216  	}
   217  
   218  	inner := ctxt &^ fallthroughOk
   219  	switch s := s.(type) {
   220  	case *ast.BadStmt, *ast.EmptyStmt:
   221  		// ignore
   222  
   223  	case *ast.DeclStmt:
   224  		check.declStmt(s.Decl)
   225  
   226  	case *ast.LabeledStmt:
   227  		check.hasLabel = true
   228  		check.stmt(ctxt, s.Stmt)
   229  
   230  	case *ast.ExprStmt:
   231  		// spec: "With the exception of specific built-in functions,
   232  		// function and method calls and receive operations can appear
   233  		// in statement context. Such statements may be parenthesized."
   234  		var x operand
   235  		kind := check.rawExpr(&x, s.X, nil)
   236  		var msg string
   237  		switch x.mode {
   238  		default:
   239  			if kind == statement {
   240  				return
   241  			}
   242  			msg = "is not used"
   243  		case builtin:
   244  			msg = "must be called"
   245  		case typexpr:
   246  			msg = "is not an expression"
   247  		}
   248  		check.errorf(x.pos(), "%s %s", &x, msg)
   249  
   250  	case *ast.SendStmt:
   251  		var ch, x operand
   252  		check.expr(&ch, s.Chan)
   253  		check.expr(&x, s.Value)
   254  		if ch.mode == invalid || x.mode == invalid {
   255  			return
   256  		}
   257  		if tch, ok := ch.typ.Underlying().(*Chan); !ok || tch.dir == RecvOnly || !check.assignment(&x, tch.elem) {
   258  			if x.mode != invalid {
   259  				check.invalidOp(ch.pos(), "cannot send %s to channel %s", &x, &ch)
   260  			}
   261  		}
   262  
   263  	case *ast.IncDecStmt:
   264  		var op token.Token
   265  		switch s.Tok {
   266  		case token.INC:
   267  			op = token.ADD
   268  		case token.DEC:
   269  			op = token.SUB
   270  		default:
   271  			check.invalidAST(s.TokPos, "unknown inc/dec operation %s", s.Tok)
   272  			return
   273  		}
   274  		var x operand
   275  		Y := &ast.BasicLit{ValuePos: s.X.Pos(), Kind: token.INT, Value: "1"} // use x's position
   276  		check.binary(&x, s.X, Y, op)
   277  		if x.mode == invalid {
   278  			return
   279  		}
   280  		check.assignVar(s.X, &x)
   281  
   282  	case *ast.AssignStmt:
   283  		switch s.Tok {
   284  		case token.ASSIGN, token.DEFINE:
   285  			if len(s.Lhs) == 0 {
   286  				check.invalidAST(s.Pos(), "missing lhs in assignment")
   287  				return
   288  			}
   289  			if s.Tok == token.DEFINE {
   290  				check.shortVarDecl(s.TokPos, s.Lhs, s.Rhs)
   291  			} else {
   292  				// regular assignment
   293  				check.assignVars(s.Lhs, s.Rhs)
   294  			}
   295  
   296  		default:
   297  			// assignment operations
   298  			if len(s.Lhs) != 1 || len(s.Rhs) != 1 {
   299  				check.errorf(s.TokPos, "assignment operation %s requires single-valued expressions", s.Tok)
   300  				return
   301  			}
   302  			op := assignOp(s.Tok)
   303  			if op == token.ILLEGAL {
   304  				check.invalidAST(s.TokPos, "unknown assignment operation %s", s.Tok)
   305  				return
   306  			}
   307  			var x operand
   308  			check.binary(&x, s.Lhs[0], s.Rhs[0], op)
   309  			if x.mode == invalid {
   310  				return
   311  			}
   312  			check.assignVar(s.Lhs[0], &x)
   313  		}
   314  
   315  	case *ast.GoStmt:
   316  		check.suspendedCall("go", s.Call)
   317  
   318  	case *ast.DeferStmt:
   319  		check.suspendedCall("defer", s.Call)
   320  
   321  	case *ast.ReturnStmt:
   322  		res := check.sig.results
   323  		if res.Len() > 0 {
   324  			// function returns results
   325  			// (if one, say the first, result parameter is named, all of them are named)
   326  			if len(s.Results) == 0 && res.vars[0].name != "" {
   327  				// spec: "Implementation restriction: A compiler may disallow an empty expression
   328  				// list in a "return" statement if a different entity (constant, type, or variable)
   329  				// with the same name as a result parameter is in scope at the place of the return."
   330  				for _, obj := range res.vars {
   331  					if _, alt := check.scope.LookupParent(obj.name); alt != nil && alt != obj {
   332  						check.errorf(s.Pos(), "result parameter %s not in scope at return", obj.name)
   333  						check.errorf(alt.Pos(), "\tinner declaration of %s", obj)
   334  						// ok to continue
   335  					}
   336  				}
   337  			} else {
   338  				// return has results or result parameters are unnamed
   339  				check.initVars(res.vars, s.Results, s.Return)
   340  			}
   341  		} else if len(s.Results) > 0 {
   342  			check.error(s.Results[0].Pos(), "no result values expected")
   343  			check.use(s.Results...)
   344  		}
   345  
   346  	case *ast.BranchStmt:
   347  		if s.Label != nil {
   348  			check.hasLabel = true
   349  			return // checked in 2nd pass (check.labels)
   350  		}
   351  		switch s.Tok {
   352  		case token.BREAK:
   353  			if ctxt&breakOk == 0 {
   354  				check.error(s.Pos(), "break not in for, switch, or select statement")
   355  			}
   356  		case token.CONTINUE:
   357  			if ctxt&continueOk == 0 {
   358  				check.error(s.Pos(), "continue not in for statement")
   359  			}
   360  		case token.FALLTHROUGH:
   361  			if ctxt&fallthroughOk == 0 {
   362  				check.error(s.Pos(), "fallthrough statement out of place")
   363  			}
   364  		default:
   365  			check.invalidAST(s.Pos(), "branch statement: %s", s.Tok)
   366  		}
   367  
   368  	case *ast.BlockStmt:
   369  		check.openScope(s, "block")
   370  		defer check.closeScope()
   371  
   372  		check.stmtList(inner, s.List)
   373  
   374  	case *ast.IfStmt:
   375  		check.openScope(s, "if")
   376  		defer check.closeScope()
   377  
   378  		check.simpleStmt(s.Init)
   379  		var x operand
   380  		check.expr(&x, s.Cond)
   381  		if x.mode != invalid && !isBoolean(x.typ) {
   382  			check.error(s.Cond.Pos(), "non-boolean condition in if statement")
   383  		}
   384  		check.stmt(inner, s.Body)
   385  		if s.Else != nil {
   386  			check.stmt(inner, s.Else)
   387  		}
   388  
   389  	case *ast.SwitchStmt:
   390  		inner |= breakOk
   391  		check.openScope(s, "switch")
   392  		defer check.closeScope()
   393  
   394  		check.simpleStmt(s.Init)
   395  		var x operand
   396  		if s.Tag != nil {
   397  			check.expr(&x, s.Tag)
   398  		} else {
   399  			// spec: "A missing switch expression is
   400  			// equivalent to the boolean value true."
   401  			x.mode = constant
   402  			x.typ = Typ[Bool]
   403  			x.val = exact.MakeBool(true)
   404  			x.expr = &ast.Ident{NamePos: s.Body.Lbrace, Name: "true"}
   405  		}
   406  
   407  		check.multipleDefaults(s.Body.List)
   408  
   409  		for i, c := range s.Body.List {
   410  			clause, _ := c.(*ast.CaseClause)
   411  			if clause == nil {
   412  				check.invalidAST(c.Pos(), "incorrect expression switch case")
   413  				continue
   414  			}
   415  			if x.mode != invalid {
   416  				check.caseValues(x, clause.List)
   417  			}
   418  			check.openScope(clause, "case")
   419  			inner := inner
   420  			if i+1 < len(s.Body.List) {
   421  				inner |= fallthroughOk
   422  			}
   423  			check.stmtList(inner, clause.Body)
   424  			check.closeScope()
   425  		}
   426  
   427  	case *ast.TypeSwitchStmt:
   428  		inner |= breakOk
   429  		check.openScope(s, "type switch")
   430  		defer check.closeScope()
   431  
   432  		check.simpleStmt(s.Init)
   433  
   434  		// A type switch guard must be of the form:
   435  		//
   436  		//     TypeSwitchGuard = [ identifier ":=" ] PrimaryExpr "." "(" "type" ")" .
   437  		//
   438  		// The parser is checking syntactic correctness;
   439  		// remaining syntactic errors are considered AST errors here.
   440  		// TODO(gri) better factoring of error handling (invalid ASTs)
   441  		//
   442  		var lhs *ast.Ident // lhs identifier or nil
   443  		var rhs ast.Expr
   444  		switch guard := s.Assign.(type) {
   445  		case *ast.ExprStmt:
   446  			rhs = guard.X
   447  		case *ast.AssignStmt:
   448  			if len(guard.Lhs) != 1 || guard.Tok != token.DEFINE || len(guard.Rhs) != 1 {
   449  				check.invalidAST(s.Pos(), "incorrect form of type switch guard")
   450  				return
   451  			}
   452  
   453  			lhs, _ = guard.Lhs[0].(*ast.Ident)
   454  			if lhs == nil {
   455  				check.invalidAST(s.Pos(), "incorrect form of type switch guard")
   456  				return
   457  			}
   458  
   459  			if lhs.Name == "_" {
   460  				// _ := x.(type) is an invalid short variable declaration
   461  				check.softErrorf(lhs.Pos(), "no new variable on left side of :=")
   462  				lhs = nil // avoid declared but not used error below
   463  			} else {
   464  				check.recordDef(lhs, nil) // lhs variable is implicitly declared in each cause clause
   465  			}
   466  
   467  			rhs = guard.Rhs[0]
   468  
   469  		default:
   470  			check.invalidAST(s.Pos(), "incorrect form of type switch guard")
   471  			return
   472  		}
   473  
   474  		// rhs must be of the form: expr.(type) and expr must be an interface
   475  		expr, _ := rhs.(*ast.TypeAssertExpr)
   476  		if expr == nil || expr.Type != nil {
   477  			check.invalidAST(s.Pos(), "incorrect form of type switch guard")
   478  			return
   479  		}
   480  		var x operand
   481  		check.expr(&x, expr.X)
   482  		if x.mode == invalid {
   483  			return
   484  		}
   485  		xtyp, _ := x.typ.Underlying().(*Interface)
   486  		if xtyp == nil {
   487  			check.errorf(x.pos(), "%s is not an interface", &x)
   488  			return
   489  		}
   490  
   491  		check.multipleDefaults(s.Body.List)
   492  
   493  		var lhsVars []*Var               // list of implicitly declared lhs variables
   494  		seen := make(map[Type]token.Pos) // map of seen types to positions
   495  		for _, s := range s.Body.List {
   496  			clause, _ := s.(*ast.CaseClause)
   497  			if clause == nil {
   498  				check.invalidAST(s.Pos(), "incorrect type switch case")
   499  				continue
   500  			}
   501  			// Check each type in this type switch case.
   502  			T := check.caseTypes(&x, xtyp, clause.List, seen)
   503  			check.openScope(clause, "case")
   504  			// If lhs exists, declare a corresponding variable in the case-local scope.
   505  			if lhs != nil {
   506  				// spec: "The TypeSwitchGuard may include a short variable declaration.
   507  				// When that form is used, the variable is declared at the beginning of
   508  				// the implicit block in each clause. In clauses with a case listing
   509  				// exactly one type, the variable has that type; otherwise, the variable
   510  				// has the type of the expression in the TypeSwitchGuard."
   511  				if len(clause.List) != 1 || T == nil {
   512  					T = x.typ
   513  				}
   514  				obj := NewVar(lhs.Pos(), check.pkg, lhs.Name, T)
   515  				check.declare(check.scope, nil, obj)
   516  				check.recordImplicit(clause, obj)
   517  				// For the "declared but not used" error, all lhs variables act as
   518  				// one; i.e., if any one of them is 'used', all of them are 'used'.
   519  				// Collect them for later analysis.
   520  				lhsVars = append(lhsVars, obj)
   521  			}
   522  			check.stmtList(inner, clause.Body)
   523  			check.closeScope()
   524  		}
   525  
   526  		// If lhs exists, we must have at least one lhs variable that was used.
   527  		if lhs != nil {
   528  			var used bool
   529  			for _, v := range lhsVars {
   530  				if v.used {
   531  					used = true
   532  				}
   533  				v.used = true // avoid usage error when checking entire function
   534  			}
   535  			if !used {
   536  				check.softErrorf(lhs.Pos(), "%s declared but not used", lhs.Name)
   537  			}
   538  		}
   539  
   540  	case *ast.SelectStmt:
   541  		inner |= breakOk
   542  
   543  		check.multipleDefaults(s.Body.List)
   544  
   545  		for _, s := range s.Body.List {
   546  			clause, _ := s.(*ast.CommClause)
   547  			if clause == nil {
   548  				continue // error reported before
   549  			}
   550  
   551  			// clause.Comm must be a SendStmt, RecvStmt, or default case
   552  			valid := false
   553  			var rhs ast.Expr // rhs of RecvStmt, or nil
   554  			switch s := clause.Comm.(type) {
   555  			case nil, *ast.SendStmt:
   556  				valid = true
   557  			case *ast.AssignStmt:
   558  				if len(s.Rhs) == 1 {
   559  					rhs = s.Rhs[0]
   560  				}
   561  			case *ast.ExprStmt:
   562  				rhs = s.X
   563  			}
   564  
   565  			// if present, rhs must be a receive operation
   566  			if rhs != nil {
   567  				if x, _ := unparen(rhs).(*ast.UnaryExpr); x != nil && x.Op == token.ARROW {
   568  					valid = true
   569  				}
   570  			}
   571  
   572  			if !valid {
   573  				check.error(clause.Comm.Pos(), "select case must be send or receive (possibly with assignment)")
   574  				continue
   575  			}
   576  
   577  			check.openScope(s, "case")
   578  			if clause.Comm != nil {
   579  				check.stmt(inner, clause.Comm)
   580  			}
   581  			check.stmtList(inner, clause.Body)
   582  			check.closeScope()
   583  		}
   584  
   585  	case *ast.ForStmt:
   586  		inner |= breakOk | continueOk
   587  		check.openScope(s, "for")
   588  		defer check.closeScope()
   589  
   590  		check.simpleStmt(s.Init)
   591  		if s.Cond != nil {
   592  			var x operand
   593  			check.expr(&x, s.Cond)
   594  			if x.mode != invalid && !isBoolean(x.typ) {
   595  				check.error(s.Cond.Pos(), "non-boolean condition in for statement")
   596  			}
   597  		}
   598  		check.simpleStmt(s.Post)
   599  		// spec: "The init statement may be a short variable
   600  		// declaration, but the post statement must not."
   601  		if s, _ := s.Post.(*ast.AssignStmt); s != nil && s.Tok == token.DEFINE {
   602  			check.softErrorf(s.Pos(), "cannot declare in post statement")
   603  			check.use(s.Lhs...) // avoid follow-up errors
   604  		}
   605  		check.stmt(inner, s.Body)
   606  
   607  	case *ast.RangeStmt:
   608  		inner |= breakOk | continueOk
   609  		check.openScope(s, "for")
   610  		defer check.closeScope()
   611  
   612  		// check expression to iterate over
   613  		var x operand
   614  		check.expr(&x, s.X)
   615  
   616  		// determine key/value types
   617  		var key, val Type
   618  		if x.mode != invalid {
   619  			switch typ := x.typ.Underlying().(type) {
   620  			case *Basic:
   621  				if isString(typ) {
   622  					key = Typ[Int]
   623  					val = UniverseRune // use 'rune' name
   624  				}
   625  			case *Array:
   626  				key = Typ[Int]
   627  				val = typ.elem
   628  			case *Slice:
   629  				key = Typ[Int]
   630  				val = typ.elem
   631  			case *Pointer:
   632  				if typ, _ := typ.base.Underlying().(*Array); typ != nil {
   633  					key = Typ[Int]
   634  					val = typ.elem
   635  				}
   636  			case *Map:
   637  				key = typ.key
   638  				val = typ.elem
   639  			case *Chan:
   640  				key = typ.elem
   641  				val = Typ[Invalid]
   642  				if typ.dir == SendOnly {
   643  					check.errorf(x.pos(), "cannot range over send-only channel %s", &x)
   644  					// ok to continue
   645  				}
   646  				if s.Value != nil {
   647  					check.errorf(s.Value.Pos(), "iteration over %s permits only one iteration variable", &x)
   648  					// ok to continue
   649  				}
   650  			}
   651  		}
   652  
   653  		if key == nil {
   654  			check.errorf(x.pos(), "cannot range over %s", &x)
   655  			// ok to continue
   656  		}
   657  
   658  		// check assignment to/declaration of iteration variables
   659  		// (irregular assignment, cannot easily map to existing assignment checks)
   660  
   661  		// lhs expressions and initialization value (rhs) types
   662  		lhs := [2]ast.Expr{s.Key, s.Value}
   663  		rhs := [2]Type{key, val} // key, val may be nil
   664  
   665  		if s.Tok == token.DEFINE {
   666  			// short variable declaration; variable scope starts after the range clause
   667  			// (the for loop opens a new scope, so variables on the lhs never redeclare
   668  			// previously declared variables)
   669  			var vars []*Var
   670  			for i, lhs := range lhs {
   671  				if lhs == nil {
   672  					continue
   673  				}
   674  
   675  				// determine lhs variable
   676  				var obj *Var
   677  				if ident, _ := lhs.(*ast.Ident); ident != nil {
   678  					// declare new variable
   679  					name := ident.Name
   680  					obj = NewVar(ident.Pos(), check.pkg, name, nil)
   681  					check.recordDef(ident, obj)
   682  					// _ variables don't count as new variables
   683  					if name != "_" {
   684  						vars = append(vars, obj)
   685  					}
   686  				} else {
   687  					check.errorf(lhs.Pos(), "cannot declare %s", lhs)
   688  					obj = NewVar(lhs.Pos(), check.pkg, "_", nil) // dummy variable
   689  				}
   690  
   691  				// initialize lhs variable
   692  				if typ := rhs[i]; typ != nil {
   693  					x.mode = value
   694  					x.expr = lhs // we don't have a better rhs expression to use here
   695  					x.typ = typ
   696  					check.initVar(obj, &x, false)
   697  				} else {
   698  					obj.typ = Typ[Invalid]
   699  					obj.used = true // don't complain about unused variable
   700  				}
   701  			}
   702  
   703  			// declare variables
   704  			if len(vars) > 0 {
   705  				for _, obj := range vars {
   706  					check.declare(check.scope, nil /* recordDef already called */, obj)
   707  				}
   708  			} else {
   709  				check.error(s.TokPos, "no new variables on left side of :=")
   710  			}
   711  		} else {
   712  			// ordinary assignment
   713  			for i, lhs := range lhs {
   714  				if lhs == nil {
   715  					continue
   716  				}
   717  				if typ := rhs[i]; typ != nil {
   718  					x.mode = value
   719  					x.expr = lhs // we don't have a better rhs expression to use here
   720  					x.typ = typ
   721  					check.assignVar(lhs, &x)
   722  				}
   723  			}
   724  		}
   725  
   726  		check.stmt(inner, s.Body)
   727  
   728  	default:
   729  		check.error(s.Pos(), "invalid statement")
   730  	}
   731  }