mirror of
https://github.com/golang/go
synced 2024-11-25 01:27:56 -07:00
undo CL 4896053 / c62cf48b7dc4: fix build
The subtle AST changes introduced with CL 4896053 broke type checking of type switches in gofix. Coming up with a correct fix will take some time. Undoing this change for now. ««« original CL description go/parser: fix type switch scoping The variable declared by a TypeSwitchGuard must be visible in each TypeCaseClause and must not conflict with other variables declared by the initial SimpleStmt of a type switch. Also: - explicitly detect type switches (as opposed to detecting regular (expression switches) and then do extra testing for type switches - fix all outstanding TODOs in parser.go R=rsc CC=golang-dev https://golang.org/cl/4896053 »»» R=rsc CC=golang-dev https://golang.org/cl/4902052
This commit is contained in:
parent
18248ced36
commit
8843262599
17
src/cmd/gotype/testdata/test1.go
vendored
17
src/cmd/gotype/testdata/test1.go
vendored
@ -4,20 +4,3 @@ func _() {
|
|||||||
// the scope of a local type declaration starts immediately after the type name
|
// the scope of a local type declaration starts immediately after the type name
|
||||||
type T struct{ _ *T }
|
type T struct{ _ *T }
|
||||||
}
|
}
|
||||||
|
|
||||||
func _(x interface{}) {
|
|
||||||
// the variable defined by a TypeSwitchGuard is declared in each TypeCaseClause
|
|
||||||
switch t := x.(type) {
|
|
||||||
case int:
|
|
||||||
_ = t
|
|
||||||
case float32:
|
|
||||||
_ = t
|
|
||||||
default:
|
|
||||||
_ = t
|
|
||||||
}
|
|
||||||
|
|
||||||
// the variable defined by a TypeSwitchGuard must not conflict with other
|
|
||||||
// variables declared in the initial simple statement
|
|
||||||
switch t := 0; t := x.(type) {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -587,6 +587,7 @@ func (p *parser) parseStructType() *ast.StructType {
|
|||||||
}
|
}
|
||||||
rbrace := p.expect(token.RBRACE)
|
rbrace := p.expect(token.RBRACE)
|
||||||
|
|
||||||
|
// TODO(gri): store struct scope in AST
|
||||||
return &ast.StructType{pos, &ast.FieldList{lbrace, list, rbrace}, false}
|
return &ast.StructType{pos, &ast.FieldList{lbrace, list, rbrace}, false}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -799,6 +800,7 @@ func (p *parser) parseInterfaceType() *ast.InterfaceType {
|
|||||||
}
|
}
|
||||||
rbrace := p.expect(token.RBRACE)
|
rbrace := p.expect(token.RBRACE)
|
||||||
|
|
||||||
|
// TODO(gri): store interface scope in AST
|
||||||
return &ast.InterfaceType{pos, &ast.FieldList{lbrace, list, rbrace}, false}
|
return &ast.InterfaceType{pos, &ast.FieldList{lbrace, list, rbrace}, false}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1434,14 +1436,14 @@ func (p *parser) parseSimpleStmt(mode int) (ast.Stmt, bool) {
|
|||||||
case token.ARROW:
|
case token.ARROW:
|
||||||
// send statement
|
// send statement
|
||||||
arrow := p.pos
|
arrow := p.pos
|
||||||
p.next()
|
p.next() // consume "<-"
|
||||||
y := p.parseRhs()
|
y := p.parseRhs()
|
||||||
return &ast.SendStmt{x[0], arrow, y}, false
|
return &ast.SendStmt{x[0], arrow, y}, false
|
||||||
|
|
||||||
case token.INC, token.DEC:
|
case token.INC, token.DEC:
|
||||||
// increment or decrement
|
// increment or decrement
|
||||||
s := &ast.IncDecStmt{x[0], p.pos, p.tok}
|
s := &ast.IncDecStmt{x[0], p.pos, p.tok}
|
||||||
p.next()
|
p.next() // consume "++" or "--"
|
||||||
return s, false
|
return s, false
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1589,7 +1591,7 @@ func (p *parser) parseTypeList() (list []ast.Expr) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *parser) parseCaseClause(varName string) *ast.CaseClause {
|
func (p *parser) parseCaseClause(exprSwitch bool) *ast.CaseClause {
|
||||||
if p.trace {
|
if p.trace {
|
||||||
defer un(trace(p, "CaseClause"))
|
defer un(trace(p, "CaseClause"))
|
||||||
}
|
}
|
||||||
@ -1598,12 +1600,10 @@ func (p *parser) parseCaseClause(varName string) *ast.CaseClause {
|
|||||||
var list []ast.Expr
|
var list []ast.Expr
|
||||||
if p.tok == token.CASE {
|
if p.tok == token.CASE {
|
||||||
p.next()
|
p.next()
|
||||||
if varName != "" {
|
if exprSwitch {
|
||||||
// type switch
|
|
||||||
list = p.parseTypeList()
|
|
||||||
} else {
|
|
||||||
// expression switch
|
|
||||||
list = p.parseRhsList()
|
list = p.parseRhsList()
|
||||||
|
} else {
|
||||||
|
list = p.parseTypeList()
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
p.expect(token.DEFAULT)
|
p.expect(token.DEFAULT)
|
||||||
@ -1611,31 +1611,21 @@ func (p *parser) parseCaseClause(varName string) *ast.CaseClause {
|
|||||||
|
|
||||||
colon := p.expect(token.COLON)
|
colon := p.expect(token.COLON)
|
||||||
p.openScope()
|
p.openScope()
|
||||||
// If we have a type switch declaring a variable in its TypeSwitchGuard,
|
|
||||||
// declare that variable in each of the TypeCaseClauses.
|
|
||||||
if varName != "" && varName != "_" {
|
|
||||||
ident := &ast.Ident{Name: varName} // dummy identifier
|
|
||||||
p.declare(nil, nil, p.topScope, ast.Var, ident)
|
|
||||||
}
|
|
||||||
body := p.parseStmtList()
|
body := p.parseStmtList()
|
||||||
p.closeScope()
|
p.closeScope()
|
||||||
|
|
||||||
return &ast.CaseClause{pos, list, colon, body}
|
return &ast.CaseClause{pos, list, colon, body}
|
||||||
}
|
}
|
||||||
|
|
||||||
func isTypeSwitchAssert(x ast.Expr) bool {
|
func isExprSwitch(s ast.Stmt) bool {
|
||||||
a, ok := x.(*ast.TypeAssertExpr)
|
if s == nil {
|
||||||
return ok && a.Type == nil
|
return true
|
||||||
}
|
}
|
||||||
|
if e, ok := s.(*ast.ExprStmt); ok {
|
||||||
func isTypeSwitchGuard(s ast.Stmt) bool {
|
if a, ok := e.X.(*ast.TypeAssertExpr); ok {
|
||||||
switch t := s.(type) {
|
return a.Type != nil // regular type assertion
|
||||||
case *ast.ExprStmt:
|
}
|
||||||
// x.(nil)
|
return true
|
||||||
return isTypeSwitchAssert(t.X)
|
|
||||||
case *ast.AssignStmt:
|
|
||||||
// v := x.(nil)
|
|
||||||
return len(t.Lhs) == 1 && t.Tok == token.DEFINE && len(t.Rhs) == 1 && isTypeSwitchAssert(t.Rhs[0])
|
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
@ -1650,65 +1640,39 @@ func (p *parser) parseSwitchStmt() ast.Stmt {
|
|||||||
defer p.closeScope()
|
defer p.closeScope()
|
||||||
|
|
||||||
var s1, s2 ast.Stmt
|
var s1, s2 ast.Stmt
|
||||||
var scope *ast.Scope // scope for variable declared in TypeSwitchGuard, if any
|
|
||||||
if p.tok != token.LBRACE {
|
if p.tok != token.LBRACE {
|
||||||
prevLev := p.exprLev
|
prevLev := p.exprLev
|
||||||
p.exprLev = -1
|
p.exprLev = -1
|
||||||
if p.tok != token.SEMICOLON {
|
if p.tok != token.SEMICOLON {
|
||||||
s2, _ = p.parseSimpleStmt(basic)
|
s2, _ = p.parseSimpleStmt(basic)
|
||||||
scope = p.topScope
|
|
||||||
}
|
}
|
||||||
if p.tok == token.SEMICOLON {
|
if p.tok == token.SEMICOLON {
|
||||||
p.next()
|
p.next()
|
||||||
s1 = s2
|
s1 = s2
|
||||||
s2 = nil
|
s2 = nil
|
||||||
if p.tok != token.LBRACE {
|
if p.tok != token.LBRACE {
|
||||||
// A TypeSwitchGuard may declare a variable in addition
|
|
||||||
// to the variable declared in the initial SimpleStmt.
|
|
||||||
// Put it into an extra scope to avoid redeclaration
|
|
||||||
// errors, as in:
|
|
||||||
//
|
|
||||||
// switch t := 0; t := x.(T) { ... }
|
|
||||||
//
|
|
||||||
// (this code is still not valid Go because the first t
|
|
||||||
// will never be used - but the extra scope is needed
|
|
||||||
// for the correct error message).
|
|
||||||
//
|
|
||||||
// If we don't have a type switch, s2 must be an expression.
|
|
||||||
// Having the extra (empty) scope here won't affect it.
|
|
||||||
p.openScope()
|
|
||||||
s2, _ = p.parseSimpleStmt(basic)
|
s2, _ = p.parseSimpleStmt(basic)
|
||||||
scope = p.topScope
|
|
||||||
p.closeScope()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
p.exprLev = prevLev
|
p.exprLev = prevLev
|
||||||
}
|
}
|
||||||
|
|
||||||
// If varName != "", we have a type switch. If varName != "_" it
|
exprSwitch := isExprSwitch(s2)
|
||||||
// is the name of the variable declared by the TypeSwitchGuard.
|
|
||||||
varName := ""
|
|
||||||
if isTypeSwitchGuard(s2) {
|
|
||||||
varName = "_"
|
|
||||||
// If s2 declared a variable, there is exactly one.
|
|
||||||
for varName = range scope.Objects {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
lbrace := p.expect(token.LBRACE)
|
lbrace := p.expect(token.LBRACE)
|
||||||
var list []ast.Stmt
|
var list []ast.Stmt
|
||||||
for p.tok == token.CASE || p.tok == token.DEFAULT {
|
for p.tok == token.CASE || p.tok == token.DEFAULT {
|
||||||
list = append(list, p.parseCaseClause(varName))
|
list = append(list, p.parseCaseClause(exprSwitch))
|
||||||
}
|
}
|
||||||
rbrace := p.expect(token.RBRACE)
|
rbrace := p.expect(token.RBRACE)
|
||||||
p.expectSemi()
|
p.expectSemi()
|
||||||
body := &ast.BlockStmt{lbrace, list, rbrace}
|
body := &ast.BlockStmt{lbrace, list, rbrace}
|
||||||
|
|
||||||
if varName != "" {
|
if exprSwitch {
|
||||||
return &ast.TypeSwitchStmt{pos, s1, s2, body}
|
return &ast.SwitchStmt{pos, s1, p.makeExpr(s2), body}
|
||||||
}
|
}
|
||||||
|
// type switch
|
||||||
return &ast.SwitchStmt{pos, s1, p.makeExpr(s2), body}
|
// TODO(gri): do all the checks!
|
||||||
|
return &ast.TypeSwitchStmt{pos, s1, s2, body}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *parser) parseCommClause() *ast.CommClause {
|
func (p *parser) parseCommClause() *ast.CommClause {
|
||||||
@ -2037,12 +2001,14 @@ func (p *parser) parseReceiver(scope *ast.Scope) *ast.FieldList {
|
|||||||
defer un(trace(p, "Receiver"))
|
defer un(trace(p, "Receiver"))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pos := p.pos
|
||||||
par := p.parseParameters(scope, false)
|
par := p.parseParameters(scope, false)
|
||||||
|
|
||||||
// must have exactly one receiver
|
// must have exactly one receiver
|
||||||
if par.NumFields() != 1 {
|
if par.NumFields() != 1 {
|
||||||
p.errorExpected(par.Opening, "exactly one receiver")
|
p.errorExpected(pos, "exactly one receiver")
|
||||||
par.List = []*ast.Field{&ast.Field{Type: &ast.BadExpr{par.Opening, par.Closing + 1}}}
|
// TODO determine a better range for BadExpr below
|
||||||
|
par.List = []*ast.Field{&ast.Field{Type: &ast.BadExpr{pos, pos}}}
|
||||||
return par
|
return par
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -26,9 +26,6 @@ var illegalInputs = []interface{}{
|
|||||||
`package p; func f() { for _ = range x ; ; {} };`,
|
`package p; func f() { for _ = range x ; ; {} };`,
|
||||||
`package p; func f() { for ; ; _ = range x {} };`,
|
`package p; func f() { for ; ; _ = range x {} };`,
|
||||||
`package p; func f() { for ; _ = range x ; {} };`,
|
`package p; func f() { for ; _ = range x ; {} };`,
|
||||||
`package p; func f() { switch t = t.(type) {} };`,
|
|
||||||
`package p; func f() { switch t, t = t.(type) {} };`,
|
|
||||||
`package p; func f() { switch t = t.(type), t {} };`,
|
|
||||||
`package p; var a = [1]int; /* illegal expression */`,
|
`package p; var a = [1]int; /* illegal expression */`,
|
||||||
`package p; var a = [...]int; /* illegal expression */`,
|
`package p; var a = [...]int; /* illegal expression */`,
|
||||||
`package p; var a = struct{} /* illegal expression */`,
|
`package p; var a = struct{} /* illegal expression */`,
|
||||||
|
Loading…
Reference in New Issue
Block a user