mirror of
https://github.com/golang/go
synced 2024-11-05 18:36:08 +00:00
912a638b0c
Instead of a separate check control flow pass (checkcfg.go) operating on nodes, perform this check at parse time on the new syntax tree. Permits this check to be done concurrently, and doesn't depend on the specifics of the symbol's dclstack implementation anymore. The remaining dclstack uses will be removed in a follow-up change. - added CheckBranches Mode flag (so we can turn off the check if we only care about syntactic correctness, e.g. for tests) - adjusted test/goto.go error messages: the new branches checker only reports if a goto jumps into a block, but not which block (we may want to improve this again, eventually) - also, the new branches checker reports one variable that is being jumped over by a goto, but it may not be the first one declared (this is fine either way) - the new branches checker reports additional errors for fixedbugs/issue14006.go (not crucial to avoid those errors) - the new branches checker now correctly reports only variable declarations being jumped over, rather than all declarations (issue 8042). Added respective tests. Fixes #8042. Change-Id: I53b6e1bda189748e1e1fb5b765a8a64337c27d40 Reviewed-on: https://go-review.googlesource.com/39998 Reviewed-by: Matthew Dempsky <mdempsky@google.com>
538 lines
9.2 KiB
Go
538 lines
9.2 KiB
Go
// 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 x at LINE+1|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 x at LINE+1|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 L jumps into block|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 L jumps into block|goto jumps into block"
|
|
}
|
|
|
|
// error shows first (outermost) offending block
|
|
func _() {
|
|
goto L // ERROR "goto L jumps into block starting at LINE+1|goto L jumps into block|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 L jumps into block|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 L jumps into block|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 L jumps into block|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+1|goto L jumps into block|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 L jumps into block|goto jumps into block"
|
|
}
|
|
}
|
|
|
|
func _() {
|
|
if true {
|
|
goto L // ERROR "goto L jumps into block starting at LINE+1|goto L jumps into block|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 L jumps into block|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 L jumps into block|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+1|goto L jumps into block|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 L jumps into block|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 L1 jumps into block|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 L jumps into block|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 L jumps into block|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 L jumps into block|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 L jumps into block|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 L jumps into block|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 L jumps into block|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+1|goto L jumps into block|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+1|goto L jumps into block|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+1|goto L jumps into block|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 L jumps into block|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 L jumps into block|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 L jumps into block|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 L jumps into block|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 L jumps into block|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 L jumps into block|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 L jumps into block|goto jumps into block"
|
|
}
|
|
}
|