From ae5d40202b2ef51833d0e9a30ced7705357c80cc Mon Sep 17 00:00:00 2001 From: Ernest Micklei Date: Fri, 16 Mar 2018 13:31:02 +0100 Subject: [PATCH] allow options to have keywords in names (#72) * allow options to have keywords in names --- Makefile | 3 +++ option.go | 10 ++++++---- option_test.go | 18 ++++++++++++++++++ parser.go | 10 +++++++++- parser_test.go | 20 ++++++++++++++++++-- protobuf_test.go | 1 + 6 files changed, 55 insertions(+), 7 deletions(-) diff --git a/Makefile b/Makefile index 66470da..91ba22f 100644 --- a/Makefile +++ b/Makefile @@ -76,3 +76,6 @@ test: testdeps lint .PHONY: clean clean: go clean -i ./... + +integration: + PB=y go test -cover \ No newline at end of file diff --git a/option.go b/option.go index 8edd59a..d40c061 100644 --- a/option.go +++ b/option.go @@ -54,7 +54,7 @@ func (o *Option) parse(p *Parser) error { } pos, tok, _ = p.next() if tok != tRIGHTPAREN { - return p.unexpected(lit, "full identifier closing )", o) + return p.unexpected(lit, "option full identifier closing )", o) } o.Name = fmt.Sprintf("(%s)", lit) } else { @@ -69,15 +69,17 @@ func (o *Option) parse(p *Parser) error { pos, tok, lit = p.next() if tDOT == tok { // extend identifier - pos, tok, lit = p.nextIdentifier() + pos, tok, lit = p.nextIdent(true) // keyword allowed as start if tok != tIDENT { - return p.unexpected(lit, "option postfix identifier", o) + if !isKeyword(tok) { + return p.unexpected(lit, "option postfix identifier", o) + } } o.Name = fmt.Sprintf("%s.%s", o.Name, lit) pos, tok, lit = p.next() } if tEQUALS != tok { - return p.unexpected(lit, "option constant =", o) + return p.unexpected(lit, "option value assignment =", o) } r := p.peekNonWhitespace() if '{' == r { diff --git a/option_test.go b/option_test.go index b892d7c..2352552 100644 --- a/option_test.go +++ b/option_test.go @@ -58,6 +58,11 @@ func TestOptionCases(t *testing.T) { "optimize_for", "", "SPEED", + }, { + "option (my.enum.service.is.like).rpc = 1;", + "(my.enum.service.is.like).rpc", + "", + "1", }} { p := newParserOn(each.proto) pr, err := p.Parse() @@ -244,6 +249,19 @@ func TestFieldCustomOptions(t *testing.T) { } } +func TestFieldCustomOptionExtendedIdent(t *testing.T) { + proto := `Type field = 1 [(validate.rules).enum.defined_only = true];` + p := newParserOn(proto) + f := newNormalField() + err := f.parse(p) + if err != nil { + t.Fatal(err) + } + if got, want := f.Options[0].Name, "(validate.rules).enum.defined_only"; got != want { + t.Errorf("got [%v] want [%v]", got, want) + } +} + // issue #50 func TestNestedAggregateConstants(t *testing.T) { src := `syntax = "proto3"; diff --git a/parser.go b/parser.go index d967853..fdf646f 100644 --- a/parser.go +++ b/parser.go @@ -132,9 +132,17 @@ func (p *Parser) nextInteger() (i int, err error) { // nextIdentifier consumes tokens which may have one or more dot separators (namespaced idents). func (p *Parser) nextIdentifier() (pos scanner.Position, tok token, lit string) { + return p.nextIdent(false) +} + +func (p *Parser) nextIdent(keywordStartAllowed bool) (pos scanner.Position, tok token, lit string) { pos, tok, lit = p.next() if tIDENT != tok { - return + // can be keyword + if !(isKeyword(tok) && keywordStartAllowed) { + return + } + // proceed with keyword as first literal } startPos := pos fullLit := lit diff --git a/parser_test.go b/parser_test.go index 9940c5c..d0ae8d4 100644 --- a/parser_test.go +++ b/parser_test.go @@ -89,13 +89,29 @@ func TestNextIdentifier(t *testing.T) { } func TestNextIdentifierWithKeyword(t *testing.T) { - ident := " aap.rpc.mies " + ident := " aap.rpc.mies.enum =" p := newParserOn(ident) _, tok, lit := p.nextIdentifier() if got, want := tok, tIDENT; got != want { t.Errorf("got [%v] want [%v]", got, want) } - if got, want := lit, strings.TrimSpace(ident); got != want { + if got, want := lit, "aap.rpc.mies.enum"; got != want { + t.Errorf("got [%v] want [%v]", got, want) + } + _, tok, _ = p.next() + if got, want := tok, tEQUALS; got != want { + t.Errorf("got [%v] want [%v]", got, want) + } +} + +func TestNextIdentifierNoIdent(t *testing.T) { + ident := "(" + p := newParserOn(ident) + _, tok, lit := p.nextIdentifier() + if got, want := tok, tLEFTPAREN; got != want { + t.Errorf("got [%v] want [%v]", got, want) + } + if got, want := lit, "("; got != want { t.Errorf("got [%v] want [%v]", got, want) } } diff --git a/protobuf_test.go b/protobuf_test.go index 8a9afa8..5468772 100644 --- a/protobuf_test.go +++ b/protobuf_test.go @@ -30,6 +30,7 @@ func TestPublicProtoDefinitions(t *testing.T) { "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", + // "https://raw.githubusercontent.com/envoyproxy/data-plane-api/master/envoy/api/v2/auth/cert.proto", } { def := fetchAndParse(t, each) checkParent(def, t)