]> Cypherpunks repositories - gostls13.git/commitdiff
go/ast, go/parser: parse alias declarations
authorRobert Griesemer <gri@golang.org>
Mon, 3 Oct 2016 20:28:25 +0000 (13:28 -0700)
committerRobert Griesemer <gri@golang.org>
Tue, 4 Oct 2016 22:45:01 +0000 (22:45 +0000)
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 <mdempsky@google.com>
src/go/ast/ast.go
src/go/ast/filter.go
src/go/ast/walk.go
src/go/parser/parser.go
src/go/parser/short_test.go

index b6dc2a6c16fc3957aea3caef8930499019237913..8ffbaf9869d1c35ecae0522ee756fa0eae763b73 100644 (file)
@@ -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
index bb571166f47209bd738514ac231db83150336766..bd0bf87f11b7d0cd15b93b9b1fdb6864274ae971 100644 (file)
@@ -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 {
index 8ca21959b11a012cae79c9f018100cb3bf1fe7fc..b474e1e29ab2568a94b479e2ebbab62e14d230ca 100644 (file)
@@ -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)
index d3ef7db31eafbfe3c0f35007ce92e536bfebc8be..0cff9f005ed1b0f28f5f1b8a7e6f98b86dbf2265 100644 (file)
@@ -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)
 
index cdd343ea3c1fd6df8c67772c24eaba6e8544725c..0360cea0a6f1bcdddbbef59829a37e94d4b9fb86 100644 (file)
@@ -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) {