package types
import (
)
func ( *Checker) ( *ast.BlockStmt) {
:= NewScope(nil, .Pos(), .End(), "label")
:= .blockBranches(, nil, nil, .List)
for , := range {
var string
var errorCode
:= .Label.Name
if := .Lookup(); != nil {
= "goto %s jumps into block"
.(*Label).used = true
= _JumpIntoBlock
} else {
= "label %s not declared"
= _UndeclaredLabel
}
.errorf(.Label, , , )
}
for , := range .elems {
= resolve(, )
if := .(*Label); !.used {
.softErrorf(, _UnusedLabel, "label %s declared but not used", .name)
}
}
}
type block struct {
parent *block
lstmt *ast.LabeledStmt
labels map[string]*ast.LabeledStmt
}
func ( *block) ( *ast.LabeledStmt) {
:= .Label.Name
if debug {
assert(.gotoTarget() == nil)
}
:= .labels
if == nil {
= make(map[string]*ast.LabeledStmt)
.labels =
}
[] =
}
func ( *block) ( string) *ast.LabeledStmt {
for := ; != nil; = .parent {
if := .labels[]; != nil {
return
}
}
return nil
}
func ( *block) ( string) *ast.LabeledStmt {
for := ; != nil; = .parent {
if := .lstmt; != nil && .Label.Name == {
return
}
}
return nil
}
func ( *Checker) ( *Scope, *block, *ast.LabeledStmt, []ast.Stmt) []*ast.BranchStmt {
:= &block{parent: , lstmt: }
var (
token.Pos
, []*ast.BranchStmt
)
:= func( token.Pos) {
=
= append([:0], ...)
}
:= func( *ast.BranchStmt) bool {
if .IsValid() {
for , := range {
if == {
return true
}
}
}
return false
}
:= func( *ast.LabeledStmt, []ast.Stmt) {
= 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:
if := .Label.Name; != "_" {
:= NewLabel(.Label.Pos(), .pkg, )
if := .Insert(); != nil {
.softErrorf(, _DuplicateLabel, "label %s already declared", )
.reportAltDecl()
} else {
.insert()
.recordDef(.Label, )
}
:= 0
for , := range {
if .Label.Name == {
.used = true
.recordUse(.Label, )
if () {
.softErrorf(
.Label,
_JumpOverDecl,
"goto %s jumps over variable declaration at line %d",
,
.fset.Position().Line,
)
}
} else {
[] =
++
}
}
= [:]
=
}
(.Stmt)
case *ast.BranchStmt:
if .Label == nil {
return
}
:= .Label.Name
switch .Tok {
case token.BREAK:
:= 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:
:= 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 {
= append(, )
return
}
default:
.invalidAST(, "branch statement: %s %s", .Tok, )
return
}
:= .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
}