From 71af0f854f7ab56340c45a56efdd5cc23fda467b Mon Sep 17 00:00:00 2001 From: xushiwei Date: Tue, 9 Apr 2024 17:23:21 +0800 Subject: [PATCH] #1847 Static methods --- ast/ast.go | 1 + parser/_testdata/staticmthd1/parser.expect | 37 +++++++++++++++++++ .../_testdata/staticmthd1/static_method.gop | 2 + parser/parser.go | 31 ++++++++++++---- parser/parser_test.go | 14 +++++++ 5 files changed, 77 insertions(+), 8 deletions(-) create mode 100644 parser/_testdata/staticmthd1/parser.expect create mode 100644 parser/_testdata/staticmthd1/static_method.gop diff --git a/ast/ast.go b/ast/ast.go index 2ddec5687..9f3753a58 100644 --- a/ast/ast.go +++ b/ast/ast.go @@ -1055,6 +1055,7 @@ type ( Operator bool // is operator or not Shadow bool // is a shadow entry IsClass bool // recv set by class + Static bool // recv is static (class method) } ) diff --git a/parser/_testdata/staticmthd1/parser.expect b/parser/_testdata/staticmthd1/parser.expect new file mode 100644 index 000000000..2fc5405f5 --- /dev/null +++ b/parser/_testdata/staticmthd1/parser.expect @@ -0,0 +1,37 @@ +package main + +file static_method.gop +ast.FuncDecl: + Recv: + ast.FieldList: + List: + ast.Field: + Type: + ast.Ident: + Name: T + Name: + ast.Ident: + Name: foo + Type: + ast.FuncType: + Params: + ast.FieldList: + List: + ast.Field: + Names: + ast.Ident: + Name: a + ast.Ident: + Name: b + Type: + ast.Ident: + Name: int + Results: + ast.FieldList: + List: + ast.Field: + Type: + ast.Ident: + Name: string + Body: + ast.BlockStmt: diff --git a/parser/_testdata/staticmthd1/static_method.gop b/parser/_testdata/staticmthd1/static_method.gop new file mode 100644 index 000000000..b5abce4bf --- /dev/null +++ b/parser/_testdata/staticmthd1/static_method.gop @@ -0,0 +1,2 @@ +func T.foo(a, b int) string { +} diff --git a/parser/parser.go b/parser/parser.go index f2d2fc42b..3ad037599 100644 --- a/parser/parser.go +++ b/parser/parser.go @@ -3648,7 +3648,7 @@ func (p *parser) parseGenDecl(keyword token.Token, f parseSpecFunction) *ast.Gen } } -func isOverloadOps(tok token.Token) bool { +func isOverloadOp(tok token.Token) bool { return int(tok) < len(overloadOps) && overloadOps[tok] != 0 } @@ -3705,6 +3705,8 @@ func (p *parser) parseOverloadDecl(decl *ast.OverloadFuncDecl) *ast.OverloadFunc // `func identOrOp(params) results {...}` // `func identOrOp = (overloadFuncs)` // +// `func T.ident(params) results { ... }` +// // `func (recv) identOrOp(params) results { ... }` // `func (T).identOrOp = (overloadFuncs)` // `func (params) results { ... }()` @@ -3719,13 +3721,15 @@ func (p *parser) parseFuncDeclOrCall() (ast.Decl, *ast.CallExpr) { var recv, params, results *ast.FieldList var ident *ast.Ident - var isOp, isFunLit, ok bool + var isOp, isStatic, isFunLit, ok bool if p.tok != token.LPAREN { // func: `func identOrOp(...) results` // overload: `func identOrOp = (overloadFuncs)` + // static method: `func T.ident(...) results` ident, isOp = p.parseIdentOrOp() - if p.tok == token.ASSIGN { + switch p.tok { + case token.ASSIGN: // func identOrOp = (overloadFuncs) return p.parseOverloadDecl(&ast.OverloadFuncDecl{ Doc: doc, @@ -3733,6 +3737,14 @@ func (p *parser) parseFuncDeclOrCall() (ast.Decl, *ast.CallExpr) { Name: ident, Operator: isOp, }), nil + case token.PERIOD: + // func T.ident(...) results + if !isOp { + p.next() + recv = &ast.FieldList{List: []*ast.Field{{Type: ident}}} + ident = p.parseIdent() + isStatic = true + } } params, results = p.parseSignature(scope) } else { @@ -3754,8 +3766,8 @@ func (p *parser) parseFuncDeclOrCall() (ast.Decl, *ast.CallExpr) { Name: ident, Operator: isOp, }), nil - } else if isOp = isOverloadOps(p.tok); isOp { - oldtok, oldpos := p.tok, p.pos + } else if isOp = isOverloadOp(p.tok); isOp { + oldtok, oldpos, oldlit := p.tok, p.pos, p.lit p.next() if p.tok == token.LPAREN { // func (recv) op(params) results { ... } @@ -3763,10 +3775,12 @@ func (p *parser) parseFuncDeclOrCall() (ast.Decl, *ast.CallExpr) { params, results = p.parseSignature(scope) } else { // func (params) typ { ... }() - p.unget(oldpos, oldtok, "") + p.unget(oldpos, oldtok, oldlit) typ := p.tryType() if typ == nil { - panic("TODO: invalid result type") + p.errorExpected(oldpos, "type", 1) + p.next() + typ = &ast.BadExpr{From: oldpos, To: p.pos} } isFunLit, results = true, &ast.FieldList{List: []*ast.Field{{Type: typ}}} } @@ -3799,7 +3813,7 @@ func (p *parser) parseFuncDeclOrCall() (ast.Decl, *ast.CallExpr) { if isOp { if params == nil || len(params.List) != 1 { - log.Panicln("TODO: overload operator can only have one parameter") + p.error(ident.Pos(), "overload operator can only have one parameter") } } var body *ast.BlockStmt @@ -3829,6 +3843,7 @@ func (p *parser) parseFuncDeclOrCall() (ast.Decl, *ast.CallExpr) { }, Body: body, Operator: isOp, + Static: isStatic, } if recv == nil { // Go spec: The scope of an identifier denoting a constant, type, diff --git a/parser/parser_test.go b/parser/parser_test.go index c7cc34505..e0815f3b9 100644 --- a/parser/parser_test.go +++ b/parser/parser_test.go @@ -325,4 +325,18 @@ func TestCheckExpr(t *testing.T) { p.checkExpr(&ast.FuncLit{}) } +func TestErrFuncDecl(t *testing.T) { + testErrCode(t, `func test() +{ +} +`, `/foo/bar.gop:2:1: unexpected semicolon or newline before {`, ``) + testErrCode(t, `func test() +1 +`, `/foo/bar.gop:1:13: expected ';', found '+'`, ``) + testErrCode(t, ` +func (a T) +{} +`, `/foo/bar.gop:2:12: expected type, found '+'`, ``) + testErrCode(t, `func +(a T, b T) {} +`, `/foo/bar.gop:1:6: overload operator can only have one parameter`, ``) +} + // -----------------------------------------------------------------------------