interp: detect local redeclaration (#673)

* interp: detect local redecleration

Fixes #666

* make make check happy

* fix typo in filenames, add them to the exclusion list

Co-authored-by: Marc Vertes <mvertes@free.fr>
This commit is contained in:
mpl
2020-06-03 17:21:00 +02:00
committed by GitHub
parent eb25c84797
commit 6f878052f8
8 changed files with 127 additions and 17 deletions

15
_test/redeclaration0.go Normal file
View File

@@ -0,0 +1,15 @@
package main
func main() {
type foo struct {
yolo string
}
var foo int
foo = 2
println(foo)
}
// Error:
// ../_test/redeclaration0.go:8:6: foo redeclared in this block
// previous declaration at ../_test/redeclaration0.go:4:7

12
_test/redeclaration1.go Normal file
View File

@@ -0,0 +1,12 @@
package main
func main() {
var foo string
var foo int
foo = 2
println(foo)
}
// Error:
// ../_test/redeclaration1.go:6:6: foo redeclared in this block

15
_test/redeclaration2.go Normal file
View File

@@ -0,0 +1,15 @@
package main
func main() {
var foo struct {
yolo string
}
var foo int
foo = 2
println(foo)
}
// Error:
// ../_test/redeclaration2.go:8:6: foo redeclared in this block
// previous declaration at ../_test/redeclaration2.go:4:6

13
_test/redeclaration3.go Normal file
View File

@@ -0,0 +1,13 @@
package main
func main() {
var foo int
foo = 2
type foo struct{}
var bar foo
println(bar)
}
// Error:
// ../_test/redeclaration3.go:7:7: foo redeclared in this block

14
_test/redeclaration4.go Normal file
View File

@@ -0,0 +1,14 @@
package main
func main() {
var foo struct{
yolo string
}
type foo struct{}
var bar foo
println(bar)
}
// Error:
// ../_test/redeclaration4.go:8:7: foo redeclared in this block

14
_test/redeclaration5.go Normal file
View File

@@ -0,0 +1,14 @@
package main
func main() {
type foo struct{
yolo string
}
type foo struct{}
var bar foo
println(bar)
}
// Error:
// ../_test/redeclaration5.go:8:7: foo redeclared in this block

View File

@@ -355,24 +355,32 @@ func (interp *Interpreter) cfg(root *node, pkgID string) ([]*node, error) {
case typeSpec:
// processing already done in GTA pass for global types, only parses inlined types
if sc.def != nil {
typeName := n.child[0].ident
var typ *itype
if typ, err = nodeType(interp, sc, n.child[1]); err != nil {
return false
}
if typ.incomplete {
err = n.cfgErrorf("invalid type declaration")
return false
}
if n.child[1].kind == identExpr {
n.typ = &itype{cat: aliasT, val: typ, name: typeName}
} else {
n.typ = typ
n.typ.name = typeName
}
sc.sym[typeName] = &symbol{kind: typeSym, typ: n.typ}
if sc.def == nil {
return false
}
typeName := n.child[0].ident
var typ *itype
if typ, err = nodeType(interp, sc, n.child[1]); err != nil {
return false
}
if typ.incomplete {
err = n.cfgErrorf("invalid type declaration")
return false
}
if _, exists := sc.sym[typeName]; exists {
// TODO(mpl): find the exact location of the previous declaration
err = n.cfgErrorf("%s redeclared in this block", typeName)
return false
}
if n.child[1].kind == identExpr {
n.typ = &itype{cat: aliasT, val: typ, name: typeName}
} else {
n.typ = typ
n.typ.name = typeName
}
sc.sym[typeName] = &symbol{kind: typeSym, typ: n.typ}
return false
case constDecl:
@@ -1676,6 +1684,19 @@ func (interp *Interpreter) cfg(root *node, pkgID string) ([]*node, error) {
// Global object allocation is already performed in GTA.
index = sc.sym[c.ident].index
} else {
if sym, exists := sc.sym[c.ident]; exists {
if sym.typ.node != nil &&
sym.typ.node.anc != nil {
// for non-predeclared identifiers (struct, map, etc)
prevDecl := n.interp.fset.Position(sym.typ.node.anc.pos)
err = n.cfgErrorf("%s redeclared in this block\n\tprevious declaration at %v", c.ident, prevDecl)
return
}
// for predeclared identifiers (int, string, etc)
// TODO(mpl): find the exact location of the previous declaration in all cases.
err = n.cfgErrorf("%s redeclared in this block", c.ident)
return
}
index = sc.add(n.typ)
sc.sym[c.ident] = &symbol{index: index, kind: varSym, typ: n.typ}
}

View File

@@ -58,6 +58,12 @@ func TestInterpConsistencyBuild(t *testing.T) {
file.Name() == "type5.go" || // used to illustrate a limitation with no workaround, related to the fact that the reflect package does not allow the creation of named types
file.Name() == "type6.go" || // used to illustrate a limitation with no workaround, related to the fact that the reflect package does not allow the creation of named types
file.Name() == "redeclaration0.go" || // expect error
file.Name() == "redeclaration1.go" || // expect error
file.Name() == "redeclaration2.go" || // expect error
file.Name() == "redeclaration3.go" || // expect error
file.Name() == "redeclaration4.go" || // expect error
file.Name() == "redeclaration5.go" || // expect error
file.Name() == "server6.go" || // syntax parsing
file.Name() == "server5.go" || // syntax parsing
file.Name() == "server4.go" || // syntax parsing