diff --git a/test/errchk b/test/errchk index fbb021ce417..e89d7595088 100755 --- a/test/errchk +++ b/test/errchk @@ -81,6 +81,19 @@ sub chk { next; } $regexp = $1; + + # Turn relative line number in message into absolute line number. + if($regexp =~ /LINE(([+-])([0-9]+))?/) { + my $n = $line; + if(defined($1)) { + if($2 eq "+") { + $n += int($3); + } else { + $n -= int($3); + } + } + $regexp = "$`$file:$n$'"; + } @errmsg = grep { /$file:$line[:[]/ } @out; @out = grep { !/$file:$line[:[]/ } @out; @@ -93,6 +106,9 @@ sub chk { if(@match == 0) { bug(); print STDERR "errchk: $file:$line: error message does not match '$regexp'\n"; + foreach my $l (@errmsg) { + print STDERR "> $l"; + } next; } } diff --git a/test/goto.go b/test/goto.go new file mode 100644 index 00000000000..0a50938dcff --- /dev/null +++ b/test/goto.go @@ -0,0 +1,535 @@ +// errchk $G -e $D/$F.go + +// 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. + +// 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" + x := 1 + _ = 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" + { + x := 1 + _ = x + } + x := 1 + _ = 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" + x := 1 + _ = 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" + x := 1 + _ = 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" + { + L: + } +} + +// goto backward into inner block still not okay +func _() { + { + L: + } + goto L // ERROR "goto L jumps into block starting at LINE-3" +} + +// error shows first (outermost) offending block +func _() { + goto L // ERROR "goto L jumps into block starting at LINE+1" + { + { + { + L: + } + } + } +} + +// error prefers block diagnostic over declaration diagnostic +func _() { + goto L // ERROR "goto L jumps into block starting at LINE+3" + x := 1 + _ = x + { + 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" + if true { + L: + } +} + +func _() { + goto L // ERROR "goto L jumps into block starting at LINE+1" + if true { + L: + } else { + } +} + +func _() { + goto L // ERROR "goto L jumps into block starting at LINE+1" + if true { + } else { + L: + } +} + +func _() { + if false { + L: + } else { + goto L // ERROR "goto L jumps into block starting at LINE-3" + } +} + +func _() { + if true { + goto L // ERROR "goto L jumps into block starting at LINE+1" + } else { + L: + } +} + +func _() { + if true { + goto L // ERROR "goto L jumps into block starting at LINE+1" + } else if false { + L: + } +} + +func _() { + if true { + goto L // ERROR "goto L jumps into block starting at LINE+1" + } else if false { + 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" + } else if false { + } else { + 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 { + L: + } else + goto L //// ERROR "goto L jumps into block starting at LINE-3" +} + +*/ + +// for + +func _() { + for { + goto L + } +L: +} + +func _() { + for { + goto L + L: + } +} + +func _() { + for { + L: + } + goto L // ERROR "goto L jumps into block starting at LINE-3" +} + +func _() { + for { + goto L + L1: + } +L: + goto L1 // ERROR "goto L1 jumps into block starting at LINE-5" +} + +func _() { + for i < n { + L: + } + goto L // ERROR "goto L jumps into block starting at LINE-3" +} + +func _() { + for i = 0; i < n; i++ { + L: + } + goto L // ERROR "goto L jumps into block starting at LINE-3" +} + +func _() { + for i = range x { + L: + } + goto L // ERROR "goto L jumps into block starting at LINE-3" +} + +func _() { + for i = range c { + L: + } + goto L // ERROR "goto L jumps into block starting at LINE-3" +} + +func _() { + for i = range m { + L: + } + goto L // ERROR "goto L jumps into block starting at LINE-3" +} + +func _() { + for i = range s { + L: + } + goto L // ERROR "goto L jumps into block starting at LINE-3" +} + +// 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" + switch i { + case 0: + L: + } +} + +func _() { + goto L // ERROR "goto L jumps into block starting at LINE+1" + switch i { + case 0: + L: + ; + default: + } +} + +func _() { + goto L // ERROR "goto L jumps into block starting at LINE+1" + switch i { + case 0: + default: + L: + } +} + +func _() { + switch i { + default: + goto L // ERROR "goto L jumps into block starting at LINE+1" + case 0: + L: + } +} + +func _() { + switch i { + case 0: + L: + ; + default: + goto L // ERROR "goto L jumps into block starting at LINE-4" + } +} + +// 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" + select { + case c <- 1: + L: + } +} + +func _() { + goto L // ERROR "goto L jumps into block starting at LINE+2" + select { + case c <- 1: + L: + ; + default: + } +} + +func _() { + goto L // ERROR "goto L jumps into block starting at LINE+3" + select { + case <-c: + default: + L: + } +} + +func _() { + select { + default: + goto L // ERROR "goto L jumps into block starting at LINE+1" + case <-c: + L: + } +} + +func _() { + select { + case <-c: + L: + ; + default: + goto L // ERROR "goto L jumps into block starting at LINE-4" + } +}