From 752428d317a3d084f3a0a7279f03e030361f8a20 Mon Sep 17 00:00:00 2001 From: Ernest Micklei Date: Thu, 8 Mar 2018 22:09:53 +0100 Subject: [PATCH] add Parent field to all Visitee (#68) * add Parent field to all Visitee --- comment.go | 3 ++ enum.go | 22 ++++++-- extensions.go | 4 ++ field.go | 3 ++ group.go | 4 ++ import.go | 3 ++ message.go | 4 ++ oneof.go | 12 +++-- option.go | 3 ++ package.go | 3 ++ parent_accessor.go | 88 ++++++++++++++++++++++++++++++++ parent_test.go | 124 +++++++++++++++++++++++++++++++++++++++++++++ proto.go | 28 +++++++--- protobuf_test.go | 28 +++++----- reserved.go | 3 ++ service.go | 14 +++-- syntax.go | 3 ++ visitor.go | 2 + 18 files changed, 318 insertions(+), 33 deletions(-) create mode 100644 parent_accessor.go create mode 100644 parent_test.go diff --git a/comment.go b/comment.go index 6aefc20..f8545f1 100644 --- a/comment.go +++ b/comment.go @@ -141,3 +141,6 @@ func mergeOrReturnComment(elements []Visitee, lit string, pos scanner.Position) last.Merge(com) return nil } + +// parent is part of elementContainer +func (c *Comment) parent(Visitee) {} diff --git a/enum.go b/enum.go index dc27f9d..2e979d9 100644 --- a/enum.go +++ b/enum.go @@ -33,6 +33,7 @@ type Enum struct { Comment *Comment Name string Elements []Visitee + Parent Visitee } // Accept dispatches the call to the visitor. @@ -47,6 +48,7 @@ func (e *Enum) Doc() *Comment { // addElement is part of elementContainer func (e *Enum) addElement(v Visitee) { + v.parent(e) e.Elements = append(e.Elements, v) } @@ -79,7 +81,7 @@ func (e *Enum) parse(p *Parser) error { switch tok { case tCOMMENT: if com := mergeOrReturnComment(e.elements(), lit, pos); com != nil { // not merged? - e.Elements = append(e.Elements, com) + e.addElement(com) } case tOPTION: v := new(Option) @@ -89,7 +91,7 @@ func (e *Enum) parse(p *Parser) error { if err != nil { return err } - e.Elements = append(e.Elements, v) + e.addElement(v) case tRIGHTCURLY, tEOF: goto done case tSEMICOLON: @@ -103,7 +105,7 @@ func (e *Enum) parse(p *Parser) error { if err != nil { return err } - e.Elements = append(e.Elements, f) + e.addElement(f) } } done: @@ -113,6 +115,9 @@ done: return nil } +// parent is part of elementContainer +func (e *Enum) parent(p Visitee) { e.Parent = p } + // EnumField is part of the body of an Enum. type EnumField struct { Position scanner.Position @@ -123,6 +128,7 @@ type EnumField struct { ValueOption *Option Elements []Visitee // such as Option and Comment InlineComment *Comment + Parent Visitee } // Accept dispatches the call to the visitor. @@ -169,7 +175,7 @@ func (f *EnumField) parse(p *Parser) error { } // update deprecated field with the last option found f.ValueOption = o - f.Elements = append(f.Elements, o) + f.addElement(o) pos, tok, lit = p.next() if tok == tCOMMA { continue @@ -184,3 +190,11 @@ func (f *EnumField) parse(p *Parser) error { } return nil } + +// addElement is part of elementContainer +func (f *EnumField) addElement(v Visitee) { + v.parent(f) + f.Elements = append(f.Elements, v) +} + +func (f *EnumField) parent(v Visitee) { f.Parent = v } diff --git a/extensions.go b/extensions.go index 7e52d93..5c615b6 100644 --- a/extensions.go +++ b/extensions.go @@ -34,6 +34,7 @@ type Extensions struct { Comment *Comment Ranges []Range InlineComment *Comment + Parent Visitee } // inlineComment is part of commentInliner. @@ -55,3 +56,6 @@ func (e *Extensions) parse(p *Parser) error { e.Ranges = list return nil } + +// parent is part of elementContainer +func (e *Extensions) parent(p Visitee) { e.Parent = p } diff --git a/field.go b/field.go index efe2c88..e75c9e9 100644 --- a/field.go +++ b/field.go @@ -36,6 +36,7 @@ type Field struct { Sequence int Options []*Option InlineComment *Comment + Parent Visitee } // inlineComment is part of commentInliner. @@ -175,3 +176,5 @@ func (f *MapField) parse(p *Parser) error { } return parseFieldAfterType(f.Field, p) } + +func (f *Field) parent(v Visitee) { f.Parent = v } diff --git a/group.go b/group.go index 1ef9458..f865d1c 100644 --- a/group.go +++ b/group.go @@ -38,6 +38,7 @@ type Group struct { Required bool Sequence int Elements []Visitee + Parent Visitee } // Accept dispatches the call to the visitor. @@ -47,6 +48,7 @@ func (g *Group) Accept(v Visitor) { // addElement is part of elementContainer func (g *Group) addElement(v Visitee) { + v.parent(g) g.Elements = append(g.Elements, v) } @@ -92,3 +94,5 @@ func (g *Group) parse(p *Parser) error { } return parseMessageBody(p, g) } + +func (g *Group) parent(v Visitee) { g.Parent = v } diff --git a/import.go b/import.go index fe978ca..aaf27ac 100644 --- a/import.go +++ b/import.go @@ -34,6 +34,7 @@ type Import struct { Filename string Kind string // weak, public, InlineComment *Comment + Parent Visitee } func (i *Import) parse(p *Parser) error { @@ -67,3 +68,5 @@ func (i *Import) inlineComment(c *Comment) { func (i *Import) Doc() *Comment { return i.Comment } + +func (i *Import) parent(v Visitee) { i.Parent = v } diff --git a/message.go b/message.go index c7546bf..6fe3682 100644 --- a/message.go +++ b/message.go @@ -34,6 +34,7 @@ type Message struct { Name string IsExtend bool Elements []Visitee + Parent Visitee } func (m *Message) groupName() string { @@ -209,6 +210,7 @@ func (m *Message) Accept(v Visitor) { // addElement is part of elementContainer func (m *Message) addElement(v Visitee) { + v.parent(m) m.Elements = append(m.Elements, v) } @@ -226,3 +228,5 @@ func (m *Message) takeLastComment(expectedOnLine int) (last *Comment) { func (m *Message) Doc() *Comment { return m.Comment } + +func (m *Message) parent(v Visitee) { m.Parent = v } diff --git a/oneof.go b/oneof.go index 9412c55..c093bbd 100644 --- a/oneof.go +++ b/oneof.go @@ -33,10 +33,12 @@ type Oneof struct { Comment *Comment Name string Elements []Visitee + Parent Visitee } // addElement is part of elementContainer func (o *Oneof) addElement(v Visitee) { + v.parent(o) o.Elements = append(o.Elements, v) } @@ -71,7 +73,7 @@ func (o *Oneof) parse(p *Parser) error { switch tok { case tCOMMENT: if com := mergeOrReturnComment(o.elements(), lit, pos); com != nil { // not merged? - o.Elements = append(o.Elements, com) + o.addElement(com) } case tIDENT: f := newOneOfField() @@ -81,7 +83,7 @@ func (o *Oneof) parse(p *Parser) error { if err := parseFieldAfterType(f.Field, p); err != nil { return err } - o.Elements = append(o.Elements, f) + o.addElement(f) case tGROUP: g := new(Group) g.Position = pos @@ -89,7 +91,7 @@ func (o *Oneof) parse(p *Parser) error { if err := g.parse(p); err != nil { return err } - o.Elements = append(o.Elements, g) + o.addElement(g) case tOPTION: opt := new(Option) opt.Position = pos @@ -97,7 +99,7 @@ func (o *Oneof) parse(p *Parser) error { if err := opt.parse(p); err != nil { return err } - o.Elements = append(o.Elements, opt) + o.addElement(opt) case tSEMICOLON: maybeScanInlineComment(p, o) // continue @@ -134,3 +136,5 @@ func (o *OneOfField) Accept(v Visitor) { func (o *OneOfField) Doc() *Comment { return o.Comment } + +func (o *Oneof) parent(v Visitee) { o.Parent = v } diff --git a/option.go b/option.go index 475084f..8edd59a 100644 --- a/option.go +++ b/option.go @@ -38,6 +38,7 @@ type Option struct { IsEmbedded bool AggregatedConstants []*NamedLiteral InlineComment *Comment + Parent Visitee } // parse reads an Option body @@ -233,3 +234,5 @@ func parseAggregateConstants(p *Parser, container interface{}) (list []*NamedLit list = append(list, &NamedLiteral{Name: key, Literal: l, PrintsColon: printsColon}) } } + +func (o *Option) parent(v Visitee) { o.Parent = v } diff --git a/package.go b/package.go index c51390b..929a71a 100644 --- a/package.go +++ b/package.go @@ -31,6 +31,7 @@ type Package struct { Comment *Comment Name string InlineComment *Comment + Parent Visitee } // Doc is part of Documented @@ -58,3 +59,5 @@ func (p *Package) Accept(v Visitor) { func (p *Package) inlineComment(c *Comment) { p.InlineComment = c } + +func (p *Package) parent(v Visitee) { p.Parent = v } diff --git a/parent_accessor.go b/parent_accessor.go new file mode 100644 index 0000000..f85eb5e --- /dev/null +++ b/parent_accessor.go @@ -0,0 +1,88 @@ +// Copyright (c) 2018 Ernest Micklei +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +package proto + +func getParent(child Visitee) Visitee { + if child == nil { + return nil + } + pa := new(parentAccessor) + child.Accept(pa) + return pa.parent +} + +type parentAccessor struct { + parent Visitee +} + +func (p *parentAccessor) VisitMessage(m *Message) { + p.parent = m.Parent +} +func (p *parentAccessor) VisitService(v *Service) { + p.parent = v.Parent +} +func (p *parentAccessor) VisitSyntax(s *Syntax) { + p.parent = s.Parent +} +func (p *parentAccessor) VisitPackage(pkg *Package) { + p.parent = pkg.Parent +} +func (p *parentAccessor) VisitOption(o *Option) { + p.parent = o.Parent +} +func (p *parentAccessor) VisitImport(i *Import) { + p.parent = i.Parent +} +func (p *parentAccessor) VisitNormalField(i *NormalField) { + p.parent = i.Parent +} +func (p *parentAccessor) VisitEnumField(i *EnumField) { + p.parent = i.Parent +} +func (p *parentAccessor) VisitEnum(e *Enum) { + p.parent = e.Parent +} +func (p *parentAccessor) VisitComment(e *Comment) {} +func (p *parentAccessor) VisitOneof(o *Oneof) { + p.parent = o.Parent +} +func (p *parentAccessor) VisitOneofField(o *OneOfField) { + p.parent = o.Parent +} +func (p *parentAccessor) VisitReserved(rs *Reserved) { + p.parent = rs.Parent +} +func (p *parentAccessor) VisitRPC(rpc *RPC) { + p.parent = rpc.Parent +} +func (p *parentAccessor) VisitMapField(f *MapField) { + p.parent = f.Parent +} +func (p *parentAccessor) VisitGroup(g *Group) { + p.parent = g.Parent +} +func (p *parentAccessor) VisitExtensions(e *Extensions) { + p.parent = e.Parent +} +func (p *parentAccessor) VisitProto(*Proto) {} diff --git a/parent_test.go b/parent_test.go new file mode 100644 index 0000000..cc986b6 --- /dev/null +++ b/parent_test.go @@ -0,0 +1,124 @@ +// Copyright (c) 2018 Ernest Micklei +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +package proto + +import ( + "fmt" + "testing" +) + +type parentChecker struct { + errors []error +} + +func checkParent(v Visitee, t *testing.T) { + pc := new(parentChecker) + v.Accept(pc) + if len(pc.errors) == 0 { + return + } + for _, each := range pc.errors { + t.Error(each) + } +} + +func (pc *parentChecker) checkAll(list []Visitee, parent Visitee) { + for _, each := range list { + if _, ok := each.(*Comment); ok { + continue + } + if got, want := getParent(each), parent; got != want { + pc.errors = append(pc.errors, fmt.Errorf("%T has wrong parent set, got %v want %v", each, got, want)) + } + each.Accept(pc) + } +} +func (pc *parentChecker) check(astType, astName string, parent Visitee) { + if parent == nil { + pc.errors = append(pc.errors, fmt.Errorf("%s %s has no parent set", astType, astName)) + } +} + +func (pc *parentChecker) VisitProto(p *Proto) { + pc.checkAll(p.Elements, p) +} +func (pc *parentChecker) VisitMessage(m *Message) { + pc.check("Message", m.Name, m.Parent) + pc.checkAll(m.Elements, m) +} +func (pc *parentChecker) VisitService(v *Service) { + pc.check("Service", v.Name, v.Parent) + pc.checkAll(v.Elements, v) +} +func (pc *parentChecker) VisitSyntax(s *Syntax) { + pc.check("Syntax", s.Value, s.Parent) +} +func (pc *parentChecker) VisitPackage(p *Package) { + pc.check("Package", p.Name, p.Parent) +} +func (pc *parentChecker) VisitOption(o *Option) { + pc.check("Option", o.Name, o.Parent) +} +func (pc *parentChecker) VisitImport(i *Import) { + pc.check("Import", i.Filename, i.Parent) +} +func (pc *parentChecker) VisitNormalField(i *NormalField) { + pc.check("NormalField", i.Name, i.Parent) +} +func (pc *parentChecker) VisitEnumField(i *EnumField) { + pc.check("EnumField", i.Name, i.Parent) +} +func (pc *parentChecker) VisitEnum(e *Enum) { + pc.check("Enum", e.Name, e.Parent) + pc.checkAll(e.Elements, e) +} +func (pc *parentChecker) VisitComment(e *Comment) {} +func (pc *parentChecker) VisitOneof(o *Oneof) { + pc.check("Oneof", o.Name, o.Parent) + pc.checkAll(o.Elements, o) +} +func (pc *parentChecker) VisitOneofField(o *OneOfField) { + pc.check("OneOfField", o.Name, o.Parent) +} +func (pc *parentChecker) VisitReserved(r *Reserved) { + pc.check("Reserved", "", r.Parent) +} +func (pc *parentChecker) VisitRPC(r *RPC) { + pc.check("RPC", r.Name, r.Parent) + //pc.checkAll(r.Options, r) + for _, each := range r.Options { + pc.check("Option", each.Name, r) + } +} +func (pc *parentChecker) VisitMapField(f *MapField) { + pc.check("MapField", f.Name, f.Parent) +} + +// proto2 +func (pc *parentChecker) VisitGroup(g *Group) { + pc.check("Group", g.Name, g.Parent) + pc.checkAll(g.Elements, g) +} +func (pc *parentChecker) VisitExtensions(e *Extensions) { + pc.check("Extensions", "", e.Parent) +} diff --git a/proto.go b/proto.go index b8d3875..2bbdb29 100644 --- a/proto.go +++ b/proto.go @@ -29,8 +29,18 @@ type Proto struct { Elements []Visitee } +// Accept dispatches the call to the visitor. +func (proto *Proto) Accept(v Visitor) { + // As Proto is not (yet) a Visitee, we enumerate its elements instead + //v.VisitProto(proto) + for _, each := range proto.Elements { + each.Accept(v) + } +} + // addElement is part of elementContainer func (proto *Proto) addElement(v Visitee) { + v.parent(proto) proto.Elements = append(proto.Elements, v) } @@ -62,7 +72,7 @@ func (proto *Proto) parse(p *Parser) error { if err := o.parse(p); err != nil { return err } - proto.Elements = append(proto.Elements, o) + proto.addElement(o) case tSYNTAX == tok: s := new(Syntax) s.Position = pos @@ -70,7 +80,7 @@ func (proto *Proto) parse(p *Parser) error { if err := s.parse(p); err != nil { return err } - proto.Elements = append(proto.Elements, s) + proto.addElement(s) case tIMPORT == tok: im := new(Import) im.Position = pos @@ -78,7 +88,7 @@ func (proto *Proto) parse(p *Parser) error { if err := im.parse(p); err != nil { return err } - proto.Elements = append(proto.Elements, im) + proto.addElement(im) case tENUM == tok: enum := new(Enum) enum.Position = pos @@ -86,7 +96,7 @@ func (proto *Proto) parse(p *Parser) error { if err := enum.parse(p); err != nil { return err } - proto.Elements = append(proto.Elements, enum) + proto.addElement(enum) case tSERVICE == tok: service := new(Service) service.Position = pos @@ -95,7 +105,7 @@ func (proto *Proto) parse(p *Parser) error { if err != nil { return err } - proto.Elements = append(proto.Elements, service) + proto.addElement(service) case tPACKAGE == tok: pkg := new(Package) pkg.Position = pos @@ -103,7 +113,7 @@ func (proto *Proto) parse(p *Parser) error { if err := pkg.parse(p); err != nil { return err } - proto.Elements = append(proto.Elements, pkg) + proto.addElement(pkg) case tMESSAGE == tok: msg := new(Message) msg.Position = pos @@ -111,7 +121,7 @@ func (proto *Proto) parse(p *Parser) error { if err := msg.parse(p); err != nil { return err } - proto.Elements = append(proto.Elements, msg) + proto.addElement(msg) // BEGIN proto2 case tEXTEND == tok: msg := new(Message) @@ -121,7 +131,7 @@ func (proto *Proto) parse(p *Parser) error { if err := msg.parse(p); err != nil { return err } - proto.Elements = append(proto.Elements, msg) + proto.addElement(msg) // END proto2 case tSEMICOLON == tok: maybeScanInlineComment(p, proto) @@ -136,6 +146,8 @@ done: return nil } +func (proto *Proto) parent(v Visitee) {} + // elementContainer unifies types that have elements. type elementContainer interface { addElement(v Visitee) diff --git a/protobuf_test.go b/protobuf_test.go index b74c07a..8a9afa8 100644 --- a/protobuf_test.go +++ b/protobuf_test.go @@ -6,32 +6,32 @@ import ( "testing" ) -// PB=y go test -v -run ^TestParseTheTest$ -func TestParseTheTest(t *testing.T) { - if len(os.Getenv("PB")) == 0 { - t.Skip("PB test not run") - } - fetchAndParse(t, "https://raw.githubusercontent.com/gogo/protobuf/master/test/thetest.proto") -} - -func fetchAndParse(t *testing.T, url string) { +func fetchAndParse(t *testing.T, url string) *Proto { resp, err := http.Get(url) if err != nil { - t.Fatal(err) + t.Fatal(url, err) } defer resp.Body.Close() parser := NewParser(resp.Body) def, err := parser.Parse() if err != nil { - t.Fatal(err) + t.Fatal(url, err) } t.Log("elements:", len(def.Elements)) + return def } -// PB=y go test -v -run ^TestParseTheProto3$ -func TestParseTheProto3(t *testing.T) { +// PB=y go test -v -run ^TestPublicProtoDefinitions$ +func TestPublicProtoDefinitions(t *testing.T) { if len(os.Getenv("PB")) == 0 { t.Skip("PB test not run") } - fetchAndParse(t, "https://raw.githubusercontent.com/gogo/protobuf/master/test/theproto3/theproto3.proto") + for _, each := range []string{ + "https://raw.githubusercontent.com/gogo/protobuf/master/test/thetest.proto", + "https://raw.githubusercontent.com/gogo/protobuf/master/test/theproto3/theproto3.proto", + "https://raw.githubusercontent.com/googleapis/googleapis/master/google/privacy/dlp/v2beta2/dlp.proto", + } { + def := fetchAndParse(t, each) + checkParent(def, t) + } } diff --git a/reserved.go b/reserved.go index bfaefd3..726c441 100644 --- a/reserved.go +++ b/reserved.go @@ -32,6 +32,7 @@ type Reserved struct { Ranges []Range FieldNames []string InlineComment *Comment + Parent Visitee } // inlineComment is part of commentInliner. @@ -73,3 +74,5 @@ func (r *Reserved) parse(p *Parser) error { } return nil } + +func (r *Reserved) parent(v Visitee) { r.Parent = v } diff --git a/service.go b/service.go index 31522f1..08c5948 100644 --- a/service.go +++ b/service.go @@ -33,6 +33,7 @@ type Service struct { Comment *Comment Name string Elements []Visitee + Parent Visitee } // Accept dispatches the call to the visitor. @@ -47,6 +48,7 @@ func (s *Service) Doc() *Comment { // addElement is part of elementContainer func (s *Service) addElement(v Visitee) { + v.parent(s) s.Elements = append(s.Elements, v) } @@ -80,7 +82,7 @@ func (s *Service) parse(p *Parser) error { switch tok { case tCOMMENT: if com := mergeOrReturnComment(s.Elements, lit, pos); com != nil { // not merged? - s.Elements = append(s.Elements, com) + s.addElement(com) } case tOPTION: opt := new(Option) @@ -89,7 +91,7 @@ func (s *Service) parse(p *Parser) error { if err := opt.parse(p); err != nil { return err } - s.Elements = append(s.Elements, opt) + s.addElement(opt) case tRPC: rpc := new(RPC) rpc.Position = pos @@ -98,7 +100,7 @@ func (s *Service) parse(p *Parser) error { if err != nil { return err } - s.Elements = append(s.Elements, rpc) + s.addElement(rpc) maybeScanInlineComment(p, s) case tSEMICOLON: maybeScanInlineComment(p, s) @@ -112,6 +114,8 @@ done: return nil } +func (s *Service) parent(v Visitee) { s.Parent = v } + // RPC represents an rpc entry in a message. type RPC struct { Position scanner.Position @@ -123,6 +127,7 @@ type RPC struct { StreamsReturns bool Elements []Visitee InlineComment *Comment + Parent Visitee // Options field is DEPRECATED, use Elements instead. Options []*Option @@ -226,6 +231,7 @@ func (r *RPC) parse(p *Parser) error { // addElement is part of elementContainer func (r *RPC) addElement(v Visitee) { + v.parent(r) r.Elements = append(r.Elements, v) // handle deprecated field if option, ok := v.(*Option); ok { @@ -242,3 +248,5 @@ func (r *RPC) takeLastComment(expectedOnLine int) (last *Comment) { last, r.Elements = takeLastCommentIfEndsOnLine(r.Elements, expectedOnLine) return } + +func (r *RPC) parent(v Visitee) { r.Parent = v } diff --git a/syntax.go b/syntax.go index f4c5f74..79958c8 100644 --- a/syntax.go +++ b/syntax.go @@ -33,6 +33,7 @@ type Syntax struct { Comment *Comment Value string InlineComment *Comment + Parent Visitee } func (s *Syntax) parse(p *Parser) error { @@ -61,3 +62,5 @@ func (s *Syntax) Doc() *Comment { func (s *Syntax) inlineComment(c *Comment) { s.InlineComment = c } + +func (s *Syntax) parent(v Visitee) { s.Parent = v } diff --git a/visitor.go b/visitor.go index f1441a1..be0850a 100644 --- a/visitor.go +++ b/visitor.go @@ -25,6 +25,7 @@ package proto // Visitor is for dispatching Proto elements. type Visitor interface { + //VisitProto(p *Proto) VisitMessage(m *Message) VisitService(v *Service) VisitSyntax(s *Syntax) @@ -48,6 +49,7 @@ type Visitor interface { // Visitee is implemented by all Proto elements. type Visitee interface { Accept(v Visitor) + parent(e Visitee) } // Documented is for types that may have an associated comment (not inlined).