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:
15
_test/redeclaration0.go
Normal file
15
_test/redeclaration0.go
Normal 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
12
_test/redeclaration1.go
Normal 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
15
_test/redeclaration2.go
Normal 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
13
_test/redeclaration3.go
Normal 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
14
_test/redeclaration4.go
Normal 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
14
_test/redeclaration5.go
Normal 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
|
||||
@@ -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}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user