// errorcheck

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

// Verify goto semantics.
// Does not compile.
//
// Each test is in a separate function just so that if the
// compiler stops processing after one error, we don't
// lose other ones.

package main

var (
	i, n int
	x    []int
	c    chan int
	m    map[int]int
	s    string
)

// goto after declaration okay
func _() {
	x := 1
	goto L
L:
	_ = x
}

// goto before declaration okay
func _() {
	goto L
L:
	x := 1
	_ = x
}

// goto across declaration not okay
func _() {
	goto L // ERROR "goto L jumps over declaration of x at LINE+1|goto jumps over declaration"
	x := 1 // GCCGO_ERROR "defined here"
	_ = x
L:
}

// goto across declaration in inner scope okay
func _() {
	goto L
	{
		x := 1
		_ = x
	}
L:
}

// goto across declaration after inner scope not okay
func _() {
	goto L // ERROR "goto L jumps over declaration of x at LINE+5|goto jumps over declaration"
	{
		x := 1
		_ = x
	}
	x := 1 // GCCGO_ERROR "defined here"
	_ = x
L:
}

// goto across declaration in reverse okay
func _() {
L:
	x := 1
	_ = x
	goto L
}

// error shows first offending variable
func _() {
	goto L // ERROR "goto L jumps over declaration of y at LINE+3|goto jumps over declaration"
	x := 1 // GCCGO_ERROR "defined here"
	_ = x
	y := 1
	_ = y
L:
}

// goto not okay even if code path is dead
func _() {
	goto L // ERROR "goto L jumps over declaration of y at LINE+3|goto jumps over declaration"
	x := 1 // GCCGO_ERROR "defined here"
	_ = x
	y := 1
	_ = y
	return
L:
}

// goto into outer block okay
func _() {
	{
		goto L
	}
L:
}

// goto backward into outer block okay
func _() {
L:
	{
		goto L
	}
}

// goto into inner block not okay
func _() {
	goto L // ERROR "goto L jumps into block starting at LINE+1|goto jumps into block"
	{      // GCCGO_ERROR "block starts here"
	L:
	}
}

// goto backward into inner block still not okay
func _() {
	{ // GCCGO_ERROR "block starts here"
	L:
	}
	goto L // ERROR "goto L jumps into block starting at LINE-3|goto jumps into block"
}

// error shows first (outermost) offending block
func _() {
	goto L // ERROR "goto L jumps into block starting at LINE+3|goto jumps into block"
	{
		{
			{ // GCCGO_ERROR "block starts here"
			L:
			}
		}
	}
}

// error prefers block diagnostic over declaration diagnostic
func _() {
	goto L // ERROR "goto L jumps into block starting at LINE+3|goto jumps into block"
	x := 1
	_ = x
	{ // GCCGO_ERROR "block starts here"
	L:
	}
}

// many kinds of blocks, all invalid to jump into or among,
// but valid to jump out of

// if

func _() {
L:
	if true {
		goto L
	}
}

func _() {
L:
	if true {
		goto L
	} else {
	}
}

func _() {
L:
	if false {
	} else {
		goto L
	}
}

func _() {
	goto L    // ERROR "goto L jumps into block starting at LINE+1|goto jumps into block"
	if true { // GCCGO_ERROR "block starts here"
	L:
	}
}

func _() {
	goto L    // ERROR "goto L jumps into block starting at LINE+1|goto jumps into block"
	if true { // GCCGO_ERROR "block starts here"
	L:
	} else {
	}
}

func _() {
	goto L // ERROR "goto L jumps into block starting at LINE+2|goto jumps into block"
	if true {
	} else { // GCCGO_ERROR "block starts here"
	L:
	}
}

func _() {
	if false { // GCCGO_ERROR "block starts here"
	L:
	} else {
		goto L // ERROR "goto L jumps into block starting at LINE-3|goto jumps into block"
	}
}

func _() {
	if true {
		goto L // ERROR "goto L jumps into block starting at LINE+1|goto jumps into block"
	} else { // GCCGO_ERROR "block starts here"
	L:
	}
}

func _() {
	if true {
		goto L // ERROR "goto L jumps into block starting at LINE+1|goto jumps into block"
	} else if false { // GCCGO_ERROR "block starts here"
	L:
	}
}

func _() {
	if true {
		goto L // ERROR "goto L jumps into block starting at LINE+1|goto jumps into block"
	} else if false { // GCCGO_ERROR "block starts here"
	L:
	} else {
	}
}

func _() {
	// This one is tricky.  There is an implicit scope
	// starting at the second if statement, and it contains
	// the final else, so the outermost offending scope
	// really is LINE+1 (like in the previous test),
	// even though it looks like it might be LINE+3 instead.
	if true {
		goto L // ERROR "goto L jumps into block starting at LINE+2|goto jumps into block"
	} else if false {
	} else { // GCCGO_ERROR "block starts here"
	L:
	}
}

