// 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.

package types

import (
	
	
	. 
)

// labels checks correct label use in body.
func ( *Checker) ( *ast.BlockStmt) {
	// set of all labels in this body
	 := NewScope(nil, .Pos(), .End(), "label")

	 := .blockBranches(, nil, nil, .List)

	// If there are any forward jumps left, no label was found for
	// the corresponding goto statements. Either those labels were
	// never defined, or they are inside blocks and not reachable
	// for the respective gotos.
	for ,  := range  {
		var  string
		var  Code
		 := .Label.Name
		if  := .Lookup();  != nil {
			 = "goto %s jumps into block"
			 = JumpIntoBlock
			.(*Label).used = true // avoid another error
		} else {
			 = "label %s not declared"
			 = UndeclaredLabel
		}
		.errorf(.Label, , , )
	}

	// spec: "It is illegal to define a label that is never used."
	for ,  := range .elems {
		 = resolve(, )
		if  := .(*Label); !.used {
			.softErrorf(, UnusedLabel, "label %s declared and not used", .name)
		}
	}
}

// A block tracks label declarations in a block and its enclosing blocks.
type block struct {
	parent *block                      // enclosing block
	lstmt  *ast.LabeledStmt            // labeled statement to which this block belongs, or nil
	labels map[string]*ast.LabeledStmt // allocated lazily
}

// insert records a new label declaration for the current block.
// The label must not have been declared before in any block.
func ( *block) ( *ast.LabeledStmt) {
	 := .Label.Name
	if debug {
		assert(.gotoTarget() == nil)
	}
	 := .labels
	if  == nil {
		 = make(map[string]*ast.LabeledStmt)
		.labels = 
	}
	[] = 
}

// gotoTarget returns the labeled statement in the current
// or an enclosing block with the given label name, or nil.
func ( *block) ( string) *ast.LabeledStmt {
	for  := ;  != nil;  = .parent {
		if  := .labels[];  != nil {
			return 
		}
	}
	return nil
}

// enclosingTarget returns the innermost enclosing labeled
// statement with the given label name, or nil.
func ( *block) ( string) *ast.LabeledStmt {
	for  := ;  != nil;  = .parent {
		if  := .lstmt;  != nil && .Label.Name ==  {
			return 
		}
	}
	return nil
}

// blockBranches processes a block's statement list and returns the set of outgoing forward jumps.
// all is the scope of all declared labels, parent the set of labels declared in the immediately
// enclosing block, and lstmt is the labeled statement this block is associated with (or nil).
func ( *Checker) ( *Scope,  *block,  *ast.LabeledStmt,  []ast.Stmt) []*ast.BranchStmt {
	 := &block{parent: , lstmt: }

	var (
		         token.Pos
		,  []*ast.BranchStmt
	)

	// All forward jumps jumping over a variable declaration are possibly
	// invalid (they may still jump out of the block and be ok).
	// recordVarDecl records them for the given position.
	 := func( token.Pos) {
		 = 
		 = append([:0], ...) // copy fwdJumps to badJumps
	}

	 := func( *ast.BranchStmt) bool {
		if .IsValid() {
			for ,  := range  {
				if  ==  {
					return true
				}
			}
		}
		return false
	}

	 := func( *ast.LabeledStmt,  []ast.Stmt) {
		// Unresolved forward jumps inside the nested block
		// become forward jumps in the current block.
		 = append(, .(, , , )...)
	}

	var  func(ast.Stmt)
	 = func( ast.Stmt) {
		switch s := .(type) {
		case *ast.DeclStmt:
			if ,  := .Decl.(*ast.GenDecl);  != nil && .Tok == token.VAR {
				(.Pos())
			}

		case *ast.LabeledStmt:
			// declare non-blank label
			if  := .Label.Name;  != "_" {
				 := NewLabel(.Label.Pos(), .pkg, )
				if  := .Insert();  != nil {
					 := .newError(DuplicateLabel)
					.soft = true
					.addf(, "label %s already declared", )
					.addAltDecl()
					.report()
					// ok to continue
				} else {
					.insert()
					.recordDef(.Label, )
				}
				// resolve matching forward jumps and remove them from fwdJumps
				 := 0
				for ,  := range  {
					if .Label.Name ==  {
						// match
						.used = true
						.recordUse(.Label, )
						if () {
							.softErrorf(
								.Label,
								JumpOverDecl,
								"goto %s jumps over variable declaration at line %d",
								,
								.fset.Position().Line,
							)
							// ok to continue
						}
					} else {
						// no match - record new forward jump
						[] = 
						++
					}
				}
				 = [:]
				 = 
			}
			(.Stmt)

		case *ast.BranchStmt:
			if .Label == nil {
				return // checked in 1st pass (check.stmt)
			}

			// determine and validate target
			 := .Label.Name
			switch .Tok {
			case token.BREAK:
				// spec: "If there is a label, it must be that of an enclosing
				// "for", "switch", or "select" statement, and that is the one
				// whose execution terminates."
				 := false
				if  := .enclosingTarget();  != nil {
					switch .Stmt.(type) {
					case *ast.SwitchStmt, *ast.TypeSwitchStmt, *ast.SelectStmt, *ast.ForStmt, *ast.RangeStmt:
						 = true
					}
				}
				if ! {
					.errorf(.Label, MisplacedLabel, "invalid break label %s", )
					return
				}

			case token.CONTINUE:
				// spec: "If there is a label, it must be that of an enclosing
				// "for" statement, and that is the one whose execution advances."
				 := false
				if  := .enclosingTarget();  != nil {
					switch .Stmt.(type) {
					case *ast.ForStmt, *ast.RangeStmt:
						 = true
					}
				}
				if ! {
					.errorf(.Label, MisplacedLabel, "invalid continue label %s", )
					return
				}

			case token.GOTO:
				if .gotoTarget() == nil {
					// label may be declared later - add branch to forward jumps
					 = append(, )
					return
				}

			default:
				.errorf(, InvalidSyntaxTree, "branch statement: %s %s", .Tok, )
				return
			}

			// record label use
			 := .Lookup()
			.(*Label).used = true
			.recordUse(.Label, )

		case *ast.AssignStmt:
			if .Tok == token.DEFINE {
				(.Pos())
			}

		case *ast.BlockStmt:
			(, .List)

		case *ast.IfStmt:
			(.Body)
			if .Else != nil {
				(.Else)
			}

		case *ast.CaseClause:
			(nil, .Body)

		case *ast.SwitchStmt:
			(.Body)

		case *ast.TypeSwitchStmt:
			(.Body)

		case *ast.CommClause:
			(nil, .Body)

		case *ast.SelectStmt:
			(.Body)

		case *ast.ForStmt:
			(.Body)

		case *ast.RangeStmt:
			(.Body)
		}
	}

	for ,  := range  {
		()
	}

	return 
}