Commit 57ae8330 authored by Robert Griesemer's avatar Robert Griesemer

go/ast, go/parser: parse alias declarations

For now, we also accept "type p = p.T" (using = instead of =>, for
type aliases only), so we can experiment with an approach that only
uses type aliases. This concession is implemened in the parser.

For #16339

Change-Id: I88b5522a8b6cfc2e97ca146ede8b32af340220f8
Reviewed-on: https://go-review.googlesource.com/30211Reviewed-by: default avatarMatthew Dempsky <mdempsky@google.com>
parent 776a9010
...@@ -818,7 +818,7 @@ func (*RangeStmt) stmtNode() {} ...@@ -818,7 +818,7 @@ func (*RangeStmt) stmtNode() {}
// constant, type, or variable declaration. // constant, type, or variable declaration.
// //
type ( type (
// The Spec type stands for any of *ImportSpec, *ValueSpec, and *TypeSpec. // The Spec type stands for any of *ImportSpec, *AliasSpec, *ValueSpec, or *TypeSpec.
Spec interface { Spec interface {
Node Node
specNode() specNode()
...@@ -833,6 +833,14 @@ type ( ...@@ -833,6 +833,14 @@ type (
EndPos token.Pos // end of spec (overrides Path.Pos if nonzero) EndPos token.Pos // end of spec (overrides Path.Pos if nonzero)
} }
// An AliasSpec node represents a constant, type, variable, or function alias.
AliasSpec struct {
Doc *CommentGroup // associated documentation; or nil
Name *Ident // alias name
Orig Expr // original (possibly qualified) name
Comment *CommentGroup // line comments; or nil
}
// A ValueSpec node represents a constant or variable declaration // A ValueSpec node represents a constant or variable declaration
// (ConstSpec or VarSpec production). // (ConstSpec or VarSpec production).
// //
...@@ -861,6 +869,7 @@ func (s *ImportSpec) Pos() token.Pos { ...@@ -861,6 +869,7 @@ func (s *ImportSpec) Pos() token.Pos {
} }
return s.Path.Pos() return s.Path.Pos()
} }
func (s *AliasSpec) Pos() token.Pos { return s.Name.Pos() }
func (s *ValueSpec) Pos() token.Pos { return s.Names[0].Pos() } func (s *ValueSpec) Pos() token.Pos { return s.Names[0].Pos() }
func (s *TypeSpec) Pos() token.Pos { return s.Name.Pos() } func (s *TypeSpec) Pos() token.Pos { return s.Name.Pos() }
...@@ -870,7 +879,7 @@ func (s *ImportSpec) End() token.Pos { ...@@ -870,7 +879,7 @@ func (s *ImportSpec) End() token.Pos {
} }
return s.Path.End() return s.Path.End()
} }
func (s *AliasSpec) End() token.Pos { return s.Orig.End() }
func (s *ValueSpec) End() token.Pos { func (s *ValueSpec) End() token.Pos {
if n := len(s.Values); n > 0 { if n := len(s.Values); n > 0 {
return s.Values[n-1].End() return s.Values[n-1].End()
...@@ -886,6 +895,7 @@ func (s *TypeSpec) End() token.Pos { return s.Type.End() } ...@@ -886,6 +895,7 @@ func (s *TypeSpec) End() token.Pos { return s.Type.End() }
// assigned to a Spec. // assigned to a Spec.
// //
func (*ImportSpec) specNode() {} func (*ImportSpec) specNode() {}
func (*AliasSpec) specNode() {}
func (*ValueSpec) specNode() {} func (*ValueSpec) specNode() {}
func (*TypeSpec) specNode() {} func (*TypeSpec) specNode() {}
...@@ -901,20 +911,22 @@ type ( ...@@ -901,20 +911,22 @@ type (
} }
// A GenDecl node (generic declaration node) represents an import, // A GenDecl node (generic declaration node) represents an import,
// constant, type or variable declaration. A valid Lparen position // constant, type, or variable declaration, or a function alias
// (Lparen.Line > 0) indicates a parenthesized declaration. // declaration. A valid Lparen position (Lparen.Line > 0) indicates
// a parenthesized declaration.
// //
// Relationship between Tok value and Specs element type: // Relationship between Tok value and Specs element type:
// //
// token.IMPORT *ImportSpec // token.IMPORT *ImportSpec
// token.CONST *ValueSpec // token.CONST *ValueSpec or *AliasSpec
// token.TYPE *TypeSpec // token.TYPE *TypeSpec or *AliasSpec
// token.VAR *ValueSpec // token.VAR *ValueSpec or *AliasSpec
// token.FUNC *AliasSpec
// //
GenDecl struct { GenDecl struct {
Doc *CommentGroup // associated documentation; or nil Doc *CommentGroup // associated documentation; or nil
TokPos token.Pos // position of Tok TokPos token.Pos // position of Tok
Tok token.Token // IMPORT, CONST, TYPE, VAR Tok token.Token // IMPORT, CONST, TYPE, VAR, FUNC (alias decl only)
Lparen token.Pos // position of '(', if any Lparen token.Pos // position of '(', if any
Specs []Spec Specs []Spec
Rparen token.Pos // position of ')', if any Rparen token.Pos // position of ')', if any
......
...@@ -156,6 +156,10 @@ func filterType(typ Expr, f Filter, export bool) bool { ...@@ -156,6 +156,10 @@ func filterType(typ Expr, f Filter, export bool) bool {
func filterSpec(spec Spec, f Filter, export bool) bool { func filterSpec(spec Spec, f Filter, export bool) bool {
switch s := spec.(type) { switch s := spec.(type) {
case *AliasSpec:
if f(s.Name.Name) {
return true
}
case *ValueSpec: case *ValueSpec:
s.Names = filterIdentList(s.Names, f) s.Names = filterIdentList(s.Names, f)
if len(s.Names) > 0 { if len(s.Names) > 0 {
......
...@@ -297,6 +297,16 @@ func Walk(v Visitor, node Node) { ...@@ -297,6 +297,16 @@ func Walk(v Visitor, node Node) {
Walk(v, n.Comment) Walk(v, n.Comment)
} }
case *AliasSpec:
if n.Doc != nil {
Walk(v, n.Doc)
}
Walk(v, n.Name)
Walk(v, n.Orig)
if n.Comment != nil {
Walk(v, n.Comment)
}
case *ValueSpec: case *ValueSpec:
if n.Doc != nil { if n.Doc != nil {
Walk(v, n.Doc) Walk(v, n.Doc)
......
...@@ -542,18 +542,18 @@ func (p *parser) parseIdent() *ast.Ident { ...@@ -542,18 +542,18 @@ func (p *parser) parseIdent() *ast.Ident {
return &ast.Ident{NamePos: pos, Name: name} return &ast.Ident{NamePos: pos, Name: name}
} }
func (p *parser) parseIdentList() (list []*ast.Ident) { func (p *parser) parseIdentList(first *ast.Ident) []*ast.Ident {
if p.trace { if p.trace {
defer un(trace(p, "IdentList")) defer un(trace(p, "IdentList"))
} }
list = append(list, p.parseIdent()) list := []*ast.Ident{first}
for p.tok == token.COMMA { for p.tok == token.COMMA {
p.next() p.next()
list = append(list, p.parseIdent()) list = append(list, p.parseIdent())
} }
return return list
} }
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
...@@ -640,11 +640,10 @@ func (p *parser) parseTypeName() ast.Expr { ...@@ -640,11 +640,10 @@ func (p *parser) parseTypeName() ast.Expr {
// don't resolve ident yet - it may be a parameter or field name // don't resolve ident yet - it may be a parameter or field name
if p.tok == token.PERIOD { if p.tok == token.PERIOD {
// ident is a package name // ident must be a package name
p.next() p.next()
p.resolve(ident) p.resolve(ident)
sel := p.parseIdent() return &ast.SelectorExpr{X: ident, Sel: p.parseIdent()}
return &ast.SelectorExpr{X: ident, Sel: sel}
} }
return ident return ident
...@@ -842,7 +841,7 @@ func (p *parser) parseParameterList(scope *ast.Scope, ellipsisOk bool) (params [ ...@@ -842,7 +841,7 @@ func (p *parser) parseParameterList(scope *ast.Scope, ellipsisOk bool) (params [
} }
p.next() p.next()
for p.tok != token.RPAREN && p.tok != token.EOF { for p.tok != token.RPAREN && p.tok != token.EOF {
idents := p.parseIdentList() idents := p.parseIdentList(p.parseIdent())
typ := p.parseVarType(ellipsisOk) typ := p.parseVarType(ellipsisOk)
field := &ast.Field{Names: idents, Type: typ} field := &ast.Field{Names: idents, Type: typ}
params = append(params, field) params = append(params, field)
...@@ -1170,16 +1169,6 @@ func (p *parser) parseOperand(lhs bool) ast.Expr { ...@@ -1170,16 +1169,6 @@ func (p *parser) parseOperand(lhs bool) ast.Expr {
return &ast.BadExpr{From: pos, To: p.pos} return &ast.BadExpr{From: pos, To: p.pos}
} }
func (p *parser) parseSelector(x ast.Expr) ast.Expr {
if p.trace {
defer un(trace(p, "Selector"))
}
sel := p.parseIdent()
return &ast.SelectorExpr{X: x, Sel: sel}
}
func (p *parser) parseTypeAssertion(x ast.Expr) ast.Expr { func (p *parser) parseTypeAssertion(x ast.Expr) ast.Expr {
if p.trace { if p.trace {
defer un(trace(p, "TypeAssertion")) defer un(trace(p, "TypeAssertion"))
...@@ -1474,7 +1463,7 @@ L: ...@@ -1474,7 +1463,7 @@ L:
} }
switch p.tok { switch p.tok {
case token.IDENT: case token.IDENT:
x = p.parseSelector(p.checkExprOrType(x)) x = &ast.SelectorExpr{X: p.checkExprOrType(x), Sel: p.parseIdent()}
case token.LPAREN: case token.LPAREN:
x = p.parseTypeAssertion(p.checkExpr(x)) x = p.parseTypeAssertion(p.checkExpr(x))
default: default:
...@@ -2267,13 +2256,51 @@ func (p *parser) parseImportSpec(doc *ast.CommentGroup, _ token.Token, _ int) as ...@@ -2267,13 +2256,51 @@ func (p *parser) parseImportSpec(doc *ast.CommentGroup, _ token.Token, _ int) as
return spec return spec
} }
// AliasSpec = identifier "=>" [ PackageName "." ] identifier .
func (p *parser) parseAliasSpec(doc *ast.CommentGroup, kind ast.ObjKind, ident *ast.Ident) ast.Spec {
// no tracing since this is already called from a parse(Value/Type)Spec or parseFuncDecl
// lhs identifier and "=>" have been consumed already
var orig ast.Expr = p.parseIdent()
if p.tok == token.PERIOD {
// orig must be a package name
p.next()
p.resolve(orig)
orig = &ast.SelectorExpr{X: orig, Sel: p.parseIdent()}
}
p.expectSemi() // call before accessing p.linecomment
spec := &ast.AliasSpec{
Doc: doc,
Name: ident,
Orig: orig,
Comment: p.lineComment,
}
p.declare(spec, nil, p.topScope, kind, ident)
return spec
}
func (p *parser) parseValueSpec(doc *ast.CommentGroup, keyword token.Token, iota int) ast.Spec { func (p *parser) parseValueSpec(doc *ast.CommentGroup, keyword token.Token, iota int) ast.Spec {
if p.trace { if p.trace {
defer un(trace(p, keyword.String()+"Spec")) defer un(trace(p, keyword.String()+"Spec"))
} }
kind := ast.Con
if keyword == token.VAR {
kind = ast.Var
}
pos := p.pos pos := p.pos
idents := p.parseIdentList() ident := p.parseIdent()
if p.tok == token.ALIAS {
p.next()
return p.parseAliasSpec(doc, kind, ident)
}
idents := p.parseIdentList(ident)
typ := p.tryType() typ := p.tryType()
var values []ast.Expr var values []ast.Expr
// always permit optional initialization for more tolerant parsing // always permit optional initialization for more tolerant parsing
...@@ -2305,10 +2332,6 @@ func (p *parser) parseValueSpec(doc *ast.CommentGroup, keyword token.Token, iota ...@@ -2305,10 +2332,6 @@ func (p *parser) parseValueSpec(doc *ast.CommentGroup, keyword token.Token, iota
Values: values, Values: values,
Comment: p.lineComment, Comment: p.lineComment,
} }
kind := ast.Con
if keyword == token.VAR {
kind = ast.Var
}
p.declare(spec, iota, p.topScope, kind, idents...) p.declare(spec, iota, p.topScope, kind, idents...)
return spec return spec
...@@ -2320,6 +2343,11 @@ func (p *parser) parseTypeSpec(doc *ast.CommentGroup, _ token.Token, _ int) ast. ...@@ -2320,6 +2343,11 @@ func (p *parser) parseTypeSpec(doc *ast.CommentGroup, _ token.Token, _ int) ast.
} }
ident := p.parseIdent() ident := p.parseIdent()
// permit both: type T => p.T and: type T = p.T for now
if p.tok == token.ALIAS || p.tok == token.ASSIGN {
p.next()
return p.parseAliasSpec(doc, ast.Typ, ident)
}
// Go spec: The scope of a type identifier declared inside a function begins // Go spec: The scope of a type identifier declared inside a function begins
// at the identifier in the TypeSpec and ends at the end of the innermost // at the identifier in the TypeSpec and ends at the end of the innermost
...@@ -2366,7 +2394,7 @@ func (p *parser) parseGenDecl(keyword token.Token, f parseSpecFunction) *ast.Gen ...@@ -2366,7 +2394,7 @@ func (p *parser) parseGenDecl(keyword token.Token, f parseSpecFunction) *ast.Gen
} }
} }
func (p *parser) parseFuncDecl() *ast.FuncDecl { func (p *parser) parseFuncDecl() ast.Decl {
if p.trace { if p.trace {
defer un(trace(p, "FunctionDecl")) defer un(trace(p, "FunctionDecl"))
} }
...@@ -2381,6 +2409,15 @@ func (p *parser) parseFuncDecl() *ast.FuncDecl { ...@@ -2381,6 +2409,15 @@ func (p *parser) parseFuncDecl() *ast.FuncDecl {
} }
ident := p.parseIdent() ident := p.parseIdent()
if recv == nil && p.tok == token.ALIAS {
p.next()
return &ast.GenDecl{
Doc: doc,
TokPos: pos,
Tok: token.FUNC,
Specs: []ast.Spec{p.parseAliasSpec(nil, ast.Fun, ident)},
}
}
params, results := p.parseSignature(scope) params, results := p.parseSignature(scope)
......
...@@ -46,6 +46,8 @@ var valids = []string{ ...@@ -46,6 +46,8 @@ var valids = []string{
`package p; const (x = 0; y; z)`, // issue 9639 `package p; const (x = 0; y; z)`, // issue 9639
`package p; var _ = map[P]int{P{}:0, {}:1}`, `package p; var _ = map[P]int{P{}:0, {}:1}`,
`package p; var _ = map[*P]int{&P{}:0, {}:1}`, `package p; var _ = map[*P]int{&P{}:0, {}:1}`,
`package p; const c => p.C; var x => X; type T => p.T; func F => p.F`,
`package p; var (_ int; x => p.X; y => Y); type (t => T; t1 = p.T1)`,
} }
func TestValid(t *testing.T) { func TestValid(t *testing.T) {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment