From 57ae83307fc4cb90338b39dcc6fe3c61ee8ce0b7 Mon Sep 17 00:00:00 2001 From: Robert Griesemer Date: Mon, 3 Oct 2016 13:28:25 -0700 Subject: [PATCH] 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/30211 Reviewed-by: Matthew Dempsky --- src/go/ast/ast.go | 28 ++++++++---- src/go/ast/filter.go | 4 ++ src/go/ast/walk.go | 10 +++++ src/go/parser/parser.go | 85 ++++++++++++++++++++++++++----------- src/go/parser/short_test.go | 2 + 5 files changed, 97 insertions(+), 32 deletions(-) diff --git a/src/go/ast/ast.go b/src/go/ast/ast.go index b6dc2a6c16..8ffbaf9869 100644 --- a/src/go/ast/ast.go +++ b/src/go/ast/ast.go @@ -818,7 +818,7 @@ func (*RangeStmt) stmtNode() {} // constant, type, or variable declaration. // 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 { Node specNode() @@ -833,6 +833,14 @@ type ( 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 // (ConstSpec or VarSpec production). // @@ -861,6 +869,7 @@ func (s *ImportSpec) Pos() token.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 *TypeSpec) Pos() token.Pos { return s.Name.Pos() } @@ -870,7 +879,7 @@ func (s *ImportSpec) End() token.Pos { } return s.Path.End() } - +func (s *AliasSpec) End() token.Pos { return s.Orig.End() } func (s *ValueSpec) End() token.Pos { if n := len(s.Values); n > 0 { return s.Values[n-1].End() @@ -886,6 +895,7 @@ func (s *TypeSpec) End() token.Pos { return s.Type.End() } // assigned to a Spec. // func (*ImportSpec) specNode() {} +func (*AliasSpec) specNode() {} func (*ValueSpec) specNode() {} func (*TypeSpec) specNode() {} @@ -901,20 +911,22 @@ type ( } // A GenDecl node (generic declaration node) represents an import, - // constant, type or variable declaration. A valid Lparen position - // (Lparen.Line > 0) indicates a parenthesized declaration. + // constant, type, or variable declaration, or a function alias + // declaration. A valid Lparen position (Lparen.Line > 0) indicates + // a parenthesized declaration. // // Relationship between Tok value and Specs element type: // // token.IMPORT *ImportSpec - // token.CONST *ValueSpec - // token.TYPE *TypeSpec - // token.VAR *ValueSpec + // token.CONST *ValueSpec or *AliasSpec + // token.TYPE *TypeSpec or *AliasSpec + // token.VAR *ValueSpec or *AliasSpec + // token.FUNC *AliasSpec // GenDecl struct { Doc *CommentGroup // associated documentation; or nil 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 Specs []Spec Rparen token.Pos // position of ')', if any diff --git a/src/go/ast/filter.go b/src/go/ast/filter.go index bb571166f4..bd0bf87f11 100644 --- a/src/go/ast/filter.go +++ b/src/go/ast/filter.go @@ -156,6 +156,10 @@ func filterType(typ Expr, f Filter, export bool) bool { func filterSpec(spec Spec, f Filter, export bool) bool { switch s := spec.(type) { + case *AliasSpec: + if f(s.Name.Name) { + return true + } case *ValueSpec: s.Names = filterIdentList(s.Names, f) if len(s.Names) > 0 { diff --git a/src/go/ast/walk.go b/src/go/ast/walk.go index 8ca21959b1..b474e1e29a 100644 --- a/src/go/ast/walk.go +++ b/src/go/ast/walk.go @@ -297,6 +297,16 @@ func Walk(v Visitor, node Node) { 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: if n.Doc != nil { Walk(v, n.Doc) diff --git a/src/go/parser/parser.go b/src/go/parser/parser.go index d3ef7db31e..0cff9f005e 100644 --- a/src/go/parser/parser.go +++ b/src/go/parser/parser.go @@ -542,18 +542,18 @@ func (p *parser) parseIdent() *ast.Ident { 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 { defer un(trace(p, "IdentList")) } - list = append(list, p.parseIdent()) + list := []*ast.Ident{first} for p.tok == token.COMMA { p.next() list = append(list, p.parseIdent()) } - return + return list } // ---------------------------------------------------------------------------- @@ -640,11 +640,10 @@ func (p *parser) parseTypeName() ast.Expr { // don't resolve ident yet - it may be a parameter or field name if p.tok == token.PERIOD { - // ident is a package name + // ident must be a package name p.next() p.resolve(ident) - sel := p.parseIdent() - return &ast.SelectorExpr{X: ident, Sel: sel} + return &ast.SelectorExpr{X: ident, Sel: p.parseIdent()} } return ident @@ -842,7 +841,7 @@ func (p *parser) parseParameterList(scope *ast.Scope, ellipsisOk bool) (params [ } p.next() for p.tok != token.RPAREN && p.tok != token.EOF { - idents := p.parseIdentList() + idents := p.parseIdentList(p.parseIdent()) typ := p.parseVarType(ellipsisOk) field := &ast.Field{Names: idents, Type: typ} params = append(params, field) @@ -1170,16 +1169,6 @@ func (p *parser) parseOperand(lhs bool) ast.Expr { 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 { if p.trace { defer un(trace(p, "TypeAssertion")) @@ -1474,7 +1463,7 @@ L: } switch p.tok { case token.IDENT: - x = p.parseSelector(p.checkExprOrType(x)) + x = &ast.SelectorExpr{X: p.checkExprOrType(x), Sel: p.parseIdent()} case token.LPAREN: x = p.parseTypeAssertion(p.checkExpr(x)) default: @@ -2267,13 +2256,51 @@ func (p *parser) parseImportSpec(doc *ast.CommentGroup, _ token.Token, _ int) as 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 { if p.trace { defer un(trace(p, keyword.String()+"Spec")) } + kind := ast.Con + if keyword == token.VAR { + kind = ast.Var + } + 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() var values []ast.Expr // always permit optional initialization for more tolerant parsing @@ -2305,10 +2332,6 @@ func (p *parser) parseValueSpec(doc *ast.CommentGroup, keyword token.Token, iota Values: values, Comment: p.lineComment, } - kind := ast.Con - if keyword == token.VAR { - kind = ast.Var - } p.declare(spec, iota, p.topScope, kind, idents...) return spec @@ -2320,6 +2343,11 @@ func (p *parser) parseTypeSpec(doc *ast.CommentGroup, _ token.Token, _ int) ast. } 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 // 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 } } -func (p *parser) parseFuncDecl() *ast.FuncDecl { +func (p *parser) parseFuncDecl() ast.Decl { if p.trace { defer un(trace(p, "FunctionDecl")) } @@ -2381,6 +2409,15 @@ func (p *parser) parseFuncDecl() *ast.FuncDecl { } 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) diff --git a/src/go/parser/short_test.go b/src/go/parser/short_test.go index cdd343ea3c..0360cea0a6 100644 --- a/src/go/parser/short_test.go +++ b/src/go/parser/short_test.go @@ -46,6 +46,8 @@ var valids = []string{ `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; 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) { -- 2.48.1