/* Want to enable these tests but gofmt mangles them.  Issue 1972.

func _() {
	// This one is okay, because the else is in the
	// implicit whole-if block and has no inner block
	// (no { }) around it.
	if true {
		goto L
	} else
		L:
}

func _() {
	// Still not okay.
	if true {	//// GCCGO_ERROR "block starts here"
	L:
	} else
		goto L //// ERROR "goto L jumps into block starting at LINE-3|goto jumps into block"
}

*/

// for

func _() {
	for {
		goto L
	}
L:
}

func _() {
	for {
		goto L
	L:
	}
}

func _() {
	for { // GCCGO_ERROR "block starts here"
	L:
	}
	goto L // ERROR "goto L jumps into block starting at LINE-3|goto jumps into block"
}

func _() {
	for { // GCCGO_ERROR "block starts here"
		goto L
	L1:
	}
L:
	goto L1 // ERROR "goto L1 jumps into block starting at LINE-5|goto jumps into block"
}

func _() {
	for i < n { // GCCGO_ERROR "block starts here"
	L:
	}
	goto L // ERROR "goto L jumps into block starting at LINE-3|goto jumps into block"
}

func _() {
	for i = 0; i < n; i++ { // GCCGO_ERROR "block starts here"
	L:
	}
	goto L // ERROR "goto L jumps into block starting at LINE-3|goto jumps into block"
}

func _() {
	for i = range x { // GCCGO_ERROR "block starts here"
	L:
	}
	goto L // ERROR "goto L jumps into block starting at LINE-3|goto jumps into block"
}

func _() {
	for i = range c { // GCCGO_ERROR "block starts here"
	L:
	}
	goto L // ERROR "goto L jumps into block starting at LINE-3|goto jumps into block"
}

func _() {
	for i = range m { // GCCGO_ERROR "block starts here"
	L:
	}
	goto L // ERROR "goto L jumps into block starting at LINE-3|goto jumps into block"
}

func _() {
	for i = range s { // GCCGO_ERROR "block starts here"
	L:
	}
	goto L // ERROR "goto L jumps into block starting at LINE-3|goto jumps into block"
}

// switch

func _() {
L:
	switch i {
	case 0:
		goto L
	}
}

func _() {
L:
	switch i {
	case 0:

	default:
		goto L
	}
}

func _() {
	switch i {
	case 0:

	default:
	L:
		goto L
	}
}

func _() {
	switch i {
	case 0:

	default:
		goto L
	L:
	}
}

func _() {
	switch i {
	case 0:
		goto L
	L:
		;
	default:
	}
}

func _() {
	goto L // ERROR "goto L jumps into block starting at LINE+2|goto jumps into block"
	switch i {
	case 0:
	L: // GCCGO_ERROR "block starts here"
	}
}

func _() {
	goto L // ERROR "goto L jumps into block starting at LINE+2|goto jumps into block"
	switch i {
	case 0:
	L: // GCCGO_ERROR "block starts here"
		;
	default:
	}
}

func _() {
	goto L // ERROR "goto L jumps into block starting at LINE+3|goto jumps into block"
	switch i {
	case 0:
	default:
	L: // GCCGO_ERROR "block starts here"
	}
}

func _() {
	switch i {
	default:
		goto L // ERROR "goto L jumps into block starting at LINE+1|goto jumps into block"
	case 0:
	L: // GCCGO_ERROR "block starts here"
	}
}

func _() {
	switch i {
	case 0:
	L: // GCCGO_ERROR "block starts here"
		;
	default:
		goto L // ERROR "goto L jumps into block starting at LINE-4|goto jumps into block"
	}
}

// select
// different from switch.  the statement has no implicit block around it.

func _() {
L:
	select {
	case <-c:
		goto L
	}
}

func _() {
L:
	select {
	case c <- 1:

	default:
		goto L
	}
}

func _() {
	select {
	case <-c:

	default:
	L:
		goto L
	}
}

func _() {
	select {
	case c <- 1:

	default:
		goto L
	L:
	}
}

func _() {
	select {
	case <-c:
		goto L
	L:
		;
	default:
	}
}

func _() {
	goto L // ERROR "goto L jumps into block starting at LINE+2|goto jumps into block"
	select {
	case c <- 1:
	L: // GCCGO_ERROR "block starts here"
	}
}

func _() {
	goto L // ERROR "goto L jumps into block starting at LINE+2|goto jumps into block"
	select {
	case c <- 1:
	L: // GCCGO_ERROR "block starts here"
		;
	default:
	}
}

func _() {
	goto L // ERROR "goto L jumps into block starting at LINE+3|goto jumps into block"
	select {
	case <-c:
	default:
	L: // GCCGO_ERROR "block starts here"
	}
}

func _() {
	select {
	default:
		goto L // ERROR "goto L jumps into block starting at LINE+1|goto jumps into block"
	case <-c:
	L: // GCCGO_ERROR "block starts here"
	}
}

func _() {
	select {
	case <-c:
	L: // GCCGO_ERROR "block starts here"
		;
	default:
		goto L // ERROR "goto L jumps into block starting at LINE-4|goto jumps into block"
	}
}
