diff options
| author | mo khan <mo@mokhan.ca> | 2025-05-20 14:28:06 -0600 |
|---|---|---|
| committer | mo khan <mo@mokhan.ca> | 2025-05-23 14:49:19 -0600 |
| commit | 4beee46dc6c7642316e118a4d3aa51e4b407256e (patch) | |
| tree | 039bdf57b99061844aeb0fe55ad0bc1c864166af /vendor/github.com/bufbuild | |
| parent | 0ba49bfbde242920d8675a193d7af89420456fc0 (diff) | |
feat: add external authorization service (authzd) with JWT authentication
- Add new authzd gRPC service implementing Envoy's external authorization API
- Integrate JWT authentication filter in Envoy configuration with claim extraction
- Update middleware to support both cookie-based and header-based user authentication
- Add comprehensive test coverage for authorization service and server
- Configure proper service orchestration with authzd, sparkled, and Envoy
- Update build system and Docker configuration for multi-service deployment
- Add grpcurl tool for gRPC service debugging and testing
This enables fine-grained authorization control through Envoy's ext_authz filter
while maintaining backward compatibility with existing cookie-based authentication.
Diffstat (limited to 'vendor/github.com/bufbuild')
69 files changed, 26089 insertions, 0 deletions
diff --git a/vendor/github.com/bufbuild/protocompile/.gitignore b/vendor/github.com/bufbuild/protocompile/.gitignore new file mode 100644 index 0000000..65b3b16 --- /dev/null +++ b/vendor/github.com/bufbuild/protocompile/.gitignore @@ -0,0 +1,3 @@ +*.iml +.idea/ +/.tmp/ diff --git a/vendor/github.com/bufbuild/protocompile/.golangci.yml b/vendor/github.com/bufbuild/protocompile/.golangci.yml new file mode 100644 index 0000000..0f00b31 --- /dev/null +++ b/vendor/github.com/bufbuild/protocompile/.golangci.yml @@ -0,0 +1,105 @@ +linters-settings: + errcheck: + check-type-assertions: true + forbidigo: + forbid: + - '^fmt\.Print' + - '^log\.' + - '^print$' + - '^println$' + - '^panic$' + gci: + # Section configuration to compare against. + # Section names are case-insensitive and may contain parameters in (). + # The default order of sections is `standard > default > custom > blank > dot`, + # If `custom-order` is `true`, it follows the order of `sections` option. + # Default: ["standard", "default"] + sections: + - standard # Standard section: captures all standard packages. + - default # Default section: contains all imports that could not be matched to another section type. + - prefix(github.com/bufbuild/protocompile) # Custom section: groups all imports with the specified Prefix. + godox: + # TODO, OPT, etc. comments are fine to commit. Use FIXME comments for + # temporary hacks, and use godox to prevent committing them. + keywords: [FIXME] + govet: + enable: + - fieldalignment + varnamelen: + ignore-decls: + - T any + - i int + - wg sync.WaitGroup +linters: + enable-all: true + disable: + # TODO: TCN-350 - initial exclusions for failing linters. + # Should enable all of these? + - depguard + - dupl + - errname + - errorlint + - exhaustive + - exhaustruct + - forbidigo + - forcetypeassert + - gochecknoglobals + - gochecknoinits + - goconst + - gocyclo + - err113 + - interfacebloat + - nestif + - nilerr + - nilnil + - nonamedreturns + - thelper + - varnamelen + # Other disabled linters + - cyclop # covered by gocyclo + - execinquery # deprecated in golangci v1.58.0 + - funlen # rely on code review to limit function length + - gocognit # dubious "cognitive overhead" quantification + - gofumpt # prefer standard gofmt + - gomnd # some unnamed constants are okay + - inamedparam # named params in interface signatures are not always necessary + - ireturn # "accept interfaces, return structs" isn't ironclad + - lll # don't want hard limits for line length + - maintidx # covered by gocyclo + - mnd # some unnamed constants are okay + - nlreturn # generous whitespace violates house style + - protogetter # lots of false positives: can't use getter to check if field is present + - rowserrcheck # no SQL code in protocompile + - sqlclosecheck # no SQL code in protocompile + - testpackage # internal tests are fine + - wastedassign # not supported with generics + - wrapcheck # don't _always_ need to wrap errors + - wsl # generous whitespace violates house style +issues: + exclude-dirs-use-default: false + exclude-files: + - ".*\\.y\\.go$" + exclude: + # Don't ban use of fmt.Errorf to create new errors, but the remaining + # checks from err113 are useful. + - "do not define dynamic errors.*" + exclude-rules: + # Benchmarks can't be run in parallel + - path: benchmark_test\.go + linters: + - paralleltest + # dupword reports several errors in .proto test fixtures + # gosec reports a few minor issues in tests + - path: _test\.go + linters: + - dupword + - gosec + # exclude field alignment linter in tests + - path: _test\.go + text: "fieldalignment:" + linters: + - govet + # exclude fieldalignment "pointer bytes" failures + - text: "pointer bytes" + linters: + - govet diff --git a/vendor/github.com/bufbuild/protocompile/.protoc_version b/vendor/github.com/bufbuild/protocompile/.protoc_version new file mode 100644 index 0000000..a0d6856 --- /dev/null +++ b/vendor/github.com/bufbuild/protocompile/.protoc_version @@ -0,0 +1 @@ +27.0 diff --git a/vendor/github.com/bufbuild/protocompile/LICENSE b/vendor/github.com/bufbuild/protocompile/LICENSE new file mode 100644 index 0000000..553cbbf --- /dev/null +++ b/vendor/github.com/bufbuild/protocompile/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2020-2024 Buf Technologies, Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/vendor/github.com/bufbuild/protocompile/Makefile b/vendor/github.com/bufbuild/protocompile/Makefile new file mode 100644 index 0000000..6ec2e78 --- /dev/null +++ b/vendor/github.com/bufbuild/protocompile/Makefile @@ -0,0 +1,201 @@ +# See https://tech.davis-hansson.com/p/make/ +SHELL := bash +.DELETE_ON_ERROR: +.SHELLFLAGS := -eu -o pipefail -c +.DEFAULT_GOAL := all +MAKEFLAGS += --warn-undefined-variables +MAKEFLAGS += --no-builtin-rules +MAKEFLAGS += --no-print-directory +BIN ?= $(abspath .tmp/bin) +CACHE := $(abspath .tmp/cache) +COPYRIGHT_YEARS := 2020-2024 +LICENSE_IGNORE := -e /testdata/ +# Set to use a different compiler. For example, `GO=go1.18rc1 make test`. +GO ?= go +TOOLS_MOD_DIR := ./internal/tools +UNAME_OS := $(shell uname -s) +UNAME_ARCH := $(shell uname -m) +PATH_SEP ?= ":" + +PROTOC_VERSION := $(shell cat ./.protoc_version) +# For release candidates, the download artifact has a dash between "rc" and the number even +# though the version tag does not :( +PROTOC_ARTIFACT_VERSION := $(shell echo $(PROTOC_VERSION) | sed -E 's/-rc([0-9]+)$$/-rc-\1/g') +PROTOC_DIR := $(abspath $(CACHE)/protoc/$(PROTOC_VERSION)) +PROTOC := $(PROTOC_DIR)/bin/protoc + +LOWER_UNAME_OS := $(shell echo $(UNAME_OS) | tr A-Z a-z) +ifeq ($(LOWER_UNAME_OS),darwin) + PROTOC_OS := osx + ifeq ($(UNAME_ARCH),arm64) + PROTOC_ARCH := aarch_64 + else + PROTOC_ARCH := x86_64 + endif +else + PROTOC_OS := $(LOWER_UNAME_OS) + PROTOC_ARCH := $(UNAME_ARCH) +endif +PROTOC_ARTIFACT_SUFFIX ?= $(PROTOC_OS)-$(PROTOC_ARCH) + +.PHONY: help +help: ## Describe useful make targets + @grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "%-30s %s\n", $$1, $$2}' + +.PHONY: all +all: ## Build, test, and lint (default) + $(MAKE) test + $(MAKE) lint + +.PHONY: clean +clean: ## Delete intermediate build artifacts + @# -X only removes untracked files, -d recurses into directories, -f actually removes files/dirs + git clean -Xdf + +.PHONY: test +test: build ## Run unit tests + $(GO) test -race -cover ./... + $(GO) test -tags protolegacy ./... + $(GO) test -tags purego ./... + cd internal/benchmarks && SKIP_DOWNLOAD_GOOGLEAPIS=true $(GO) test -race -cover ./... + +.PHONY: benchmarks +benchmarks: build ## Run benchmarks + cd internal/benchmarks && $(GO) test -bench=. -benchmem -v ./... + +.PHONY: build +build: generate ## Build all packages + $(GO) build ./... + +.PHONY: install +install: ## Install all binaries + $(GO) install ./... + +.PHONY: lint +lint: $(BIN)/golangci-lint ## Lint Go + $(GO) vet ./... ./internal/benchmarks/... + $(BIN)/golangci-lint run + cd internal/benchmarks && $(BIN)/golangci-lint run + +.PHONY: lintfix +lintfix: $(BIN)/golangci-lint ## Automatically fix some lint errors + $(BIN)/golangci-lint run --fix + cd internal/benchmarks && $(BIN)/golangci-lint run --fix + +.PHONY: generate +generate: $(BIN)/license-header $(BIN)/goyacc test-descriptors ext-features-descriptors ## Regenerate code and licenses + PATH="$(BIN)$(PATH_SEP)$(PATH)" $(GO) generate ./... + @# We want to operate on a list of modified and new files, excluding + @# deleted and ignored files. git-ls-files can't do this alone. comm -23 takes + @# two files and prints the union, dropping lines common to both (-3) and + @# those only in the second file (-2). We make one git-ls-files call for + @# the modified, cached, and new (--others) files, and a second for the + @# deleted files. + comm -23 \ + <(git ls-files --cached --modified --others --no-empty-directory --exclude-standard | sort -u | grep -v $(LICENSE_IGNORE) ) \ + <(git ls-files --deleted | sort -u) | \ + xargs $(BIN)/license-header \ + --license-type apache \ + --copyright-holder "Buf Technologies, Inc." \ + --year-range "$(COPYRIGHT_YEARS)" + +.PHONY: upgrade +upgrade: ## Upgrade dependencies + go get -u -t ./... && go mod tidy -v + +.PHONY: checkgenerate +checkgenerate: + @# Used in CI to verify that `make generate` doesn't produce a diff. + test -z "$$(git status --porcelain | tee /dev/stderr)" + +$(BIN)/license-header: internal/tools/go.mod internal/tools/go.sum + @mkdir -p $(@D) + cd $(TOOLS_MOD_DIR) && \ + GOWORK=off $(GO) build -o $@ github.com/bufbuild/buf/private/pkg/licenseheader/cmd/license-header + +$(BIN)/golangci-lint: internal/tools/go.mod internal/tools/go.sum + @mkdir -p $(@D) + cd $(TOOLS_MOD_DIR) && \ + GOWORK=off $(GO) build -o $@ github.com/golangci/golangci-lint/cmd/golangci-lint + +$(BIN)/goyacc: internal/tools/go.mod internal/tools/go.sum + @mkdir -p $(@D) + cd $(TOOLS_MOD_DIR) && \ + GOWORK=off $(GO) build -o $@ golang.org/x/tools/cmd/goyacc + +$(CACHE)/protoc-$(PROTOC_VERSION).zip: + @mkdir -p $(@D) + curl -o $@ -fsSL https://github.com/protocolbuffers/protobuf/releases/download/v$(PROTOC_VERSION)/protoc-$(PROTOC_ARTIFACT_VERSION)-$(PROTOC_ARTIFACT_SUFFIX).zip + +.PHONY: protoc +protoc: $(PROTOC) + +$(PROTOC): $(CACHE)/protoc-$(PROTOC_VERSION).zip + @mkdir -p $(@D) + unzip -o -q $< -d $(PROTOC_DIR) && \ + touch $@ + +.PHONY: wellknownimports +wellknownimports: $(PROTOC) $(sort $(wildcard $(PROTOC_DIR)/include/google/protobuf/*.proto)) $(sort $(wildcard $(PROTOC_DIR)/include/google/protobuf/*/*.proto)) + @rm -rf wellknownimports/google 2>/dev/null && true + @mkdir -p wellknownimports/google/protobuf/compiler + cp -R $(PROTOC_DIR)/include/google/protobuf/*.proto wellknownimports/google/protobuf + cp -R $(PROTOC_DIR)/include/google/protobuf/compiler/*.proto wellknownimports/google/protobuf/compiler + +internal/testdata/all.protoset: $(PROTOC) $(sort $(wildcard internal/testdata/*.proto)) + cd $(@D) && $(PROTOC) --descriptor_set_out=$(@F) --include_imports -I. $(filter-out protoc,$(^F)) + +internal/testdata/desc_test_complex.protoset: $(PROTOC) internal/testdata/desc_test_complex.proto + cd $(@D) && $(PROTOC) --descriptor_set_out=$(@F) --include_imports -I. $(filter-out protoc,$(^F)) + +internal/testdata/desc_test_defaults.protoset: $(PROTOC) internal/testdata/desc_test_defaults.proto + cd $(@D) && $(PROTOC) --descriptor_set_out=$(@F) --include_imports -I. $(filter-out protoc,$(^F)) + +internal/testdata/desc_test_proto3_optional.protoset: $(PROTOC) internal/testdata/desc_test_proto3_optional.proto + cd $(@D) && $(PROTOC) --descriptor_set_out=$(@F) --include_imports -I. $(filter-out protoc,$(^F)) + +internal/testdata/descriptor_impl_tests.protoset: $(PROTOC) internal/testdata/desc_test2.proto internal/testdata/desc_test_complex.proto internal/testdata/desc_test_defaults.proto internal/testdata/desc_test_proto3.proto internal/testdata/desc_test_proto3_optional.proto + cd $(@D) && $(PROTOC) --descriptor_set_out=$(@F) --include_imports -I. $(filter-out protoc,$(^F)) + +internal/testdata/descriptor_editions_impl_tests.protoset: $(PROTOC) internal/testdata/editions/all_default_features.proto internal/testdata/editions/features_with_overrides.proto internal/testdata/editions/file_default_delimited.proto + cd $(@D)/editions && $(PROTOC) --descriptor_set_out=../$(@F) --include_imports -I. $(filter-out protoc,$(^F)) + +internal/testdata/editions/all.protoset: $(PROTOC) $(sort $(wildcard internal/testdata/editions/*.proto)) + cd $(@D) && $(PROTOC) --descriptor_set_out=$(@F) --include_imports -I. $(filter-out protoc,$(^F)) + +internal/testdata/source_info.protoset: $(PROTOC) internal/testdata/desc_test_options.proto internal/testdata/desc_test_comments.proto internal/testdata/desc_test_complex.proto + cd $(@D) && $(PROTOC) --descriptor_set_out=$(@F) --include_source_info -I. $(filter-out protoc,$(^F)) + +internal/testdata/options/options.protoset: $(PROTOC) internal/testdata/options/options.proto + cd $(@D) && $(PROTOC) --descriptor_set_out=$(@F) -I. $(filter-out protoc,$(^F)) + +internal/testdata/options/test.protoset: $(PROTOC) internal/testdata/options/test.proto + cd $(@D) && $(PROTOC) --descriptor_set_out=$(@F) -I. $(filter-out protoc,$(^F)) + +internal/testdata/options/test_proto3.protoset: $(PROTOC) internal/testdata/options/test_proto3.proto + cd $(@D) && $(PROTOC) --descriptor_set_out=$(@F) -I. $(filter-out protoc,$(^F)) + +internal/testdata/options/test_editions.protoset: $(PROTOC) internal/testdata/options/test_editions.proto + cd $(@D) && $(PROTOC) --descriptor_set_out=$(@F) -I. $(filter-out protoc,$(^F)) + +.PHONY: test-descriptors +test-descriptors: internal/testdata/all.protoset +test-descriptors: internal/testdata/desc_test_complex.protoset +test-descriptors: internal/testdata/desc_test_defaults.protoset +test-descriptors: internal/testdata/desc_test_proto3_optional.protoset +test-descriptors: internal/testdata/descriptor_impl_tests.protoset +test-descriptors: internal/testdata/descriptor_editions_impl_tests.protoset +test-descriptors: internal/testdata/editions/all.protoset +test-descriptors: internal/testdata/source_info.protoset +test-descriptors: internal/testdata/options/options.protoset +test-descriptors: internal/testdata/options/test.protoset +test-descriptors: internal/testdata/options/test_proto3.protoset +test-descriptors: internal/testdata/options/test_editions.protoset + +internal/featuresext/cpp_features.protoset: $(PROTOC) + cd $(@D) && $(PROTOC) --descriptor_set_out=$(@F) google/protobuf/cpp_features.proto +internal/featuresext/java_features.protoset: $(PROTOC) + cd $(@D) && $(PROTOC) --descriptor_set_out=$(@F) google/protobuf/java_features.proto + +.PHONY: ext-features-descriptors +ext-features-descriptors: internal/featuresext/cpp_features.protoset internal/featuresext/java_features.protoset diff --git a/vendor/github.com/bufbuild/protocompile/README.md b/vendor/github.com/bufbuild/protocompile/README.md new file mode 100644 index 0000000..9d87333 --- /dev/null +++ b/vendor/github.com/bufbuild/protocompile/README.md @@ -0,0 +1,91 @@ + + +# Protocompile + +[](https://github.com/bufbuild/protocompile/actions/workflows/ci.yaml) +[](https://goreportcard.com/report/github.com/bufbuild/protocompile) +[](https://pkg.go.dev/github.com/bufbuild/protocompile) + +This repo contains a parsing/linking engine for Protocol Buffers, written in pure Go. It is suitable as an alternative +to `protoc` (Google's official reference compiler for Protocol Buffers). This is the compiler that powers [Buf](https://buf.build) +and its bevy of tools. + +This repo is also the spiritual successor to the [`github.com/jhump/protoreflect/desc/protoparse`](https://godoc.org/github.com/jhump/protoreflect/desc/protoparse) +package. If you are looking for a newer version of `protoparse` that natively works with the newer Protobuf runtime +API for Go (`google.golang.org/protobuf`), you have found it! + +## Protocol Buffers + +If you've come across this repo but don't know what Protocol Buffers are, you might acquaint yourself with the [official +documentation](https://developers.google.com/protocol-buffers). Protocol Buffers, or Protobuf for short, is an IDL for +describing APIs and data structures and also a binary encoding format for efficiently transmitting and storing that +data. + +If you want to know more about the language itself, which is what this repo implements, take a look at Buf's +[Protobuf Guide](https://protobuf.com), which includes a very detailed language specification. + +### Descriptors + +Descriptors are the "lingua franca" for describing Protobuf data schemas. They are the basis of runtime features like +reflection and dynamic messages. They are also the output of a Protobuf compiler: a compiler can produce them and write +them to a file (whose contents are the binary-encoded form of a [`FileDescriptorSet`](https://github.com/protocolbuffers/protobuf/blob/v21.7/src/google/protobuf/descriptor.proto#L55-L59)) +or send them to a [plugin](https://docs.buf.build/reference/images#plugins) to generate code for a particular +programming language. + +Descriptors are similar to nodes in a syntax tree: the contents of a file descriptor correspond closely to the elements +in the source file from which it was generated. Also, the descriptor model's data structures are themselves defined in +[Protobuf](https://github.com/protocolbuffers/protobuf/blob/v21.7/src/google/protobuf/descriptor.proto). + +## Using This Repo + +The primary API of this repo is in this root package: `github.com/bufbuild/protocompile`. This is the suggested entry +point and provides a type named `Compiler`, for compiling Protobuf source files into descriptors. There are also +numerous sub-packages, most of which implement various stages of the compiler. Here's an overview (_not_ in alphabetical +order): + + * [`protocompile`](https://pkg.go.dev/github.com/bufbuild/protocompile): + This is the entry point, used to configure and initiate a compilation operation. + * [`parser`](https://pkg.go.dev/github.com/bufbuild/protocompile/parser): + This is the first stage of the compiler. It parses Protobuf source code and produces an AST. This package can also + generate a file descriptor proto from an AST. + * [`ast`](https://pkg.go.dev/github.com/bufbuild/protocompile/ast): + This package models an Abstract Syntax Tree (AST) for the Protobuf language. + * [`linker`](https://pkg.go.dev/github.com/bufbuild/protocompile/linker): + This is the second stage of the compiler. The descriptor proto (generated from an AST) is linked, producing a more + useful data structure than simple descriptor protos. This step also performs numerous validations on the source, + like making sure that all type references are correct and that sources don't try to define two elements with the same + name. + * [`options`](https://pkg.go.dev/github.com/bufbuild/protocompile/options): + This is the next stage of the compiler: interpreting options. The linked data structures that come from the previous + stage are used to validate and interpret all options. + * [`sourceinfo`](https://pkg.go.dev/github.com/bufbuild/protocompile/sourceinfo): + This is the last stage of the compiler: generating source code info. Source code info contains metadata that maps + elements in the descriptor to the location in the original source file from which it came. This includes access to + comments. In order to provide correct source info for options, it must happen last, after options have been + interpreted. + * [`reporter`](https://pkg.go.dev/github.com/bufbuild/protocompile/reporter): This package provides error types + generated by the compiler and interfaces used by the compiler to report errors and warnings to the calling code. + * [`walk`](https://pkg.go.dev/github.com/bufbuild/protocompile/walk): + This package provides functions for walking through all of the elements in a descriptor (or descriptor proto) + hierarchy. + * [`protoutil`](https://pkg.go.dev/github.com/bufbuild/protocompile/protoutil): + This package contains some other useful functions for interacting with Protobuf descriptors. + +### Migrating from `protoparse` + +There are a few differences between this repo and its predecessor, `github.com/jhump/protoreflect/desc/protoparse`. + +* If you want to include "standard imports", for the well-known files that are included with `protoc`, you have to do + so explicitly. To do this, wrap your resolver using `protocompile.WithStandardImports`. +* If you used `protoparse.FileContentsFromMap`, in this new repo you'll use a `protocompile.SourceResolver` and then use + `protocompile.SourceAccessorFromMap` as its accessor function. +* If you used `Parser.ParseToAST`, you won't use the `protocompile` package but instead directly use `parser.Parse` in + this repo's `parser` sub-package. This returns an AST for the given file contents. +* If you used `Parser.ParseFilesButDoNotLink`, that is still possible in this repo, but not provided directly via a + single function. Instead, you need to take a few steps: + 1. Parse the source using `parser.Parse`. Then use `parser.ResultFromAST` to construct a result that contains a file + descriptor proto. + 2. Interpret whatever options can be interpreted without linking using `options.InterpretUnlinkedOptions`. This may + leave some options in the descriptor proto uninterpreted (including all custom options). + 3. If you want source code info for the file, finally call `sourceinfo.GenerateSourceInfo` using the index returned + from the previous step and store that in the file descriptor proto. diff --git a/vendor/github.com/bufbuild/protocompile/ast/doc.go b/vendor/github.com/bufbuild/protocompile/ast/doc.go new file mode 100644 index 0000000..cda4068 --- /dev/null +++ b/vendor/github.com/bufbuild/protocompile/ast/doc.go @@ -0,0 +1,75 @@ +// Copyright 2020-2024 Buf Technologies, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Package ast defines types for modeling the AST (Abstract Syntax +// Tree) for the Protocol Buffers interface definition language. +// +// # Nodes +// +// All nodes of the tree implement the [Node] interface. Leaf nodes in the +// tree implement [TerminalNode], and all others implement [CompositeNode]. +// The root of the tree for a proto source file is a *[FileNode]. +// +// A [TerminalNode] represents a single lexical element, or [Token]. A +// [CompositeNode] represents a sub-tree of the AST and range of tokens. +// +// Position information is tracked using a *[FileInfo]. The lexer invokes its +// various Add* methods to add details as the file is tokenized. Storing +// the position information in the *[FileInfo], instead of in each AST node, +// allows the AST to have a much more compact representation. To extract +// detailed position information, you must use the NodeInfo method, available +// on either the *[FileInfo] which produced the node's items or the *[FileNode] +// root of the tree that contains the node. +// +// # Items, Tokens, and Comments +// +// An [Item] represents a lexical item, excluding whitespace. This can be +// either a [Token] or a [Comment]. +// +// Comments are not represented as nodes in the tree. Instead, they are +// attributed to terminal nodes in the tree. So, when lexing, comments +// are accumulated until the next non-comment token is found. The AST +// model in this package thus provides access to all comments in the +// file, regardless of location (unlike the SourceCodeInfo present in +// descriptor protos, which is lossy). The comments associated with a +// non-leaf/non-token node (i.e. a CompositeNode) come from the first +// and last nodes in its sub-tree, for leading and trailing comments +// respectively. +// +// A [Comment] value corresponds to a line ("//") or block ("/*") style +// comment in the source. These have no bearing on the grammar and are +// effectively ignored as the parser is determining the shape of the +// syntax tree. +// +// A [Token] value corresponds to a component of the grammar, that is +// used to produce an AST. They correspond to leaves in the AST (i.e. +// [TerminalNode]). +// +// The *[FileInfo] and *[FileNode] types provide methods for querying +// and iterating through all the items or tokens in the file. They also +// include a method for resolving an [Item] into a [Token] or [Comment]. +// +// # Factory Functions +// +// Creation of AST nodes should use the factory functions in this +// package instead of struct literals. Some factory functions accept +// optional arguments, which means the arguments can be nil. If nil +// values are provided for other (non-optional) arguments, the resulting +// node may be invalid and cause panics later in the program. +// +// This package defines numerous interfaces. However, user code should +// not attempt to implement any of them. Most consumers of an AST will +// not work correctly if they encounter concrete implementations other +// than the ones defined in this package. +package ast diff --git a/vendor/github.com/bufbuild/protocompile/ast/enum.go b/vendor/github.com/bufbuild/protocompile/ast/enum.go new file mode 100644 index 0000000..55a6229 --- /dev/null +++ b/vendor/github.com/bufbuild/protocompile/ast/enum.go @@ -0,0 +1,185 @@ +// Copyright 2020-2024 Buf Technologies, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package ast + +import "fmt" + +// EnumNode represents an enum declaration. Example: +// +// enum Foo { BAR = 0; BAZ = 1 } +type EnumNode struct { + compositeNode + Keyword *KeywordNode + Name *IdentNode + OpenBrace *RuneNode + Decls []EnumElement + CloseBrace *RuneNode +} + +func (*EnumNode) fileElement() {} +func (*EnumNode) msgElement() {} + +// NewEnumNode creates a new *EnumNode. All arguments must be non-nil. While +// it is technically allowed for decls to be nil or empty, the resulting node +// will not be a valid enum, which must have at least one value. +// - keyword: The token corresponding to the "enum" keyword. +// - name: The token corresponding to the enum's name. +// - openBrace: The token corresponding to the "{" rune that starts the body. +// - decls: All declarations inside the enum body. +// - closeBrace: The token corresponding to the "}" rune that ends the body. +func NewEnumNode(keyword *KeywordNode, name *IdentNode, openBrace *RuneNode, decls []EnumElement, closeBrace *RuneNode) *EnumNode { + if keyword == nil { + panic("keyword is nil") + } + if name == nil { + panic("name is nil") + } + if openBrace == nil { + panic("openBrace is nil") + } + if closeBrace == nil { + panic("closeBrace is nil") + } + children := make([]Node, 0, 4+len(decls)) + children = append(children, keyword, name, openBrace) + for _, decl := range decls { + switch decl.(type) { + case *OptionNode, *EnumValueNode, *ReservedNode, *EmptyDeclNode: + default: + panic(fmt.Sprintf("invalid EnumElement type: %T", decl)) + } + children = append(children, decl) + } + children = append(children, closeBrace) + + return &EnumNode{ + compositeNode: compositeNode{ + children: children, + }, + Keyword: keyword, + Name: name, + OpenBrace: openBrace, + CloseBrace: closeBrace, + Decls: decls, + } +} + +func (n *EnumNode) RangeOptions(fn func(*OptionNode) bool) { + for _, decl := range n.Decls { + if opt, ok := decl.(*OptionNode); ok { + if !fn(opt) { + return + } + } + } +} + +// EnumElement is an interface implemented by all AST nodes that can +// appear in the body of an enum declaration. +type EnumElement interface { + Node + enumElement() +} + +var _ EnumElement = (*OptionNode)(nil) +var _ EnumElement = (*EnumValueNode)(nil) +var _ EnumElement = (*ReservedNode)(nil) +var _ EnumElement = (*EmptyDeclNode)(nil) + +// EnumValueDeclNode is a placeholder interface for AST nodes that represent +// enum values. This allows NoSourceNode to be used in place of *EnumValueNode +// for some usages. +type EnumValueDeclNode interface { + NodeWithOptions + GetName() Node + GetNumber() Node +} + +var _ EnumValueDeclNode = (*EnumValueNode)(nil) +var _ EnumValueDeclNode = (*NoSourceNode)(nil) + +// EnumValueNode represents an enum declaration. Example: +// +// UNSET = 0 [deprecated = true]; +type EnumValueNode struct { + compositeNode + Name *IdentNode + Equals *RuneNode + Number IntValueNode + Options *CompactOptionsNode + Semicolon *RuneNode +} + +func (*EnumValueNode) enumElement() {} + +// NewEnumValueNode creates a new *EnumValueNode. All arguments must be non-nil +// except opts which is only non-nil if the declaration included options. +// - name: The token corresponding to the enum value's name. +// - equals: The token corresponding to the '=' rune after the name. +// - number: The token corresponding to the enum value's number. +// - opts: Optional set of enum value options. +// - semicolon: The token corresponding to the ";" rune that ends the declaration. +func NewEnumValueNode(name *IdentNode, equals *RuneNode, number IntValueNode, opts *CompactOptionsNode, semicolon *RuneNode) *EnumValueNode { + if name == nil { + panic("name is nil") + } + if equals == nil { + panic("equals is nil") + } + if number == nil { + panic("number is nil") + } + numChildren := 3 + if semicolon != nil { + numChildren++ + } + if opts != nil { + numChildren++ + } + children := make([]Node, 0, numChildren) + children = append(children, name, equals, number) + if opts != nil { + children = append(children, opts) + } + if semicolon != nil { + children = append(children, semicolon) + } + return &EnumValueNode{ + compositeNode: compositeNode{ + children: children, + }, + Name: name, + Equals: equals, + Number: number, + Options: opts, + Semicolon: semicolon, + } +} + +func (e *EnumValueNode) GetName() Node { + return e.Name +} + +func (e *EnumValueNode) GetNumber() Node { + return e.Number +} + +func (e *EnumValueNode) RangeOptions(fn func(*OptionNode) bool) { + for _, opt := range e.Options.Options { + if !fn(opt) { + return + } + } +} diff --git a/vendor/github.com/bufbuild/protocompile/ast/field.go b/vendor/github.com/bufbuild/protocompile/ast/field.go new file mode 100644 index 0000000..63d65b3 --- /dev/null +++ b/vendor/github.com/bufbuild/protocompile/ast/field.go @@ -0,0 +1,795 @@ +// Copyright 2020-2024 Buf Technologies, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package ast + +import "fmt" + +// FieldDeclNode is a node in the AST that defines a field. This includes +// normal message fields as well as extensions. There are multiple types +// of AST nodes that declare fields: +// - *FieldNode +// - *GroupNode +// - *MapFieldNode +// - *SyntheticMapField +// +// This also allows NoSourceNode and SyntheticMapField to be used in place of +// one of the above for some usages. +type FieldDeclNode interface { + NodeWithOptions + FieldLabel() Node + FieldName() Node + FieldType() Node + FieldTag() Node + FieldExtendee() Node + GetGroupKeyword() Node + GetOptions() *CompactOptionsNode +} + +var _ FieldDeclNode = (*FieldNode)(nil) +var _ FieldDeclNode = (*GroupNode)(nil) +var _ FieldDeclNode = (*MapFieldNode)(nil) +var _ FieldDeclNode = (*SyntheticMapField)(nil) +var _ FieldDeclNode = (*NoSourceNode)(nil) + +// FieldNode represents a normal field declaration (not groups or maps). It +// can represent extension fields as well as non-extension fields (both inside +// of messages and inside of one-ofs). Example: +// +// optional string foo = 1; +type FieldNode struct { + compositeNode + Label FieldLabel + FldType IdentValueNode + Name *IdentNode + Equals *RuneNode + Tag *UintLiteralNode + Options *CompactOptionsNode + Semicolon *RuneNode + + // This is an up-link to the containing *ExtendNode for fields + // that are defined inside of "extend" blocks. + Extendee *ExtendNode +} + +func (*FieldNode) msgElement() {} +func (*FieldNode) oneofElement() {} +func (*FieldNode) extendElement() {} + +// NewFieldNode creates a new *FieldNode. The label and options arguments may be +// nil but the others must be non-nil. +// - label: The token corresponding to the label keyword if present ("optional", +// "required", or "repeated"). +// - fieldType: The token corresponding to the field's type. +// - name: The token corresponding to the field's name. +// - equals: The token corresponding to the '=' rune after the name. +// - tag: The token corresponding to the field's tag number. +// - opts: Optional set of field options. +// - semicolon: The token corresponding to the ";" rune that ends the declaration. +func NewFieldNode(label *KeywordNode, fieldType IdentValueNode, name *IdentNode, equals *RuneNode, tag *UintLiteralNode, opts *CompactOptionsNode, semicolon *RuneNode) *FieldNode { + if fieldType == nil { + panic("fieldType is nil") + } + if name == nil { + panic("name is nil") + } + numChildren := 2 + if equals != nil { + numChildren++ + } + if tag != nil { + numChildren++ + } + if semicolon != nil { + numChildren++ + } + if label != nil { + numChildren++ + } + if opts != nil { + numChildren++ + } + children := make([]Node, 0, numChildren) + if label != nil { + children = append(children, label) + } + children = append(children, fieldType, name) + if equals != nil { + children = append(children, equals) + } + if tag != nil { + children = append(children, tag) + } + if opts != nil { + children = append(children, opts) + } + if semicolon != nil { + children = append(children, semicolon) + } + + return &FieldNode{ + compositeNode: compositeNode{ + children: children, + }, + Label: newFieldLabel(label), + FldType: fieldType, + Name: name, + Equals: equals, + Tag: tag, + Options: opts, + Semicolon: semicolon, + } +} + +func (n *FieldNode) FieldLabel() Node { + // proto3 fields and fields inside one-ofs will not have a label and we need + // this check in order to return a nil node -- otherwise we'd return a + // non-nil node that has a nil pointer value in it :/ + if n.Label.KeywordNode == nil { + return nil + } + return n.Label.KeywordNode +} + +func (n *FieldNode) FieldName() Node { + return n.Name +} + +func (n *FieldNode) FieldType() Node { + return n.FldType +} + +func (n *FieldNode) FieldTag() Node { + if n.Tag == nil { + return n + } + return n.Tag +} + +func (n *FieldNode) FieldExtendee() Node { + if n.Extendee != nil { + return n.Extendee.Extendee + } + return nil +} + +func (n *FieldNode) GetGroupKeyword() Node { + return nil +} + +func (n *FieldNode) GetOptions() *CompactOptionsNode { + return n.Options +} + +func (n *FieldNode) RangeOptions(fn func(*OptionNode) bool) { + for _, opt := range n.Options.Options { + if !fn(opt) { + return + } + } +} + +// FieldLabel represents the label of a field, which indicates its cardinality +// (i.e. whether it is optional, required, or repeated). +type FieldLabel struct { + *KeywordNode + Repeated bool + Required bool +} + +func newFieldLabel(lbl *KeywordNode) FieldLabel { + repeated, required := false, false + if lbl != nil { + repeated = lbl.Val == "repeated" + required = lbl.Val == "required" + } + return FieldLabel{ + KeywordNode: lbl, + Repeated: repeated, + Required: required, + } +} + +// IsPresent returns true if a label keyword was present in the declaration +// and false if it was absent. +func (f *FieldLabel) IsPresent() bool { + return f.KeywordNode != nil +} + +// GroupNode represents a group declaration, which doubles as a field and inline +// message declaration. It can represent extension fields as well as +// non-extension fields (both inside of messages and inside of one-ofs). +// Example: +// +// optional group Key = 4 { +// optional uint64 id = 1; +// optional string name = 2; +// } +type GroupNode struct { + compositeNode + Label FieldLabel + Keyword *KeywordNode + Name *IdentNode + Equals *RuneNode + Tag *UintLiteralNode + Options *CompactOptionsNode + MessageBody + + // This is an up-link to the containing *ExtendNode for groups + // that are defined inside of "extend" blocks. + Extendee *ExtendNode +} + +func (*GroupNode) msgElement() {} +func (*GroupNode) oneofElement() {} +func (*GroupNode) extendElement() {} + +// NewGroupNode creates a new *GroupNode. The label and options arguments may be +// nil but the others must be non-nil. +// - label: The token corresponding to the label keyword if present ("optional", +// "required", or "repeated"). +// - keyword: The token corresponding to the "group" keyword. +// - name: The token corresponding to the field's name. +// - equals: The token corresponding to the '=' rune after the name. +// - tag: The token corresponding to the field's tag number. +// - opts: Optional set of field options. +// - openBrace: The token corresponding to the "{" rune that starts the body. +// - decls: All declarations inside the group body. +// - closeBrace: The token corresponding to the "}" rune that ends the body. +func NewGroupNode(label *KeywordNode, keyword *KeywordNode, name *IdentNode, equals *RuneNode, tag *UintLiteralNode, opts *CompactOptionsNode, openBrace *RuneNode, decls []MessageElement, closeBrace *RuneNode) *GroupNode { + if keyword == nil { + panic("fieldType is nil") + } + if name == nil { + panic("name is nil") + } + if openBrace == nil { + panic("openBrace is nil") + } + if closeBrace == nil { + panic("closeBrace is nil") + } + numChildren := 4 + len(decls) + if label != nil { + numChildren++ + } + if equals != nil { + numChildren++ + } + if tag != nil { + numChildren++ + } + if opts != nil { + numChildren++ + } + children := make([]Node, 0, numChildren) + if label != nil { + children = append(children, label) + } + children = append(children, keyword, name) + if equals != nil { + children = append(children, equals) + } + if tag != nil { + children = append(children, tag) + } + if opts != nil { + children = append(children, opts) + } + children = append(children, openBrace) + for _, decl := range decls { + children = append(children, decl) + } + children = append(children, closeBrace) + + ret := &GroupNode{ + compositeNode: compositeNode{ + children: children, + }, + Label: newFieldLabel(label), + Keyword: keyword, + Name: name, + Equals: equals, + Tag: tag, + Options: opts, + } + populateMessageBody(&ret.MessageBody, openBrace, decls, closeBrace) + return ret +} + +func (n *GroupNode) FieldLabel() Node { + if n.Label.KeywordNode == nil { + // return nil interface to indicate absence, not a typed nil + return nil + } + return n.Label.KeywordNode +} + +func (n *GroupNode) FieldName() Node { + return n.Name +} + +func (n *GroupNode) FieldType() Node { + return n.Keyword +} + +func (n *GroupNode) FieldTag() Node { + if n.Tag == nil { + return n + } + return n.Tag +} + +func (n *GroupNode) FieldExtendee() Node { + if n.Extendee != nil { + return n.Extendee.Extendee + } + return nil +} + +func (n *GroupNode) GetGroupKeyword() Node { + return n.Keyword +} + +func (n *GroupNode) GetOptions() *CompactOptionsNode { + return n.Options +} + +func (n *GroupNode) RangeOptions(fn func(*OptionNode) bool) { + for _, opt := range n.Options.Options { + if !fn(opt) { + return + } + } +} + +func (n *GroupNode) AsMessage() *SyntheticGroupMessageNode { + return (*SyntheticGroupMessageNode)(n) +} + +// SyntheticGroupMessageNode is a view of a GroupNode that implements MessageDeclNode. +// Since a group field implicitly defines a message type, this node represents +// that message type while the corresponding GroupNode represents the field. +// +// This type is considered synthetic since it never appears in a file's AST, but +// is only returned from other accessors (e.g. GroupNode.AsMessage). +type SyntheticGroupMessageNode GroupNode + +func (n *SyntheticGroupMessageNode) MessageName() Node { + return n.Name +} + +func (n *SyntheticGroupMessageNode) RangeOptions(fn func(*OptionNode) bool) { + for _, decl := range n.Decls { + if opt, ok := decl.(*OptionNode); ok { + if !fn(opt) { + return + } + } + } +} + +// OneofDeclNode is a node in the AST that defines a oneof. There are +// multiple types of AST nodes that declare oneofs: +// - *OneofNode +// - *SyntheticOneof +// +// This also allows NoSourceNode to be used in place of one of the above +// for some usages. +type OneofDeclNode interface { + NodeWithOptions + OneofName() Node +} + +var _ OneofDeclNode = (*OneofNode)(nil) +var _ OneofDeclNode = (*SyntheticOneof)(nil) +var _ OneofDeclNode = (*NoSourceNode)(nil) + +// OneofNode represents a one-of declaration. Example: +// +// oneof query { +// string by_name = 2; +// Type by_type = 3; +// Address by_address = 4; +// Labels by_label = 5; +// } +type OneofNode struct { + compositeNode + Keyword *KeywordNode + Name *IdentNode + OpenBrace *RuneNode + Decls []OneofElement + CloseBrace *RuneNode +} + +func (*OneofNode) msgElement() {} + +// NewOneofNode creates a new *OneofNode. All arguments must be non-nil. While +// it is technically allowed for decls to be nil or empty, the resulting node +// will not be a valid oneof, which must have at least one field. +// - keyword: The token corresponding to the "oneof" keyword. +// - name: The token corresponding to the oneof's name. +// - openBrace: The token corresponding to the "{" rune that starts the body. +// - decls: All declarations inside the oneof body. +// - closeBrace: The token corresponding to the "}" rune that ends the body. +func NewOneofNode(keyword *KeywordNode, name *IdentNode, openBrace *RuneNode, decls []OneofElement, closeBrace *RuneNode) *OneofNode { + if keyword == nil { + panic("keyword is nil") + } + if name == nil { + panic("name is nil") + } + if openBrace == nil { + panic("openBrace is nil") + } + if closeBrace == nil { + panic("closeBrace is nil") + } + children := make([]Node, 0, 4+len(decls)) + children = append(children, keyword, name, openBrace) + for _, decl := range decls { + children = append(children, decl) + } + children = append(children, closeBrace) + + for _, decl := range decls { + switch decl := decl.(type) { + case *OptionNode, *FieldNode, *GroupNode, *EmptyDeclNode: + default: + panic(fmt.Sprintf("invalid OneofElement type: %T", decl)) + } + } + + return &OneofNode{ + compositeNode: compositeNode{ + children: children, + }, + Keyword: keyword, + Name: name, + OpenBrace: openBrace, + Decls: decls, + CloseBrace: closeBrace, + } +} + +func (n *OneofNode) OneofName() Node { + return n.Name +} + +func (n *OneofNode) RangeOptions(fn func(*OptionNode) bool) { + for _, decl := range n.Decls { + if opt, ok := decl.(*OptionNode); ok { + if !fn(opt) { + return + } + } + } +} + +// OneofElement is an interface implemented by all AST nodes that can +// appear in the body of a oneof declaration. +type OneofElement interface { + Node + oneofElement() +} + +var _ OneofElement = (*OptionNode)(nil) +var _ OneofElement = (*FieldNode)(nil) +var _ OneofElement = (*GroupNode)(nil) +var _ OneofElement = (*EmptyDeclNode)(nil) + +// SyntheticOneof is not an actual node in the AST but a synthetic node +// that represents the oneof implied by a proto3 optional field. +// +// This type is considered synthetic since it never appears in a file's AST, +// but is only returned from other functions (e.g. NewSyntheticOneof). +type SyntheticOneof struct { + // The proto3 optional field that implies the presence of this oneof. + Field *FieldNode +} + +var _ Node = (*SyntheticOneof)(nil) + +// NewSyntheticOneof creates a new *SyntheticOneof that corresponds to the +// given proto3 optional field. +func NewSyntheticOneof(field *FieldNode) *SyntheticOneof { + return &SyntheticOneof{Field: field} +} + +func (n *SyntheticOneof) Start() Token { + return n.Field.Start() +} + +func (n *SyntheticOneof) End() Token { + return n.Field.End() +} + +func (n *SyntheticOneof) LeadingComments() []Comment { + return nil +} + +func (n *SyntheticOneof) TrailingComments() []Comment { + return nil +} + +func (n *SyntheticOneof) OneofName() Node { + return n.Field.FieldName() +} + +func (n *SyntheticOneof) RangeOptions(_ func(*OptionNode) bool) { +} + +// MapTypeNode represents the type declaration for a map field. It defines +// both the key and value types for the map. Example: +// +// map<string, Values> +type MapTypeNode struct { + compositeNode + Keyword *KeywordNode + OpenAngle *RuneNode + KeyType *IdentNode + Comma *RuneNode + ValueType IdentValueNode + CloseAngle *RuneNode +} + +// NewMapTypeNode creates a new *MapTypeNode. All arguments must be non-nil. +// - keyword: The token corresponding to the "map" keyword. +// - openAngle: The token corresponding to the "<" rune after the keyword. +// - keyType: The token corresponding to the key type for the map. +// - comma: The token corresponding to the "," rune between key and value types. +// - valType: The token corresponding to the value type for the map. +// - closeAngle: The token corresponding to the ">" rune that ends the declaration. +func NewMapTypeNode(keyword *KeywordNode, openAngle *RuneNode, keyType *IdentNode, comma *RuneNode, valType IdentValueNode, closeAngle *RuneNode) *MapTypeNode { + if keyword == nil { + panic("keyword is nil") + } + if openAngle == nil { + panic("openAngle is nil") + } + if keyType == nil { + panic("keyType is nil") + } + if comma == nil { + panic("comma is nil") + } + if valType == nil { + panic("valType is nil") + } + if closeAngle == nil { + panic("closeAngle is nil") + } + children := []Node{keyword, openAngle, keyType, comma, valType, closeAngle} + return &MapTypeNode{ + compositeNode: compositeNode{ + children: children, + }, + Keyword: keyword, + OpenAngle: openAngle, + KeyType: keyType, + Comma: comma, + ValueType: valType, + CloseAngle: closeAngle, + } +} + +// MapFieldNode represents a map field declaration. Example: +// +// map<string,string> replacements = 3 [deprecated = true]; +type MapFieldNode struct { + compositeNode + MapType *MapTypeNode + Name *IdentNode + Equals *RuneNode + Tag *UintLiteralNode + Options *CompactOptionsNode + Semicolon *RuneNode +} + +func (*MapFieldNode) msgElement() {} + +// NewMapFieldNode creates a new *MapFieldNode. All arguments must be non-nil +// except opts, which may be nil. +// - mapType: The token corresponding to the map type. +// - name: The token corresponding to the field's name. +// - equals: The token corresponding to the '=' rune after the name. +// - tag: The token corresponding to the field's tag number. +// - opts: Optional set of field options. +// - semicolon: The token corresponding to the ";" rune that ends the declaration. +func NewMapFieldNode(mapType *MapTypeNode, name *IdentNode, equals *RuneNode, tag *UintLiteralNode, opts *CompactOptionsNode, semicolon *RuneNode) *MapFieldNode { + if mapType == nil { + panic("mapType is nil") + } + if name == nil { + panic("name is nil") + } + numChildren := 2 + if equals != nil { + numChildren++ + } + if tag != nil { + numChildren++ + } + if opts != nil { + numChildren++ + } + if semicolon != nil { + numChildren++ + } + children := make([]Node, 0, numChildren) + children = append(children, mapType, name) + if equals != nil { + children = append(children, equals) + } + if tag != nil { + children = append(children, tag) + } + if opts != nil { + children = append(children, opts) + } + if semicolon != nil { + children = append(children, semicolon) + } + + return &MapFieldNode{ + compositeNode: compositeNode{ + children: children, + }, + MapType: mapType, + Name: name, + Equals: equals, + Tag: tag, + Options: opts, + Semicolon: semicolon, + } +} + +func (n *MapFieldNode) FieldLabel() Node { + return nil +} + +func (n *MapFieldNode) FieldName() Node { + return n.Name +} + +func (n *MapFieldNode) FieldType() Node { + return n.MapType +} + +func (n *MapFieldNode) FieldTag() Node { + if n.Tag == nil { + return n + } + return n.Tag +} + +func (n *MapFieldNode) FieldExtendee() Node { + return nil +} + +func (n *MapFieldNode) GetGroupKeyword() Node { + return nil +} + +func (n *MapFieldNode) GetOptions() *CompactOptionsNode { + return n.Options +} + +func (n *MapFieldNode) RangeOptions(fn func(*OptionNode) bool) { + for _, opt := range n.Options.Options { + if !fn(opt) { + return + } + } +} + +func (n *MapFieldNode) AsMessage() *SyntheticMapEntryNode { + return (*SyntheticMapEntryNode)(n) +} + +func (n *MapFieldNode) KeyField() *SyntheticMapField { + return NewSyntheticMapField(n.MapType.KeyType, 1) +} + +func (n *MapFieldNode) ValueField() *SyntheticMapField { + return NewSyntheticMapField(n.MapType.ValueType, 2) +} + +// SyntheticMapEntryNode is a view of a MapFieldNode that implements MessageDeclNode. +// Since a map field implicitly defines a message type for the map entry, +// this node represents that message type. +// +// This type is considered synthetic since it never appears in a file's AST, but +// is only returned from other accessors (e.g. MapFieldNode.AsMessage). +type SyntheticMapEntryNode MapFieldNode + +func (n *SyntheticMapEntryNode) MessageName() Node { + return n.Name +} + +func (n *SyntheticMapEntryNode) RangeOptions(_ func(*OptionNode) bool) { +} + +// SyntheticMapField is not an actual node in the AST but a synthetic node +// that implements FieldDeclNode. These are used to represent the implicit +// field declarations of the "key" and "value" fields in a map entry. +// +// This type is considered synthetic since it never appears in a file's AST, +// but is only returned from other accessors and functions (e.g. +// MapFieldNode.KeyField, MapFieldNode.ValueField, and NewSyntheticMapField). +type SyntheticMapField struct { + Ident IdentValueNode + Tag *UintLiteralNode +} + +// NewSyntheticMapField creates a new *SyntheticMapField for the given +// identifier (either a key or value type in a map declaration) and tag +// number (1 for key, 2 for value). +func NewSyntheticMapField(ident IdentValueNode, tagNum uint64) *SyntheticMapField { + tag := &UintLiteralNode{ + terminalNode: ident.Start().asTerminalNode(), + Val: tagNum, + } + return &SyntheticMapField{Ident: ident, Tag: tag} +} + +func (n *SyntheticMapField) Start() Token { + return n.Ident.Start() +} + +func (n *SyntheticMapField) End() Token { + return n.Ident.End() +} + +func (n *SyntheticMapField) LeadingComments() []Comment { + return nil +} + +func (n *SyntheticMapField) TrailingComments() []Comment { + return nil +} + +func (n *SyntheticMapField) FieldLabel() Node { + return n.Ident +} + +func (n *SyntheticMapField) FieldName() Node { + return n.Ident +} + +func (n *SyntheticMapField) FieldType() Node { + return n.Ident +} + +func (n *SyntheticMapField) FieldTag() Node { + if n.Tag == nil { + return n + } + return n.Tag +} + +func (n *SyntheticMapField) FieldExtendee() Node { + return nil +} + +func (n *SyntheticMapField) GetGroupKeyword() Node { + return nil +} + +func (n *SyntheticMapField) GetOptions() *CompactOptionsNode { + return nil +} + +func (n *SyntheticMapField) RangeOptions(_ func(*OptionNode) bool) { +} diff --git a/vendor/github.com/bufbuild/protocompile/ast/file.go b/vendor/github.com/bufbuild/protocompile/ast/file.go new file mode 100644 index 0000000..50d4ca9 --- /dev/null +++ b/vendor/github.com/bufbuild/protocompile/ast/file.go @@ -0,0 +1,358 @@ +// Copyright 2020-2024 Buf Technologies, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package ast + +import "fmt" + +// FileDeclNode is a placeholder interface for AST nodes that represent files. +// This allows NoSourceNode to be used in place of *FileNode for some usages. +type FileDeclNode interface { + NodeWithOptions + Name() string + NodeInfo(n Node) NodeInfo +} + +var _ FileDeclNode = (*FileNode)(nil) +var _ FileDeclNode = (*NoSourceNode)(nil) + +// FileNode is the root of the AST hierarchy. It represents an entire +// protobuf source file. +type FileNode struct { + compositeNode + fileInfo *FileInfo + + // A file has either a Syntax or Edition node, never both. + // If both are nil, neither declaration is present and the + // file is assumed to use "proto2" syntax. + Syntax *SyntaxNode + Edition *EditionNode + + Decls []FileElement + + // This synthetic node allows access to final comments and whitespace + EOF *RuneNode +} + +// NewFileNode creates a new *FileNode. The syntax parameter is optional. If it +// is absent, it means the file had no syntax declaration. +// +// This function panics if the concrete type of any element of decls is not +// from this package. +func NewFileNode(info *FileInfo, syntax *SyntaxNode, decls []FileElement, eof Token) *FileNode { + return newFileNode(info, syntax, nil, decls, eof) +} + +// NewFileNodeWithEdition creates a new *FileNode. The edition parameter is required. If a file +// has no edition declaration, use NewFileNode instead. +// +// This function panics if the concrete type of any element of decls is not +// from this package. +func NewFileNodeWithEdition(info *FileInfo, edition *EditionNode, decls []FileElement, eof Token) *FileNode { + if edition == nil { + panic("edition is nil") + } + return newFileNode(info, nil, edition, decls, eof) +} + +func newFileNode(info *FileInfo, syntax *SyntaxNode, edition *EditionNode, decls []FileElement, eof Token) *FileNode { + numChildren := len(decls) + 1 + if syntax != nil || edition != nil { + numChildren++ + } + children := make([]Node, 0, numChildren) + if syntax != nil { + children = append(children, syntax) + } else if edition != nil { + children = append(children, edition) + } + for _, decl := range decls { + switch decl := decl.(type) { + case *PackageNode, *ImportNode, *OptionNode, *MessageNode, + *EnumNode, *ExtendNode, *ServiceNode, *EmptyDeclNode: + default: + panic(fmt.Sprintf("invalid FileElement type: %T", decl)) + } + children = append(children, decl) + } + + eofNode := NewRuneNode(0, eof) + children = append(children, eofNode) + + return &FileNode{ + compositeNode: compositeNode{ + children: children, + }, + fileInfo: info, + Syntax: syntax, + Edition: edition, + Decls: decls, + EOF: eofNode, + } +} + +// NewEmptyFileNode returns an empty AST for a file with the given name. +func NewEmptyFileNode(filename string) *FileNode { + fileInfo := NewFileInfo(filename, []byte{}) + return NewFileNode(fileInfo, nil, nil, fileInfo.AddToken(0, 0)) +} + +func (f *FileNode) Name() string { + return f.fileInfo.Name() +} + +func (f *FileNode) NodeInfo(n Node) NodeInfo { + return f.fileInfo.NodeInfo(n) +} + +func (f *FileNode) TokenInfo(t Token) NodeInfo { + return f.fileInfo.TokenInfo(t) +} + +func (f *FileNode) ItemInfo(i Item) ItemInfo { + return f.fileInfo.ItemInfo(i) +} + +func (f *FileNode) GetItem(i Item) (Token, Comment) { + return f.fileInfo.GetItem(i) +} + +func (f *FileNode) Items() Sequence[Item] { + return f.fileInfo.Items() +} + +func (f *FileNode) Tokens() Sequence[Token] { + return f.fileInfo.Tokens() +} + +func (f *FileNode) RangeOptions(fn func(*OptionNode) bool) { + for _, decl := range f.Decls { + if opt, ok := decl.(*OptionNode); ok { + if !fn(opt) { + return + } + } + } +} + +// FileElement is an interface implemented by all AST nodes that are +// allowed as top-level declarations in the file. +type FileElement interface { + Node + fileElement() +} + +var _ FileElement = (*ImportNode)(nil) +var _ FileElement = (*PackageNode)(nil) +var _ FileElement = (*OptionNode)(nil) +var _ FileElement = (*MessageNode)(nil) +var _ FileElement = (*EnumNode)(nil) +var _ FileElement = (*ExtendNode)(nil) +var _ FileElement = (*ServiceNode)(nil) +var _ FileElement = (*EmptyDeclNode)(nil) + +// SyntaxNode represents a syntax declaration, which if present must be +// the first non-comment content. Example: +// +// syntax = "proto2"; +// +// Files that don't have a syntax node are assumed to use proto2 syntax. +type SyntaxNode struct { + compositeNode + Keyword *KeywordNode + Equals *RuneNode + Syntax StringValueNode + Semicolon *RuneNode +} + +// NewSyntaxNode creates a new *SyntaxNode. All four arguments must be non-nil: +// - keyword: The token corresponding to the "syntax" keyword. +// - equals: The token corresponding to the "=" rune. +// - syntax: The actual syntax value, e.g. "proto2" or "proto3". +// - semicolon: The token corresponding to the ";" rune that ends the declaration. +func NewSyntaxNode(keyword *KeywordNode, equals *RuneNode, syntax StringValueNode, semicolon *RuneNode) *SyntaxNode { + if keyword == nil { + panic("keyword is nil") + } + if equals == nil { + panic("equals is nil") + } + if syntax == nil { + panic("syntax is nil") + } + var children []Node + if semicolon == nil { + children = []Node{keyword, equals, syntax} + } else { + children = []Node{keyword, equals, syntax, semicolon} + } + return &SyntaxNode{ + compositeNode: compositeNode{ + children: children, + }, + Keyword: keyword, + Equals: equals, + Syntax: syntax, + Semicolon: semicolon, + } +} + +// EditionNode represents an edition declaration, which if present must be +// the first non-comment content. Example: +// +// edition = "2023"; +// +// Files may include either an edition node or a syntax node, but not both. +// If neither are present, the file is assumed to use proto2 syntax. +type EditionNode struct { + compositeNode + Keyword *KeywordNode + Equals *RuneNode + Edition StringValueNode + Semicolon *RuneNode +} + +// NewEditionNode creates a new *EditionNode. All four arguments must be non-nil: +// - keyword: The token corresponding to the "edition" keyword. +// - equals: The token corresponding to the "=" rune. +// - edition: The actual edition value, e.g. "2023". +// - semicolon: The token corresponding to the ";" rune that ends the declaration. +func NewEditionNode(keyword *KeywordNode, equals *RuneNode, edition StringValueNode, semicolon *RuneNode) *EditionNode { + if keyword == nil { + panic("keyword is nil") + } + if equals == nil { + panic("equals is nil") + } + if edition == nil { + panic("edition is nil") + } + if semicolon == nil { + panic("semicolon is nil") + } + children := []Node{keyword, equals, edition, semicolon} + return &EditionNode{ + compositeNode: compositeNode{ + children: children, + }, + Keyword: keyword, + Equals: equals, + Edition: edition, + Semicolon: semicolon, + } +} + +// ImportNode represents an import statement. Example: +// +// import "google/protobuf/empty.proto"; +type ImportNode struct { + compositeNode + Keyword *KeywordNode + // Optional; if present indicates this is a public import + Public *KeywordNode + // Optional; if present indicates this is a weak import + Weak *KeywordNode + Name StringValueNode + Semicolon *RuneNode +} + +// NewImportNode creates a new *ImportNode. The public and weak arguments are optional +// and only one or the other (or neither) may be specified, not both. When public is +// non-nil, it indicates the "public" keyword in the import statement and means this is +// a public import. When weak is non-nil, it indicates the "weak" keyword in the import +// statement and means this is a weak import. When both are nil, this is a normal import. +// The other arguments must be non-nil: +// - keyword: The token corresponding to the "import" keyword. +// - public: The token corresponding to the optional "public" keyword. +// - weak: The token corresponding to the optional "weak" keyword. +// - name: The actual imported file name. +// - semicolon: The token corresponding to the ";" rune that ends the declaration. +func NewImportNode(keyword *KeywordNode, public *KeywordNode, weak *KeywordNode, name StringValueNode, semicolon *RuneNode) *ImportNode { + if keyword == nil { + panic("keyword is nil") + } + if name == nil { + panic("name is nil") + } + numChildren := 2 + if semicolon == nil { + numChildren++ + } + if public != nil || weak != nil { + numChildren++ + } + children := make([]Node, 0, numChildren) + children = append(children, keyword) + if public != nil { + children = append(children, public) + } else if weak != nil { + children = append(children, weak) + } + children = append(children, name) + if semicolon != nil { + children = append(children, semicolon) + } + + return &ImportNode{ + compositeNode: compositeNode{ + children: children, + }, + Keyword: keyword, + Public: public, + Weak: weak, + Name: name, + Semicolon: semicolon, + } +} + +func (*ImportNode) fileElement() {} + +// PackageNode represents a package declaration. Example: +// +// package foobar.com; +type PackageNode struct { + compositeNode + Keyword *KeywordNode + Name IdentValueNode + Semicolon *RuneNode +} + +func (*PackageNode) fileElement() {} + +// NewPackageNode creates a new *PackageNode. All three arguments must be non-nil: +// - keyword: The token corresponding to the "package" keyword. +// - name: The package name declared for the file. +// - semicolon: The token corresponding to the ";" rune that ends the declaration. +func NewPackageNode(keyword *KeywordNode, name IdentValueNode, semicolon *RuneNode) *PackageNode { + if keyword == nil { + panic("keyword is nil") + } + if name == nil { + panic("name is nil") + } + var children []Node + if semicolon == nil { + children = []Node{keyword, name} + } else { + children = []Node{keyword, name, semicolon} + } + return &PackageNode{ + compositeNode: compositeNode{ + children: children, + }, + Keyword: keyword, + Name: name, + Semicolon: semicolon, + } +} diff --git a/vendor/github.com/bufbuild/protocompile/ast/file_info.go b/vendor/github.com/bufbuild/protocompile/ast/file_info.go new file mode 100644 index 0000000..7c2d904 --- /dev/null +++ b/vendor/github.com/bufbuild/protocompile/ast/file_info.go @@ -0,0 +1,701 @@ +// Copyright 2020-2024 Buf Technologies, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package ast + +import ( + "fmt" + "sort" + "unicode/utf8" +) + +// FileInfo contains information about the contents of a source file, including +// details about comments and items. A lexer accumulates these details as it +// scans the file contents. This allows efficient representation of things like +// source positions. +type FileInfo struct { + // The name of the source file. + name string + // The raw contents of the source file. + data []byte + // The offsets for each line in the file. The value is the zero-based byte + // offset for a given line. The line is given by its index. So the value at + // index 0 is the offset for the first line (which is always zero). The + // value at index 1 is the offset at which the second line begins. Etc. + lines []int + // The info for every comment in the file. This is empty if the file has no + // comments. The first entry corresponds to the first comment in the file, + // and so on. + comments []commentInfo + // The info for every lexed item in the file. The last item in the slice + // corresponds to the EOF, so every file (even an empty one) should have at + // least one entry. This includes all terminal symbols (tokens) in the AST + // as well as all comments. + items []itemSpan +} + +type commentInfo struct { + // the index of the item, in the file's items slice, that represents this + // comment + index int + // the index of the token to which this comment is attributed. + attributedToIndex int +} + +type itemSpan struct { + // the offset into the file of the first character of an item. + offset int + // the length of the item + length int +} + +// NewFileInfo creates a new instance for the given file. +func NewFileInfo(filename string, contents []byte) *FileInfo { + return &FileInfo{ + name: filename, + data: contents, + lines: []int{0}, + } +} + +func (f *FileInfo) Name() string { + return f.name +} + +// AddLine adds the offset representing the beginning of the "next" line in the file. +// The first line always starts at offset 0, the second line starts at offset-of-newline-char+1. +func (f *FileInfo) AddLine(offset int) { + if offset < 0 { + panic(fmt.Sprintf("invalid offset: %d must not be negative", offset)) + } + if offset > len(f.data) { + panic(fmt.Sprintf("invalid offset: %d is greater than file size %d", offset, len(f.data))) + } + + if len(f.lines) > 0 { + lastOffset := f.lines[len(f.lines)-1] + if offset <= lastOffset { + panic(fmt.Sprintf("invalid offset: %d is not greater than previously observed line offset %d", offset, lastOffset)) + } + } + + f.lines = append(f.lines, offset) +} + +// AddToken adds info about a token at the given location to this file. It +// returns a value that allows access to all of the token's details. +func (f *FileInfo) AddToken(offset, length int) Token { + if offset < 0 { + panic(fmt.Sprintf("invalid offset: %d must not be negative", offset)) + } + if length < 0 { + panic(fmt.Sprintf("invalid length: %d must not be negative", length)) + } + if offset+length > len(f.data) { + panic(fmt.Sprintf("invalid offset+length: %d is greater than file size %d", offset+length, len(f.data))) + } + + tokenID := len(f.items) + if len(f.items) > 0 { + lastToken := f.items[tokenID-1] + lastEnd := lastToken.offset + lastToken.length - 1 + if offset <= lastEnd { + panic(fmt.Sprintf("invalid offset: %d is not greater than previously observed token end %d", offset, lastEnd)) + } + } + + f.items = append(f.items, itemSpan{offset: offset, length: length}) + return Token(tokenID) +} + +// AddComment adds info about a comment to this file. Comments must first be +// added as items via f.AddToken(). The given comment argument is the Token +// from that step. The given attributedTo argument indicates another token in the +// file with which the comment is associated. If comment's offset is before that +// of attributedTo, then this is a leading comment. Otherwise, it is a trailing +// comment. +func (f *FileInfo) AddComment(comment, attributedTo Token) Comment { + if len(f.comments) > 0 { + lastComment := f.comments[len(f.comments)-1] + if int(comment) <= lastComment.index { + panic(fmt.Sprintf("invalid index: %d is not greater than previously observed comment index %d", comment, lastComment.index)) + } + if int(attributedTo) < lastComment.attributedToIndex { + panic(fmt.Sprintf("invalid attribution: %d is not greater than previously observed comment attribution index %d", attributedTo, lastComment.attributedToIndex)) + } + } + + f.comments = append(f.comments, commentInfo{index: int(comment), attributedToIndex: int(attributedTo)}) + return Comment{ + fileInfo: f, + index: len(f.comments) - 1, + } +} + +// NodeInfo returns details from the original source for the given AST node. +// +// If the given n is out of range, this returns an invalid NodeInfo (i.e. +// nodeInfo.IsValid() returns false). If the given n is not out of range but +// also from a different file than f, then the result is undefined. +func (f *FileInfo) NodeInfo(n Node) NodeInfo { + return f.nodeInfo(int(n.Start()), int(n.End())) +} + +// TokenInfo returns details from the original source for the given token. +// +// If the given t is out of range, this returns an invalid NodeInfo (i.e. +// nodeInfo.IsValid() returns false). If the given t is not out of range but +// also from a different file than f, then the result is undefined. +func (f *FileInfo) TokenInfo(t Token) NodeInfo { + return f.nodeInfo(int(t), int(t)) +} + +func (f *FileInfo) nodeInfo(start, end int) NodeInfo { + if start < 0 || start >= len(f.items) { + return NodeInfo{fileInfo: f} + } + if end < 0 || end >= len(f.items) { + return NodeInfo{fileInfo: f} + } + return NodeInfo{fileInfo: f, startIndex: start, endIndex: end} +} + +// ItemInfo returns details from the original source for the given item. +// +// If the given i is out of range, this returns nil. If the given i is not +// out of range but also from a different file than f, then the result is +// undefined. +func (f *FileInfo) ItemInfo(i Item) ItemInfo { + tok, cmt := f.GetItem(i) + if tok != TokenError { + return f.TokenInfo(tok) + } + if cmt.IsValid() { + return cmt + } + return nil +} + +// GetItem returns the token or comment represented by the given item. Only one +// of the return values will be valid. If the item is a token then the returned +// comment will be a zero value and thus invalid (i.e. comment.IsValid() returns +// false). If the item is a comment then the returned token will be TokenError. +// +// If the given i is out of range, this returns (TokenError, Comment{}). If the +// given i is not out of range but also from a different file than f, then +// the result is undefined. +func (f *FileInfo) GetItem(i Item) (Token, Comment) { + if i < 0 || int(i) >= len(f.items) { + return TokenError, Comment{} + } + if !f.isComment(i) { + return Token(i), Comment{} + } + // It's a comment, so find its location in f.comments + c := sort.Search(len(f.comments), func(c int) bool { + return f.comments[c].index >= int(i) + }) + if c < len(f.comments) && f.comments[c].index == int(i) { + return TokenError, Comment{fileInfo: f, index: c} + } + // f.isComment(i) returned true, but we couldn't find it + // in f.comments? Uh oh... that shouldn't be possible. + return TokenError, Comment{} +} + +func (f *FileInfo) isDummyFile() bool { + return f == nil || f.lines == nil +} + +// Sequence represents a navigable sequence of elements. +type Sequence[T any] interface { + // First returns the first element in the sequence. The bool return + // is false if this sequence contains no elements. For example, an + // empty file has no items or tokens. + First() (T, bool) + // Next returns the next element in the sequence that comes after + // the given element. The bool returns is false if there is no next + // item (i.e. the given element is the last one). It also returns + // false if the given element is invalid. + Next(T) (T, bool) + // Last returns the last element in the sequence. The bool return + // is false if this sequence contains no elements. For example, an + // empty file has no items or tokens. + Last() (T, bool) + // Previous returns the previous element in the sequence that comes + // before the given element. The bool returns is false if there is no + // previous item (i.e. the given element is the first one). It also + // returns false if the given element is invalid. + Previous(T) (T, bool) +} + +func (f *FileInfo) Items() Sequence[Item] { + return items{fileInfo: f} +} + +func (f *FileInfo) Tokens() Sequence[Token] { + return tokens{fileInfo: f} +} + +type items struct { + fileInfo *FileInfo +} + +func (i items) First() (Item, bool) { + if len(i.fileInfo.items) == 0 { + return 0, false + } + return 0, true +} + +func (i items) Next(item Item) (Item, bool) { + if item < 0 || int(item) >= len(i.fileInfo.items)-1 { + return 0, false + } + return i.fileInfo.itemForward(item+1, true) +} + +func (i items) Last() (Item, bool) { + if len(i.fileInfo.items) == 0 { + return 0, false + } + return Item(len(i.fileInfo.items) - 1), true +} + +func (i items) Previous(item Item) (Item, bool) { + if item <= 0 || int(item) >= len(i.fileInfo.items) { + return 0, false + } + return i.fileInfo.itemBackward(item-1, true) +} + +type tokens struct { + fileInfo *FileInfo +} + +func (t tokens) First() (Token, bool) { + i, ok := t.fileInfo.itemForward(0, false) + return Token(i), ok +} + +func (t tokens) Next(tok Token) (Token, bool) { + if tok < 0 || int(tok) >= len(t.fileInfo.items)-1 { + return 0, false + } + i, ok := t.fileInfo.itemForward(Item(tok+1), false) + return Token(i), ok +} + +func (t tokens) Last() (Token, bool) { + i, ok := t.fileInfo.itemBackward(Item(len(t.fileInfo.items))-1, false) + return Token(i), ok +} + +func (t tokens) Previous(tok Token) (Token, bool) { + if tok <= 0 || int(tok) >= len(t.fileInfo.items) { + return 0, false + } + i, ok := t.fileInfo.itemBackward(Item(tok-1), false) + return Token(i), ok +} + +func (f *FileInfo) itemForward(i Item, allowComment bool) (Item, bool) { + end := Item(len(f.items)) + for i < end { + if allowComment || !f.isComment(i) { + return i, true + } + i++ + } + return 0, false +} + +func (f *FileInfo) itemBackward(i Item, allowComment bool) (Item, bool) { + for i >= 0 { + if allowComment || !f.isComment(i) { + return i, true + } + i-- + } + return 0, false +} + +// isComment is comment returns true if i refers to a comment. +// (If it returns false, i refers to a token.) +func (f *FileInfo) isComment(i Item) bool { + item := f.items[i] + if item.length < 2 { + return false + } + // see if item text starts with "//" or "/*" + if f.data[item.offset] != '/' { + return false + } + c := f.data[item.offset+1] + return c == '/' || c == '*' +} + +func (f *FileInfo) SourcePos(offset int) SourcePos { + lineNumber := sort.Search(len(f.lines), func(n int) bool { + return f.lines[n] > offset + }) + + // If it weren't for tabs and multibyte unicode characters, we + // could trivially compute the column just based on offset and the + // starting offset of lineNumber :( + // Wish this were more efficient... that would require also storing + // computed line+column information, which would triple the size of + // f's items slice... + col := 0 + for i := f.lines[lineNumber-1]; i < offset; i++ { + if f.data[i] == '\t' { + nextTabStop := 8 - (col % 8) + col += nextTabStop + } else if utf8.RuneStart(f.data[i]) { + col++ + } + } + + return SourcePos{ + Filename: f.name, + Offset: offset, + Line: lineNumber, + // Columns are 1-indexed in this AST + Col: col + 1, + } +} + +// Token represents a single lexed token. +type Token int + +// TokenError indicates an invalid token. It is returned from query +// functions when no valid token satisfies the request. +const TokenError = Token(-1) + +// AsItem returns the Item that corresponds to t. +func (t Token) AsItem() Item { + return Item(t) +} + +func (t Token) asTerminalNode() terminalNode { + return terminalNode(t) +} + +// Item represents an item lexed from source. It represents either +// a Token or a Comment. +type Item int + +// ItemInfo provides details about an item's location in the source file and +// its contents. +type ItemInfo interface { + SourceSpan + LeadingWhitespace() string + RawText() string +} + +// NodeInfo represents the details for a node or token in the source file's AST. +// It provides access to information about the node's location in the source +// file. It also provides access to the original text in the source file (with +// all the original formatting intact) and also provides access to surrounding +// comments. +type NodeInfo struct { + fileInfo *FileInfo + startIndex, endIndex int +} + +var _ ItemInfo = NodeInfo{} + +// IsValid returns true if this node info is valid. If n is a zero-value struct, +// it is not valid. +func (n NodeInfo) IsValid() bool { + return n.fileInfo != nil +} + +// Start returns the starting position of the element. This is the first +// character of the node or token. +func (n NodeInfo) Start() SourcePos { + if n.fileInfo.isDummyFile() { + return UnknownPos(n.fileInfo.name) + } + + tok := n.fileInfo.items[n.startIndex] + return n.fileInfo.SourcePos(tok.offset) +} + +// End returns the ending position of the element, exclusive. This is the +// location after the last character of the node or token. If n returns +// the same position for Start() and End(), the element in source had a +// length of zero (which should only happen for the special EOF token +// that designates the end of the file). +func (n NodeInfo) End() SourcePos { + if n.fileInfo.isDummyFile() { + return UnknownPos(n.fileInfo.name) + } + + tok := n.fileInfo.items[n.endIndex] + // find offset of last character in the span + offset := tok.offset + if tok.length > 0 { + offset += tok.length - 1 + } + pos := n.fileInfo.SourcePos(offset) + if tok.length > 0 { + // We return "open range", so end is the position *after* the + // last character in the span. So we adjust + pos.Col++ + } + return pos +} + +// LeadingWhitespace returns any whitespace prior to the element. If there +// were comments in between this element and the previous one, this will +// return the whitespace between the last such comment in the element. If +// there were no such comments, this returns the whitespace between the +// previous element and the current one. +func (n NodeInfo) LeadingWhitespace() string { + if n.fileInfo.isDummyFile() { + return "" + } + + tok := n.fileInfo.items[n.startIndex] + var prevEnd int + if n.startIndex > 0 { + prevTok := n.fileInfo.items[n.startIndex-1] + prevEnd = prevTok.offset + prevTok.length + } + return string(n.fileInfo.data[prevEnd:tok.offset]) +} + +// LeadingComments returns all comments in the source that exist between the +// element and the previous element, except for any trailing comment on the +// previous element. +func (n NodeInfo) LeadingComments() Comments { + if n.fileInfo.isDummyFile() { + return EmptyComments + } + + start := sort.Search(len(n.fileInfo.comments), func(i int) bool { + return n.fileInfo.comments[i].attributedToIndex >= n.startIndex + }) + + if start == len(n.fileInfo.comments) || n.fileInfo.comments[start].attributedToIndex != n.startIndex { + // no comments associated with this token + return EmptyComments + } + + numComments := 0 + for i := start; i < len(n.fileInfo.comments); i++ { + comment := n.fileInfo.comments[i] + if comment.attributedToIndex == n.startIndex && + comment.index < n.startIndex { + numComments++ + } else { + break + } + } + + return Comments{ + fileInfo: n.fileInfo, + first: start, + num: numComments, + } +} + +// TrailingComments returns the trailing comment for the element, if any. +// An element will have a trailing comment only if it is the last token +// on a line and is followed by a comment on the same line. Typically, the +// following comment is a line-style comment (starting with "//"). +// +// If the following comment is a block-style comment that spans multiple +// lines, and the next token is on the same line as the end of the comment, +// the comment is NOT considered a trailing comment. +// +// Examples: +// +// foo // this is a trailing comment for foo +// +// bar /* this is a trailing comment for bar */ +// +// baz /* this is a trailing +// comment for baz */ +// +// fizz /* this is NOT a trailing +// comment for fizz because +// its on the same line as the +// following token buzz */ buzz +func (n NodeInfo) TrailingComments() Comments { + if n.fileInfo.isDummyFile() { + return EmptyComments + } + + start := sort.Search(len(n.fileInfo.comments), func(i int) bool { + comment := n.fileInfo.comments[i] + return comment.attributedToIndex >= n.endIndex && + comment.index > n.endIndex + }) + + if start == len(n.fileInfo.comments) || n.fileInfo.comments[start].attributedToIndex != n.endIndex { + // no comments associated with this token + return EmptyComments + } + + numComments := 0 + for i := start; i < len(n.fileInfo.comments); i++ { + comment := n.fileInfo.comments[i] + if comment.attributedToIndex == n.endIndex { + numComments++ + } else { + break + } + } + + return Comments{ + fileInfo: n.fileInfo, + first: start, + num: numComments, + } +} + +// RawText returns the actual text in the source file that corresponds to the +// element. If the element is a node in the AST that encompasses multiple +// items (like an entire declaration), the full text of all items is returned +// including any interior whitespace and comments. +func (n NodeInfo) RawText() string { + startTok := n.fileInfo.items[n.startIndex] + endTok := n.fileInfo.items[n.endIndex] + return string(n.fileInfo.data[startTok.offset : endTok.offset+endTok.length]) +} + +// SourcePos identifies a location in a proto source file. +type SourcePos struct { + Filename string + // The line and column numbers for this position. These are + // one-based, so the first line and column is 1 (not zero). If + // either is zero, then the line and column are unknown and + // only the file name is known. + Line, Col int + // The offset, in bytes, from the beginning of the file. This + // is zero-based: the first character in the file is offset zero. + Offset int +} + +func (pos SourcePos) String() string { + if pos.Line <= 0 || pos.Col <= 0 { + return pos.Filename + } + return fmt.Sprintf("%s:%d:%d", pos.Filename, pos.Line, pos.Col) +} + +// SourceSpan represents a range of source positions. +type SourceSpan interface { + Start() SourcePos + End() SourcePos +} + +// NewSourceSpan creates a new span that covers the given range. +func NewSourceSpan(start SourcePos, end SourcePos) SourceSpan { + return sourceSpan{StartPos: start, EndPos: end} +} + +type sourceSpan struct { + StartPos SourcePos + EndPos SourcePos +} + +func (p sourceSpan) Start() SourcePos { + return p.StartPos +} + +func (p sourceSpan) End() SourcePos { + return p.EndPos +} + +var _ SourceSpan = sourceSpan{} + +// Comments represents a range of sequential comments in a source file +// (e.g. no interleaving items or AST nodes). +type Comments struct { + fileInfo *FileInfo + first, num int +} + +// EmptyComments is an empty set of comments. +var EmptyComments = Comments{} + +// Len returns the number of comments in c. +func (c Comments) Len() int { + return c.num +} + +func (c Comments) Index(i int) Comment { + if i < 0 || i >= c.num { + panic(fmt.Sprintf("index %d out of range (len = %d)", i, c.num)) + } + return Comment{ + fileInfo: c.fileInfo, + index: c.first + i, + } +} + +// Comment represents a single comment in a source file. It indicates +// the position of the comment and its contents. A single comment means +// one line-style comment ("//" to end of line) or one block comment +// ("/*" through "*/"). If a longer comment uses multiple line comments, +// each line is considered to be a separate comment. For example: +// +// // This is a single comment, and +// // this is a separate comment. +type Comment struct { + fileInfo *FileInfo + index int +} + +var _ ItemInfo = Comment{} + +// IsValid returns true if this comment is valid. If this comment is +// a zero-value struct, it is not valid. +func (c Comment) IsValid() bool { + return c.fileInfo != nil && c.index >= 0 +} + +// AsItem returns the Item that corresponds to c. +func (c Comment) AsItem() Item { + return Item(c.fileInfo.comments[c.index].index) +} + +func (c Comment) Start() SourcePos { + span := c.fileInfo.items[c.AsItem()] + return c.fileInfo.SourcePos(span.offset) +} + +func (c Comment) End() SourcePos { + span := c.fileInfo.items[c.AsItem()] + return c.fileInfo.SourcePos(span.offset + span.length - 1) +} + +func (c Comment) LeadingWhitespace() string { + item := c.AsItem() + span := c.fileInfo.items[item] + var prevEnd int + if item > 0 { + prevItem := c.fileInfo.items[item-1] + prevEnd = prevItem.offset + prevItem.length + } + return string(c.fileInfo.data[prevEnd:span.offset]) +} + +func (c Comment) RawText() string { + span := c.fileInfo.items[c.AsItem()] + return string(c.fileInfo.data[span.offset : span.offset+span.length]) +} diff --git a/vendor/github.com/bufbuild/protocompile/ast/identifiers.go b/vendor/github.com/bufbuild/protocompile/ast/identifiers.go new file mode 100644 index 0000000..511389d --- /dev/null +++ b/vendor/github.com/bufbuild/protocompile/ast/identifiers.go @@ -0,0 +1,153 @@ +// Copyright 2020-2024 Buf Technologies, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package ast + +import ( + "fmt" + "strings" +) + +// Identifier is a possibly-qualified name. This is used to distinguish +// ValueNode values that are references/identifiers vs. those that are +// string literals. +type Identifier string + +// IdentValueNode is an AST node that represents an identifier. +type IdentValueNode interface { + ValueNode + AsIdentifier() Identifier +} + +var _ IdentValueNode = (*IdentNode)(nil) +var _ IdentValueNode = (*CompoundIdentNode)(nil) + +// IdentNode represents a simple, unqualified identifier. These are used to name +// elements declared in a protobuf file or to refer to elements. Example: +// +// foobar +type IdentNode struct { + terminalNode + Val string +} + +// NewIdentNode creates a new *IdentNode. The given val is the identifier text. +func NewIdentNode(val string, tok Token) *IdentNode { + return &IdentNode{ + terminalNode: tok.asTerminalNode(), + Val: val, + } +} + +func (n *IdentNode) Value() interface{} { + return n.AsIdentifier() +} + +func (n *IdentNode) AsIdentifier() Identifier { + return Identifier(n.Val) +} + +// ToKeyword is used to convert identifiers to keywords. Since keywords are not +// reserved in the protobuf language, they are initially lexed as identifiers +// and then converted to keywords based on context. +func (n *IdentNode) ToKeyword() *KeywordNode { + return (*KeywordNode)(n) +} + +// CompoundIdentNode represents a qualified identifier. A qualified identifier +// has at least one dot and possibly multiple identifier names (all separated by +// dots). If the identifier has a leading dot, then it is a *fully* qualified +// identifier. Example: +// +// .com.foobar.Baz +type CompoundIdentNode struct { + compositeNode + // Optional leading dot, indicating that the identifier is fully qualified. + LeadingDot *RuneNode + Components []*IdentNode + // Dots[0] is the dot after Components[0]. The length of Dots is always + // one less than the length of Components. + Dots []*RuneNode + // The text value of the identifier, with all components and dots + // concatenated. + Val string +} + +// NewCompoundIdentNode creates a *CompoundIdentNode. The leadingDot may be nil. +// The dots arg must have a length that is one less than the length of +// components. The components arg must not be empty. +func NewCompoundIdentNode(leadingDot *RuneNode, components []*IdentNode, dots []*RuneNode) *CompoundIdentNode { + if len(components) == 0 { + panic("must have at least one component") + } + if len(dots) != len(components)-1 && len(dots) != len(components) { + panic(fmt.Sprintf("%d components requires %d dots, not %d", len(components), len(components)-1, len(dots))) + } + numChildren := len(components) + len(dots) + if leadingDot != nil { + numChildren++ + } + children := make([]Node, 0, numChildren) + var b strings.Builder + if leadingDot != nil { + children = append(children, leadingDot) + b.WriteRune(leadingDot.Rune) + } + for i, comp := range components { + if i > 0 { + dot := dots[i-1] + children = append(children, dot) + b.WriteRune(dot.Rune) + } + children = append(children, comp) + b.WriteString(comp.Val) + } + if len(dots) == len(components) { + dot := dots[len(dots)-1] + children = append(children, dot) + b.WriteRune(dot.Rune) + } + return &CompoundIdentNode{ + compositeNode: compositeNode{ + children: children, + }, + LeadingDot: leadingDot, + Components: components, + Dots: dots, + Val: b.String(), + } +} + +func (n *CompoundIdentNode) Value() interface{} { + return n.AsIdentifier() +} + +func (n *CompoundIdentNode) AsIdentifier() Identifier { + return Identifier(n.Val) +} + +// KeywordNode is an AST node that represents a keyword. Keywords are +// like identifiers, but they have special meaning in particular contexts. +// Example: +// +// message +type KeywordNode IdentNode + +// NewKeywordNode creates a new *KeywordNode. The given val is the keyword. +func NewKeywordNode(val string, tok Token) *KeywordNode { + return &KeywordNode{ + terminalNode: tok.asTerminalNode(), + Val: val, + } +} diff --git a/vendor/github.com/bufbuild/protocompile/ast/message.go b/vendor/github.com/bufbuild/protocompile/ast/message.go new file mode 100644 index 0000000..eede28e --- /dev/null +++ b/vendor/github.com/bufbuild/protocompile/ast/message.go @@ -0,0 +1,223 @@ +// Copyright 2020-2024 Buf Technologies, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package ast + +import "fmt" + +// MessageDeclNode is a node in the AST that defines a message type. This +// includes normal message fields as well as implicit messages: +// - *MessageNode +// - *SyntheticGroupMessageNode (the group is a field and inline message type) +// - *SyntheticMapEntryNode (map fields implicitly define a MapEntry message type) +// +// This also allows NoSourceNode to be used in place of one of the above +// for some usages. +type MessageDeclNode interface { + NodeWithOptions + MessageName() Node +} + +var _ MessageDeclNode = (*MessageNode)(nil) +var _ MessageDeclNode = (*SyntheticGroupMessageNode)(nil) +var _ MessageDeclNode = (*SyntheticMapEntryNode)(nil) +var _ MessageDeclNode = (*NoSourceNode)(nil) + +// MessageNode represents a message declaration. Example: +// +// message Foo { +// string name = 1; +// repeated string labels = 2; +// bytes extra = 3; +// } +type MessageNode struct { + compositeNode + Keyword *KeywordNode + Name *IdentNode + MessageBody +} + +func (*MessageNode) fileElement() {} +func (*MessageNode) msgElement() {} + +// NewMessageNode creates a new *MessageNode. All arguments must be non-nil. +// - keyword: The token corresponding to the "message" keyword. +// - name: The token corresponding to the field's name. +// - openBrace: The token corresponding to the "{" rune that starts the body. +// - decls: All declarations inside the message body. +// - closeBrace: The token corresponding to the "}" rune that ends the body. +func NewMessageNode(keyword *KeywordNode, name *IdentNode, openBrace *RuneNode, decls []MessageElement, closeBrace *RuneNode) *MessageNode { + if keyword == nil { + panic("keyword is nil") + } + if name == nil { + panic("name is nil") + } + if openBrace == nil { + panic("openBrace is nil") + } + if closeBrace == nil { + panic("closeBrace is nil") + } + children := make([]Node, 0, 4+len(decls)) + children = append(children, keyword, name, openBrace) + for _, decl := range decls { + children = append(children, decl) + } + children = append(children, closeBrace) + + ret := &MessageNode{ + compositeNode: compositeNode{ + children: children, + }, + Keyword: keyword, + Name: name, + } + populateMessageBody(&ret.MessageBody, openBrace, decls, closeBrace) + return ret +} + +func (n *MessageNode) MessageName() Node { + return n.Name +} + +func (n *MessageNode) RangeOptions(fn func(*OptionNode) bool) { + for _, decl := range n.Decls { + if opt, ok := decl.(*OptionNode); ok { + if !fn(opt) { + return + } + } + } +} + +// MessageBody represents the body of a message. It is used by both +// MessageNodes and GroupNodes. +type MessageBody struct { + OpenBrace *RuneNode + Decls []MessageElement + CloseBrace *RuneNode +} + +func populateMessageBody(m *MessageBody, openBrace *RuneNode, decls []MessageElement, closeBrace *RuneNode) { + m.OpenBrace = openBrace + m.Decls = decls + for _, decl := range decls { + switch decl.(type) { + case *OptionNode, *FieldNode, *MapFieldNode, *GroupNode, *OneofNode, + *MessageNode, *EnumNode, *ExtendNode, *ExtensionRangeNode, + *ReservedNode, *EmptyDeclNode: + default: + panic(fmt.Sprintf("invalid MessageElement type: %T", decl)) + } + } + m.CloseBrace = closeBrace +} + +// MessageElement is an interface implemented by all AST nodes that can +// appear in a message body. +type MessageElement interface { + Node + msgElement() +} + +var _ MessageElement = (*OptionNode)(nil) +var _ MessageElement = (*FieldNode)(nil) +var _ MessageElement = (*MapFieldNode)(nil) +var _ MessageElement = (*OneofNode)(nil) +var _ MessageElement = (*GroupNode)(nil) +var _ MessageElement = (*MessageNode)(nil) +var _ MessageElement = (*EnumNode)(nil) +var _ MessageElement = (*ExtendNode)(nil) +var _ MessageElement = (*ExtensionRangeNode)(nil) +var _ MessageElement = (*ReservedNode)(nil) +var _ MessageElement = (*EmptyDeclNode)(nil) + +// ExtendNode represents a declaration of extension fields. Example: +// +// extend google.protobuf.FieldOptions { +// bool redacted = 33333; +// } +type ExtendNode struct { + compositeNode + Keyword *KeywordNode + Extendee IdentValueNode + OpenBrace *RuneNode + Decls []ExtendElement + CloseBrace *RuneNode +} + +func (*ExtendNode) fileElement() {} +func (*ExtendNode) msgElement() {} + +// NewExtendNode creates a new *ExtendNode. All arguments must be non-nil. +// - keyword: The token corresponding to the "extend" keyword. +// - extendee: The token corresponding to the name of the extended message. +// - openBrace: The token corresponding to the "{" rune that starts the body. +// - decls: All declarations inside the message body. +// - closeBrace: The token corresponding to the "}" rune that ends the body. +func NewExtendNode(keyword *KeywordNode, extendee IdentValueNode, openBrace *RuneNode, decls []ExtendElement, closeBrace *RuneNode) *ExtendNode { + if keyword == nil { + panic("keyword is nil") + } + if extendee == nil { + panic("extendee is nil") + } + if openBrace == nil { + panic("openBrace is nil") + } + if closeBrace == nil { + panic("closeBrace is nil") + } + children := make([]Node, 0, 4+len(decls)) + children = append(children, keyword, extendee, openBrace) + for _, decl := range decls { + children = append(children, decl) + } + children = append(children, closeBrace) + + ret := &ExtendNode{ + compositeNode: compositeNode{ + children: children, + }, + Keyword: keyword, + Extendee: extendee, + OpenBrace: openBrace, + Decls: decls, + CloseBrace: closeBrace, + } + for _, decl := range decls { + switch decl := decl.(type) { + case *FieldNode: + decl.Extendee = ret + case *GroupNode: + decl.Extendee = ret + case *EmptyDeclNode: + default: + panic(fmt.Sprintf("invalid ExtendElement type: %T", decl)) + } + } + return ret +} + +// ExtendElement is an interface implemented by all AST nodes that can +// appear in the body of an extends declaration. +type ExtendElement interface { + Node + extendElement() +} + +var _ ExtendElement = (*FieldNode)(nil) +var _ ExtendElement = (*GroupNode)(nil) +var _ ExtendElement = (*EmptyDeclNode)(nil) diff --git a/vendor/github.com/bufbuild/protocompile/ast/no_source.go b/vendor/github.com/bufbuild/protocompile/ast/no_source.go new file mode 100644 index 0000000..44dbb71 --- /dev/null +++ b/vendor/github.com/bufbuild/protocompile/ast/no_source.go @@ -0,0 +1,142 @@ +// Copyright 2020-2024 Buf Technologies, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package ast + +// UnknownPos is a placeholder position when only the source file +// name is known. +func UnknownPos(filename string) SourcePos { + return SourcePos{Filename: filename} +} + +// UnknownSpan is a placeholder span when only the source file +// name is known. +func UnknownSpan(filename string) SourceSpan { + return unknownSpan{filename: filename} +} + +type unknownSpan struct { + filename string +} + +func (s unknownSpan) Start() SourcePos { + return UnknownPos(s.filename) +} + +func (s unknownSpan) End() SourcePos { + return UnknownPos(s.filename) +} + +// NoSourceNode is a placeholder AST node that implements numerous +// interfaces in this package. It can be used to represent an AST +// element for a file whose source is not available. +type NoSourceNode FileInfo + +// NewNoSourceNode creates a new NoSourceNode for the given filename. +func NewNoSourceNode(filename string) *NoSourceNode { + return &NoSourceNode{name: filename} +} + +func (n *NoSourceNode) Name() string { + return n.name +} + +func (n *NoSourceNode) Start() Token { + return 0 +} + +func (n *NoSourceNode) End() Token { + return 0 +} + +func (n *NoSourceNode) NodeInfo(Node) NodeInfo { + return NodeInfo{ + fileInfo: (*FileInfo)(n), + } +} + +func (n *NoSourceNode) GetSyntax() Node { + return n +} + +func (n *NoSourceNode) GetName() Node { + return n +} + +func (n *NoSourceNode) GetValue() ValueNode { + return n +} + +func (n *NoSourceNode) FieldLabel() Node { + return n +} + +func (n *NoSourceNode) FieldName() Node { + return n +} + +func (n *NoSourceNode) FieldType() Node { + return n +} + +func (n *NoSourceNode) FieldTag() Node { + return n +} + +func (n *NoSourceNode) FieldExtendee() Node { + return n +} + +func (n *NoSourceNode) GetGroupKeyword() Node { + return n +} + +func (n *NoSourceNode) GetOptions() *CompactOptionsNode { + return nil +} + +func (n *NoSourceNode) RangeStart() Node { + return n +} + +func (n *NoSourceNode) RangeEnd() Node { + return n +} + +func (n *NoSourceNode) GetNumber() Node { + return n +} + +func (n *NoSourceNode) MessageName() Node { + return n +} + +func (n *NoSourceNode) OneofName() Node { + return n +} + +func (n *NoSourceNode) GetInputType() Node { + return n +} + +func (n *NoSourceNode) GetOutputType() Node { + return n +} + +func (n *NoSourceNode) Value() interface{} { + return nil +} + +func (n *NoSourceNode) RangeOptions(func(*OptionNode) bool) { +} diff --git a/vendor/github.com/bufbuild/protocompile/ast/node.go b/vendor/github.com/bufbuild/protocompile/ast/node.go new file mode 100644 index 0000000..abb7643 --- /dev/null +++ b/vendor/github.com/bufbuild/protocompile/ast/node.go @@ -0,0 +1,139 @@ +// Copyright 2020-2024 Buf Technologies, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package ast + +// Node is the interface implemented by all nodes in the AST. It +// provides information about the span of this AST node in terms +// of location in the source file. It also provides information +// about all prior comments (attached as leading comments) and +// optional subsequent comments (attached as trailing comments). +type Node interface { + Start() Token + End() Token +} + +// TerminalNode represents a leaf in the AST. These represent +// the items/lexemes in the protobuf language. Comments and +// whitespace are accumulated by the lexer and associated with +// the following lexed token. +type TerminalNode interface { + Node + Token() Token +} + +var _ TerminalNode = (*StringLiteralNode)(nil) +var _ TerminalNode = (*UintLiteralNode)(nil) +var _ TerminalNode = (*FloatLiteralNode)(nil) +var _ TerminalNode = (*IdentNode)(nil) +var _ TerminalNode = (*SpecialFloatLiteralNode)(nil) +var _ TerminalNode = (*KeywordNode)(nil) +var _ TerminalNode = (*RuneNode)(nil) + +// CompositeNode represents any non-terminal node in the tree. These +// are interior or root nodes and have child nodes. +type CompositeNode interface { + Node + // Children contains all AST nodes that are immediate children of this one. + Children() []Node +} + +// terminalNode contains bookkeeping shared by all TerminalNode +// implementations. It is embedded in all such node types in this +// package. It provides the implementation of the TerminalNode +// interface. +type terminalNode Token + +func (n terminalNode) Start() Token { + return Token(n) +} + +func (n terminalNode) End() Token { + return Token(n) +} + +func (n terminalNode) Token() Token { + return Token(n) +} + +// compositeNode contains bookkeeping shared by all CompositeNode +// implementations. It is embedded in all such node types in this +// package. It provides the implementation of the CompositeNode +// interface. +type compositeNode struct { + children []Node +} + +func (n *compositeNode) Children() []Node { + return n.children +} + +func (n *compositeNode) Start() Token { + return n.children[0].Start() +} + +func (n *compositeNode) End() Token { + return n.children[len(n.children)-1].End() +} + +// RuneNode represents a single rune in protobuf source. Runes +// are typically collected into items, but some runes stand on +// their own, such as punctuation/symbols like commas, semicolons, +// equals signs, open and close symbols (braces, brackets, angles, +// and parentheses), and periods/dots. +// TODO: make this more compact; if runes don't have attributed comments +// then we don't need a Token to represent them and only need an offset +// into the file's contents. +type RuneNode struct { + terminalNode + Rune rune +} + +// NewRuneNode creates a new *RuneNode with the given properties. +func NewRuneNode(r rune, tok Token) *RuneNode { + return &RuneNode{ + terminalNode: tok.asTerminalNode(), + Rune: r, + } +} + +// EmptyDeclNode represents an empty declaration in protobuf source. +// These amount to extra semicolons, with no actual content preceding +// the semicolon. +type EmptyDeclNode struct { + compositeNode + Semicolon *RuneNode +} + +// NewEmptyDeclNode creates a new *EmptyDeclNode. The one argument must +// be non-nil. +func NewEmptyDeclNode(semicolon *RuneNode) *EmptyDeclNode { + if semicolon == nil { + panic("semicolon is nil") + } + return &EmptyDeclNode{ + compositeNode: compositeNode{ + children: []Node{semicolon}, + }, + Semicolon: semicolon, + } +} + +func (e *EmptyDeclNode) fileElement() {} +func (e *EmptyDeclNode) msgElement() {} +func (e *EmptyDeclNode) extendElement() {} +func (e *EmptyDeclNode) oneofElement() {} +func (e *EmptyDeclNode) enumElement() {} +func (e *EmptyDeclNode) serviceElement() {} +func (e *EmptyDeclNode) methodElement() {} diff --git a/vendor/github.com/bufbuild/protocompile/ast/options.go b/vendor/github.com/bufbuild/protocompile/ast/options.go new file mode 100644 index 0000000..be31f0b --- /dev/null +++ b/vendor/github.com/bufbuild/protocompile/ast/options.go @@ -0,0 +1,413 @@ +// Copyright 2020-2024 Buf Technologies, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package ast + +import "fmt" + +// OptionDeclNode is a placeholder interface for AST nodes that represent +// options. This allows NoSourceNode to be used in place of *OptionNode +// for some usages. +type OptionDeclNode interface { + Node + GetName() Node + GetValue() ValueNode +} + +var _ OptionDeclNode = (*OptionNode)(nil) +var _ OptionDeclNode = (*NoSourceNode)(nil) + +// OptionNode represents the declaration of a single option for an element. +// It is used both for normal option declarations (start with "option" keyword +// and end with semicolon) and for compact options found in fields, enum values, +// and extension ranges. Example: +// +// option (custom.option) = "foo"; +type OptionNode struct { + compositeNode + Keyword *KeywordNode // absent for compact options + Name *OptionNameNode + Equals *RuneNode + Val ValueNode + Semicolon *RuneNode // absent for compact options +} + +func (*OptionNode) fileElement() {} +func (*OptionNode) msgElement() {} +func (*OptionNode) oneofElement() {} +func (*OptionNode) enumElement() {} +func (*OptionNode) serviceElement() {} +func (*OptionNode) methodElement() {} + +// NewOptionNode creates a new *OptionNode for a full option declaration (as +// used in files, messages, oneofs, enums, services, and methods). All arguments +// must be non-nil. (Also see NewCompactOptionNode.) +// - keyword: The token corresponding to the "option" keyword. +// - name: The token corresponding to the name of the option. +// - equals: The token corresponding to the "=" rune after the name. +// - val: The token corresponding to the option value. +// - semicolon: The token corresponding to the ";" rune that ends the declaration. +func NewOptionNode(keyword *KeywordNode, name *OptionNameNode, equals *RuneNode, val ValueNode, semicolon *RuneNode) *OptionNode { + if keyword == nil { + panic("keyword is nil") + } + if name == nil { + panic("name is nil") + } + if equals == nil { + panic("equals is nil") + } + if val == nil { + panic("val is nil") + } + var children []Node + if semicolon == nil { + children = []Node{keyword, name, equals, val} + } else { + children = []Node{keyword, name, equals, val, semicolon} + } + + return &OptionNode{ + compositeNode: compositeNode{ + children: children, + }, + Keyword: keyword, + Name: name, + Equals: equals, + Val: val, + Semicolon: semicolon, + } +} + +// NewCompactOptionNode creates a new *OptionNode for a full compact declaration +// (as used in fields, enum values, and extension ranges). All arguments must be +// non-nil. +// - name: The token corresponding to the name of the option. +// - equals: The token corresponding to the "=" rune after the name. +// - val: The token corresponding to the option value. +func NewCompactOptionNode(name *OptionNameNode, equals *RuneNode, val ValueNode) *OptionNode { + if name == nil { + panic("name is nil") + } + if equals == nil && val != nil { + panic("equals is nil but val is not") + } + if val == nil && equals != nil { + panic("val is nil but equals is not") + } + var children []Node + if equals == nil && val == nil { + children = []Node{name} + } else { + children = []Node{name, equals, val} + } + return &OptionNode{ + compositeNode: compositeNode{ + children: children, + }, + Name: name, + Equals: equals, + Val: val, + } +} + +func (n *OptionNode) GetName() Node { + return n.Name +} + +func (n *OptionNode) GetValue() ValueNode { + return n.Val +} + +// OptionNameNode represents an option name or even a traversal through message +// types to name a nested option field. Example: +// +// (foo.bar).baz.(bob) +type OptionNameNode struct { + compositeNode + Parts []*FieldReferenceNode + // Dots represent the separating '.' characters between name parts. The + // length of this slice must be exactly len(Parts)-1, each item in Parts + // having a corresponding item in this slice *except the last* (since a + // trailing dot is not allowed). + // + // These do *not* include dots that are inside of an extension name. For + // example: (foo.bar).baz.(bob) has three parts: + // 1. (foo.bar) - an extension name + // 2. baz - a regular field in foo.bar + // 3. (bob) - an extension field in baz + // Note that the dot in foo.bar will thus not be present in Dots but is + // instead in Parts[0]. + Dots []*RuneNode +} + +// NewOptionNameNode creates a new *OptionNameNode. The dots arg must have a +// length that is one less than the length of parts. The parts arg must not be +// empty. +func NewOptionNameNode(parts []*FieldReferenceNode, dots []*RuneNode) *OptionNameNode { + if len(parts) == 0 { + panic("must have at least one part") + } + if len(dots) != len(parts)-1 && len(dots) != len(parts) { + panic(fmt.Sprintf("%d parts requires %d dots, not %d", len(parts), len(parts)-1, len(dots))) + } + children := make([]Node, 0, len(parts)+len(dots)) + for i, part := range parts { + if part == nil { + panic(fmt.Sprintf("parts[%d] is nil", i)) + } + if i > 0 { + if dots[i-1] == nil { + panic(fmt.Sprintf("dots[%d] is nil", i-1)) + } + children = append(children, dots[i-1]) + } + children = append(children, part) + } + if len(dots) == len(parts) { // Add the erroneous, but tolerated trailing dot. + if dots[len(dots)-1] == nil { + panic(fmt.Sprintf("dots[%d] is nil", len(dots)-1)) + } + children = append(children, dots[len(dots)-1]) + } + return &OptionNameNode{ + compositeNode: compositeNode{ + children: children, + }, + Parts: parts, + Dots: dots, + } +} + +// FieldReferenceNode is a reference to a field name. It can indicate a regular +// field (simple unqualified name), an extension field (possibly-qualified name +// that is enclosed either in brackets or parentheses), or an "any" type +// reference (a type URL in the form "server.host/fully.qualified.Name" that is +// enclosed in brackets). +// +// Extension names are used in options to refer to custom options (which are +// actually extensions), in which case the name is enclosed in parentheses "(" +// and ")". They can also be used to refer to extension fields of options. +// +// Extension names are also used in message literals to set extension fields, +// in which case the name is enclosed in square brackets "[" and "]". +// +// "Any" type references can only be used in message literals, and are not +// allowed in option names. They are always enclosed in square brackets. An +// "any" type reference is distinguished from an extension name by the presence +// of a slash, which must be present in an "any" type reference and must be +// absent in an extension name. +// +// Examples: +// +// foobar +// (foo.bar) +// [foo.bar] +// [type.googleapis.com/foo.bar] +type FieldReferenceNode struct { + compositeNode + Open *RuneNode // only present for extension names and "any" type references + + // only present for "any" type references + URLPrefix IdentValueNode + Slash *RuneNode + + Name IdentValueNode + + Close *RuneNode // only present for extension names and "any" type references +} + +// NewFieldReferenceNode creates a new *FieldReferenceNode for a regular field. +// The name arg must not be nil. +func NewFieldReferenceNode(name *IdentNode) *FieldReferenceNode { + if name == nil { + panic("name is nil") + } + children := []Node{name} + return &FieldReferenceNode{ + compositeNode: compositeNode{ + children: children, + }, + Name: name, + } +} + +// NewExtensionFieldReferenceNode creates a new *FieldReferenceNode for an +// extension field. All args must be non-nil. The openSym and closeSym runes +// should be "(" and ")" or "[" and "]". +func NewExtensionFieldReferenceNode(openSym *RuneNode, name IdentValueNode, closeSym *RuneNode) *FieldReferenceNode { + if name == nil { + panic("name is nil") + } + if openSym == nil { + panic("openSym is nil") + } + if closeSym == nil { + panic("closeSym is nil") + } + children := []Node{openSym, name, closeSym} + return &FieldReferenceNode{ + compositeNode: compositeNode{ + children: children, + }, + Open: openSym, + Name: name, + Close: closeSym, + } +} + +// NewAnyTypeReferenceNode creates a new *FieldReferenceNode for an "any" +// type reference. All args must be non-nil. The openSym and closeSym runes +// should be "[" and "]". The slashSym run should be "/". +func NewAnyTypeReferenceNode(openSym *RuneNode, urlPrefix IdentValueNode, slashSym *RuneNode, name IdentValueNode, closeSym *RuneNode) *FieldReferenceNode { + if name == nil { + panic("name is nil") + } + if openSym == nil { + panic("openSym is nil") + } + if closeSym == nil { + panic("closeSym is nil") + } + if urlPrefix == nil { + panic("urlPrefix is nil") + } + if slashSym == nil { + panic("slashSym is nil") + } + children := []Node{openSym, urlPrefix, slashSym, name, closeSym} + return &FieldReferenceNode{ + compositeNode: compositeNode{ + children: children, + }, + Open: openSym, + URLPrefix: urlPrefix, + Slash: slashSym, + Name: name, + Close: closeSym, + } +} + +// IsExtension reports if this is an extension name or not (e.g. enclosed in +// punctuation, such as parentheses or brackets). +func (a *FieldReferenceNode) IsExtension() bool { + return a.Open != nil && a.Slash == nil +} + +// IsAnyTypeReference reports if this is an Any type reference. +func (a *FieldReferenceNode) IsAnyTypeReference() bool { + return a.Slash != nil +} + +func (a *FieldReferenceNode) Value() string { + if a.Open != nil { + if a.Slash != nil { + return string(a.Open.Rune) + string(a.URLPrefix.AsIdentifier()) + string(a.Slash.Rune) + string(a.Name.AsIdentifier()) + string(a.Close.Rune) + } + return string(a.Open.Rune) + string(a.Name.AsIdentifier()) + string(a.Close.Rune) + } + return string(a.Name.AsIdentifier()) +} + +// CompactOptionsNode represents a compact options declaration, as used with +// fields, enum values, and extension ranges. Example: +// +// [deprecated = true, json_name = "foo_bar"] +type CompactOptionsNode struct { + compositeNode + OpenBracket *RuneNode + Options []*OptionNode + // Commas represent the separating ',' characters between options. The + // length of this slice must be exactly len(Options)-1, with each item + // in Options having a corresponding item in this slice *except the last* + // (since a trailing comma is not allowed). + Commas []*RuneNode + CloseBracket *RuneNode +} + +// NewCompactOptionsNode creates a *CompactOptionsNode. All args must be +// non-nil. The commas arg must have a length that is one less than the +// length of opts. The opts arg must not be empty. +func NewCompactOptionsNode(openBracket *RuneNode, opts []*OptionNode, commas []*RuneNode, closeBracket *RuneNode) *CompactOptionsNode { + if openBracket == nil { + panic("openBracket is nil") + } + if closeBracket == nil { + panic("closeBracket is nil") + } + if len(opts) == 0 && len(commas) != 0 { + panic("opts is empty but commas is not") + } + if len(opts) != len(commas) && len(opts) != len(commas)+1 { + panic(fmt.Sprintf("%d opts requires %d commas, not %d", len(opts), len(opts)-1, len(commas))) + } + children := make([]Node, 0, len(opts)+len(commas)+2) + children = append(children, openBracket) + if len(opts) > 0 { + for i, opt := range opts { + if i > 0 { + if commas[i-1] == nil { + panic(fmt.Sprintf("commas[%d] is nil", i-1)) + } + children = append(children, commas[i-1]) + } + if opt == nil { + panic(fmt.Sprintf("opts[%d] is nil", i)) + } + children = append(children, opt) + } + if len(opts) == len(commas) { // Add the erroneous, but tolerated trailing comma. + if commas[len(commas)-1] == nil { + panic(fmt.Sprintf("commas[%d] is nil", len(commas)-1)) + } + children = append(children, commas[len(commas)-1]) + } + } + children = append(children, closeBracket) + + return &CompactOptionsNode{ + compositeNode: compositeNode{ + children: children, + }, + OpenBracket: openBracket, + Options: opts, + Commas: commas, + CloseBracket: closeBracket, + } +} + +func (e *CompactOptionsNode) GetElements() []*OptionNode { + if e == nil { + return nil + } + return e.Options +} + +// NodeWithOptions represents a node in the AST that contains +// option statements. +type NodeWithOptions interface { + Node + RangeOptions(func(*OptionNode) bool) +} + +var _ NodeWithOptions = FileDeclNode(nil) +var _ NodeWithOptions = MessageDeclNode(nil) +var _ NodeWithOptions = OneofDeclNode(nil) +var _ NodeWithOptions = (*EnumNode)(nil) +var _ NodeWithOptions = (*ServiceNode)(nil) +var _ NodeWithOptions = RPCDeclNode(nil) +var _ NodeWithOptions = FieldDeclNode(nil) +var _ NodeWithOptions = EnumValueDeclNode(nil) +var _ NodeWithOptions = (*ExtensionRangeNode)(nil) +var _ NodeWithOptions = (*NoSourceNode)(nil) diff --git a/vendor/github.com/bufbuild/protocompile/ast/ranges.go b/vendor/github.com/bufbuild/protocompile/ast/ranges.go new file mode 100644 index 0000000..c42908e --- /dev/null +++ b/vendor/github.com/bufbuild/protocompile/ast/ranges.go @@ -0,0 +1,386 @@ +// Copyright 2020-2024 Buf Technologies, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package ast + +import "fmt" + +// ExtensionRangeNode represents an extension range declaration in an extendable +// message. Example: +// +// extensions 100 to max; +type ExtensionRangeNode struct { + compositeNode + Keyword *KeywordNode + Ranges []*RangeNode + // Commas represent the separating ',' characters between ranges. The + // length of this slice must be exactly len(Ranges)-1, each item in Ranges + // having a corresponding item in this slice *except the last* (since a + // trailing comma is not allowed). + Commas []*RuneNode + Options *CompactOptionsNode + Semicolon *RuneNode +} + +func (*ExtensionRangeNode) msgElement() {} + +// NewExtensionRangeNode creates a new *ExtensionRangeNode. All args must be +// non-nil except opts, which may be nil. +// - keyword: The token corresponding to the "extends" keyword. +// - ranges: One or more range expressions. +// - commas: Tokens that represent the "," runes that delimit the range expressions. +// The length of commas must be one less than the length of ranges. +// - opts: The node corresponding to options that apply to each of the ranges. +// - semicolon The token corresponding to the ";" rune that ends the declaration. +func NewExtensionRangeNode(keyword *KeywordNode, ranges []*RangeNode, commas []*RuneNode, opts *CompactOptionsNode, semicolon *RuneNode) *ExtensionRangeNode { + if keyword == nil { + panic("keyword is nil") + } + if semicolon == nil { + panic("semicolon is nil") + } + if len(ranges) == 0 { + panic("must have at least one range") + } + if len(commas) != len(ranges)-1 { + panic(fmt.Sprintf("%d ranges requires %d commas, not %d", len(ranges), len(ranges)-1, len(commas))) + } + numChildren := len(ranges)*2 + 1 + if opts != nil { + numChildren++ + } + children := make([]Node, 0, numChildren) + children = append(children, keyword) + for i, rng := range ranges { + if i > 0 { + if commas[i-1] == nil { + panic(fmt.Sprintf("commas[%d] is nil", i-1)) + } + children = append(children, commas[i-1]) + } + if rng == nil { + panic(fmt.Sprintf("ranges[%d] is nil", i)) + } + children = append(children, rng) + } + if opts != nil { + children = append(children, opts) + } + children = append(children, semicolon) + return &ExtensionRangeNode{ + compositeNode: compositeNode{ + children: children, + }, + Keyword: keyword, + Ranges: ranges, + Commas: commas, + Options: opts, + Semicolon: semicolon, + } +} + +func (e *ExtensionRangeNode) RangeOptions(fn func(*OptionNode) bool) { + for _, opt := range e.Options.Options { + if !fn(opt) { + return + } + } +} + +// RangeDeclNode is a placeholder interface for AST nodes that represent +// numeric values. This allows NoSourceNode to be used in place of *RangeNode +// for some usages. +type RangeDeclNode interface { + Node + RangeStart() Node + RangeEnd() Node +} + +var _ RangeDeclNode = (*RangeNode)(nil) +var _ RangeDeclNode = (*NoSourceNode)(nil) + +// RangeNode represents a range expression, used in both extension ranges and +// reserved ranges. Example: +// +// 1000 to max +type RangeNode struct { + compositeNode + StartVal IntValueNode + // if To is non-nil, then exactly one of EndVal or Max must also be non-nil + To *KeywordNode + // EndVal and Max are mutually exclusive + EndVal IntValueNode + Max *KeywordNode +} + +// NewRangeNode creates a new *RangeNode. The start argument must be non-nil. +// The to argument represents the "to" keyword. If present (i.e. if it is non-nil), +// then so must be exactly one of end or max. If max is non-nil, it indicates a +// "100 to max" style range. But if end is non-nil, the end of the range is a +// literal, such as "100 to 200". +func NewRangeNode(start IntValueNode, to *KeywordNode, end IntValueNode, maxEnd *KeywordNode) *RangeNode { + if start == nil { + panic("start is nil") + } + numChildren := 1 + if to != nil { + if end == nil && maxEnd == nil { + panic("to is not nil, but end and max both are") + } + if end != nil && maxEnd != nil { + panic("end and max cannot be both non-nil") + } + numChildren = 3 + } else { + if end != nil { + panic("to is nil, but end is not") + } + if maxEnd != nil { + panic("to is nil, but max is not") + } + } + children := make([]Node, 0, numChildren) + children = append(children, start) + if to != nil { + children = append(children, to) + if end != nil { + children = append(children, end) + } else { + children = append(children, maxEnd) + } + } + return &RangeNode{ + compositeNode: compositeNode{ + children: children, + }, + StartVal: start, + To: to, + EndVal: end, + Max: maxEnd, + } +} + +func (n *RangeNode) RangeStart() Node { + return n.StartVal +} + +func (n *RangeNode) RangeEnd() Node { + if n.Max != nil { + return n.Max + } + if n.EndVal != nil { + return n.EndVal + } + return n.StartVal +} + +func (n *RangeNode) StartValue() interface{} { + return n.StartVal.Value() +} + +func (n *RangeNode) StartValueAsInt32(minVal, maxVal int32) (int32, bool) { + return AsInt32(n.StartVal, minVal, maxVal) +} + +func (n *RangeNode) EndValue() interface{} { + if n.EndVal == nil { + return nil + } + return n.EndVal.Value() +} + +func (n *RangeNode) EndValueAsInt32(minVal, maxVal int32) (int32, bool) { + if n.Max != nil { + return maxVal, true + } + if n.EndVal == nil { + return n.StartValueAsInt32(minVal, maxVal) + } + return AsInt32(n.EndVal, minVal, maxVal) +} + +// ReservedNode represents reserved declaration, which can be used to reserve +// either names or numbers. Examples: +// +// reserved 1, 10-12, 15; +// reserved "foo", "bar", "baz"; +// reserved foo, bar, baz; +type ReservedNode struct { + compositeNode + Keyword *KeywordNode + // If non-empty, this node represents reserved ranges, and Names and Identifiers + // will be empty. + Ranges []*RangeNode + // If non-empty, this node represents reserved names as string literals, and + // Ranges and Identifiers will be empty. String literals are used for reserved + // names in proto2 and proto3 syntax. + Names []StringValueNode + // If non-empty, this node represents reserved names as identifiers, and Ranges + // and Names will be empty. Identifiers are used for reserved names in editions. + Identifiers []*IdentNode + // Commas represent the separating ',' characters between options. The + // length of this slice must be exactly len(Ranges)-1 or len(Names)-1, depending + // on whether this node represents reserved ranges or reserved names. Each item + // in Ranges or Names has a corresponding item in this slice *except the last* + // (since a trailing comma is not allowed). + Commas []*RuneNode + Semicolon *RuneNode +} + +func (*ReservedNode) msgElement() {} +func (*ReservedNode) enumElement() {} + +// NewReservedRangesNode creates a new *ReservedNode that represents reserved +// numeric ranges. All args must be non-nil. +// - keyword: The token corresponding to the "reserved" keyword. +// - ranges: One or more range expressions. +// - commas: Tokens that represent the "," runes that delimit the range expressions. +// The length of commas must be one less than the length of ranges. +// - semicolon The token corresponding to the ";" rune that ends the declaration. +func NewReservedRangesNode(keyword *KeywordNode, ranges []*RangeNode, commas []*RuneNode, semicolon *RuneNode) *ReservedNode { + if keyword == nil { + panic("keyword is nil") + } + if semicolon == nil { + panic("semicolon is nil") + } + if len(ranges) == 0 { + panic("must have at least one range") + } + if len(commas) != len(ranges)-1 { + panic(fmt.Sprintf("%d ranges requires %d commas, not %d", len(ranges), len(ranges)-1, len(commas))) + } + children := make([]Node, 0, len(ranges)*2+1) + children = append(children, keyword) + for i, rng := range ranges { + if i > 0 { + if commas[i-1] == nil { + panic(fmt.Sprintf("commas[%d] is nil", i-1)) + } + children = append(children, commas[i-1]) + } + if rng == nil { + panic(fmt.Sprintf("ranges[%d] is nil", i)) + } + children = append(children, rng) + } + children = append(children, semicolon) + return &ReservedNode{ + compositeNode: compositeNode{ + children: children, + }, + Keyword: keyword, + Ranges: ranges, + Commas: commas, + Semicolon: semicolon, + } +} + +// NewReservedNamesNode creates a new *ReservedNode that represents reserved +// names. All args must be non-nil. +// - keyword: The token corresponding to the "reserved" keyword. +// - names: One or more names. +// - commas: Tokens that represent the "," runes that delimit the names. +// The length of commas must be one less than the length of names. +// - semicolon The token corresponding to the ";" rune that ends the declaration. +func NewReservedNamesNode(keyword *KeywordNode, names []StringValueNode, commas []*RuneNode, semicolon *RuneNode) *ReservedNode { + if keyword == nil { + panic("keyword is nil") + } + if len(names) == 0 { + panic("must have at least one name") + } + if len(commas) != len(names)-1 { + panic(fmt.Sprintf("%d names requires %d commas, not %d", len(names), len(names)-1, len(commas))) + } + numChildren := len(names) * 2 + if semicolon != nil { + numChildren++ + } + children := make([]Node, 0, numChildren) + children = append(children, keyword) + for i, name := range names { + if i > 0 { + if commas[i-1] == nil { + panic(fmt.Sprintf("commas[%d] is nil", i-1)) + } + children = append(children, commas[i-1]) + } + if name == nil { + panic(fmt.Sprintf("names[%d] is nil", i)) + } + children = append(children, name) + } + if semicolon != nil { + children = append(children, semicolon) + } + return &ReservedNode{ + compositeNode: compositeNode{ + children: children, + }, + Keyword: keyword, + Names: names, + Commas: commas, + Semicolon: semicolon, + } +} + +// NewReservedIdentifiersNode creates a new *ReservedNode that represents reserved +// names. All args must be non-nil. +// - keyword: The token corresponding to the "reserved" keyword. +// - names: One or more names. +// - commas: Tokens that represent the "," runes that delimit the names. +// The length of commas must be one less than the length of names. +// - semicolon The token corresponding to the ";" rune that ends the declaration. +func NewReservedIdentifiersNode(keyword *KeywordNode, names []*IdentNode, commas []*RuneNode, semicolon *RuneNode) *ReservedNode { + if keyword == nil { + panic("keyword is nil") + } + if len(names) == 0 { + panic("must have at least one name") + } + if len(commas) != len(names)-1 { + panic(fmt.Sprintf("%d names requires %d commas, not %d", len(names), len(names)-1, len(commas))) + } + numChildren := len(names) * 2 + if semicolon != nil { + numChildren++ + } + children := make([]Node, 0, numChildren) + children = append(children, keyword) + for i, name := range names { + if i > 0 { + if commas[i-1] == nil { + panic(fmt.Sprintf("commas[%d] is nil", i-1)) + } + children = append(children, commas[i-1]) + } + if name == nil { + panic(fmt.Sprintf("names[%d] is nil", i)) + } + children = append(children, name) + } + if semicolon != nil { + children = append(children, semicolon) + } + return &ReservedNode{ + compositeNode: compositeNode{ + children: children, + }, + Keyword: keyword, + Identifiers: names, + Commas: commas, + Semicolon: semicolon, + } +} diff --git a/vendor/github.com/bufbuild/protocompile/ast/service.go b/vendor/github.com/bufbuild/protocompile/ast/service.go new file mode 100644 index 0000000..eba22fd --- /dev/null +++ b/vendor/github.com/bufbuild/protocompile/ast/service.go @@ -0,0 +1,308 @@ +// Copyright 2020-2024 Buf Technologies, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package ast + +import "fmt" + +// ServiceNode represents a service declaration. Example: +// +// service Foo { +// rpc Bar (Baz) returns (Bob); +// rpc Frobnitz (stream Parts) returns (Gyzmeaux); +// } +type ServiceNode struct { + compositeNode + Keyword *KeywordNode + Name *IdentNode + OpenBrace *RuneNode + Decls []ServiceElement + CloseBrace *RuneNode +} + +func (*ServiceNode) fileElement() {} + +// NewServiceNode creates a new *ServiceNode. All arguments must be non-nil. +// - keyword: The token corresponding to the "service" keyword. +// - name: The token corresponding to the service's name. +// - openBrace: The token corresponding to the "{" rune that starts the body. +// - decls: All declarations inside the service body. +// - closeBrace: The token corresponding to the "}" rune that ends the body. +func NewServiceNode(keyword *KeywordNode, name *IdentNode, openBrace *RuneNode, decls []ServiceElement, closeBrace *RuneNode) *ServiceNode { + if keyword == nil { + panic("keyword is nil") + } + if name == nil { + panic("name is nil") + } + if openBrace == nil { + panic("openBrace is nil") + } + if closeBrace == nil { + panic("closeBrace is nil") + } + children := make([]Node, 0, 4+len(decls)) + children = append(children, keyword, name, openBrace) + for _, decl := range decls { + switch decl := decl.(type) { + case *OptionNode, *RPCNode, *EmptyDeclNode: + default: + panic(fmt.Sprintf("invalid ServiceElement type: %T", decl)) + } + children = append(children, decl) + } + children = append(children, closeBrace) + + return &ServiceNode{ + compositeNode: compositeNode{ + children: children, + }, + Keyword: keyword, + Name: name, + OpenBrace: openBrace, + Decls: decls, + CloseBrace: closeBrace, + } +} + +func (n *ServiceNode) RangeOptions(fn func(*OptionNode) bool) { + for _, decl := range n.Decls { + if opt, ok := decl.(*OptionNode); ok { + if !fn(opt) { + return + } + } + } +} + +// ServiceElement is an interface implemented by all AST nodes that can +// appear in the body of a service declaration. +type ServiceElement interface { + Node + serviceElement() +} + +var _ ServiceElement = (*OptionNode)(nil) +var _ ServiceElement = (*RPCNode)(nil) +var _ ServiceElement = (*EmptyDeclNode)(nil) + +// RPCDeclNode is a placeholder interface for AST nodes that represent RPC +// declarations. This allows NoSourceNode to be used in place of *RPCNode +// for some usages. +type RPCDeclNode interface { + NodeWithOptions + GetName() Node + GetInputType() Node + GetOutputType() Node +} + +var _ RPCDeclNode = (*RPCNode)(nil) +var _ RPCDeclNode = (*NoSourceNode)(nil) + +// RPCNode represents an RPC declaration. Example: +// +// rpc Foo (Bar) returns (Baz); +type RPCNode struct { + compositeNode + Keyword *KeywordNode + Name *IdentNode + Input *RPCTypeNode + Returns *KeywordNode + Output *RPCTypeNode + Semicolon *RuneNode + OpenBrace *RuneNode + Decls []RPCElement + CloseBrace *RuneNode +} + +func (n *RPCNode) serviceElement() {} + +// NewRPCNode creates a new *RPCNode with no body. All arguments must be non-nil. +// - keyword: The token corresponding to the "rpc" keyword. +// - name: The token corresponding to the RPC's name. +// - input: The token corresponding to the RPC input message type. +// - returns: The token corresponding to the "returns" keyword that precedes the output type. +// - output: The token corresponding to the RPC output message type. +// - semicolon: The token corresponding to the ";" rune that ends the declaration. +func NewRPCNode(keyword *KeywordNode, name *IdentNode, input *RPCTypeNode, returns *KeywordNode, output *RPCTypeNode, semicolon *RuneNode) *RPCNode { + if keyword == nil { + panic("keyword is nil") + } + if name == nil { + panic("name is nil") + } + if input == nil { + panic("input is nil") + } + if returns == nil { + panic("returns is nil") + } + if output == nil { + panic("output is nil") + } + var children []Node + if semicolon == nil { + children = []Node{keyword, name, input, returns, output} + } else { + children = []Node{keyword, name, input, returns, output, semicolon} + } + return &RPCNode{ + compositeNode: compositeNode{ + children: children, + }, + Keyword: keyword, + Name: name, + Input: input, + Returns: returns, + Output: output, + Semicolon: semicolon, + } +} + +// NewRPCNodeWithBody creates a new *RPCNode that includes a body (and possibly +// options). All arguments must be non-nil. +// - keyword: The token corresponding to the "rpc" keyword. +// - name: The token corresponding to the RPC's name. +// - input: The token corresponding to the RPC input message type. +// - returns: The token corresponding to the "returns" keyword that precedes the output type. +// - output: The token corresponding to the RPC output message type. +// - openBrace: The token corresponding to the "{" rune that starts the body. +// - decls: All declarations inside the RPC body. +// - closeBrace: The token corresponding to the "}" rune that ends the body. +func NewRPCNodeWithBody(keyword *KeywordNode, name *IdentNode, input *RPCTypeNode, returns *KeywordNode, output *RPCTypeNode, openBrace *RuneNode, decls []RPCElement, closeBrace *RuneNode) *RPCNode { + if keyword == nil { + panic("keyword is nil") + } + if name == nil { + panic("name is nil") + } + if input == nil { + panic("input is nil") + } + if returns == nil { + panic("returns is nil") + } + if output == nil { + panic("output is nil") + } + if openBrace == nil { + panic("openBrace is nil") + } + if closeBrace == nil { + panic("closeBrace is nil") + } + children := make([]Node, 0, 7+len(decls)) + children = append(children, keyword, name, input, returns, output, openBrace) + for _, decl := range decls { + switch decl := decl.(type) { + case *OptionNode, *EmptyDeclNode: + default: + panic(fmt.Sprintf("invalid RPCElement type: %T", decl)) + } + children = append(children, decl) + } + children = append(children, closeBrace) + + return &RPCNode{ + compositeNode: compositeNode{ + children: children, + }, + Keyword: keyword, + Name: name, + Input: input, + Returns: returns, + Output: output, + OpenBrace: openBrace, + Decls: decls, + CloseBrace: closeBrace, + } +} + +func (n *RPCNode) GetName() Node { + return n.Name +} + +func (n *RPCNode) GetInputType() Node { + return n.Input.MessageType +} + +func (n *RPCNode) GetOutputType() Node { + return n.Output.MessageType +} + +func (n *RPCNode) RangeOptions(fn func(*OptionNode) bool) { + for _, decl := range n.Decls { + if opt, ok := decl.(*OptionNode); ok { + if !fn(opt) { + return + } + } + } +} + +// RPCElement is an interface implemented by all AST nodes that can +// appear in the body of an rpc declaration (aka method). +type RPCElement interface { + Node + methodElement() +} + +var _ RPCElement = (*OptionNode)(nil) +var _ RPCElement = (*EmptyDeclNode)(nil) + +// RPCTypeNode represents the declaration of a request or response type for an +// RPC. Example: +// +// (stream foo.Bar) +type RPCTypeNode struct { + compositeNode + OpenParen *RuneNode + Stream *KeywordNode + MessageType IdentValueNode + CloseParen *RuneNode +} + +// NewRPCTypeNode creates a new *RPCTypeNode. All arguments must be non-nil +// except stream, which may be nil. +// - openParen: The token corresponding to the "(" rune that starts the declaration. +// - stream: The token corresponding to the "stream" keyword or nil if not present. +// - msgType: The token corresponding to the message type's name. +// - closeParen: The token corresponding to the ")" rune that ends the declaration. +func NewRPCTypeNode(openParen *RuneNode, stream *KeywordNode, msgType IdentValueNode, closeParen *RuneNode) *RPCTypeNode { + if openParen == nil { + panic("openParen is nil") + } + if msgType == nil { + panic("msgType is nil") + } + if closeParen == nil { + panic("closeParen is nil") + } + var children []Node + if stream != nil { + children = []Node{openParen, stream, msgType, closeParen} + } else { + children = []Node{openParen, msgType, closeParen} + } + + return &RPCTypeNode{ + compositeNode: compositeNode{ + children: children, + }, + OpenParen: openParen, + Stream: stream, + MessageType: msgType, + CloseParen: closeParen, + } +} diff --git a/vendor/github.com/bufbuild/protocompile/ast/values.go b/vendor/github.com/bufbuild/protocompile/ast/values.go new file mode 100644 index 0000000..22bd208 --- /dev/null +++ b/vendor/github.com/bufbuild/protocompile/ast/values.go @@ -0,0 +1,519 @@ +// Copyright 2020-2024 Buf Technologies, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package ast + +import ( + "fmt" + "math" + "strings" +) + +// ValueNode is an AST node that represents a literal value. +// +// It also includes references (e.g. IdentifierValueNode), which can be +// used as values in some contexts, such as describing the default value +// for a field, which can refer to an enum value. +// +// This also allows NoSourceNode to be used in place of a real value node +// for some usages. +type ValueNode interface { + Node + // Value returns a Go representation of the value. For scalars, this + // will be a string, int64, uint64, float64, or bool. This could also + // be an Identifier (e.g. IdentValueNodes). It can also be a composite + // literal: + // * For array literals, the type returned will be []ValueNode + // * For message literals, the type returned will be []*MessageFieldNode + // + // If the ValueNode is a NoSourceNode, indicating that there is no actual + // source code (and thus not AST information), then this method always + // returns nil. + Value() interface{} +} + +var _ ValueNode = (*IdentNode)(nil) +var _ ValueNode = (*CompoundIdentNode)(nil) +var _ ValueNode = (*StringLiteralNode)(nil) +var _ ValueNode = (*CompoundStringLiteralNode)(nil) +var _ ValueNode = (*UintLiteralNode)(nil) +var _ ValueNode = (*NegativeIntLiteralNode)(nil) +var _ ValueNode = (*FloatLiteralNode)(nil) +var _ ValueNode = (*SpecialFloatLiteralNode)(nil) +var _ ValueNode = (*SignedFloatLiteralNode)(nil) +var _ ValueNode = (*ArrayLiteralNode)(nil) +var _ ValueNode = (*MessageLiteralNode)(nil) +var _ ValueNode = (*NoSourceNode)(nil) + +// StringValueNode is an AST node that represents a string literal. +// Such a node can be a single literal (*StringLiteralNode) or a +// concatenation of multiple literals (*CompoundStringLiteralNode). +type StringValueNode interface { + ValueNode + AsString() string +} + +var _ StringValueNode = (*StringLiteralNode)(nil) +var _ StringValueNode = (*CompoundStringLiteralNode)(nil) + +// StringLiteralNode represents a simple string literal. Example: +// +// "proto2" +type StringLiteralNode struct { + terminalNode + // Val is the actual string value that the literal indicates. + Val string +} + +// NewStringLiteralNode creates a new *StringLiteralNode with the given val. +func NewStringLiteralNode(val string, tok Token) *StringLiteralNode { + return &StringLiteralNode{ + terminalNode: tok.asTerminalNode(), + Val: val, + } +} + +func (n *StringLiteralNode) Value() interface{} { + return n.AsString() +} + +func (n *StringLiteralNode) AsString() string { + return n.Val +} + +// CompoundStringLiteralNode represents a compound string literal, which is +// the concatenaton of adjacent string literals. Example: +// +// "this " "is" " all one " "string" +type CompoundStringLiteralNode struct { + compositeNode + Val string +} + +// NewCompoundLiteralStringNode creates a new *CompoundStringLiteralNode that +// consists of the given string components. The components argument may not be +// empty. +func NewCompoundLiteralStringNode(components ...*StringLiteralNode) *CompoundStringLiteralNode { + if len(components) == 0 { + panic("must have at least one component") + } + children := make([]Node, len(components)) + var b strings.Builder + for i, comp := range components { + children[i] = comp + b.WriteString(comp.Val) + } + return &CompoundStringLiteralNode{ + compositeNode: compositeNode{ + children: children, + }, + Val: b.String(), + } +} + +func (n *CompoundStringLiteralNode) Value() interface{} { + return n.AsString() +} + +func (n *CompoundStringLiteralNode) AsString() string { + return n.Val +} + +// IntValueNode is an AST node that represents an integer literal. If +// an integer literal is too large for an int64 (or uint64 for +// positive literals), it is represented instead by a FloatValueNode. +type IntValueNode interface { + ValueNode + AsInt64() (int64, bool) + AsUint64() (uint64, bool) +} + +// AsInt32 range checks the given int value and returns its value is +// in the range or 0, false if it is outside the range. +func AsInt32(n IntValueNode, minVal, maxVal int32) (int32, bool) { + i, ok := n.AsInt64() + if !ok { + return 0, false + } + if i < int64(minVal) || i > int64(maxVal) { + return 0, false + } + return int32(i), true +} + +var _ IntValueNode = (*UintLiteralNode)(nil) +var _ IntValueNode = (*NegativeIntLiteralNode)(nil) + +// UintLiteralNode represents a simple integer literal with no sign character. +type UintLiteralNode struct { + terminalNode + // Val is the numeric value indicated by the literal + Val uint64 +} + +// NewUintLiteralNode creates a new *UintLiteralNode with the given val. +func NewUintLiteralNode(val uint64, tok Token) *UintLiteralNode { + return &UintLiteralNode{ + terminalNode: tok.asTerminalNode(), + Val: val, + } +} + +func (n *UintLiteralNode) Value() interface{} { + return n.Val +} + +func (n *UintLiteralNode) AsInt64() (int64, bool) { + if n.Val > math.MaxInt64 { + return 0, false + } + return int64(n.Val), true +} + +func (n *UintLiteralNode) AsUint64() (uint64, bool) { + return n.Val, true +} + +func (n *UintLiteralNode) AsFloat() float64 { + return float64(n.Val) +} + +// NegativeIntLiteralNode represents an integer literal with a negative (-) sign. +type NegativeIntLiteralNode struct { + compositeNode + Minus *RuneNode + Uint *UintLiteralNode + Val int64 +} + +// NewNegativeIntLiteralNode creates a new *NegativeIntLiteralNode. Both +// arguments must be non-nil. +func NewNegativeIntLiteralNode(sign *RuneNode, i *UintLiteralNode) *NegativeIntLiteralNode { + if sign == nil { + panic("sign is nil") + } + if i == nil { + panic("i is nil") + } + children := []Node{sign, i} + return &NegativeIntLiteralNode{ + compositeNode: compositeNode{ + children: children, + }, + Minus: sign, + Uint: i, + Val: -int64(i.Val), + } +} + +func (n *NegativeIntLiteralNode) Value() interface{} { + return n.Val +} + +func (n *NegativeIntLiteralNode) AsInt64() (int64, bool) { + return n.Val, true +} + +func (n *NegativeIntLiteralNode) AsUint64() (uint64, bool) { + if n.Val < 0 { + return 0, false + } + return uint64(n.Val), true +} + +// FloatValueNode is an AST node that represents a numeric literal with +// a floating point, in scientific notation, or too large to fit in an +// int64 or uint64. +type FloatValueNode interface { + ValueNode + AsFloat() float64 +} + +var _ FloatValueNode = (*FloatLiteralNode)(nil) +var _ FloatValueNode = (*SpecialFloatLiteralNode)(nil) +var _ FloatValueNode = (*UintLiteralNode)(nil) + +// FloatLiteralNode represents a floating point numeric literal. +type FloatLiteralNode struct { + terminalNode + // Val is the numeric value indicated by the literal + Val float64 +} + +// NewFloatLiteralNode creates a new *FloatLiteralNode with the given val. +func NewFloatLiteralNode(val float64, tok Token) *FloatLiteralNode { + return &FloatLiteralNode{ + terminalNode: tok.asTerminalNode(), + Val: val, + } +} + +func (n *FloatLiteralNode) Value() interface{} { + return n.AsFloat() +} + +func (n *FloatLiteralNode) AsFloat() float64 { + return n.Val +} + +// SpecialFloatLiteralNode represents a special floating point numeric literal +// for "inf" and "nan" values. +type SpecialFloatLiteralNode struct { + *KeywordNode + Val float64 +} + +// NewSpecialFloatLiteralNode returns a new *SpecialFloatLiteralNode for the +// given keyword. The given keyword should be "inf", "infinity", or "nan" +// in any case. +func NewSpecialFloatLiteralNode(name *KeywordNode) *SpecialFloatLiteralNode { + var f float64 + switch strings.ToLower(name.Val) { + case "inf", "infinity": + f = math.Inf(1) + default: + f = math.NaN() + } + return &SpecialFloatLiteralNode{ + KeywordNode: name, + Val: f, + } +} + +func (n *SpecialFloatLiteralNode) Value() interface{} { + return n.AsFloat() +} + +func (n *SpecialFloatLiteralNode) AsFloat() float64 { + return n.Val +} + +// SignedFloatLiteralNode represents a signed floating point number. +type SignedFloatLiteralNode struct { + compositeNode + Sign *RuneNode + Float FloatValueNode + Val float64 +} + +// NewSignedFloatLiteralNode creates a new *SignedFloatLiteralNode. Both +// arguments must be non-nil. +func NewSignedFloatLiteralNode(sign *RuneNode, f FloatValueNode) *SignedFloatLiteralNode { + if sign == nil { + panic("sign is nil") + } + if f == nil { + panic("f is nil") + } + children := []Node{sign, f} + val := f.AsFloat() + if sign.Rune == '-' { + val = -val + } + return &SignedFloatLiteralNode{ + compositeNode: compositeNode{ + children: children, + }, + Sign: sign, + Float: f, + Val: val, + } +} + +func (n *SignedFloatLiteralNode) Value() interface{} { + return n.Val +} + +func (n *SignedFloatLiteralNode) AsFloat() float64 { + return n.Val +} + +// ArrayLiteralNode represents an array literal, which is only allowed inside of +// a MessageLiteralNode, to indicate values for a repeated field. Example: +// +// ["foo", "bar", "baz"] +type ArrayLiteralNode struct { + compositeNode + OpenBracket *RuneNode + Elements []ValueNode + // Commas represent the separating ',' characters between elements. The + // length of this slice must be exactly len(Elements)-1, with each item + // in Elements having a corresponding item in this slice *except the last* + // (since a trailing comma is not allowed). + Commas []*RuneNode + CloseBracket *RuneNode +} + +// NewArrayLiteralNode creates a new *ArrayLiteralNode. The openBracket and +// closeBracket args must be non-nil and represent the "[" and "]" runes that +// surround the array values. The given commas arg must have a length that is +// one less than the length of the vals arg. However, vals may be empty, in +// which case commas must also be empty. +func NewArrayLiteralNode(openBracket *RuneNode, vals []ValueNode, commas []*RuneNode, closeBracket *RuneNode) *ArrayLiteralNode { + if openBracket == nil { + panic("openBracket is nil") + } + if closeBracket == nil { + panic("closeBracket is nil") + } + if len(vals) == 0 && len(commas) != 0 { + panic("vals is empty but commas is not") + } + if len(vals) > 0 && len(commas) != len(vals)-1 { + panic(fmt.Sprintf("%d vals requires %d commas, not %d", len(vals), len(vals)-1, len(commas))) + } + children := make([]Node, 0, len(vals)*2+1) + children = append(children, openBracket) + for i, val := range vals { + if i > 0 { + if commas[i-1] == nil { + panic(fmt.Sprintf("commas[%d] is nil", i-1)) + } + children = append(children, commas[i-1]) + } + if val == nil { + panic(fmt.Sprintf("vals[%d] is nil", i)) + } + children = append(children, val) + } + children = append(children, closeBracket) + + return &ArrayLiteralNode{ + compositeNode: compositeNode{ + children: children, + }, + OpenBracket: openBracket, + Elements: vals, + Commas: commas, + CloseBracket: closeBracket, + } +} + +func (n *ArrayLiteralNode) Value() interface{} { + return n.Elements +} + +// MessageLiteralNode represents a message literal, which is compatible with the +// protobuf text format and can be used for custom options with message types. +// Example: +// +// { foo:1 foo:2 foo:3 bar:<name:"abc" id:123> } +type MessageLiteralNode struct { + compositeNode + Open *RuneNode // should be '{' or '<' + Elements []*MessageFieldNode + // Separator characters between elements, which can be either ',' + // or ';' if present. This slice must be exactly len(Elements) in + // length, with each item in Elements having one corresponding item + // in Seps. Separators in message literals are optional, so a given + // item in this slice may be nil to indicate absence of a separator. + Seps []*RuneNode + Close *RuneNode // should be '}' or '>', depending on Open +} + +// NewMessageLiteralNode creates a new *MessageLiteralNode. The openSym and +// closeSym runes must not be nil and should be "{" and "}" or "<" and ">". +// +// Unlike separators (dots and commas) used for other AST nodes that represent +// a list of elements, the seps arg must be the SAME length as vals, and it may +// contain nil values to indicate absence of a separator (in fact, it could be +// all nils). +func NewMessageLiteralNode(openSym *RuneNode, vals []*MessageFieldNode, seps []*RuneNode, closeSym *RuneNode) *MessageLiteralNode { + if openSym == nil { + panic("openSym is nil") + } + if closeSym == nil { + panic("closeSym is nil") + } + if len(seps) != len(vals) { + panic(fmt.Sprintf("%d vals requires %d commas, not %d", len(vals), len(vals), len(seps))) + } + numChildren := len(vals) + 2 + for _, sep := range seps { + if sep != nil { + numChildren++ + } + } + children := make([]Node, 0, numChildren) + children = append(children, openSym) + for i, val := range vals { + if val == nil { + panic(fmt.Sprintf("vals[%d] is nil", i)) + } + children = append(children, val) + if seps[i] != nil { + children = append(children, seps[i]) + } + } + children = append(children, closeSym) + + return &MessageLiteralNode{ + compositeNode: compositeNode{ + children: children, + }, + Open: openSym, + Elements: vals, + Seps: seps, + Close: closeSym, + } +} + +func (n *MessageLiteralNode) Value() interface{} { + return n.Elements +} + +// MessageFieldNode represents a single field (name and value) inside of a +// message literal. Example: +// +// foo:"bar" +type MessageFieldNode struct { + compositeNode + Name *FieldReferenceNode + // Sep represents the ':' separator between the name and value. If + // the value is a message or list literal (and thus starts with '<', + // '{', or '['), then the separator may be omitted and this field may + // be nil. + Sep *RuneNode + Val ValueNode +} + +// NewMessageFieldNode creates a new *MessageFieldNode. All args except sep +// must be non-nil. +func NewMessageFieldNode(name *FieldReferenceNode, sep *RuneNode, val ValueNode) *MessageFieldNode { + if name == nil { + panic("name is nil") + } + if val == nil { + panic("val is nil") + } + numChildren := 2 + if sep != nil { + numChildren++ + } + children := make([]Node, 0, numChildren) + children = append(children, name) + if sep != nil { + children = append(children, sep) + } + children = append(children, val) + + return &MessageFieldNode{ + compositeNode: compositeNode{ + children: children, + }, + Name: name, + Sep: sep, + Val: val, + } +} diff --git a/vendor/github.com/bufbuild/protocompile/ast/walk.go b/vendor/github.com/bufbuild/protocompile/ast/walk.go new file mode 100644 index 0000000..00e71ab --- /dev/null +++ b/vendor/github.com/bufbuild/protocompile/ast/walk.go @@ -0,0 +1,931 @@ +// Copyright 2020-2024 Buf Technologies, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package ast + +import "fmt" + +// Walk conducts a walk of the AST rooted at the given root using the +// given visitor. It performs a "pre-order traversal", visiting a +// given AST node before it visits that node's descendants. +// +// If a visitor returns an error while walking the tree, the entire +// operation is aborted and that error is returned. +func Walk(root Node, v Visitor, opts ...WalkOption) error { + var wOpts walkOptions + for _, opt := range opts { + opt(&wOpts) + } + return walk(root, v, wOpts) +} + +// WalkOption represents an option used with the Walk function. These +// allow optional before and after hooks to be invoked as each node in +// the tree is visited. +type WalkOption func(*walkOptions) + +type walkOptions struct { + before, after func(Node) error +} + +// WithBefore returns a WalkOption that will cause the given function to be +// invoked before a node is visited during a walk operation. If this hook +// returns an error, the node is not visited and the walk operation is aborted. +func WithBefore(fn func(Node) error) WalkOption { + return func(options *walkOptions) { + options.before = fn + } +} + +// WithAfter returns a WalkOption that will cause the given function to be +// invoked after a node (as well as any descendants) is visited during a walk +// operation. If this hook returns an error, the node is not visited and the +// walk operation is aborted. +// +// If the walk is aborted due to some other visitor or before hook returning an +// error, the after hook is still called for all nodes that have been visited. +// However, the walk operation fails with the first error it encountered, so any +// error returned from an after hook is effectively ignored. +func WithAfter(fn func(Node) error) WalkOption { + return func(options *walkOptions) { + options.after = fn + } +} + +func walk(root Node, v Visitor, opts walkOptions) (err error) { + if opts.before != nil { + if err := opts.before(root); err != nil { + return err + } + } + if opts.after != nil { + defer func() { + if afterErr := opts.after(root); afterErr != nil { + // if another call already returned an error then we + // have to ignore the error from the after hook + if err == nil { + err = afterErr + } + } + }() + } + + if err := Visit(root, v); err != nil { + return err + } + + if comp, ok := root.(CompositeNode); ok { + for _, child := range comp.Children() { + if err := walk(child, v, opts); err != nil { + return err + } + } + } + return nil +} + +// Visit implements the double-dispatch idiom and visits the given node by +// calling the appropriate method of the given visitor. +func Visit(n Node, v Visitor) error { + switch n := n.(type) { + case *FileNode: + return v.VisitFileNode(n) + case *SyntaxNode: + return v.VisitSyntaxNode(n) + case *EditionNode: + return v.VisitEditionNode(n) + case *PackageNode: + return v.VisitPackageNode(n) + case *ImportNode: + return v.VisitImportNode(n) + case *OptionNode: + return v.VisitOptionNode(n) + case *OptionNameNode: + return v.VisitOptionNameNode(n) + case *FieldReferenceNode: + return v.VisitFieldReferenceNode(n) + case *CompactOptionsNode: + return v.VisitCompactOptionsNode(n) + case *MessageNode: + return v.VisitMessageNode(n) + case *ExtendNode: + return v.VisitExtendNode(n) + case *ExtensionRangeNode: + return v.VisitExtensionRangeNode(n) + case *ReservedNode: + return v.VisitReservedNode(n) + case *RangeNode: + return v.VisitRangeNode(n) + case *FieldNode: + return v.VisitFieldNode(n) + case *GroupNode: + return v.VisitGroupNode(n) + case *MapFieldNode: + return v.VisitMapFieldNode(n) + case *MapTypeNode: + return v.VisitMapTypeNode(n) + case *OneofNode: + return v.VisitOneofNode(n) + case *EnumNode: + return v.VisitEnumNode(n) + case *EnumValueNode: + return v.VisitEnumValueNode(n) + case *ServiceNode: + return v.VisitServiceNode(n) + case *RPCNode: + return v.VisitRPCNode(n) + case *RPCTypeNode: + return v.VisitRPCTypeNode(n) + case *IdentNode: + return v.VisitIdentNode(n) + case *CompoundIdentNode: + return v.VisitCompoundIdentNode(n) + case *StringLiteralNode: + return v.VisitStringLiteralNode(n) + case *CompoundStringLiteralNode: + return v.VisitCompoundStringLiteralNode(n) + case *UintLiteralNode: + return v.VisitUintLiteralNode(n) + case *NegativeIntLiteralNode: + return v.VisitNegativeIntLiteralNode(n) + case *FloatLiteralNode: + return v.VisitFloatLiteralNode(n) + case *SpecialFloatLiteralNode: + return v.VisitSpecialFloatLiteralNode(n) + case *SignedFloatLiteralNode: + return v.VisitSignedFloatLiteralNode(n) + case *ArrayLiteralNode: + return v.VisitArrayLiteralNode(n) + case *MessageLiteralNode: + return v.VisitMessageLiteralNode(n) + case *MessageFieldNode: + return v.VisitMessageFieldNode(n) + case *KeywordNode: + return v.VisitKeywordNode(n) + case *RuneNode: + return v.VisitRuneNode(n) + case *EmptyDeclNode: + return v.VisitEmptyDeclNode(n) + default: + panic(fmt.Sprintf("unexpected type of node: %T", n)) + } +} + +// AncestorTracker is used to track the path of nodes during a walk operation. +// By passing AsWalkOptions to a call to Walk, a visitor can inspect the path to +// the node being visited using this tracker. +type AncestorTracker struct { + ancestors []Node +} + +// AsWalkOptions returns WalkOption values that will cause this ancestor tracker +// to track the path through the AST during the walk operation. +func (t *AncestorTracker) AsWalkOptions() []WalkOption { + return []WalkOption{ + WithBefore(func(n Node) error { + t.ancestors = append(t.ancestors, n) + return nil + }), + WithAfter(func(_ Node) error { + t.ancestors = t.ancestors[:len(t.ancestors)-1] + return nil + }), + } +} + +// Path returns a slice of nodes that represents the path from the root of the +// walk operaiton to the currently visited node. The first element in the path +// is the root supplied to Walk. The last element in the path is the currently +// visited node. +// +// The returned slice is not a defensive copy; so callers should NOT mutate it. +func (t *AncestorTracker) Path() []Node { + return t.ancestors +} + +// Parent returns the parent node of the currently visited node. If the node +// currently being visited is the root supplied to Walk then nil is returned. +func (t *AncestorTracker) Parent() Node { + if len(t.ancestors) <= 1 { + return nil + } + return t.ancestors[len(t.ancestors)-2] +} + +// VisitChildren visits all direct children of the given node using the given +// visitor. If visiting a child returns an error, that error is immediately +// returned, and other children will not be visited. +func VisitChildren(n CompositeNode, v Visitor) error { + for _, ch := range n.Children() { + if err := Visit(ch, v); err != nil { + return err + } + } + return nil +} + +// Visitor provides a technique for walking the AST that allows for +// dynamic dispatch, where a particular function is invoked based on +// the runtime type of the argument. +// +// It consists of a number of functions, each of which matches a +// concrete Node type. +// +// NOTE: As the language evolves, new methods may be added to this +// interface to correspond to new grammar elements. That is why it +// cannot be directly implemented outside this package. Visitor +// implementations must embed NoOpVisitor and then implement the +// subset of methods of interest. If such an implementation is used +// with an AST that has newer elements, the visitor will not do +// anything in response to the new node types. +// +// An alternative to embedding NoOpVisitor is to use an instance of +// SimpleVisitor. +// +// Visitors can be supplied to a Walk operation or passed to a call +// to Visit or VisitChildren. +// +// Note that there are some AST node types defined in this package +// that do not have corresponding visit methods. These are synthetic +// node types, that have specialized use from the parser, but never +// appear in an actual AST (which is always rooted at FileNode). +// These include SyntheticMapField, SyntheticOneof, +// SyntheticGroupMessageNode, and SyntheticMapEntryNode. +type Visitor interface { + // VisitFileNode is invoked when visiting a *FileNode in the AST. + VisitFileNode(*FileNode) error + // VisitSyntaxNode is invoked when visiting a *SyntaxNode in the AST. + VisitSyntaxNode(*SyntaxNode) error + // VisitEditionNode is invoked when visiting an *EditionNode in the AST. + VisitEditionNode(*EditionNode) error + // VisitPackageNode is invoked when visiting a *PackageNode in the AST. + VisitPackageNode(*PackageNode) error + // VisitImportNode is invoked when visiting an *ImportNode in the AST. + VisitImportNode(*ImportNode) error + // VisitOptionNode is invoked when visiting an *OptionNode in the AST. + VisitOptionNode(*OptionNode) error + // VisitOptionNameNode is invoked when visiting an *OptionNameNode in the AST. + VisitOptionNameNode(*OptionNameNode) error + // VisitFieldReferenceNode is invoked when visiting a *FieldReferenceNode in the AST. + VisitFieldReferenceNode(*FieldReferenceNode) error + // VisitCompactOptionsNode is invoked when visiting a *CompactOptionsNode in the AST. + VisitCompactOptionsNode(*CompactOptionsNode) error + // VisitMessageNode is invoked when visiting a *MessageNode in the AST. + VisitMessageNode(*MessageNode) error + // VisitExtendNode is invoked when visiting an *ExtendNode in the AST. + VisitExtendNode(*ExtendNode) error + // VisitExtensionRangeNode is invoked when visiting an *ExtensionRangeNode in the AST. + VisitExtensionRangeNode(*ExtensionRangeNode) error + // VisitReservedNode is invoked when visiting a *ReservedNode in the AST. + VisitReservedNode(*ReservedNode) error + // VisitRangeNode is invoked when visiting a *RangeNode in the AST. + VisitRangeNode(*RangeNode) error + // VisitFieldNode is invoked when visiting a *FieldNode in the AST. + VisitFieldNode(*FieldNode) error + // VisitGroupNode is invoked when visiting a *GroupNode in the AST. + VisitGroupNode(*GroupNode) error + // VisitMapFieldNode is invoked when visiting a *MapFieldNode in the AST. + VisitMapFieldNode(*MapFieldNode) error + // VisitMapTypeNode is invoked when visiting a *MapTypeNode in the AST. + VisitMapTypeNode(*MapTypeNode) error + // VisitOneofNode is invoked when visiting a *OneofNode in the AST. + VisitOneofNode(*OneofNode) error + // VisitEnumNode is invoked when visiting an *EnumNode in the AST. + VisitEnumNode(*EnumNode) error + // VisitEnumValueNode is invoked when visiting an *EnumValueNode in the AST. + VisitEnumValueNode(*EnumValueNode) error + // VisitServiceNode is invoked when visiting a *ServiceNode in the AST. + VisitServiceNode(*ServiceNode) error + // VisitRPCNode is invoked when visiting an *RPCNode in the AST. + VisitRPCNode(*RPCNode) error + // VisitRPCTypeNode is invoked when visiting an *RPCTypeNode in the AST. + VisitRPCTypeNode(*RPCTypeNode) error + // VisitIdentNode is invoked when visiting an *IdentNode in the AST. + VisitIdentNode(*IdentNode) error + // VisitCompoundIdentNode is invoked when visiting a *CompoundIdentNode in the AST. + VisitCompoundIdentNode(*CompoundIdentNode) error + // VisitStringLiteralNode is invoked when visiting a *StringLiteralNode in the AST. + VisitStringLiteralNode(*StringLiteralNode) error + // VisitCompoundStringLiteralNode is invoked when visiting a *CompoundStringLiteralNode in the AST. + VisitCompoundStringLiteralNode(*CompoundStringLiteralNode) error + // VisitUintLiteralNode is invoked when visiting a *UintLiteralNode in the AST. + VisitUintLiteralNode(*UintLiteralNode) error + // VisitNegativeIntLiteralNode is invoked when visiting a *NegativeIntLiteralNode in the AST. + VisitNegativeIntLiteralNode(*NegativeIntLiteralNode) error + // VisitFloatLiteralNode is invoked when visiting a *FloatLiteralNode in the AST. + VisitFloatLiteralNode(*FloatLiteralNode) error + // VisitSpecialFloatLiteralNode is invoked when visiting a *SpecialFloatLiteralNode in the AST. + VisitSpecialFloatLiteralNode(*SpecialFloatLiteralNode) error + // VisitSignedFloatLiteralNode is invoked when visiting a *SignedFloatLiteralNode in the AST. + VisitSignedFloatLiteralNode(*SignedFloatLiteralNode) error + // VisitArrayLiteralNode is invoked when visiting an *ArrayLiteralNode in the AST. + VisitArrayLiteralNode(*ArrayLiteralNode) error + // VisitMessageLiteralNode is invoked when visiting a *MessageLiteralNode in the AST. + VisitMessageLiteralNode(*MessageLiteralNode) error + // VisitMessageFieldNode is invoked when visiting a *MessageFieldNode in the AST. + VisitMessageFieldNode(*MessageFieldNode) error + // VisitKeywordNode is invoked when visiting a *KeywordNode in the AST. + VisitKeywordNode(*KeywordNode) error + // VisitRuneNode is invoked when visiting a *RuneNode in the AST. + VisitRuneNode(*RuneNode) error + // VisitEmptyDeclNode is invoked when visiting a *EmptyDeclNode in the AST. + VisitEmptyDeclNode(*EmptyDeclNode) error + + // Unexported method prevents callers from directly implementing. + isVisitor() +} + +// NoOpVisitor is a visitor implementation that does nothing. All methods +// unconditionally return nil. This can be embedded into a struct to make that +// struct implement the Visitor interface, and only the relevant visit methods +// then need to be implemented on the struct. +type NoOpVisitor struct{} + +var _ Visitor = NoOpVisitor{} + +func (n NoOpVisitor) isVisitor() {} + +func (n NoOpVisitor) VisitFileNode(_ *FileNode) error { + return nil +} + +func (n NoOpVisitor) VisitSyntaxNode(_ *SyntaxNode) error { + return nil +} + +func (n NoOpVisitor) VisitEditionNode(_ *EditionNode) error { + return nil +} + +func (n NoOpVisitor) VisitPackageNode(_ *PackageNode) error { + return nil +} + +func (n NoOpVisitor) VisitImportNode(_ *ImportNode) error { + return nil +} + +func (n NoOpVisitor) VisitOptionNode(_ *OptionNode) error { + return nil +} + +func (n NoOpVisitor) VisitOptionNameNode(_ *OptionNameNode) error { + return nil +} + +func (n NoOpVisitor) VisitFieldReferenceNode(_ *FieldReferenceNode) error { + return nil +} + +func (n NoOpVisitor) VisitCompactOptionsNode(_ *CompactOptionsNode) error { + return nil +} + +func (n NoOpVisitor) VisitMessageNode(_ *MessageNode) error { + return nil +} + +func (n NoOpVisitor) VisitExtendNode(_ *ExtendNode) error { + return nil +} + +func (n NoOpVisitor) VisitExtensionRangeNode(_ *ExtensionRangeNode) error { + return nil +} + +func (n NoOpVisitor) VisitReservedNode(_ *ReservedNode) error { + return nil +} + +func (n NoOpVisitor) VisitRangeNode(_ *RangeNode) error { + return nil +} + +func (n NoOpVisitor) VisitFieldNode(_ *FieldNode) error { + return nil +} + +func (n NoOpVisitor) VisitGroupNode(_ *GroupNode) error { + return nil +} + +func (n NoOpVisitor) VisitMapFieldNode(_ *MapFieldNode) error { + return nil +} + +func (n NoOpVisitor) VisitMapTypeNode(_ *MapTypeNode) error { + return nil +} + +func (n NoOpVisitor) VisitOneofNode(_ *OneofNode) error { + return nil +} + +func (n NoOpVisitor) VisitEnumNode(_ *EnumNode) error { + return nil +} + +func (n NoOpVisitor) VisitEnumValueNode(_ *EnumValueNode) error { + return nil +} + +func (n NoOpVisitor) VisitServiceNode(_ *ServiceNode) error { + return nil +} + +func (n NoOpVisitor) VisitRPCNode(_ *RPCNode) error { + return nil +} + +func (n NoOpVisitor) VisitRPCTypeNode(_ *RPCTypeNode) error { + return nil +} + +func (n NoOpVisitor) VisitIdentNode(_ *IdentNode) error { + return nil +} + +func (n NoOpVisitor) VisitCompoundIdentNode(_ *CompoundIdentNode) error { + return nil +} + +func (n NoOpVisitor) VisitStringLiteralNode(_ *StringLiteralNode) error { + return nil +} + +func (n NoOpVisitor) VisitCompoundStringLiteralNode(_ *CompoundStringLiteralNode) error { + return nil +} + +func (n NoOpVisitor) VisitUintLiteralNode(_ *UintLiteralNode) error { + return nil +} + +func (n NoOpVisitor) VisitNegativeIntLiteralNode(_ *NegativeIntLiteralNode) error { + return nil +} + +func (n NoOpVisitor) VisitFloatLiteralNode(_ *FloatLiteralNode) error { + return nil +} + +func (n NoOpVisitor) VisitSpecialFloatLiteralNode(_ *SpecialFloatLiteralNode) error { + return nil +} + +func (n NoOpVisitor) VisitSignedFloatLiteralNode(_ *SignedFloatLiteralNode) error { + return nil +} + +func (n NoOpVisitor) VisitArrayLiteralNode(_ *ArrayLiteralNode) error { + return nil +} + +func (n NoOpVisitor) VisitMessageLiteralNode(_ *MessageLiteralNode) error { + return nil +} + +func (n NoOpVisitor) VisitMessageFieldNode(_ *MessageFieldNode) error { + return nil +} + +func (n NoOpVisitor) VisitKeywordNode(_ *KeywordNode) error { + return nil +} + +func (n NoOpVisitor) VisitRuneNode(_ *RuneNode) error { + return nil +} + +func (n NoOpVisitor) VisitEmptyDeclNode(_ *EmptyDeclNode) error { + return nil +} + +// SimpleVisitor is a visitor implementation that uses numerous function fields. +// If a relevant function field is not nil, then it will be invoked when a node +// is visited. +// +// In addition to a function for each concrete node type (and thus for each +// Visit* method of the Visitor interface), it also has function fields that +// accept interface types. So a visitor can, for example, easily treat all +// ValueNodes uniformly by providing a non-nil value for DoVisitValueNode +// instead of having to supply values for the various DoVisit*Node methods +// corresponding to all types that implement ValueNode. +// +// The most specific function provided that matches a given node is the one that +// will be invoked. For example, DoVisitStringValueNode will be called if +// present and applicable before DoVisitValueNode. Similarly, DoVisitValueNode +// would be called before DoVisitTerminalNode or DoVisitCompositeNode. The +// DoVisitNode is the most generic function and is called only if no more +// specific function is present for a given node type. +// +// The *UintLiteralNode type implements both IntValueNode and FloatValueNode. +// In this case, the DoVisitIntValueNode function is considered more specific +// than DoVisitFloatValueNode, so will be preferred if present. +// +// Similarly, *MapFieldNode and *GroupNode implement both FieldDeclNode and +// MessageDeclNode. In this case, the DoVisitFieldDeclNode function is +// treated as more specific than DoVisitMessageDeclNode, so will be preferred +// if both are present. +type SimpleVisitor struct { + DoVisitFileNode func(*FileNode) error + DoVisitSyntaxNode func(*SyntaxNode) error + DoVisitEditionNode func(*EditionNode) error + DoVisitPackageNode func(*PackageNode) error + DoVisitImportNode func(*ImportNode) error + DoVisitOptionNode func(*OptionNode) error + DoVisitOptionNameNode func(*OptionNameNode) error + DoVisitFieldReferenceNode func(*FieldReferenceNode) error + DoVisitCompactOptionsNode func(*CompactOptionsNode) error + DoVisitMessageNode func(*MessageNode) error + DoVisitExtendNode func(*ExtendNode) error + DoVisitExtensionRangeNode func(*ExtensionRangeNode) error + DoVisitReservedNode func(*ReservedNode) error + DoVisitRangeNode func(*RangeNode) error + DoVisitFieldNode func(*FieldNode) error + DoVisitGroupNode func(*GroupNode) error + DoVisitMapFieldNode func(*MapFieldNode) error + DoVisitMapTypeNode func(*MapTypeNode) error + DoVisitOneofNode func(*OneofNode) error + DoVisitEnumNode func(*EnumNode) error + DoVisitEnumValueNode func(*EnumValueNode) error + DoVisitServiceNode func(*ServiceNode) error + DoVisitRPCNode func(*RPCNode) error + DoVisitRPCTypeNode func(*RPCTypeNode) error + DoVisitIdentNode func(*IdentNode) error + DoVisitCompoundIdentNode func(*CompoundIdentNode) error + DoVisitStringLiteralNode func(*StringLiteralNode) error + DoVisitCompoundStringLiteralNode func(*CompoundStringLiteralNode) error + DoVisitUintLiteralNode func(*UintLiteralNode) error + DoVisitNegativeIntLiteralNode func(*NegativeIntLiteralNode) error + DoVisitFloatLiteralNode func(*FloatLiteralNode) error + DoVisitSpecialFloatLiteralNode func(*SpecialFloatLiteralNode) error + DoVisitSignedFloatLiteralNode func(*SignedFloatLiteralNode) error + DoVisitArrayLiteralNode func(*ArrayLiteralNode) error + DoVisitMessageLiteralNode func(*MessageLiteralNode) error + DoVisitMessageFieldNode func(*MessageFieldNode) error + DoVisitKeywordNode func(*KeywordNode) error + DoVisitRuneNode func(*RuneNode) error + DoVisitEmptyDeclNode func(*EmptyDeclNode) error + + DoVisitFieldDeclNode func(FieldDeclNode) error + DoVisitMessageDeclNode func(MessageDeclNode) error + + DoVisitIdentValueNode func(IdentValueNode) error + DoVisitStringValueNode func(StringValueNode) error + DoVisitIntValueNode func(IntValueNode) error + DoVisitFloatValueNode func(FloatValueNode) error + DoVisitValueNode func(ValueNode) error + + DoVisitTerminalNode func(TerminalNode) error + DoVisitCompositeNode func(CompositeNode) error + DoVisitNode func(Node) error +} + +var _ Visitor = (*SimpleVisitor)(nil) + +func (v *SimpleVisitor) isVisitor() {} + +func (v *SimpleVisitor) visitInterface(node Node) error { + switch n := node.(type) { + case FieldDeclNode: + if v.DoVisitFieldDeclNode != nil { + return v.DoVisitFieldDeclNode(n) + } + // *MapFieldNode and *GroupNode both implement both FieldDeclNode and + // MessageDeclNode, so handle other case here + if fn, ok := n.(MessageDeclNode); ok && v.DoVisitMessageDeclNode != nil { + return v.DoVisitMessageDeclNode(fn) + } + case MessageDeclNode: + if v.DoVisitMessageDeclNode != nil { + return v.DoVisitMessageDeclNode(n) + } + case IdentValueNode: + if v.DoVisitIdentValueNode != nil { + return v.DoVisitIdentValueNode(n) + } + case StringValueNode: + if v.DoVisitStringValueNode != nil { + return v.DoVisitStringValueNode(n) + } + case IntValueNode: + if v.DoVisitIntValueNode != nil { + return v.DoVisitIntValueNode(n) + } + // *UintLiteralNode implements both IntValueNode and FloatValueNode, + // so handle other case here + if fn, ok := n.(FloatValueNode); ok && v.DoVisitFloatValueNode != nil { + return v.DoVisitFloatValueNode(fn) + } + case FloatValueNode: + if v.DoVisitFloatValueNode != nil { + return v.DoVisitFloatValueNode(n) + } + } + + if n, ok := node.(ValueNode); ok && v.DoVisitValueNode != nil { + return v.DoVisitValueNode(n) + } + + switch n := node.(type) { + case TerminalNode: + if v.DoVisitTerminalNode != nil { + return v.DoVisitTerminalNode(n) + } + case CompositeNode: + if v.DoVisitCompositeNode != nil { + return v.DoVisitCompositeNode(n) + } + } + + if v.DoVisitNode != nil { + return v.DoVisitNode(node) + } + + return nil +} + +func (v *SimpleVisitor) VisitFileNode(node *FileNode) error { + if v.DoVisitFileNode != nil { + return v.DoVisitFileNode(node) + } + return v.visitInterface(node) +} + +func (v *SimpleVisitor) VisitSyntaxNode(node *SyntaxNode) error { + if v.DoVisitSyntaxNode != nil { + return v.DoVisitSyntaxNode(node) + } + return v.visitInterface(node) +} + +func (v *SimpleVisitor) VisitEditionNode(node *EditionNode) error { + if v.DoVisitEditionNode != nil { + return v.DoVisitEditionNode(node) + } + return v.visitInterface(node) +} + +func (v *SimpleVisitor) VisitPackageNode(node *PackageNode) error { + if v.DoVisitPackageNode != nil { + return v.DoVisitPackageNode(node) + } + return v.visitInterface(node) +} + +func (v *SimpleVisitor) VisitImportNode(node *ImportNode) error { + if v.DoVisitImportNode != nil { + return v.DoVisitImportNode(node) + } + return v.visitInterface(node) +} + +func (v *SimpleVisitor) VisitOptionNode(node *OptionNode) error { + if v.DoVisitOptionNode != nil { + return v.DoVisitOptionNode(node) + } + return v.visitInterface(node) +} + +func (v *SimpleVisitor) VisitOptionNameNode(node *OptionNameNode) error { + if v.DoVisitOptionNameNode != nil { + return v.DoVisitOptionNameNode(node) + } + return v.visitInterface(node) +} + +func (v *SimpleVisitor) VisitFieldReferenceNode(node *FieldReferenceNode) error { + if v.DoVisitFieldReferenceNode != nil { + return v.DoVisitFieldReferenceNode(node) + } + return v.visitInterface(node) +} + +func (v *SimpleVisitor) VisitCompactOptionsNode(node *CompactOptionsNode) error { + if v.DoVisitCompactOptionsNode != nil { + return v.DoVisitCompactOptionsNode(node) + } + return v.visitInterface(node) +} + +func (v *SimpleVisitor) VisitMessageNode(node *MessageNode) error { + if v.DoVisitMessageNode != nil { + return v.DoVisitMessageNode(node) + } + return v.visitInterface(node) +} + +func (v *SimpleVisitor) VisitExtendNode(node *ExtendNode) error { + if v.DoVisitExtendNode != nil { + return v.DoVisitExtendNode(node) + } + return v.visitInterface(node) +} + +func (v *SimpleVisitor) VisitExtensionRangeNode(node *ExtensionRangeNode) error { + if v.DoVisitExtensionRangeNode != nil { + return v.DoVisitExtensionRangeNode(node) + } + return v.visitInterface(node) +} + +func (v *SimpleVisitor) VisitReservedNode(node *ReservedNode) error { + if v.DoVisitReservedNode != nil { + return v.DoVisitReservedNode(node) + } + return v.visitInterface(node) +} + +func (v *SimpleVisitor) VisitRangeNode(node *RangeNode) error { + if v.DoVisitRangeNode != nil { + return v.DoVisitRangeNode(node) + } + return v.visitInterface(node) +} + +func (v *SimpleVisitor) VisitFieldNode(node *FieldNode) error { + if v.DoVisitFieldNode != nil { + return v.DoVisitFieldNode(node) + } + return v.visitInterface(node) +} + +func (v *SimpleVisitor) VisitGroupNode(node *GroupNode) error { + if v.DoVisitGroupNode != nil { + return v.DoVisitGroupNode(node) + } + return v.visitInterface(node) +} + +func (v *SimpleVisitor) VisitMapFieldNode(node *MapFieldNode) error { + if v.DoVisitMapFieldNode != nil { + return v.DoVisitMapFieldNode(node) + } + return v.visitInterface(node) +} + +func (v *SimpleVisitor) VisitMapTypeNode(node *MapTypeNode) error { + if v.DoVisitMapTypeNode != nil { + return v.DoVisitMapTypeNode(node) + } + return v.visitInterface(node) +} + +func (v *SimpleVisitor) VisitOneofNode(node *OneofNode) error { + if v.DoVisitOneofNode != nil { + return v.DoVisitOneofNode(node) + } + return v.visitInterface(node) +} + +func (v *SimpleVisitor) VisitEnumNode(node *EnumNode) error { + if v.DoVisitEnumNode != nil { + return v.DoVisitEnumNode(node) + } + return v.visitInterface(node) +} + +func (v *SimpleVisitor) VisitEnumValueNode(node *EnumValueNode) error { + if v.DoVisitEnumValueNode != nil { + return v.DoVisitEnumValueNode(node) + } + return v.visitInterface(node) +} + +func (v *SimpleVisitor) VisitServiceNode(node *ServiceNode) error { + if v.DoVisitServiceNode != nil { + return v.DoVisitServiceNode(node) + } + return v.visitInterface(node) +} + +func (v *SimpleVisitor) VisitRPCNode(node *RPCNode) error { + if v.DoVisitRPCNode != nil { + return v.DoVisitRPCNode(node) + } + return v.visitInterface(node) +} + +func (v *SimpleVisitor) VisitRPCTypeNode(node *RPCTypeNode) error { + if v.DoVisitRPCTypeNode != nil { + return v.DoVisitRPCTypeNode(node) + } + return v.visitInterface(node) +} + +func (v *SimpleVisitor) VisitIdentNode(node *IdentNode) error { + if v.DoVisitIdentNode != nil { + return v.DoVisitIdentNode(node) + } + return v.visitInterface(node) +} + +func (v *SimpleVisitor) VisitCompoundIdentNode(node *CompoundIdentNode) error { + if v.DoVisitCompoundIdentNode != nil { + return v.DoVisitCompoundIdentNode(node) + } + return v.visitInterface(node) +} + +func (v *SimpleVisitor) VisitStringLiteralNode(node *StringLiteralNode) error { + if v.DoVisitStringLiteralNode != nil { + return v.DoVisitStringLiteralNode(node) + } + return v.visitInterface(node) +} + +func (v *SimpleVisitor) VisitCompoundStringLiteralNode(node *CompoundStringLiteralNode) error { + if v.DoVisitCompoundStringLiteralNode != nil { + return v.DoVisitCompoundStringLiteralNode(node) + } + return v.visitInterface(node) +} + +func (v *SimpleVisitor) VisitUintLiteralNode(node *UintLiteralNode) error { + if v.DoVisitUintLiteralNode != nil { + return v.DoVisitUintLiteralNode(node) + } + return v.visitInterface(node) +} + +func (v *SimpleVisitor) VisitNegativeIntLiteralNode(node *NegativeIntLiteralNode) error { + if v.DoVisitNegativeIntLiteralNode != nil { + return v.DoVisitNegativeIntLiteralNode(node) + } + return v.visitInterface(node) +} + +func (v *SimpleVisitor) VisitFloatLiteralNode(node *FloatLiteralNode) error { + if v.DoVisitFloatLiteralNode != nil { + return v.DoVisitFloatLiteralNode(node) + } + return v.visitInterface(node) +} + +func (v *SimpleVisitor) VisitSpecialFloatLiteralNode(node *SpecialFloatLiteralNode) error { + if v.DoVisitSpecialFloatLiteralNode != nil { + return v.DoVisitSpecialFloatLiteralNode(node) + } + return v.visitInterface(node) +} + +func (v *SimpleVisitor) VisitSignedFloatLiteralNode(node *SignedFloatLiteralNode) error { + if v.DoVisitSignedFloatLiteralNode != nil { + return v.DoVisitSignedFloatLiteralNode(node) + } + return v.visitInterface(node) +} + +func (v *SimpleVisitor) VisitArrayLiteralNode(node *ArrayLiteralNode) error { + if v.DoVisitArrayLiteralNode != nil { + return v.DoVisitArrayLiteralNode(node) + } + return v.visitInterface(node) +} + +func (v *SimpleVisitor) VisitMessageLiteralNode(node *MessageLiteralNode) error { + if v.DoVisitMessageLiteralNode != nil { + return v.DoVisitMessageLiteralNode(node) + } + return v.visitInterface(node) +} + +func (v *SimpleVisitor) VisitMessageFieldNode(node *MessageFieldNode) error { + if v.DoVisitMessageFieldNode != nil { + return v.DoVisitMessageFieldNode(node) + } + return v.visitInterface(node) +} + +func (v *SimpleVisitor) VisitKeywordNode(node *KeywordNode) error { + if v.DoVisitKeywordNode != nil { + return v.DoVisitKeywordNode(node) + } + return v.visitInterface(node) +} + +func (v *SimpleVisitor) VisitRuneNode(node *RuneNode) error { + if v.DoVisitRuneNode != nil { + return v.DoVisitRuneNode(node) + } + return v.visitInterface(node) +} + +func (v *SimpleVisitor) VisitEmptyDeclNode(node *EmptyDeclNode) error { + if v.DoVisitEmptyDeclNode != nil { + return v.DoVisitEmptyDeclNode(node) + } + return v.visitInterface(node) +} diff --git a/vendor/github.com/bufbuild/protocompile/compiler.go b/vendor/github.com/bufbuild/protocompile/compiler.go new file mode 100644 index 0000000..b9a6d15 --- /dev/null +++ b/vendor/github.com/bufbuild/protocompile/compiler.go @@ -0,0 +1,682 @@ +// Copyright 2020-2024 Buf Technologies, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package protocompile + +import ( + "bytes" + "context" + "errors" + "fmt" + "io" + "runtime" + "runtime/debug" + "strings" + "sync" + + "golang.org/x/sync/semaphore" + "google.golang.org/protobuf/proto" + "google.golang.org/protobuf/types/descriptorpb" + + "github.com/bufbuild/protocompile/ast" + "github.com/bufbuild/protocompile/linker" + "github.com/bufbuild/protocompile/options" + "github.com/bufbuild/protocompile/parser" + "github.com/bufbuild/protocompile/reporter" + "github.com/bufbuild/protocompile/sourceinfo" +) + +// Compiler handles compilation tasks, to turn protobuf source files, or other +// intermediate representations, into fully linked descriptors. +// +// The compilation process involves five steps for each protobuf source file: +// 1. Parsing the source into an AST (abstract syntax tree). +// 2. Converting the AST into descriptor protos. +// 3. Linking descriptor protos into fully linked descriptors. +// 4. Interpreting options. +// 5. Computing source code information. +// +// With fully linked descriptors, code generators and protoc plugins could be +// invoked (though that step is not implemented by this package and not a +// responsibility of this type). +type Compiler struct { + // Resolves path/file names into source code or intermediate representations + // for protobuf source files. This is how the compiler loads the files to + // be compiled as well as all dependencies. This field is the only required + // field. + Resolver Resolver + // The maximum parallelism to use when compiling. If unspecified or set to + // a non-positive value, then min(runtime.NumCPU(), runtime.GOMAXPROCS(-1)) + // will be used. + MaxParallelism int + // A custom error and warning reporter. If unspecified a default reporter + // is used. A default reporter fails the compilation after encountering any + // errors and ignores all warnings. + Reporter reporter.Reporter + + // If unspecified or set to SourceInfoNone, source code information will not + // be included in the resulting descriptors. Source code information is + // metadata in the file descriptor that provides position information (i.e. + // the line and column where file elements were defined) as well as comments. + // + // If set to SourceInfoStandard, normal source code information will be + // included in the resulting descriptors. This matches the output of protoc + // (the reference compiler for Protocol Buffers). If set to + // SourceInfoMoreComments, the resulting descriptor will attempt to preserve + // as many comments as possible, for all elements in the file, not just for + // complete declarations. + // + // If Resolver returns descriptors or descriptor protos for a file, then + // those descriptors will not be modified. If they do not already include + // source code info, they will be left that way when the compile operation + // concludes. Similarly, if they already have source code info but this flag + // is false, existing info will be left in place. + SourceInfoMode SourceInfoMode + + // If true, ASTs are retained in compilation results for which an AST was + // constructed. So any linker.Result value in the resulting compiled files + // will have an AST, in addition to descriptors. If left false, the AST + // will be removed as soon as it's no longer needed. This can help reduce + // total memory usage for operations involving a large number of files. + RetainASTs bool + + // If non-nil, the set of symbols already known. Any symbols in the current + // compilation will be added to it. If the compilation tries to redefine any + // of these symbols, it will be reported as a collision. + // + // This allows a large compilation to be split up into multiple, smaller + // operations and still be able to identify naming collisions and extension + // number collisions across all operations. + Symbols *linker.Symbols +} + +// SourceInfoMode indicates how source code info is generated by a Compiler. +type SourceInfoMode int + +const ( + // SourceInfoNone indicates that no source code info is generated. + SourceInfoNone = SourceInfoMode(0) + // SourceInfoStandard indicates that the standard source code info is + // generated, which includes comments only for complete declarations. + SourceInfoStandard = SourceInfoMode(1) + // SourceInfoExtraComments indicates that source code info is generated + // and will include comments for all elements (more comments than would + // be found in a descriptor produced by protoc). + SourceInfoExtraComments = SourceInfoMode(2) + // SourceInfoExtraOptionLocations indicates that source code info is + // generated with additional locations for elements inside of message + // literals in option values. This can be combined with the above by + // bitwise-OR'ing it with SourceInfoExtraComments. + SourceInfoExtraOptionLocations = SourceInfoMode(4) +) + +// Compile compiles the given file names into fully-linked descriptors. The +// compiler's resolver is used to locate source code (or intermediate artifacts +// such as parsed ASTs or descriptor protos) and then do what is necessary to +// transform that into descriptors (parsing, linking, etc). +// +// Elements in the given returned files will implement [linker.Result] if the +// compiler had to link it (i.e. the resolver provided either a descriptor proto +// or source code). That result will contain a full AST for the file if the +// compiler had to parse it (i.e. the resolver provided source code for that +// file). +func (c *Compiler) Compile(ctx context.Context, files ...string) (linker.Files, error) { + if len(files) == 0 { + return nil, nil + } + + ctx, cancel := context.WithCancel(ctx) + defer cancel() + + par := c.MaxParallelism + if par <= 0 { + par = runtime.GOMAXPROCS(-1) + cpus := runtime.NumCPU() + if par > cpus { + par = cpus + } + } + + h := reporter.NewHandler(c.Reporter) + + sym := c.Symbols + if sym == nil { + sym = &linker.Symbols{} + } + e := executor{ + c: c, + h: h, + s: semaphore.NewWeighted(int64(par)), + cancel: cancel, + sym: sym, + results: map[string]*result{}, + } + + // We lock now and create all tasks under lock to make sure that no + // async task can create a duplicate result. For example, if files + // contains both "foo.proto" and "bar.proto", then there is a race + // after we start compiling "foo.proto" between this loop and the + // async compilation task to create the result for "bar.proto". But + // we need to know if the file is directly requested for compilation, + // so we need this loop to define the result. So this loop holds the + // lock the whole time so async tasks can't create a result first. + results := make([]*result, len(files)) + func() { + e.mu.Lock() + defer e.mu.Unlock() + for i, f := range files { + results[i] = e.compileLocked(ctx, f, true) + } + }() + + descs := make([]linker.File, len(files)) + var firstError error + for i, r := range results { + select { + case <-r.ready: + case <-ctx.Done(): + return nil, ctx.Err() + } + if r.err != nil { + if firstError == nil { + firstError = r.err + } + } + descs[i] = r.res + } + + if err := h.Error(); err != nil { + return descs, err + } + // this should probably never happen; if any task returned an + // error, h.Error() should be non-nil + return descs, firstError +} + +type result struct { + name string + ready chan struct{} + + // true if this file was explicitly provided to the compiler; otherwise + // this file is an import that is implicitly included + explicitFile bool + + // produces a linker.File or error, only available when ready is closed + res linker.File + err error + + mu sync.Mutex + // the results that are dependencies of this result; this result is + // blocked, waiting on these dependencies to complete + blockedOn []string +} + +func (r *result) fail(err error) { + r.err = err + close(r.ready) +} + +func (r *result) complete(f linker.File) { + r.res = f + close(r.ready) +} + +func (r *result) setBlockedOn(deps []string) { + r.mu.Lock() + defer r.mu.Unlock() + r.blockedOn = deps +} + +func (r *result) getBlockedOn() []string { + r.mu.Lock() + defer r.mu.Unlock() + return r.blockedOn +} + +type executor struct { + c *Compiler + h *reporter.Handler + s *semaphore.Weighted + cancel context.CancelFunc + sym *linker.Symbols + + descriptorProtoCheck sync.Once + descriptorProtoIsCustom bool + + mu sync.Mutex + results map[string]*result +} + +func (e *executor) compile(ctx context.Context, file string) *result { + e.mu.Lock() + defer e.mu.Unlock() + + return e.compileLocked(ctx, file, false) +} + +func (e *executor) compileLocked(ctx context.Context, file string, explicitFile bool) *result { + r := e.results[file] + if r != nil { + return r + } + + r = &result{ + name: file, + ready: make(chan struct{}), + explicitFile: explicitFile, + } + e.results[file] = r + go func() { + defer func() { + if p := recover(); p != nil { + if r.err == nil { + // TODO: strip top frames from stack trace so that the panic is + // the top of the trace? + panicErr := PanicError{File: file, Value: p, Stack: string(debug.Stack())} + r.fail(panicErr) + } + // TODO: if r.err != nil, then this task has already + // failed and there's nothing we can really do to + // communicate this panic to parent goroutine. This + // means the panic must have happened *after* the + // failure was already recorded (or during?) + // It would be nice to do something else here, like + // send the compiler an out-of-band error? Or log? + } + }() + e.doCompile(ctx, file, r) + }() + return r +} + +// PanicError is an error value that represents a recovered panic. It includes +// the value returned by recover() as well as the stack trace. +// +// This should generally only be seen if a Resolver implementation panics. +// +// An error returned by a Compiler may wrap a PanicError, so you may need to +// use errors.As(...) to access panic details. +type PanicError struct { + // The file that was being processed when the panic occurred + File string + // The value returned by recover() + Value interface{} + // A formatted stack trace + Stack string +} + +// Error implements the error interface. It does NOT include the stack trace. +// Use a type assertion and query the Stack field directly to access that. +func (p PanicError) Error() string { + return fmt.Sprintf("panic handling %q: %v", p.File, p.Value) +} + +type errFailedToResolve struct { + err error + path string +} + +func (e errFailedToResolve) Error() string { + errMsg := e.err.Error() + if strings.Contains(errMsg, e.path) { + // underlying error already refers to path in question, so we don't need to add more context + return errMsg + } + return fmt.Sprintf("could not resolve path %q: %s", e.path, e.err.Error()) +} + +func (e errFailedToResolve) Unwrap() error { + return e.err +} + +func (e *executor) hasOverrideDescriptorProto() bool { + e.descriptorProtoCheck.Do(func() { + defer func() { + // ignore a panic here; just assume no custom descriptor.proto + _ = recover() + }() + res, err := e.c.Resolver.FindFileByPath(descriptorProtoPath) + e.descriptorProtoIsCustom = err == nil && res.Desc != standardImports[descriptorProtoPath] + }) + return e.descriptorProtoIsCustom +} + +func (e *executor) doCompile(ctx context.Context, file string, r *result) { + t := task{e: e, h: e.h.SubHandler(), r: r} + if err := e.s.Acquire(ctx, 1); err != nil { + r.fail(err) + return + } + defer t.release() + + sr, err := e.c.Resolver.FindFileByPath(file) + if err != nil { + r.fail(errFailedToResolve{err: err, path: file}) + return + } + + defer func() { + // if results included a result, don't leave it open if it can be closed + if sr.Source == nil { + return + } + if c, ok := sr.Source.(io.Closer); ok { + _ = c.Close() + } + }() + + desc, err := t.asFile(ctx, file, sr) + if err != nil { + r.fail(err) + return + } + r.complete(desc) +} + +// A compilation task. The executor has a semaphore that limits the number +// of concurrent, running tasks. +type task struct { + e *executor + + // handler for this task + h *reporter.Handler + + // If true, this task needs to acquire a semaphore permit before running. + // If false, this task needs to release its semaphore permit on completion. + released bool + + // the result that is populated by this task + r *result +} + +func (t *task) release() { + if !t.released { + t.e.s.Release(1) + t.released = true + } +} + +const descriptorProtoPath = "google/protobuf/descriptor.proto" + +func (t *task) asFile(ctx context.Context, name string, r SearchResult) (linker.File, error) { + if r.Desc != nil { + if r.Desc.Path() != name { + return nil, fmt.Errorf("search result for %q returned descriptor for %q", name, r.Desc.Path()) + } + return linker.NewFileRecursive(r.Desc) + } + + parseRes, err := t.asParseResult(name, r) + if err != nil { + return nil, err + } + if linkRes, ok := parseRes.(linker.Result); ok { + // if resolver returned a parse result that was actually a link result, + // use the link result directly (no other steps needed) + return linkRes, nil + } + + var deps []linker.File + fileDescriptorProto := parseRes.FileDescriptorProto() + var wantsDescriptorProto bool + imports := fileDescriptorProto.Dependency + + if t.e.hasOverrideDescriptorProto() { + // we only consider implicitly including descriptor.proto if it's overridden + if name != descriptorProtoPath { + var includesDescriptorProto bool + for _, dep := range fileDescriptorProto.Dependency { + if dep == descriptorProtoPath { + includesDescriptorProto = true + break + } + } + if !includesDescriptorProto { + wantsDescriptorProto = true + // make a defensive copy so we don't inadvertently mutate + // slice's backing array when adding this implicit dep + importsCopy := make([]string, len(imports)+1) + copy(importsCopy, imports) + importsCopy[len(imports)] = descriptorProtoPath + imports = importsCopy + } + } + } + + var overrideDescriptorProto linker.File + if len(imports) > 0 { + t.r.setBlockedOn(imports) + + results := make([]*result, len(fileDescriptorProto.Dependency)) + checked := map[string]struct{}{} + for i, dep := range fileDescriptorProto.Dependency { + span := findImportSpan(parseRes, dep) + if name == dep { + // doh! file imports itself + handleImportCycle(t.h, span, []string{name}, dep) + return nil, t.h.Error() + } + + res := t.e.compile(ctx, dep) + // check for dependency cycle to prevent deadlock + if err := t.e.checkForDependencyCycle(res, []string{name, dep}, span, checked); err != nil { + return nil, err + } + results[i] = res + } + deps = make([]linker.File, len(results)) + var descriptorProtoRes *result + if wantsDescriptorProto { + descriptorProtoRes = t.e.compile(ctx, descriptorProtoPath) + } + + // release our semaphore so dependencies can be processed w/out risk of deadlock + t.e.s.Release(1) + t.released = true + + // now we wait for them all to be computed + for i, res := range results { + select { + case <-res.ready: + if res.err != nil { + if rerr, ok := res.err.(errFailedToResolve); ok { + // We don't report errors to get file from resolver to handler since + // it's usually considered immediately fatal. However, if the reason + // we were resolving is due to an import, turn this into an error with + // source position that pinpoints the import statement and report it. + return nil, reporter.Error(findImportSpan(parseRes, res.name), rerr) + } + return nil, res.err + } + deps[i] = res.res + case <-ctx.Done(): + return nil, ctx.Err() + } + } + if descriptorProtoRes != nil { + select { + case <-descriptorProtoRes.ready: + // descriptor.proto wasn't explicitly imported, so we can ignore a failure + if descriptorProtoRes.err == nil { + overrideDescriptorProto = descriptorProtoRes.res + } + case <-ctx.Done(): + return nil, ctx.Err() + } + } + // all deps resolved + t.r.setBlockedOn(nil) + // reacquire semaphore so we can proceed + if err := t.e.s.Acquire(ctx, 1); err != nil { + return nil, err + } + t.released = false + } + + return t.link(parseRes, deps, overrideDescriptorProto) +} + +func (e *executor) checkForDependencyCycle(res *result, sequence []string, span ast.SourceSpan, checked map[string]struct{}) error { + if _, ok := checked[res.name]; ok { + // already checked this one + return nil + } + checked[res.name] = struct{}{} + deps := res.getBlockedOn() + for _, dep := range deps { + // is this a cycle? + for _, file := range sequence { + if file == dep { + handleImportCycle(e.h, span, sequence, dep) + return e.h.Error() + } + } + + e.mu.Lock() + depRes := e.results[dep] + e.mu.Unlock() + if depRes == nil { + continue + } + if err := e.checkForDependencyCycle(depRes, append(sequence, dep), span, checked); err != nil { + return err + } + } + return nil +} + +func handleImportCycle(h *reporter.Handler, span ast.SourceSpan, importSequence []string, dep string) { + var buf bytes.Buffer + buf.WriteString("cycle found in imports: ") + for _, imp := range importSequence { + _, _ = fmt.Fprintf(&buf, "%q -> ", imp) + } + _, _ = fmt.Fprintf(&buf, "%q", dep) + // error is saved and returned in caller + _ = h.HandleErrorWithPos(span, errors.New(buf.String())) +} + +func findImportSpan(res parser.Result, dep string) ast.SourceSpan { + root := res.AST() + if root == nil { + return ast.UnknownSpan(res.FileNode().Name()) + } + for _, decl := range root.Decls { + if imp, ok := decl.(*ast.ImportNode); ok { + if imp.Name.AsString() == dep { + return root.NodeInfo(imp.Name) + } + } + } + // this should never happen... + return ast.UnknownSpan(res.FileNode().Name()) +} + +func (t *task) link(parseRes parser.Result, deps linker.Files, overrideDescriptorProtoRes linker.File) (linker.File, error) { + file, err := linker.Link(parseRes, deps, t.e.sym, t.h) + if err != nil { + return nil, err + } + + var interpretOpts []options.InterpreterOption + if overrideDescriptorProtoRes != nil { + interpretOpts = []options.InterpreterOption{options.WithOverrideDescriptorProto(overrideDescriptorProtoRes)} + } + + optsIndex, err := options.InterpretOptions(file, t.h, interpretOpts...) + if err != nil { + return nil, err + } + // now that options are interpreted, we can do some additional checks + if err := file.ValidateOptions(t.h, t.e.sym); err != nil { + return nil, err + } + if t.r.explicitFile { + file.CheckForUnusedImports(t.h) + } + if err := t.h.Error(); err != nil { + return nil, err + } + + if needsSourceInfo(parseRes, t.e.c.SourceInfoMode) { + var srcInfoOpts []sourceinfo.GenerateOption + if t.e.c.SourceInfoMode&SourceInfoExtraComments != 0 { + srcInfoOpts = append(srcInfoOpts, sourceinfo.WithExtraComments()) + } + if t.e.c.SourceInfoMode&SourceInfoExtraOptionLocations != 0 { + srcInfoOpts = append(srcInfoOpts, sourceinfo.WithExtraOptionLocations()) + } + parseRes.FileDescriptorProto().SourceCodeInfo = sourceinfo.GenerateSourceInfo(parseRes.AST(), optsIndex, srcInfoOpts...) + } else if t.e.c.SourceInfoMode == SourceInfoNone { + // If results came from unlinked FileDescriptorProto, it could have + // source info that we should strip. + parseRes.FileDescriptorProto().SourceCodeInfo = nil + } + if len(parseRes.FileDescriptorProto().GetSourceCodeInfo().GetLocation()) > 0 { + // If we have source code info in the descriptor proto at this point, + // we have to build the index of locations. + file.PopulateSourceCodeInfo() + } + + if !t.e.c.RetainASTs { + file.RemoveAST() + } + return file, nil +} + +func needsSourceInfo(parseRes parser.Result, mode SourceInfoMode) bool { + return mode != SourceInfoNone && parseRes.AST() != nil && parseRes.FileDescriptorProto().SourceCodeInfo == nil +} + +func (t *task) asParseResult(name string, r SearchResult) (parser.Result, error) { + if r.ParseResult != nil { + if r.ParseResult.FileDescriptorProto().GetName() != name { + return nil, fmt.Errorf("search result for %q returned descriptor for %q", name, r.ParseResult.FileDescriptorProto().GetName()) + } + // If the file descriptor needs linking, it will be mutated during the + // next stage. So to make anu mutations thread-safe, we must make a + // defensive copy. + res := parser.Clone(r.ParseResult) + return res, nil + } + + if r.Proto != nil { + if r.Proto.GetName() != name { + return nil, fmt.Errorf("search result for %q returned descriptor for %q", name, r.Proto.GetName()) + } + // If the file descriptor needs linking, it will be mutated during the + // next stage. So to make any mutations thread-safe, we must make a + // defensive copy. + descProto := proto.Clone(r.Proto).(*descriptorpb.FileDescriptorProto) //nolint:errcheck + return parser.ResultWithoutAST(descProto), nil + } + + file, err := t.asAST(name, r) + if err != nil { + return nil, err + } + + return parser.ResultFromAST(file, true, t.h) +} + +func (t *task) asAST(name string, r SearchResult) (*ast.FileNode, error) { + if r.AST != nil { + if r.AST.Name() != name { + return nil, fmt.Errorf("search result for %q returned descriptor for %q", name, r.AST.Name()) + } + return r.AST, nil + } + + return parser.Parse(name, r.Source, t.h) +} diff --git a/vendor/github.com/bufbuild/protocompile/doc.go b/vendor/github.com/bufbuild/protocompile/doc.go new file mode 100644 index 0000000..40067b3 --- /dev/null +++ b/vendor/github.com/bufbuild/protocompile/doc.go @@ -0,0 +1,82 @@ +// Copyright 2020-2024 Buf Technologies, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Package protocompile provides the entry point for a high performance +// native Go protobuf compiler. "Compile" in this case just means parsing +// and validating source and generating fully-linked descriptors in the end. +// Unlike the protoc command-line tool, this package does not try to use the +// descriptors to perform code generation. +// +// The various sub-packages represent the various compile phases and contain +// models for the intermediate results. Those phases follow: +// 1. Parse into AST. +// Also see: parser.Parse +// 2. Convert AST to unlinked descriptor protos. +// Also see: parser.ResultFromAST +// 3. Link descriptor protos into "rich" descriptors. +// Also see: linker.Link +// 4. Interpret custom options. +// Also see: options.InterpretOptions +// 5. Generate source code info. +// Also see: sourceinfo.GenerateSourceInfo +// +// This package provides an easy-to-use interface that does all the relevant +// phases, based on the inputs given. If an input is provided as source, all +// phases apply. If an input is provided as a descriptor proto, only phases +// 3 to 5 apply. Nothing is necessary if provided a linked descriptor (which +// is usually only the case for select system dependencies). +// +// This package is also capable of taking advantage of multiple CPU cores, so +// a compilation involving thousands of files can be done very quickly by +// compiling things in parallel. +// +// # Resolvers +// +// A Resolver is how the compiler locates artifacts that are inputs to the +// compilation. For example, it can load protobuf source code that must be +// processed. A Resolver could also supply some already-compiled dependencies +// as fully-linked descriptors, alleviating the need to re-compile them. +// +// A Resolver can provide any of the following in response to a query for an +// input. +// - Source code: If a resolver answers a query with protobuf source, the +// compiler will parse and compile it. +// - AST: If a resolver answers a query with an AST, the parsing step can be +// skipped, and the rest of the compilation steps will be applied. +// - Descriptor proto: If a resolver answers a query with an unlinked proto, +// only the other compilation steps, including linking, need to be applied. +// - Descriptor: If a resolver answers a query with a fully-linked descriptor, +// nothing further needs to be done. The descriptor is used as-is. +// +// Compilation will use the Resolver to load the files that are to be compiled +// and also to load all dependencies (i.e. other files imported by those being +// compiled). +// +// # Compiler +// +// A Compiler accepts a list of file names and produces the list of descriptors. +// A Compiler has several fields that control how it works but only the Resolver +// field is required. A minimal Compiler, that resolves files by loading them +// from the file system based on the current working directory, can be had with +// the following simple snippet: +// +// compiler := protocompile.Compiler{ +// Resolver: &protocompile.SourceResolver{}, +// } +// +// This minimal Compiler will use default parallelism, equal to the number of +// CPU cores detected; it will not generate source code info in the resulting +// descriptors; and it will fail fast at the first sign of any error. All of +// these aspects can be customized by setting other fields. +package protocompile diff --git a/vendor/github.com/bufbuild/protocompile/go.work b/vendor/github.com/bufbuild/protocompile/go.work new file mode 100644 index 0000000..ba2d9c0 --- /dev/null +++ b/vendor/github.com/bufbuild/protocompile/go.work @@ -0,0 +1,6 @@ +go 1.21 + +use ( + . + ./internal/benchmarks +) diff --git a/vendor/github.com/bufbuild/protocompile/go.work.sum b/vendor/github.com/bufbuild/protocompile/go.work.sum new file mode 100644 index 0000000..d977cf0 --- /dev/null +++ b/vendor/github.com/bufbuild/protocompile/go.work.sum @@ -0,0 +1,235 @@ +cloud.google.com/go v0.26.0 h1:e0WKqKTd5BnrG8aKH3J3h+QvEIQtSUcf2n5UZ5ZgLtQ= +cloud.google.com/go v0.78.0/go.mod h1:QjdrLG0uq+YwhjoVOLsS1t7TW8fs36kLs4XO5R5ECHg= +cloud.google.com/go v0.79.0/go.mod h1:3bzgcEeQlzbuEAYu4mrWhKqWjmpprinYgKJLgKHnbb8= +cloud.google.com/go v0.81.0/go.mod h1:mk/AM35KwGk/Nm2YSeZbxXdrNK3KZOYHmLkOqC2V6E0= +cloud.google.com/go v0.83.0/go.mod h1:Z7MJUsANfY0pYPdw0lbnivPx4/vhy/e2FEkSkF7vAVY= +cloud.google.com/go v0.84.0/go.mod h1:RazrYuxIK6Kb7YrzzhPoLmCVzl7Sup4NrbKPg8KHSUM= +cloud.google.com/go v0.87.0/go.mod h1:TpDYlFy7vuLzZMMZ+B6iRiELaY7z/gJPaqbMx6mlWcY= +cloud.google.com/go v0.90.0/go.mod h1:kRX0mNRHe0e2rC6oNakvwQqzyDmg57xJ+SZU1eT2aDQ= +cloud.google.com/go v0.93.3/go.mod h1:8utlLll2EF5XMAV15woO4lSbWQlk8rer9aLOfLh7+YI= +cloud.google.com/go v0.94.1/go.mod h1:qAlAugsXlC+JWO+Bke5vCtc9ONxjQT3drlTTnAplMW4= +cloud.google.com/go v0.97.0/go.mod h1:GF7l59pYBVlXQIBLx3a761cZ41F9bBH3JUlihCt2Udc= +cloud.google.com/go v0.99.0/go.mod h1:w0Xx2nLzqWJPuozYQX+hFfCSI8WioryfRDzkoI/Y2ZA= +cloud.google.com/go v0.100.2/go.mod h1:4Xra9TjzAeYHrl5+oeLlzbM2k3mjVhZh4UqTZ//w99A= +cloud.google.com/go/compute v0.1.0/go.mod h1:GAesmwr110a34z04OlxYkATPBEfVhkymfTBXtfbBFow= +cloud.google.com/go/compute v1.3.0/go.mod h1:cCZiE1NHEtai4wiufUhW8I8S1JKkAnhnQJWM7YD99wM= +cloud.google.com/go/compute v1.5.0/go.mod h1:9SMHyhJlzhlkJqrPAc839t2BZFTSk6Jdj6mkzQJeu0M= +cloud.google.com/go/compute v1.6.0/go.mod h1:T29tfhtVbq1wvAPo0E3+7vhgmkOYeXjhFvz/FMzPu0s= +cloud.google.com/go/compute v1.6.1/go.mod h1:g85FgpzFvNULZ+S8AYq87axRKuf2Kh7deLqV/jJ3thU= +github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= +github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= +github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= +github.com/bufbuild/protocompile v0.2.1-0.20230123224550-da57cd758c2f/go.mod h1:tleDrpPTlLUVmgnEoN6qBliKWqJaZFJXqZdFjTd+ocU= +github.com/bufbuild/protocompile v0.13.0/go.mod h1:dr++fGGeMPWHv7jPeT06ZKukm45NJscd7rUxQVzEKRk= +github.com/census-instrumentation/opencensus-proto v0.2.1 h1:glEXhBS5PSLLv4IXzLA5yPRVX4bilULVyxxbrfOtDAk= +github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= +github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= +github.com/client9/misspell v0.3.4 h1:ta993UF76GwbvJcIo3Y68y/M3WxlpEHPWIGDkJYwzJI= +github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403 h1:cqQfy1jclcSy/FwLjemeg3SR1yaINm74aQyupQ0Bl8M= +github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI= +github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20211001041855-01bcc9b48dfe/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d h1:QyzYnTnPE15SQyUeqU6qLbWxMkwyAyu+vGksa0b7j00= +github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ= +github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0= +github.com/envoyproxy/go-control-plane v0.10.2-0.20220325020618-49ff273808a1/go.mod h1:KJwIaB5Mv44NWtYuAOFCVOjcI94vtpEz2JU/D2v6IjE= +github.com/envoyproxy/protoc-gen-validate v0.1.0 h1:EQciDnbrYxy13PgWoY8AqoxGiPrpgBZ1R8UNe3ddc+A= +github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58= +github.com/golang/mock v1.1.1 h1:G5FRp8JnTd7RQH5kemVNlMeyXQAztQ3mOWV95KxsXH8= +github.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8= +github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= +github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM= +github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE= +github.com/google/martian/v3 v3.2.1/go.mod h1:oBOf6HBosgwRXnUGWUB05QECsc6uvmMiJ3+6W4l/CUk= +github.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210601050228-01bbb1931b22/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210609004039-a478d1d731e9/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/uuid v1.1.2 h1:EVhdT+1Kseyi1/pUmXKaFxYsDNy9RQYkMWRH68J/W7Y= +github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/googleapis/gax-go/v2 v2.1.0/go.mod h1:Q3nei7sK6ybPYH7twZdmQpAd1MKb7pfu6SK+H1/DsU0= +github.com/googleapis/gax-go/v2 v2.1.1/go.mod h1:hddJymUZASv3XPyGkUpKj8pPO47Rmb0eJc8R6ouapiM= +github.com/googleapis/gax-go/v2 v2.2.0/go.mod h1:as02EH8zWkzwUoLbBaFeQ+arQaj/OthfcblKl4IGNaM= +github.com/googleapis/gax-go/v2 v2.3.0/go.mod h1:b8LNqSzNabLiUpXKkY7HAR5jr6bIT99EXz9pXxye9YM= +github.com/googleapis/gax-go/v2 v2.4.0/go.mod h1:XOTVJ59hdnfJLIP/dh8n5CGryZR2LxK9wbMD5+iXC6c= +github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= +github.com/jhump/gopoet v0.1.0 h1:gYjOPnzHd2nzB37xYQZxj4EIQNpBrBskRqQQ3q4ZgSg= +github.com/jhump/goprotoc v0.5.0 h1:Y1UgUX+txUznfqcGdDef8ZOVlyQvnV0pKWZH08RmZuo= +github.com/jhump/protoreflect v1.15.0 h1:U5T5/2LF0AZQFP9T4W5GfBjBaTruomrKobiR4E+oA/Q= +github.com/jhump/protoreflect v1.15.0/go.mod h1:qww51KYjD2hoCl/ohxw5cK2LSssFczrbO1t8Ld2TENs= +github.com/kr/pty v1.1.1 h1:VkoXIwSboBpnk99O/KFauAEILuNHv5DVFKZMBN/gUgw= +github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4 h1:gQz4mCbXsO+nc9n1hCxHcGA3Zx3Eo+UHZoInFGUIXNM= +github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= +github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= +github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= +github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E= +go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= +golang.org/x/crypto v0.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc= +golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4= +golang.org/x/crypto v0.21.0 h1:X31++rzVUdKhX5sWmSOFZxx8UW/ldWx55cbf08iNAMA= +golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs= +golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3 h1:XQyxROzUlZH+WIQwySDgnISgOivlhjIEwaQaJEJrrN0= +golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/mod v0.6.0-dev.0.20211013180041-c96bc1413d57 h1:LQmS1nU0twXLA96Kt7U9qtHJEbBk3z6Q0V4UXjZkpr4= +golang.org/x/mod v0.6.0-dev.0.20211013180041-c96bc1413d57/go.mod h1:3p9vT2HGsQu2K1YbXdKPJLVgG5VJdoTa1poYQBtP1AY= +golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc= +golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/net v0.0.0-20220325170049-de3da57026de/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/net v0.0.0-20220412020605-290c469a71a5/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/net v0.0.0-20220520000938-2e3eb7b945c2/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= +golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns= +golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be h1:vEDujvNQGv4jgYKudGeI/+DAX4Jffq6hpD55MmoEvKs= +golang.org/x/oauth2 v0.0.0-20210220000619-9bb904979d93/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210313182246-cd4f82c27b84/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210628180205-a41e5a781914/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210805134026-6f1e6394065a/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= +golang.org/x/oauth2 v0.0.0-20220309155454-6242fa91716a/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= +golang.org/x/oauth2 v0.0.0-20220411215720-9780585627b5/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= +golang.org/x/sync v0.0.0-20220513210516-0976fa681c29/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210514084401-e8d321eab015/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210603125802-9665404d3644/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210908233432-aa78b53d3365/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211210111614-af8b64212486/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220128215802-99c3d69c2c27/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220328115105-d36c6a25d886/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220502124256-b6088ccd6cba/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U= +golang.org/x/term v0.7.0/go.mod h1:P32HKFT3hSsZrRxla30E9HqToFYAQPCMs/zFMBUFqPY= +golang.org/x/term v0.13.0 h1:bb+I9cTfFazGW51MZqBVmZy7+JEJMouUHTUSKVQLBek= +golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U= +golang.org/x/term v0.18.0 h1:FcHjZXDMxI8mM3nwhX9HlKop4C0YQvCVCdwYl2wOtE8= +golang.org/x/term v0.18.0/go.mod h1:ILwASektA3OnRv7amZ1xhE/KTR+u50pbXfZ03+6Nx58= +golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE= +golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= +golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.8-0.20211029000441-d6a9af8af023 h1:0c3L82FDQ5rt1bjTBlchS8t6RQ6299/+5bWMnRLh+uI= +golang.org/x/tools v0.1.8-0.20211029000441-d6a9af8af023/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20220411194840-2f41105eb62f/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20220517211312-f3a8303e98df/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= +google.golang.org/api v0.41.0/go.mod h1:RkxM5lITDfTzmyKFPt+wGrCJbVfniCr2ool8kTBzRTU= +google.golang.org/api v0.43.0/go.mod h1:nQsDGjRXMo4lvh5hP0TKqF244gqhGcr/YSIykhUk/94= +google.golang.org/api v0.47.0/go.mod h1:Wbvgpq1HddcWVtzsVLyfLp8lDg6AA241LmgIL59tHXo= +google.golang.org/api v0.48.0/go.mod h1:71Pr1vy+TAZRPkPs/xlCf5SsU8WjuAWv1Pfjbtukyy4= +google.golang.org/api v0.50.0/go.mod h1:4bNT5pAuq5ji4SRZm+5QIkjny9JAyVD/3gaSihNefaw= +google.golang.org/api v0.51.0/go.mod h1:t4HdrdoNgyN5cbEfm7Lum0lcLDLiise1F8qDKX00sOU= +google.golang.org/api v0.54.0/go.mod h1:7C4bFFOvVDGXjfDTAsgGwDgAxRDeQ4X8NvUedIt6z3k= +google.golang.org/api v0.55.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqivdVE= +google.golang.org/api v0.56.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqivdVE= +google.golang.org/api v0.57.0/go.mod h1:dVPlbZyBo2/OjBpmvNdpn2GRm6rPy75jyU7bmhdrMgI= +google.golang.org/api v0.61.0/go.mod h1:xQRti5UdCmoCEqFxcz93fTl338AVqDgyaDRuOZ3hg9I= +google.golang.org/api v0.63.0/go.mod h1:gs4ij2ffTRXwuzzgJl/56BdwJaA194ijkfn++9tDuPo= +google.golang.org/api v0.67.0/go.mod h1:ShHKP8E60yPsKNw/w8w+VYaj9H6buA5UqDp8dhbQZ6g= +google.golang.org/api v0.70.0/go.mod h1:Bs4ZM2HGifEvXwd50TtW70ovgJffJYw2oRCOFU/SkfA= +google.golang.org/api v0.71.0/go.mod h1:4PyU6e6JogV1f9eA4voyrTY2batOLdgZ5qZ5HOCc4j8= +google.golang.org/api v0.74.0/go.mod h1:ZpfMZOVRMywNyvJFeqL9HRWBgAuRfSjJFpe9QtRRyDs= +google.golang.org/api v0.75.0/go.mod h1:pU9QmyHLnzlpar1Mjt4IbapUCy8J+6HD6GeELN69ljA= +google.golang.org/api v0.78.0/go.mod h1:1Sg78yoMLOhlQTeF+ARBoytAcH1NNyyl390YMy6rKmw= +google.golang.org/api v0.81.0/go.mod h1:FA6Mb/bZxj706H2j+j2d6mHEEaHBmbbWnkfvmorOCko= +google.golang.org/appengine v1.4.0 h1:/wp5JvzpHIxhs/dumFmF7BXTf3Z+dd4uXta4kVyO508= +google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20210222152913-aa3ee6e6a81c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210303154014-9728d6b83eeb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210310155132-4ce2db91004e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210402141018-6c239bbf2bb1/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A= +google.golang.org/genproto v0.0.0-20210513213006-bf773b8c8384/go.mod h1:P3QM42oQyzQSnHPnZ/vqoCdDmzH28fzWByN9asMeM8A= +google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= +google.golang.org/genproto v0.0.0-20210604141403-392c879c8b08/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= +google.golang.org/genproto v0.0.0-20210608205507-b6d2f5bf0d7d/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= +google.golang.org/genproto v0.0.0-20210624195500-8bfb893ecb84/go.mod h1:SzzZ/N+nwJDaO1kznhnlzqS8ocJICar6hYhVyhi++24= +google.golang.org/genproto v0.0.0-20210713002101-d411969a0d9a/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k= +google.golang.org/genproto v0.0.0-20210716133855-ce7ef5c701ea/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k= +google.golang.org/genproto v0.0.0-20210728212813-7823e685a01f/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48= +google.golang.org/genproto v0.0.0-20210805201207-89edb61ffb67/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48= +google.golang.org/genproto v0.0.0-20210813162853-db860fec028c/go.mod h1:cFeNkxwySK631ADgubI+/XFU/xp8FD5KIVV4rj8UC5w= +google.golang.org/genproto v0.0.0-20210821163610-241b8fcbd6c8/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= +google.golang.org/genproto v0.0.0-20210828152312-66f60bf46e71/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= +google.golang.org/genproto v0.0.0-20210831024726-fe130286e0e2/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= +google.golang.org/genproto v0.0.0-20210903162649-d08c68adba83/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= +google.golang.org/genproto v0.0.0-20210909211513-a8c4777a87af/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= +google.golang.org/genproto v0.0.0-20210924002016-3dee208752a0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20211118181313-81c1377c94b1/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20211206160659-862468c7d6e0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20211208223120-3a66f561d7aa/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20211221195035-429b39de9b1c/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20220126215142-9970aeb2e350/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20220207164111-0872dc986b00/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20220218161850-94dd64e39d7c/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI= +google.golang.org/genproto v0.0.0-20220222213610-43724f9ea8cf/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI= +google.golang.org/genproto v0.0.0-20220304144024-325a89244dc8/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI= +google.golang.org/genproto v0.0.0-20220310185008-1973136f34c6/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI= +google.golang.org/genproto v0.0.0-20220324131243-acbaeb5b85eb/go.mod h1:hAL49I2IFola2sVEjAn7MEwsja0xp51I0tlGAf9hz4E= +google.golang.org/genproto v0.0.0-20220407144326-9054f6ed7bac/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= +google.golang.org/genproto v0.0.0-20220413183235-5e96e2839df9/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= +google.golang.org/genproto v0.0.0-20220414192740-2d67ff6cf2b4/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= +google.golang.org/genproto v0.0.0-20220421151946-72621c1f0bd3/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= +google.golang.org/genproto v0.0.0-20220429170224-98d788798c3e/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= +google.golang.org/genproto v0.0.0-20220505152158-f39f71e6c8f3/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4= +google.golang.org/genproto v0.0.0-20220519153652-3a47de7e79bd/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4= +google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= +google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= +google.golang.org/grpc v1.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= +google.golang.org/grpc v1.37.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= +google.golang.org/grpc v1.37.1/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= +google.golang.org/grpc v1.39.0/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE= +google.golang.org/grpc v1.39.1/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE= +google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= +google.golang.org/grpc v1.40.1/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= +google.golang.org/grpc v1.44.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU= +google.golang.org/grpc v1.45.0/go.mod h1:lN7owxKUQEqMfSyQikvvk5tf/6zMPsrK+ONuO11+0rQ= +google.golang.org/grpc v1.46.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= +google.golang.org/grpc v1.46.2/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= +google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw= +google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +google.golang.org/protobuf v1.28.2-0.20220831092852-f930b1dc76e8/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= +gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc h1:/hemPrYIhOhy8zYrNj+069zDB68us2sMGsfkFJO0iZs= diff --git a/vendor/github.com/bufbuild/protocompile/internal/editions/editions.go b/vendor/github.com/bufbuild/protocompile/internal/editions/editions.go new file mode 100644 index 0000000..ee054fa --- /dev/null +++ b/vendor/github.com/bufbuild/protocompile/internal/editions/editions.go @@ -0,0 +1,420 @@ +// Copyright 2020-2024 Buf Technologies, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Package editions contains helpers related to resolving features for +// Protobuf editions. These are lower-level helpers. Higher-level helpers +// (which use this package under the hood) can be found in the exported +// protoutil package. +package editions + +import ( + "fmt" + "strings" + "sync" + + "google.golang.org/protobuf/encoding/prototext" + "google.golang.org/protobuf/proto" + "google.golang.org/protobuf/reflect/protoreflect" + "google.golang.org/protobuf/reflect/protoregistry" + "google.golang.org/protobuf/types/descriptorpb" + "google.golang.org/protobuf/types/dynamicpb" +) + +const ( + // MinSupportedEdition is the earliest edition supported by this module. + // It should be 2023 (the first edition) for the indefinite future. + MinSupportedEdition = descriptorpb.Edition_EDITION_2023 + + // MaxSupportedEdition is the most recent edition supported by this module. + MaxSupportedEdition = descriptorpb.Edition_EDITION_2023 +) + +var ( + // SupportedEditions is the exhaustive set of editions that protocompile + // can support. We don't allow it to compile future/unknown editions, to + // make sure we don't generate incorrect descriptors, in the event that + // a future edition introduces a change or new feature that requires + // new logic in the compiler. + SupportedEditions = computeSupportedEditions(MinSupportedEdition, MaxSupportedEdition) + + // FeatureSetDescriptor is the message descriptor for the compiled-in + // version (in the descriptorpb package) of the google.protobuf.FeatureSet + // message type. + FeatureSetDescriptor = (*descriptorpb.FeatureSet)(nil).ProtoReflect().Descriptor() + // FeatureSetType is the message type for the compiled-in version (in + // the descriptorpb package) of google.protobuf.FeatureSet. + FeatureSetType = (*descriptorpb.FeatureSet)(nil).ProtoReflect().Type() + + editionDefaults map[descriptorpb.Edition]*descriptorpb.FeatureSet + editionDefaultsInit sync.Once +) + +// HasFeatures is implemented by all options messages and provides a +// nil-receiver-safe way of accessing the features explicitly configured +// in those options. +type HasFeatures interface { + GetFeatures() *descriptorpb.FeatureSet +} + +var _ HasFeatures = (*descriptorpb.FileOptions)(nil) +var _ HasFeatures = (*descriptorpb.MessageOptions)(nil) +var _ HasFeatures = (*descriptorpb.FieldOptions)(nil) +var _ HasFeatures = (*descriptorpb.OneofOptions)(nil) +var _ HasFeatures = (*descriptorpb.ExtensionRangeOptions)(nil) +var _ HasFeatures = (*descriptorpb.EnumOptions)(nil) +var _ HasFeatures = (*descriptorpb.EnumValueOptions)(nil) +var _ HasFeatures = (*descriptorpb.ServiceOptions)(nil) +var _ HasFeatures = (*descriptorpb.MethodOptions)(nil) + +// ResolveFeature resolves a feature for the given descriptor. This simple +// helper examines the given element and its ancestors, searching for an +// override. If there is no overridden value, it returns a zero value. +func ResolveFeature( + element protoreflect.Descriptor, + fields ...protoreflect.FieldDescriptor, +) (protoreflect.Value, error) { + for { + var features *descriptorpb.FeatureSet + if withFeatures, ok := element.Options().(HasFeatures); ok { + // It should not really be possible for 'ok' to ever be false... + features = withFeatures.GetFeatures() + } + + // TODO: adaptFeatureSet is only looking at the first field. But if we needed to + // support an extension field inside a custom feature, we'd really need + // to check all fields. That gets particularly complicated if the traversal + // path of fields includes list and map values. Luckily, features are not + // supposed to be repeated and not supposed to themselves have extensions. + // So this should be fine, at least for now. + msgRef, err := adaptFeatureSet(features, fields[0]) + if err != nil { + return protoreflect.Value{}, err + } + // Navigate the fields to find the value + var val protoreflect.Value + for i, field := range fields { + if i > 0 { + msgRef = val.Message() + } + if !msgRef.Has(field) { + val = protoreflect.Value{} + break + } + val = msgRef.Get(field) + } + if val.IsValid() { + // All fields were set! + return val, nil + } + + parent := element.Parent() + if parent == nil { + // We've reached the end of the inheritance chain. + return protoreflect.Value{}, nil + } + element = parent + } +} + +// HasEdition should be implemented by values that implement +// [protoreflect.FileDescriptor], to provide access to the file's +// edition when its syntax is [protoreflect.Editions]. +type HasEdition interface { + // Edition returns the numeric value of a google.protobuf.Edition enum + // value that corresponds to the edition of this file. If the file does + // not use editions, it should return the enum value that corresponds + // to the syntax level, EDITION_PROTO2 or EDITION_PROTO3. + Edition() int32 +} + +// GetEdition returns the edition for a given element. It returns +// EDITION_PROTO2 or EDITION_PROTO3 if the element is in a file that +// uses proto2 or proto3 syntax, respectively. It returns EDITION_UNKNOWN +// if the syntax of the given element is not recognized or if the edition +// cannot be ascertained from the element's [protoreflect.FileDescriptor]. +func GetEdition(d protoreflect.Descriptor) descriptorpb.Edition { + switch d.ParentFile().Syntax() { + case protoreflect.Proto2: + return descriptorpb.Edition_EDITION_PROTO2 + case protoreflect.Proto3: + return descriptorpb.Edition_EDITION_PROTO3 + case protoreflect.Editions: + withEdition, ok := d.ParentFile().(HasEdition) + if !ok { + // The parent file should always be a *result, so we should + // never be able to actually get in here. If we somehow did + // have another implementation of protoreflect.FileDescriptor, + // it doesn't provide a way to get the edition, other than the + // potentially expensive step of generating a FileDescriptorProto + // and then querying for the edition from that. :/ + return descriptorpb.Edition_EDITION_UNKNOWN + } + return descriptorpb.Edition(withEdition.Edition()) + default: + return descriptorpb.Edition_EDITION_UNKNOWN + } +} + +// GetEditionDefaults returns the default feature values for the given edition. +// It returns nil if the given edition is not known. +// +// This only populates known features, those that are fields of [*descriptorpb.FeatureSet]. +// It does not populate any extension fields. +// +// The returned value must not be mutated as it references shared package state. +func GetEditionDefaults(edition descriptorpb.Edition) *descriptorpb.FeatureSet { + editionDefaultsInit.Do(func() { + editionDefaults = make(map[descriptorpb.Edition]*descriptorpb.FeatureSet, len(descriptorpb.Edition_name)) + // Compute default for all known editions in descriptorpb. + for editionInt := range descriptorpb.Edition_name { + edition := descriptorpb.Edition(editionInt) + defaults := &descriptorpb.FeatureSet{} + defaultsRef := defaults.ProtoReflect() + fields := defaultsRef.Descriptor().Fields() + // Note: we are not computing defaults for extensions. Those are not needed + // by anything in the compiler, so we can get away with just computing + // defaults for these static, non-extension fields. + for i, length := 0, fields.Len(); i < length; i++ { + field := fields.Get(i) + val, err := GetFeatureDefault(edition, FeatureSetType, field) + if err != nil { + // should we fail somehow?? + continue + } + defaultsRef.Set(field, val) + } + editionDefaults[edition] = defaults + } + }) + return editionDefaults[edition] +} + +// GetFeatureDefault computes the default value for a feature. The given container +// is the message type that contains the field. This should usually be the descriptor +// for google.protobuf.FeatureSet, but can be a different message for computing the +// default value of custom features. +// +// Note that this always re-computes the default. For known fields of FeatureSet, +// it is more efficient to query from the statically computed default messages, +// like so: +// +// editions.GetEditionDefaults(edition).ProtoReflect().Get(feature) +func GetFeatureDefault(edition descriptorpb.Edition, container protoreflect.MessageType, feature protoreflect.FieldDescriptor) (protoreflect.Value, error) { + opts, ok := feature.Options().(*descriptorpb.FieldOptions) + if !ok { + // this is most likely impossible except for contrived use cases... + return protoreflect.Value{}, fmt.Errorf("options is %T instead of *descriptorpb.FieldOptions", feature.Options()) + } + maxEdition := descriptorpb.Edition(-1) + var maxVal string + for _, def := range opts.EditionDefaults { + if def.GetEdition() <= edition && def.GetEdition() > maxEdition { + maxEdition = def.GetEdition() + maxVal = def.GetValue() + } + } + if maxEdition == -1 { + // no matching default found + return protoreflect.Value{}, fmt.Errorf("no relevant default for edition %s", edition) + } + // We use a typed nil so that it won't fall back to the global registry. Features + // should not use extensions or google.protobuf.Any, so a nil *Types is fine. + unmarshaler := prototext.UnmarshalOptions{Resolver: (*protoregistry.Types)(nil)} + // The string value is in the text format: either a field value literal or a + // message literal. (Repeated and map features aren't supported, so there's no + // array or map literal syntax to worry about.) + if feature.Kind() == protoreflect.MessageKind || feature.Kind() == protoreflect.GroupKind { + fldVal := container.Zero().NewField(feature) + err := unmarshaler.Unmarshal([]byte(maxVal), fldVal.Message().Interface()) + if err != nil { + return protoreflect.Value{}, err + } + return fldVal, nil + } + // The value is the textformat for the field. But prototext doesn't provide a way + // to unmarshal a single field value. To work around, we unmarshal into an enclosing + // message, which means we must prefix the value with the field name. + if feature.IsExtension() { + maxVal = fmt.Sprintf("[%s]: %s", feature.FullName(), maxVal) + } else { + maxVal = fmt.Sprintf("%s: %s", feature.Name(), maxVal) + } + empty := container.New() + err := unmarshaler.Unmarshal([]byte(maxVal), empty.Interface()) + if err != nil { + return protoreflect.Value{}, err + } + return empty.Get(feature), nil +} + +func adaptFeatureSet(msg *descriptorpb.FeatureSet, field protoreflect.FieldDescriptor) (protoreflect.Message, error) { + msgRef := msg.ProtoReflect() + var actualField protoreflect.FieldDescriptor + switch { + case field.IsExtension(): + // Extensions can be used directly with the feature set, even if + // field.ContainingMessage() != FeatureSetDescriptor. But only if + // the value is either not a message or is a message with the + // right descriptor, i.e. val.Descriptor() == field.Message(). + if actualField = actualDescriptor(msgRef, field); actualField == nil || actualField == field { + if msgRef.Has(field) || len(msgRef.GetUnknown()) == 0 { + return msgRef, nil + } + // The field is not present, but the message has unrecognized values. So + // let's try to parse the unrecognized bytes, just in case they contain + // this extension. + temp := &descriptorpb.FeatureSet{} + unmarshaler := proto.UnmarshalOptions{ + AllowPartial: true, + Resolver: resolverForExtension{field}, + } + if err := unmarshaler.Unmarshal(msgRef.GetUnknown(), temp); err != nil { + return nil, fmt.Errorf("failed to parse unrecognized fields of FeatureSet: %w", err) + } + return temp.ProtoReflect(), nil + } + case field.ContainingMessage() == FeatureSetDescriptor: + // Known field, not dynamically generated. Can directly use with the feature set. + return msgRef, nil + default: + actualField = FeatureSetDescriptor.Fields().ByNumber(field.Number()) + } + + // If we get here, we have a dynamic field descriptor or an extension + // descriptor whose message type does not match the descriptor of the + // stored value. We need to copy its value into a dynamic message, + // which requires marshalling/unmarshalling. + // We only need to copy over the unrecognized bytes (if any) + // and the same field (if present). + data := msgRef.GetUnknown() + if actualField != nil && msgRef.Has(actualField) { + subset := &descriptorpb.FeatureSet{} + subset.ProtoReflect().Set(actualField, msgRef.Get(actualField)) + var err error + data, err = proto.MarshalOptions{AllowPartial: true}.MarshalAppend(data, subset) + if err != nil { + return nil, fmt.Errorf("failed to marshal FeatureSet field %s to bytes: %w", field.Name(), err) + } + } + if len(data) == 0 { + // No relevant data to copy over, so we can just return + // a zero value message + return dynamicpb.NewMessageType(field.ContainingMessage()).Zero(), nil + } + + other := dynamicpb.NewMessage(field.ContainingMessage()) + // We don't need to use a resolver for this step because we know that + // field is not an extension. And features are not allowed to themselves + // have extensions. + if err := (proto.UnmarshalOptions{AllowPartial: true}).Unmarshal(data, other); err != nil { + return nil, fmt.Errorf("failed to marshal FeatureSet field %s to bytes: %w", field.Name(), err) + } + return other, nil +} + +type resolverForExtension struct { + ext protoreflect.ExtensionDescriptor +} + +func (r resolverForExtension) FindMessageByName(_ protoreflect.FullName) (protoreflect.MessageType, error) { + return nil, protoregistry.NotFound +} + +func (r resolverForExtension) FindMessageByURL(_ string) (protoreflect.MessageType, error) { + return nil, protoregistry.NotFound +} + +func (r resolverForExtension) FindExtensionByName(field protoreflect.FullName) (protoreflect.ExtensionType, error) { + if field == r.ext.FullName() { + return asExtensionType(r.ext), nil + } + return nil, protoregistry.NotFound +} + +func (r resolverForExtension) FindExtensionByNumber(message protoreflect.FullName, field protoreflect.FieldNumber) (protoreflect.ExtensionType, error) { + if message == r.ext.ContainingMessage().FullName() && field == r.ext.Number() { + return asExtensionType(r.ext), nil + } + return nil, protoregistry.NotFound +} + +func asExtensionType(ext protoreflect.ExtensionDescriptor) protoreflect.ExtensionType { + if xtd, ok := ext.(protoreflect.ExtensionTypeDescriptor); ok { + return xtd.Type() + } + return dynamicpb.NewExtensionType(ext) +} + +func computeSupportedEditions(minEdition, maxEdition descriptorpb.Edition) map[string]descriptorpb.Edition { + supportedEditions := map[string]descriptorpb.Edition{} + for editionNum := range descriptorpb.Edition_name { + edition := descriptorpb.Edition(editionNum) + if edition >= minEdition && edition <= maxEdition { + name := strings.TrimPrefix(edition.String(), "EDITION_") + supportedEditions[name] = edition + } + } + return supportedEditions +} + +// actualDescriptor returns the actual field descriptor referenced by msg that +// corresponds to the given ext (i.e. same number). It returns nil if msg has +// no reference, if the actual descriptor is the same as ext, or if ext is +// otherwise safe to use as is. +func actualDescriptor(msg protoreflect.Message, ext protoreflect.ExtensionDescriptor) protoreflect.FieldDescriptor { + if !msg.Has(ext) || ext.Message() == nil { + // nothing to match; safe as is + return nil + } + val := msg.Get(ext) + switch { + case ext.IsMap(): // should not actually be possible + expectedDescriptor := ext.MapValue().Message() + if expectedDescriptor == nil { + return nil // nothing to match + } + // We know msg.Has(field) is true, from above, so there's at least one entry. + var matches bool + val.Map().Range(func(_ protoreflect.MapKey, val protoreflect.Value) bool { + matches = val.Message().Descriptor() == expectedDescriptor + return false + }) + if matches { + return nil + } + case ext.IsList(): + // We know msg.Has(field) is true, from above, so there's at least one entry. + if val.List().Get(0).Message().Descriptor() == ext.Message() { + return nil + } + case !ext.IsMap(): + if val.Message().Descriptor() == ext.Message() { + return nil + } + } + // The underlying message descriptors do not match. So we need to return + // the actual field descriptor. Sadly, protoreflect.Message provides no way + // to query the field descriptor in a message by number. For non-extensions, + // one can query the associated message descriptor. But for extensions, we + // have to do the slow thing, and range through all fields looking for it. + var actualField protoreflect.FieldDescriptor + msg.Range(func(fd protoreflect.FieldDescriptor, _ protoreflect.Value) bool { + if fd.Number() == ext.Number() { + actualField = fd + return false + } + return true + }) + return actualField +} diff --git a/vendor/github.com/bufbuild/protocompile/internal/featuresext/cpp_features.protoset b/vendor/github.com/bufbuild/protocompile/internal/featuresext/cpp_features.protoset Binary files differnew file mode 100644 index 0000000..106ad8e --- /dev/null +++ b/vendor/github.com/bufbuild/protocompile/internal/featuresext/cpp_features.protoset diff --git a/vendor/github.com/bufbuild/protocompile/internal/featuresext/featuresext.go b/vendor/github.com/bufbuild/protocompile/internal/featuresext/featuresext.go new file mode 100644 index 0000000..892524e --- /dev/null +++ b/vendor/github.com/bufbuild/protocompile/internal/featuresext/featuresext.go @@ -0,0 +1,84 @@ +// Copyright 2020-2024 Buf Technologies, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Package featuresext provides file descriptors for the +// "google/protobuf/cpp_features.proto" and "google/protobuf/java_features.proto" +// standard import files. Unlike the other standard/well-known +// imports, these files have no standard Go package in their +// runtime with generated code. So in order to make them available +// as "standard imports" to compiler users, we must embed these +// descriptors into a Go package. +package featuresext + +import ( + _ "embed" + "fmt" + "sync" + + "google.golang.org/protobuf/proto" + "google.golang.org/protobuf/reflect/protodesc" + "google.golang.org/protobuf/reflect/protoreflect" + "google.golang.org/protobuf/reflect/protoregistry" + "google.golang.org/protobuf/types/descriptorpb" +) + +var ( + //go:embed cpp_features.protoset + cppFeatures []byte + + //go:embed java_features.protoset + javaFeatures []byte + + initOnce sync.Once + initCppFeatures protoreflect.FileDescriptor + initCppErr error + initJavaFeatures protoreflect.FileDescriptor + initJavaErr error +) + +func initDescriptors() { + initOnce.Do(func() { + initCppFeatures, initCppErr = buildDescriptor("google/protobuf/cpp_features.proto", cppFeatures) + initJavaFeatures, initJavaErr = buildDescriptor("google/protobuf/java_features.proto", javaFeatures) + }) +} + +func CppFeaturesDescriptor() (protoreflect.FileDescriptor, error) { + initDescriptors() + return initCppFeatures, initCppErr +} + +func JavaFeaturesDescriptor() (protoreflect.FileDescriptor, error) { + initDescriptors() + return initJavaFeatures, initJavaErr +} + +func buildDescriptor(name string, data []byte) (protoreflect.FileDescriptor, error) { + var files descriptorpb.FileDescriptorSet + err := proto.Unmarshal(data, &files) + if err != nil { + return nil, fmt.Errorf("failed to load descriptor for %q: %w", name, err) + } + if len(files.File) != 1 { + return nil, fmt.Errorf("failed to load descriptor for %q: expected embedded descriptor set to contain exactly one file but it instead has %d", name, len(files.File)) + } + if files.File[0].GetName() != name { + return nil, fmt.Errorf("failed to load descriptor for %q: embedded descriptor contains wrong file %q", name, files.File[0].GetName()) + } + descriptor, err := protodesc.NewFile(files.File[0], protoregistry.GlobalFiles) + if err != nil { + return nil, fmt.Errorf("failed to load descriptor for %q: %w", name, err) + } + return descriptor, nil +} diff --git a/vendor/github.com/bufbuild/protocompile/internal/featuresext/java_features.protoset b/vendor/github.com/bufbuild/protocompile/internal/featuresext/java_features.protoset Binary files differnew file mode 100644 index 0000000..60de3eb --- /dev/null +++ b/vendor/github.com/bufbuild/protocompile/internal/featuresext/java_features.protoset diff --git a/vendor/github.com/bufbuild/protocompile/internal/message_context.go b/vendor/github.com/bufbuild/protocompile/internal/message_context.go new file mode 100644 index 0000000..52acbdf --- /dev/null +++ b/vendor/github.com/bufbuild/protocompile/internal/message_context.go @@ -0,0 +1,98 @@ +// Copyright 2020-2024 Buf Technologies, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package internal + +import ( + "bytes" + "fmt" + + "google.golang.org/protobuf/types/descriptorpb" + + "github.com/bufbuild/protocompile/ast" +) + +// ParsedFile wraps an optional AST and required FileDescriptorProto. +// This is used so types like parser.Result can be passed to this internal package avoiding circular imports. +// Additionally, it makes it less likely that users might specify one or the other. +type ParsedFile interface { + // AST returns the parsed abstract syntax tree. This returns nil if the + // Result was created without an AST. + AST() *ast.FileNode + // FileDescriptorProto returns the file descriptor proto. + FileDescriptorProto() *descriptorpb.FileDescriptorProto +} + +// MessageContext provides information about the location in a descriptor +// hierarchy, for adding context to warnings and error messages. +type MessageContext struct { + // The relevant file + File ParsedFile + + // The type and fully-qualified name of the element within the file. + ElementType string + ElementName string + + // If the element being processed is an option (or *in* an option) + // on the named element above, this will be non-nil. + Option *descriptorpb.UninterpretedOption + // If the element being processed is inside a message literal in an + // option value, this will be non-empty and represent a traversal + // to the element in question. + OptAggPath string +} + +func (c *MessageContext) String() string { + var ctx bytes.Buffer + if c.ElementType != "file" { + _, _ = fmt.Fprintf(&ctx, "%s %s: ", c.ElementType, c.ElementName) + } + if c.Option != nil && c.Option.Name != nil { + ctx.WriteString("option ") + writeOptionName(&ctx, c.Option.Name) + if c.File.AST() == nil { + // if we have no source position info, try to provide as much context + // as possible (if nodes != nil, we don't need this because any errors + // will actually have file and line numbers) + if c.OptAggPath != "" { + _, _ = fmt.Fprintf(&ctx, " at %s", c.OptAggPath) + } + } + ctx.WriteString(": ") + } + return ctx.String() +} + +func writeOptionName(buf *bytes.Buffer, parts []*descriptorpb.UninterpretedOption_NamePart) { + first := true + for _, p := range parts { + if first { + first = false + } else { + buf.WriteByte('.') + } + nm := p.GetNamePart() + if nm[0] == '.' { + // skip leading dot + nm = nm[1:] + } + if p.GetIsExtension() { + buf.WriteByte('(') + buf.WriteString(nm) + buf.WriteByte(')') + } else { + buf.WriteString(nm) + } + } +} diff --git a/vendor/github.com/bufbuild/protocompile/internal/messageset/messageset.go b/vendor/github.com/bufbuild/protocompile/internal/messageset/messageset.go new file mode 100644 index 0000000..850a0c6 --- /dev/null +++ b/vendor/github.com/bufbuild/protocompile/internal/messageset/messageset.go @@ -0,0 +1,62 @@ +// Copyright 2020-2024 Buf Technologies, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package messageset + +import ( + "math" + "sync" + + "google.golang.org/protobuf/proto" + "google.golang.org/protobuf/reflect/protodesc" + "google.golang.org/protobuf/types/descriptorpb" +) + +var ( + messageSetSupport bool + messageSetSupportInit sync.Once +) + +// CanSupportMessageSets returns true if the protobuf-go runtime supports +// serializing messages with the message set wire format. +func CanSupportMessageSets() bool { + messageSetSupportInit.Do(func() { + // We check using the protodesc package, instead of just relying + // on protolegacy build tag, in case someone links in a fork of + // the protobuf-go runtime that supports legacy proto1 features + // or in case the protobuf-go runtime adds another mechanism to + // enable or disable it (such as environment variable). + _, err := protodesc.NewFile(&descriptorpb.FileDescriptorProto{ + Name: proto.String("test.proto"), + MessageType: []*descriptorpb.DescriptorProto{ + { + Name: proto.String("MessageSet"), + Options: &descriptorpb.MessageOptions{ + MessageSetWireFormat: proto.Bool(true), + }, + ExtensionRange: []*descriptorpb.DescriptorProto_ExtensionRange{ + { + Start: proto.Int32(1), + End: proto.Int32(math.MaxInt32), + }, + }, + }, + }, + }, nil) + // When message sets are not supported, the above returns an error: + // message "MessageSet" is a MessageSet, which is a legacy proto1 feature that is no longer supported + messageSetSupport = err == nil + }) + return messageSetSupport +} diff --git a/vendor/github.com/bufbuild/protocompile/internal/norace.go b/vendor/github.com/bufbuild/protocompile/internal/norace.go new file mode 100644 index 0000000..2acf4e4 --- /dev/null +++ b/vendor/github.com/bufbuild/protocompile/internal/norace.go @@ -0,0 +1,19 @@ +// Copyright 2020-2024 Buf Technologies, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//go:build !race + +package internal + +const IsRace = false diff --git a/vendor/github.com/bufbuild/protocompile/internal/options.go b/vendor/github.com/bufbuild/protocompile/internal/options.go new file mode 100644 index 0000000..4eaa0f6 --- /dev/null +++ b/vendor/github.com/bufbuild/protocompile/internal/options.go @@ -0,0 +1,71 @@ +// Copyright 2020-2024 Buf Technologies, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package internal + +import ( + "google.golang.org/protobuf/types/descriptorpb" + + "github.com/bufbuild/protocompile/ast" +) + +type hasOptionNode interface { + OptionNode(part *descriptorpb.UninterpretedOption) ast.OptionDeclNode + FileNode() ast.FileDeclNode // needed in order to query for NodeInfo +} + +type errorHandler func(span ast.SourceSpan, format string, args ...interface{}) error + +func FindFirstOption(res hasOptionNode, handler errorHandler, scope string, opts []*descriptorpb.UninterpretedOption, name string) (int, error) { + return findOption(res, handler, scope, opts, name, false, true) +} + +func FindOption(res hasOptionNode, handler errorHandler, scope string, opts []*descriptorpb.UninterpretedOption, name string) (int, error) { + return findOption(res, handler, scope, opts, name, true, false) +} + +func findOption(res hasOptionNode, handler errorHandler, scope string, opts []*descriptorpb.UninterpretedOption, name string, exact, first bool) (int, error) { + found := -1 + for i, opt := range opts { + if exact && len(opt.Name) != 1 { + continue + } + if opt.Name[0].GetIsExtension() || opt.Name[0].GetNamePart() != name { + continue + } + if first { + return i, nil + } + if found >= 0 { + optNode := res.OptionNode(opt) + fn := res.FileNode() + node := optNode.GetName() + nodeInfo := fn.NodeInfo(node) + return -1, handler(nodeInfo, "%s: option %s cannot be defined more than once", scope, name) + } + found = i + } + return found, nil +} + +func RemoveOption(uo []*descriptorpb.UninterpretedOption, indexToRemove int) []*descriptorpb.UninterpretedOption { + switch { + case indexToRemove == 0: + return uo[1:] + case indexToRemove == len(uo)-1: + return uo[:len(uo)-1] + default: + return append(uo[:indexToRemove], uo[indexToRemove+1:]...) + } +} diff --git a/vendor/github.com/bufbuild/protocompile/internal/race.go b/vendor/github.com/bufbuild/protocompile/internal/race.go new file mode 100644 index 0000000..e70e414 --- /dev/null +++ b/vendor/github.com/bufbuild/protocompile/internal/race.go @@ -0,0 +1,19 @@ +// Copyright 2020-2024 Buf Technologies, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//go:build race + +package internal + +const IsRace = true diff --git a/vendor/github.com/bufbuild/protocompile/internal/tags.go b/vendor/github.com/bufbuild/protocompile/internal/tags.go new file mode 100644 index 0000000..179728f --- /dev/null +++ b/vendor/github.com/bufbuild/protocompile/internal/tags.go @@ -0,0 +1,336 @@ +// Copyright 2020-2024 Buf Technologies, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package internal + +import "math" + +const ( + // MaxNormalTag is the maximum allowed tag number for a field in a normal message. + MaxNormalTag = 536870911 // 2^29 - 1 + + // MaxMessageSetTag is the maximum allowed tag number of a field in a message that + // uses the message set wire format. + MaxMessageSetTag = math.MaxInt32 - 1 + + // MaxTag is the maximum allowed tag number. (It is the same as MaxMessageSetTag + // since that is the absolute highest allowed.) + MaxTag = MaxMessageSetTag + + // SpecialReservedStart is the first tag in a range that is reserved and not + // allowed for use in message definitions. + SpecialReservedStart = 19000 + // SpecialReservedEnd is the last tag in a range that is reserved and not + // allowed for use in message definitions. + SpecialReservedEnd = 19999 + + // NB: It would be nice to use constants from generated code instead of + // hard-coding these here. But code-gen does not emit these as constants + // anywhere. The only places they appear in generated code are struct tags + // on fields of the generated descriptor protos. + + // FilePackageTag is the tag number of the package element in a file + // descriptor proto. + FilePackageTag = 2 + // FileDependencyTag is the tag number of the dependencies element in a + // file descriptor proto. + FileDependencyTag = 3 + // FileMessagesTag is the tag number of the messages element in a file + // descriptor proto. + FileMessagesTag = 4 + // FileEnumsTag is the tag number of the enums element in a file descriptor + // proto. + FileEnumsTag = 5 + // FileServicesTag is the tag number of the services element in a file + // descriptor proto. + FileServicesTag = 6 + // FileExtensionsTag is the tag number of the extensions element in a file + // descriptor proto. + FileExtensionsTag = 7 + // FileOptionsTag is the tag number of the options element in a file + // descriptor proto. + FileOptionsTag = 8 + // FileOptionsJavaStringCheckUTF8Tag is the tag number of the java_string_check_utf8 + // field in the FileOptions proto. + FileOptionsJavaStringCheckUTF8Tag = 27 + // FileOptionsFeaturesTag is the tag number of the features field in the + // FileOptions proto. + FileOptionsFeaturesTag = 50 + // FileSourceCodeInfoTag is the tag number of the source code info element + // in a file descriptor proto. + FileSourceCodeInfoTag = 9 + // FilePublicDependencyTag is the tag number of the public dependency element + // in a file descriptor proto. + FilePublicDependencyTag = 10 + // FileWeakDependencyTag is the tag number of the weak dependency element + // in a file descriptor proto. + FileWeakDependencyTag = 11 + // FileSyntaxTag is the tag number of the syntax element in a file + // descriptor proto. + FileSyntaxTag = 12 + // FileEditionTag is the tag number of the edition element in a file + // descriptor proto. + FileEditionTag = 14 + // MessageNameTag is the tag number of the name element in a message + // descriptor proto. + MessageNameTag = 1 + // MessageFieldsTag is the tag number of the fields element in a message + // descriptor proto. + MessageFieldsTag = 2 + // MessageNestedMessagesTag is the tag number of the nested messages + // element in a message descriptor proto. + MessageNestedMessagesTag = 3 + // MessageEnumsTag is the tag number of the enums element in a message + // descriptor proto. + MessageEnumsTag = 4 + // MessageExtensionRangesTag is the tag number of the extension ranges + // element in a message descriptor proto. + MessageExtensionRangesTag = 5 + // MessageExtensionsTag is the tag number of the extensions element in a + // message descriptor proto. + MessageExtensionsTag = 6 + // MessageOptionsTag is the tag number of the options element in a message + // descriptor proto. + MessageOptionsTag = 7 + // MessageOptionsFeaturesTag is the tag number of the features field in the + // MessageOptions proto. + MessageOptionsFeaturesTag = 12 + // MessageOneofsTag is the tag number of the one-ofs element in a message + // descriptor proto. + MessageOneofsTag = 8 + // MessageReservedRangesTag is the tag number of the reserved ranges element + // in a message descriptor proto. + MessageReservedRangesTag = 9 + // MessageReservedNamesTag is the tag number of the reserved names element + // in a message descriptor proto. + MessageReservedNamesTag = 10 + // ExtensionRangeStartTag is the tag number of the start index in an + // extension range proto. + ExtensionRangeStartTag = 1 + // ExtensionRangeEndTag is the tag number of the end index in an + // extension range proto. + ExtensionRangeEndTag = 2 + // ExtensionRangeOptionsTag is the tag number of the options element in an + // extension range proto. + ExtensionRangeOptionsTag = 3 + // ExtensionRangeOptionsDeclarationTag is the tag number of the declaration + // field in the ExtensionRangeOptions proto. + ExtensionRangeOptionsDeclarationTag = 2 + // ExtensionRangeOptionsVerificationTag is the tag number of the verification + // field in the ExtensionRangeOptions proto. + ExtensionRangeOptionsVerificationTag = 3 + // ExtensionRangeOptionsDeclarationNumberTag is the tag number of the number + // field in the ExtensionRangeOptions.Declaration proto. + ExtensionRangeOptionsDeclarationNumberTag = 1 + // ExtensionRangeOptionsDeclarationFullNameTag is the tag number of the full_name + // field in the ExtensionRangeOptions.Declaration proto. + ExtensionRangeOptionsDeclarationFullNameTag = 2 + // ExtensionRangeOptionsDeclarationTypeTag is the tag number of the type + // field in the ExtensionRangeOptions.Declaration proto. + ExtensionRangeOptionsDeclarationTypeTag = 3 + // ExtensionRangeOptionsDeclarationReservedTag is the tag number of the reserved + // field in the ExtensionRangeOptions.Declaration proto. + ExtensionRangeOptionsDeclarationReservedTag = 5 + // ExtensionRangeOptionsDeclarationRepeatedTag is the tag number of the repeated + // field in the ExtensionRangeOptions.Declaration proto. + ExtensionRangeOptionsDeclarationRepeatedTag = 6 + // ExtensionRangeOptionsFeaturesTag is the tag number of the features field in the + // ExtensionRangeOptions proto. + ExtensionRangeOptionsFeaturesTag = 50 + // ReservedRangeStartTag is the tag number of the start index in a reserved + // range proto. This field number is the same for both "flavors" of reserved + // ranges: DescriptorProto.ReservedRange and EnumDescriptorProto.EnumReservedRange. + ReservedRangeStartTag = 1 + // ReservedRangeEndTag is the tag number of the end index in a reserved + // range proto. This field number is the same for both "flavors" of reserved + // ranges: DescriptorProto.ReservedRange and EnumDescriptorProto.EnumReservedRange. + ReservedRangeEndTag = 2 + // FieldNameTag is the tag number of the name element in a field descriptor + // proto. + FieldNameTag = 1 + // FieldExtendeeTag is the tag number of the extendee element in a field + // descriptor proto. + FieldExtendeeTag = 2 + // FieldNumberTag is the tag number of the number element in a field + // descriptor proto. + FieldNumberTag = 3 + // FieldLabelTag is the tag number of the label element in a field + // descriptor proto. + FieldLabelTag = 4 + // FieldTypeTag is the tag number of the type element in a field descriptor + // proto. + FieldTypeTag = 5 + // FieldTypeNameTag is the tag number of the type name element in a field + // descriptor proto. + FieldTypeNameTag = 6 + // FieldDefaultTag is the tag number of the default value element in a + // field descriptor proto. + FieldDefaultTag = 7 + // FieldOptionsTag is the tag number of the options element in a field + // descriptor proto. + FieldOptionsTag = 8 + // FieldOptionsCTypeTag is the number of the ctype field in the + // FieldOptions proto. + FieldOptionsCTypeTag = 1 + // FieldOptionsPackedTag is the number of the packed field in the + // FieldOptions proto. + FieldOptionsPackedTag = 2 + // FieldOptionsLazyTag is the number of the lazy field in the + // FieldOptions proto. + FieldOptionsLazyTag = 5 + // FieldOptionsJSTypeTag is the number of the jstype field in the + // FieldOptions proto. + FieldOptionsJSTypeTag = 6 + // FieldOptionsUnverifiedLazyTag is the number of the unverified_lazy + // field in the FieldOptions proto. + FieldOptionsUnverifiedLazyTag = 15 + // FieldOptionsFeaturesTag is the tag number of the features field in the + // FieldOptions proto. + FieldOptionsFeaturesTag = 21 + // FieldOneofIndexTag is the tag number of the oneof index element in a + // field descriptor proto. + FieldOneofIndexTag = 9 + // FieldJSONNameTag is the tag number of the JSON name element in a field + // descriptor proto. + FieldJSONNameTag = 10 + // FieldProto3OptionalTag is the tag number of the proto3_optional element + // in a descriptor proto. + FieldProto3OptionalTag = 17 + // OneofNameTag is the tag number of the name element in a one-of + // descriptor proto. + OneofNameTag = 1 + // OneofOptionsTag is the tag number of the options element in a one-of + // descriptor proto. + OneofOptionsTag = 2 + // OneofOptionsFeaturesTag is the tag number of the features field in the + // OneofOptions proto. + OneofOptionsFeaturesTag = 1 + // EnumNameTag is the tag number of the name element in an enum descriptor + // proto. + EnumNameTag = 1 + // EnumValuesTag is the tag number of the values element in an enum + // descriptor proto. + EnumValuesTag = 2 + // EnumOptionsTag is the tag number of the options element in an enum + // descriptor proto. + EnumOptionsTag = 3 + // EnumOptionsFeaturesTag is the tag number of the features field in the + // EnumOptions proto. + EnumOptionsFeaturesTag = 7 + // EnumReservedRangesTag is the tag number of the reserved ranges element in + // an enum descriptor proto. + EnumReservedRangesTag = 4 + // EnumReservedNamesTag is the tag number of the reserved names element in + // an enum descriptor proto. + EnumReservedNamesTag = 5 + // EnumValNameTag is the tag number of the name element in an enum value + // descriptor proto. + EnumValNameTag = 1 + // EnumValNumberTag is the tag number of the number element in an enum + // value descriptor proto. + EnumValNumberTag = 2 + // EnumValOptionsTag is the tag number of the options element in an enum + // value descriptor proto. + EnumValOptionsTag = 3 + // EnumValOptionsFeaturesTag is the tag number of the features field in the + // EnumValueOptions proto. + EnumValOptionsFeaturesTag = 2 + // ServiceNameTag is the tag number of the name element in a service + // descriptor proto. + ServiceNameTag = 1 + // ServiceMethodsTag is the tag number of the methods element in a service + // descriptor proto. + ServiceMethodsTag = 2 + // ServiceOptionsTag is the tag number of the options element in a service + // descriptor proto. + ServiceOptionsTag = 3 + // ServiceOptionsFeaturesTag is the tag number of the features field in the + // ServiceOptions proto. + ServiceOptionsFeaturesTag = 34 + // MethodNameTag is the tag number of the name element in a method + // descriptor proto. + MethodNameTag = 1 + // MethodInputTag is the tag number of the input type element in a method + // descriptor proto. + MethodInputTag = 2 + // MethodOutputTag is the tag number of the output type element in a method + // descriptor proto. + MethodOutputTag = 3 + // MethodOptionsTag is the tag number of the options element in a method + // descriptor proto. + MethodOptionsTag = 4 + // MethodOptionsFeaturesTag is the tag number of the features field in the + // MethodOptions proto. + MethodOptionsFeaturesTag = 35 + // MethodInputStreamTag is the tag number of the input stream flag in a + // method descriptor proto. + MethodInputStreamTag = 5 + // MethodOutputStreamTag is the tag number of the output stream flag in a + // method descriptor proto. + MethodOutputStreamTag = 6 + + // UninterpretedOptionsTag is the tag number of the uninterpreted options + // element. All *Options messages use the same tag for the field that stores + // uninterpreted options. + UninterpretedOptionsTag = 999 + + // UninterpretedNameTag is the tag number of the name element in an + // uninterpreted options proto. + UninterpretedNameTag = 2 + // UninterpretedIdentTag is the tag number of the identifier value in an + // uninterpreted options proto. + UninterpretedIdentTag = 3 + // UninterpretedPosIntTag is the tag number of the positive int value in an + // uninterpreted options proto. + UninterpretedPosIntTag = 4 + // UninterpretedNegIntTag is the tag number of the negative int value in an + // uninterpreted options proto. + UninterpretedNegIntTag = 5 + // UninterpretedDoubleTag is the tag number of the double value in an + // uninterpreted options proto. + UninterpretedDoubleTag = 6 + // UninterpretedStringTag is the tag number of the string value in an + // uninterpreted options proto. + UninterpretedStringTag = 7 + // UninterpretedAggregateTag is the tag number of the aggregate value in an + // uninterpreted options proto. + UninterpretedAggregateTag = 8 + // UninterpretedNameNameTag is the tag number of the name element in an + // uninterpreted option name proto. + UninterpretedNameNameTag = 1 + + // AnyTypeURLTag is the tag number of the type_url field of the Any proto. + AnyTypeURLTag = 1 + // AnyValueTag is the tag number of the value field of the Any proto. + AnyValueTag = 2 + + // FeatureSetFieldPresenceTag is the tag number of the field_presence field + // in the FeatureSet proto. + FeatureSetFieldPresenceTag = 1 + // FeatureSetEnumTypeTag is the tag number of the enum_type field in the + // FeatureSet proto. + FeatureSetEnumTypeTag = 2 + // FeatureSetRepeatedFieldEncodingTag is the tag number of the repeated_field_encoding + // field in the FeatureSet proto. + FeatureSetRepeatedFieldEncodingTag = 3 + // FeatureSetUTF8ValidationTag is the tag number of the utf8_validation field + // in the FeatureSet proto. + FeatureSetUTF8ValidationTag = 4 + // FeatureSetMessageEncodingTag is the tag number of the message_encoding + // field in the FeatureSet proto. + FeatureSetMessageEncodingTag = 5 + // FeatureSetJSONFormatTag is the tag number of the json_format field in + // the FeatureSet proto. + FeatureSetJSONFormatTag = 6 +) diff --git a/vendor/github.com/bufbuild/protocompile/internal/types.go b/vendor/github.com/bufbuild/protocompile/internal/types.go new file mode 100644 index 0000000..04090a8 --- /dev/null +++ b/vendor/github.com/bufbuild/protocompile/internal/types.go @@ -0,0 +1,35 @@ +// Copyright 2020-2024 Buf Technologies, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package internal + +import "google.golang.org/protobuf/types/descriptorpb" + +var FieldTypes = map[string]descriptorpb.FieldDescriptorProto_Type{ + "double": descriptorpb.FieldDescriptorProto_TYPE_DOUBLE, + "float": descriptorpb.FieldDescriptorProto_TYPE_FLOAT, + "int32": descriptorpb.FieldDescriptorProto_TYPE_INT32, + "int64": descriptorpb.FieldDescriptorProto_TYPE_INT64, + "uint32": descriptorpb.FieldDescriptorProto_TYPE_UINT32, + "uint64": descriptorpb.FieldDescriptorProto_TYPE_UINT64, + "sint32": descriptorpb.FieldDescriptorProto_TYPE_SINT32, + "sint64": descriptorpb.FieldDescriptorProto_TYPE_SINT64, + "fixed32": descriptorpb.FieldDescriptorProto_TYPE_FIXED32, + "fixed64": descriptorpb.FieldDescriptorProto_TYPE_FIXED64, + "sfixed32": descriptorpb.FieldDescriptorProto_TYPE_SFIXED32, + "sfixed64": descriptorpb.FieldDescriptorProto_TYPE_SFIXED64, + "bool": descriptorpb.FieldDescriptorProto_TYPE_BOOL, + "string": descriptorpb.FieldDescriptorProto_TYPE_STRING, + "bytes": descriptorpb.FieldDescriptorProto_TYPE_BYTES, +} diff --git a/vendor/github.com/bufbuild/protocompile/internal/util.go b/vendor/github.com/bufbuild/protocompile/internal/util.go new file mode 100644 index 0000000..569cb3f --- /dev/null +++ b/vendor/github.com/bufbuild/protocompile/internal/util.go @@ -0,0 +1,244 @@ +// Copyright 2020-2024 Buf Technologies, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package internal + +import ( + "bytes" + "unicode" + "unicode/utf8" + + "google.golang.org/protobuf/reflect/protoreflect" +) + +// JSONName returns the default JSON name for a field with the given name. +// This mirrors the algorithm in protoc: +// +// https://github.com/protocolbuffers/protobuf/blob/v21.3/src/google/protobuf/descriptor.cc#L95 +func JSONName(name string) string { + var js []rune + nextUpper := false + for _, r := range name { + if r == '_' { + nextUpper = true + continue + } + if nextUpper { + nextUpper = false + js = append(js, unicode.ToUpper(r)) + } else { + js = append(js, r) + } + } + return string(js) +} + +// InitCap returns the given field name, but with the first letter capitalized. +func InitCap(name string) string { + r, sz := utf8.DecodeRuneInString(name) + return string(unicode.ToUpper(r)) + name[sz:] +} + +// CreatePrefixList returns a list of package prefixes to search when resolving +// a symbol name. If the given package is blank, it returns only the empty +// string. If the given package contains only one token, e.g. "foo", it returns +// that token and the empty string, e.g. ["foo", ""]. Otherwise, it returns +// successively shorter prefixes of the package and then the empty string. For +// example, for a package named "foo.bar.baz" it will return the following list: +// +// ["foo.bar.baz", "foo.bar", "foo", ""] +func CreatePrefixList(pkg string) []string { + if pkg == "" { + return []string{""} + } + + numDots := 0 + // one pass to pre-allocate the returned slice + for i := 0; i < len(pkg); i++ { + if pkg[i] == '.' { + numDots++ + } + } + if numDots == 0 { + return []string{pkg, ""} + } + + prefixes := make([]string, numDots+2) + // second pass to fill in returned slice + for i := 0; i < len(pkg); i++ { + if pkg[i] == '.' { + prefixes[numDots] = pkg[:i] + numDots-- + } + } + prefixes[0] = pkg + + return prefixes +} + +func WriteEscapedBytes(buf *bytes.Buffer, b []byte) { + // This uses the same algorithm as the protoc C++ code for escaping strings. + // The protoc C++ code in turn uses the abseil C++ library's CEscape function: + // https://github.com/abseil/abseil-cpp/blob/934f613818ffcb26c942dff4a80be9a4031c662c/absl/strings/escaping.cc#L406 + for _, c := range b { + switch c { + case '\n': + buf.WriteString("\\n") + case '\r': + buf.WriteString("\\r") + case '\t': + buf.WriteString("\\t") + case '"': + buf.WriteString("\\\"") + case '\'': + buf.WriteString("\\'") + case '\\': + buf.WriteString("\\\\") + default: + if c >= 0x20 && c < 0x7f { + // simple printable characters + buf.WriteByte(c) + } else { + // use octal escape for all other values + buf.WriteRune('\\') + buf.WriteByte('0' + ((c >> 6) & 0x7)) + buf.WriteByte('0' + ((c >> 3) & 0x7)) + buf.WriteByte('0' + (c & 0x7)) + } + } + } +} + +// IsZeroLocation returns true if the given loc is a zero value +// (which is returned from queries that have no result). +func IsZeroLocation(loc protoreflect.SourceLocation) bool { + return loc.Path == nil && + loc.StartLine == 0 && + loc.StartColumn == 0 && + loc.EndLine == 0 && + loc.EndColumn == 0 && + loc.LeadingDetachedComments == nil && + loc.LeadingComments == "" && + loc.TrailingComments == "" && + loc.Next == 0 +} + +// ComputePath computes the source location path for the given descriptor. +// The boolean value indicates whether the result is valid. If the path +// cannot be computed for d, the function returns nil, false. +func ComputePath(d protoreflect.Descriptor) (protoreflect.SourcePath, bool) { + _, ok := d.(protoreflect.FileDescriptor) + if ok { + return nil, true + } + var path protoreflect.SourcePath + for { + p := d.Parent() + switch d := d.(type) { + case protoreflect.FileDescriptor: + return reverse(path), true + case protoreflect.MessageDescriptor: + path = append(path, int32(d.Index())) + switch p.(type) { + case protoreflect.FileDescriptor: + path = append(path, FileMessagesTag) + case protoreflect.MessageDescriptor: + path = append(path, MessageNestedMessagesTag) + default: + return nil, false + } + case protoreflect.FieldDescriptor: + path = append(path, int32(d.Index())) + switch p.(type) { + case protoreflect.FileDescriptor: + if d.IsExtension() { + path = append(path, FileExtensionsTag) + } else { + return nil, false + } + case protoreflect.MessageDescriptor: + if d.IsExtension() { + path = append(path, MessageExtensionsTag) + } else { + path = append(path, MessageFieldsTag) + } + default: + return nil, false + } + case protoreflect.OneofDescriptor: + path = append(path, int32(d.Index())) + if _, ok := p.(protoreflect.MessageDescriptor); ok { + path = append(path, MessageOneofsTag) + } else { + return nil, false + } + case protoreflect.EnumDescriptor: + path = append(path, int32(d.Index())) + switch p.(type) { + case protoreflect.FileDescriptor: + path = append(path, FileEnumsTag) + case protoreflect.MessageDescriptor: + path = append(path, MessageEnumsTag) + default: + return nil, false + } + case protoreflect.EnumValueDescriptor: + path = append(path, int32(d.Index())) + if _, ok := p.(protoreflect.EnumDescriptor); ok { + path = append(path, EnumValuesTag) + } else { + return nil, false + } + case protoreflect.ServiceDescriptor: + path = append(path, int32(d.Index())) + if _, ok := p.(protoreflect.FileDescriptor); ok { + path = append(path, FileServicesTag) + } else { + return nil, false + } + case protoreflect.MethodDescriptor: + path = append(path, int32(d.Index())) + if _, ok := p.(protoreflect.ServiceDescriptor); ok { + path = append(path, ServiceMethodsTag) + } else { + return nil, false + } + } + d = p + } +} + +// CanPack returns true if a repeated field of the given kind +// can use packed encoding. +func CanPack(k protoreflect.Kind) bool { + switch k { + case protoreflect.MessageKind, protoreflect.GroupKind, protoreflect.StringKind, protoreflect.BytesKind: + return false + default: + return true + } +} + +func ClonePath(path protoreflect.SourcePath) protoreflect.SourcePath { + clone := make(protoreflect.SourcePath, len(path)) + copy(clone, path) + return clone +} + +func reverse(p protoreflect.SourcePath) protoreflect.SourcePath { + for i, j := 0, len(p)-1; i < j; i, j = i+1, j-1 { + p[i], p[j] = p[j], p[i] + } + return p +} diff --git a/vendor/github.com/bufbuild/protocompile/linker/descriptors.go b/vendor/github.com/bufbuild/protocompile/linker/descriptors.go new file mode 100644 index 0000000..cd43dcc --- /dev/null +++ b/vendor/github.com/bufbuild/protocompile/linker/descriptors.go @@ -0,0 +1,1884 @@ +// Copyright 2020-2024 Buf Technologies, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package linker + +import ( + "fmt" + "strconv" + "strings" + "unicode/utf8" + + "google.golang.org/protobuf/proto" + "google.golang.org/protobuf/reflect/protodesc" + "google.golang.org/protobuf/reflect/protoreflect" + "google.golang.org/protobuf/reflect/protoregistry" + "google.golang.org/protobuf/types/descriptorpb" + "google.golang.org/protobuf/types/dynamicpb" + + "github.com/bufbuild/protocompile/ast" + "github.com/bufbuild/protocompile/internal" + "github.com/bufbuild/protocompile/internal/editions" + "github.com/bufbuild/protocompile/parser" + "github.com/bufbuild/protocompile/protoutil" +) + +var ( + // These "noOp*" values are all descriptors. The protoreflect.Descriptor + // interface and its sub-interfaces are all marked with an unexported + // method so that they cannot be implemented outside of the google.golang.org/protobuf + // module. So, to provide implementations from this package, we must embed + // them. If we simply left the embedded interface field nil, then if/when + // new methods are added to the interfaces, it could induce panics in this + // package or users of this module (since trying to invoke one of these new + // methods would end up trying to call a method on a nil interface value). + // + // So instead of leaving the embedded interface fields nil, we embed an actual + // value. While new methods are unlikely to return the correct value (since + // the calls will be delegated to these no-op instances), it is a less + // dangerous latent bug than inducing a nil-dereference panic. + + noOpFile protoreflect.FileDescriptor + noOpMessage protoreflect.MessageDescriptor + noOpOneof protoreflect.OneofDescriptor + noOpField protoreflect.FieldDescriptor + noOpEnum protoreflect.EnumDescriptor + noOpEnumValue protoreflect.EnumValueDescriptor + noOpExtension protoreflect.ExtensionDescriptor + noOpService protoreflect.ServiceDescriptor + noOpMethod protoreflect.MethodDescriptor +) + +var ( + fieldPresenceField = editions.FeatureSetDescriptor.Fields().ByName("field_presence") + repeatedFieldEncodingField = editions.FeatureSetDescriptor.Fields().ByName("repeated_field_encoding") + messageEncodingField = editions.FeatureSetDescriptor.Fields().ByName("message_encoding") + enumTypeField = editions.FeatureSetDescriptor.Fields().ByName("enum_type") + jsonFormatField = editions.FeatureSetDescriptor.Fields().ByName("json_format") +) + +func init() { + noOpFile, _ = protodesc.NewFile( + &descriptorpb.FileDescriptorProto{ + Name: proto.String("no-op.proto"), + Syntax: proto.String("proto2"), + Dependency: []string{"google/protobuf/descriptor.proto"}, + MessageType: []*descriptorpb.DescriptorProto{ + { + Name: proto.String("NoOpMsg"), + Field: []*descriptorpb.FieldDescriptorProto{ + { + Name: proto.String("no_op"), + Type: descriptorpb.FieldDescriptorProto_TYPE_STRING.Enum(), + Label: descriptorpb.FieldDescriptorProto_LABEL_OPTIONAL.Enum(), + Number: proto.Int32(1), + JsonName: proto.String("noOp"), + OneofIndex: proto.Int32(0), + }, + }, + OneofDecl: []*descriptorpb.OneofDescriptorProto{ + { + Name: proto.String("no_op_oneof"), + }, + }, + }, + }, + EnumType: []*descriptorpb.EnumDescriptorProto{ + { + Name: proto.String("NoOpEnum"), + Value: []*descriptorpb.EnumValueDescriptorProto{ + { + Name: proto.String("NO_OP"), + Number: proto.Int32(0), + }, + }, + }, + }, + Extension: []*descriptorpb.FieldDescriptorProto{ + { + Extendee: proto.String(".google.protobuf.FileOptions"), + Name: proto.String("no_op"), + Type: descriptorpb.FieldDescriptorProto_TYPE_STRING.Enum(), + Label: descriptorpb.FieldDescriptorProto_LABEL_OPTIONAL.Enum(), + Number: proto.Int32(50000), + }, + }, + Service: []*descriptorpb.ServiceDescriptorProto{ + { + Name: proto.String("NoOpService"), + Method: []*descriptorpb.MethodDescriptorProto{ + { + Name: proto.String("NoOp"), + InputType: proto.String(".NoOpMsg"), + OutputType: proto.String(".NoOpMsg"), + }, + }, + }, + }, + }, + protoregistry.GlobalFiles, + ) + noOpMessage = noOpFile.Messages().Get(0) + noOpOneof = noOpMessage.Oneofs().Get(0) + noOpField = noOpMessage.Fields().Get(0) + noOpEnum = noOpFile.Enums().Get(0) + noOpEnumValue = noOpEnum.Values().Get(0) + noOpExtension = noOpFile.Extensions().Get(0) + noOpService = noOpFile.Services().Get(0) + noOpMethod = noOpService.Methods().Get(0) +} + +// This file contains implementations of protoreflect.Descriptor. Note that +// this is a hack since those interfaces have a "doNotImplement" tag +// interface therein. We do just enough to make dynamicpb happy; constructing +// a regular descriptor would fail because we haven't yet interpreted options +// at the point we need these, and some validations will fail if the options +// aren't present. + +type result struct { + protoreflect.FileDescriptor + parser.Result + prefix string + deps Files + + // A map of all descriptors keyed by their fully-qualified name (without + // any leading dot). + descriptors map[string]protoreflect.Descriptor + + // A set of imports that have been used in the course of linking and + // interpreting options. + usedImports map[string]struct{} + + // A map of AST nodes that represent identifiers in ast.FieldReferenceNodes + // to their fully-qualified name. The identifiers are for field names in + // message literals (in option values) that are extension fields. These names + // are resolved during linking and stored here, to be used to interpret options. + optionQualifiedNames map[ast.IdentValueNode]string + + imports fileImports + messages msgDescriptors + enums enumDescriptors + extensions extDescriptors + services svcDescriptors + srcLocations srcLocs +} + +var _ protoreflect.FileDescriptor = (*result)(nil) +var _ Result = (*result)(nil) +var _ protoutil.DescriptorProtoWrapper = (*result)(nil) +var _ editions.HasEdition = (*result)(nil) + +func (r *result) RemoveAST() { + r.Result = parser.ResultWithoutAST(r.FileDescriptorProto()) + r.optionQualifiedNames = nil +} + +func (r *result) AsProto() proto.Message { + return r.FileDescriptorProto() +} + +func (r *result) ParentFile() protoreflect.FileDescriptor { + return r +} + +func (r *result) Parent() protoreflect.Descriptor { + return nil +} + +func (r *result) Index() int { + return 0 +} + +func (r *result) Syntax() protoreflect.Syntax { + switch r.FileDescriptorProto().GetSyntax() { + case "proto2", "": + return protoreflect.Proto2 + case "proto3": + return protoreflect.Proto3 + case "editions": + return protoreflect.Editions + default: + return 0 // ??? + } +} + +func (r *result) Edition() int32 { + switch r.Syntax() { + case protoreflect.Proto2: + return int32(descriptorpb.Edition_EDITION_PROTO2) + case protoreflect.Proto3: + return int32(descriptorpb.Edition_EDITION_PROTO3) + case protoreflect.Editions: + return int32(r.FileDescriptorProto().GetEdition()) + default: + return int32(descriptorpb.Edition_EDITION_UNKNOWN) // ??? + } +} + +func (r *result) Name() protoreflect.Name { + return "" +} + +func (r *result) FullName() protoreflect.FullName { + return r.Package() +} + +func (r *result) IsPlaceholder() bool { + return false +} + +func (r *result) Options() protoreflect.ProtoMessage { + return r.FileDescriptorProto().Options +} + +func (r *result) Path() string { + return r.FileDescriptorProto().GetName() +} + +func (r *result) Package() protoreflect.FullName { + return protoreflect.FullName(r.FileDescriptorProto().GetPackage()) +} + +func (r *result) Imports() protoreflect.FileImports { + return &r.imports +} + +func (r *result) Enums() protoreflect.EnumDescriptors { + return &r.enums +} + +func (r *result) Messages() protoreflect.MessageDescriptors { + return &r.messages +} + +func (r *result) Extensions() protoreflect.ExtensionDescriptors { + return &r.extensions +} + +func (r *result) Services() protoreflect.ServiceDescriptors { + return &r.services +} + +func (r *result) PopulateSourceCodeInfo() { + srcLocProtos := asSourceLocations(r.FileDescriptorProto().GetSourceCodeInfo().GetLocation()) + srcLocIndex := computeSourceLocIndex(srcLocProtos) + r.srcLocations = srcLocs{file: r, locs: srcLocProtos, index: srcLocIndex} +} + +func (r *result) SourceLocations() protoreflect.SourceLocations { + return &r.srcLocations +} + +func computeSourceLocIndex(locs []protoreflect.SourceLocation) map[interface{}]int { + index := map[interface{}]int{} + for i, loc := range locs { + if loc.Next == 0 { + index[pathKey(loc.Path)] = i + } + } + return index +} + +func asSourceLocations(srcInfoProtos []*descriptorpb.SourceCodeInfo_Location) []protoreflect.SourceLocation { + locs := make([]protoreflect.SourceLocation, len(srcInfoProtos)) + prev := map[any]*protoreflect.SourceLocation{} + for i, loc := range srcInfoProtos { + var stLin, stCol, enLin, enCol int + if len(loc.Span) == 3 { + stLin, stCol, enCol = int(loc.Span[0]), int(loc.Span[1]), int(loc.Span[2]) + enLin = stLin + } else { + stLin, stCol, enLin, enCol = int(loc.Span[0]), int(loc.Span[1]), int(loc.Span[2]), int(loc.Span[3]) + } + locs[i] = protoreflect.SourceLocation{ + Path: loc.Path, + LeadingComments: loc.GetLeadingComments(), + LeadingDetachedComments: loc.GetLeadingDetachedComments(), + TrailingComments: loc.GetTrailingComments(), + StartLine: stLin, + StartColumn: stCol, + EndLine: enLin, + EndColumn: enCol, + } + str := pathKey(loc.Path) + pr := prev[str] + if pr != nil { + pr.Next = i + } + prev[str] = &locs[i] + } + return locs +} + +type fileImports struct { + protoreflect.FileImports + files []protoreflect.FileImport +} + +func (r *result) createImports() fileImports { + fd := r.FileDescriptorProto() + imps := make([]protoreflect.FileImport, len(fd.Dependency)) + for i, dep := range fd.Dependency { + desc := r.deps.FindFileByPath(dep) + imps[i] = protoreflect.FileImport{FileDescriptor: unwrap(desc)} + } + for _, publicIndex := range fd.PublicDependency { + imps[int(publicIndex)].IsPublic = true + } + for _, weakIndex := range fd.WeakDependency { + imps[int(weakIndex)].IsWeak = true + } + return fileImports{files: imps} +} + +func unwrap(descriptor protoreflect.FileDescriptor) protoreflect.FileDescriptor { + wrapped, ok := descriptor.(interface { + Unwrap() protoreflect.FileDescriptor + }) + if !ok { + return descriptor + } + unwrapped := wrapped.Unwrap() + if unwrapped == nil { + return descriptor // shouldn't ever happen + } + return unwrapped +} + +func (f *fileImports) Len() int { + return len(f.files) +} + +func (f *fileImports) Get(i int) protoreflect.FileImport { + return f.files[i] +} + +type srcLocs struct { + protoreflect.SourceLocations + file *result + locs []protoreflect.SourceLocation + index map[interface{}]int +} + +func (s *srcLocs) Len() int { + return len(s.locs) +} + +func (s *srcLocs) Get(i int) protoreflect.SourceLocation { + return s.locs[i] +} + +func (s *srcLocs) ByPath(p protoreflect.SourcePath) protoreflect.SourceLocation { + index, ok := s.index[pathKey(p)] + if !ok { + return protoreflect.SourceLocation{} + } + return s.locs[index] +} + +func (s *srcLocs) ByDescriptor(d protoreflect.Descriptor) protoreflect.SourceLocation { + if d.ParentFile() != s.file { + return protoreflect.SourceLocation{} + } + path, ok := internal.ComputePath(d) + if !ok { + return protoreflect.SourceLocation{} + } + return s.ByPath(path) +} + +type msgDescriptors struct { + protoreflect.MessageDescriptors + msgs []msgDescriptor +} + +func (r *result) createMessages(prefix string, parent protoreflect.Descriptor, msgProtos []*descriptorpb.DescriptorProto, pool *allocPool) msgDescriptors { + msgs := pool.getMessages(len(msgProtos)) + for i, msgProto := range msgProtos { + r.createMessageDescriptor(&msgs[i], msgProto, parent, i, prefix+msgProto.GetName(), pool) + } + return msgDescriptors{msgs: msgs} +} + +func (m *msgDescriptors) Len() int { + return len(m.msgs) +} + +func (m *msgDescriptors) Get(i int) protoreflect.MessageDescriptor { + return &m.msgs[i] +} + +func (m *msgDescriptors) ByName(s protoreflect.Name) protoreflect.MessageDescriptor { + for i := range m.msgs { + msg := &m.msgs[i] + if msg.Name() == s { + return msg + } + } + return nil +} + +type msgDescriptor struct { + protoreflect.MessageDescriptor + file *result + parent protoreflect.Descriptor + index int + proto *descriptorpb.DescriptorProto + fqn string + + fields fldDescriptors + oneofs oneofDescriptors + nestedMessages msgDescriptors + nestedEnums enumDescriptors + nestedExtensions extDescriptors + + extRanges fieldRanges + rsvdRanges fieldRanges + rsvdNames names +} + +var _ protoreflect.MessageDescriptor = (*msgDescriptor)(nil) +var _ protoutil.DescriptorProtoWrapper = (*msgDescriptor)(nil) + +func (r *result) createMessageDescriptor(ret *msgDescriptor, md *descriptorpb.DescriptorProto, parent protoreflect.Descriptor, index int, fqn string, pool *allocPool) { + r.descriptors[fqn] = ret + + ret.MessageDescriptor = noOpMessage + ret.file = r + ret.parent = parent + ret.index = index + ret.proto = md + ret.fqn = fqn + + prefix := fqn + "." + // NB: We MUST create fields before oneofs so that we can populate the + // set of fields that belong to the oneof + ret.fields = r.createFields(prefix, ret, md.Field, pool) + ret.oneofs = r.createOneofs(prefix, ret, md.OneofDecl, pool) + ret.nestedMessages = r.createMessages(prefix, ret, md.NestedType, pool) + ret.nestedEnums = r.createEnums(prefix, ret, md.EnumType, pool) + ret.nestedExtensions = r.createExtensions(prefix, ret, md.Extension, pool) + ret.extRanges = createFieldRanges(md.ExtensionRange) + ret.rsvdRanges = createFieldRanges(md.ReservedRange) + ret.rsvdNames = names{s: md.ReservedName} +} + +func (m *msgDescriptor) MessageDescriptorProto() *descriptorpb.DescriptorProto { + return m.proto +} + +func (m *msgDescriptor) AsProto() proto.Message { + return m.proto +} + +func (m *msgDescriptor) ParentFile() protoreflect.FileDescriptor { + return m.file +} + +func (m *msgDescriptor) Parent() protoreflect.Descriptor { + return m.parent +} + +func (m *msgDescriptor) Index() int { + return m.index +} + +func (m *msgDescriptor) Syntax() protoreflect.Syntax { + return m.file.Syntax() +} + +func (m *msgDescriptor) Name() protoreflect.Name { + return protoreflect.Name(m.proto.GetName()) +} + +func (m *msgDescriptor) FullName() protoreflect.FullName { + return protoreflect.FullName(m.fqn) +} + +func (m *msgDescriptor) IsPlaceholder() bool { + return false +} + +func (m *msgDescriptor) Options() protoreflect.ProtoMessage { + return m.proto.Options +} + +func (m *msgDescriptor) IsMapEntry() bool { + return m.proto.Options.GetMapEntry() +} + +func (m *msgDescriptor) Fields() protoreflect.FieldDescriptors { + return &m.fields +} + +func (m *msgDescriptor) Oneofs() protoreflect.OneofDescriptors { + return &m.oneofs +} + +func (m *msgDescriptor) ReservedNames() protoreflect.Names { + return m.rsvdNames +} + +func (m *msgDescriptor) ReservedRanges() protoreflect.FieldRanges { + return m.rsvdRanges +} + +func (m *msgDescriptor) RequiredNumbers() protoreflect.FieldNumbers { + var indexes fieldNums + for _, fld := range m.proto.Field { + if fld.GetLabel() == descriptorpb.FieldDescriptorProto_LABEL_REQUIRED { + indexes.s = append(indexes.s, fld.GetNumber()) + } + } + return indexes +} + +func (m *msgDescriptor) ExtensionRanges() protoreflect.FieldRanges { + return m.extRanges +} + +func (m *msgDescriptor) ExtensionRangeOptions(i int) protoreflect.ProtoMessage { + return m.proto.ExtensionRange[i].Options +} + +func (m *msgDescriptor) Enums() protoreflect.EnumDescriptors { + return &m.nestedEnums +} + +func (m *msgDescriptor) Messages() protoreflect.MessageDescriptors { + return &m.nestedMessages +} + +func (m *msgDescriptor) Extensions() protoreflect.ExtensionDescriptors { + return &m.nestedExtensions +} + +type names struct { + protoreflect.Names + s []string +} + +func (n names) Len() int { + return len(n.s) +} + +func (n names) Get(i int) protoreflect.Name { + return protoreflect.Name(n.s[i]) +} + +func (n names) Has(s protoreflect.Name) bool { + for _, name := range n.s { + if name == string(s) { + return true + } + } + return false +} + +type fieldNums struct { + protoreflect.FieldNumbers + s []int32 +} + +func (n fieldNums) Len() int { + return len(n.s) +} + +func (n fieldNums) Get(i int) protoreflect.FieldNumber { + return protoreflect.FieldNumber(n.s[i]) +} + +func (n fieldNums) Has(s protoreflect.FieldNumber) bool { + for _, num := range n.s { + if num == int32(s) { + return true + } + } + return false +} + +type fieldRanges struct { + protoreflect.FieldRanges + ranges [][2]protoreflect.FieldNumber +} + +type fieldRange interface { + GetStart() int32 + GetEnd() int32 +} + +func createFieldRanges[T fieldRange](rangeProtos []T) fieldRanges { + ranges := make([][2]protoreflect.FieldNumber, len(rangeProtos)) + for i, r := range rangeProtos { + ranges[i] = [2]protoreflect.FieldNumber{ + protoreflect.FieldNumber(r.GetStart()), + protoreflect.FieldNumber(r.GetEnd()), + } + } + return fieldRanges{ranges: ranges} +} + +func (f fieldRanges) Len() int { + return len(f.ranges) +} + +func (f fieldRanges) Get(i int) [2]protoreflect.FieldNumber { + return f.ranges[i] +} + +func (f fieldRanges) Has(n protoreflect.FieldNumber) bool { + for _, r := range f.ranges { + if r[0] <= n && r[1] > n { + return true + } + } + return false +} + +type enumDescriptors struct { + protoreflect.EnumDescriptors + enums []enumDescriptor +} + +func (r *result) createEnums(prefix string, parent protoreflect.Descriptor, enumProtos []*descriptorpb.EnumDescriptorProto, pool *allocPool) enumDescriptors { + enums := pool.getEnums(len(enumProtos)) + for i, enumProto := range enumProtos { + r.createEnumDescriptor(&enums[i], enumProto, parent, i, prefix+enumProto.GetName(), pool) + } + return enumDescriptors{enums: enums} +} + +func (e *enumDescriptors) Len() int { + return len(e.enums) +} + +func (e *enumDescriptors) Get(i int) protoreflect.EnumDescriptor { + return &e.enums[i] +} + +func (e *enumDescriptors) ByName(s protoreflect.Name) protoreflect.EnumDescriptor { + for i := range e.enums { + enum := &e.enums[i] + if enum.Name() == s { + return enum + } + } + return nil +} + +type enumDescriptor struct { + protoreflect.EnumDescriptor + file *result + parent protoreflect.Descriptor + index int + proto *descriptorpb.EnumDescriptorProto + fqn string + + values enValDescriptors + + rsvdRanges enumRanges + rsvdNames names +} + +var _ protoreflect.EnumDescriptor = (*enumDescriptor)(nil) +var _ protoutil.DescriptorProtoWrapper = (*enumDescriptor)(nil) + +func (r *result) createEnumDescriptor(ret *enumDescriptor, ed *descriptorpb.EnumDescriptorProto, parent protoreflect.Descriptor, index int, fqn string, pool *allocPool) { + r.descriptors[fqn] = ret + + ret.EnumDescriptor = noOpEnum + ret.file = r + ret.parent = parent + ret.index = index + ret.proto = ed + ret.fqn = fqn + + // Unlike all other elements, the fully-qualified names of enum values + // are NOT scoped to their parent element (the enum), but rather to + // the enum's parent element. This follows C++ scoping rules for + // enum values. + prefix := strings.TrimSuffix(fqn, ed.GetName()) + ret.values = r.createEnumValues(prefix, ret, ed.Value, pool) + ret.rsvdRanges = createEnumRanges(ed.ReservedRange) + ret.rsvdNames = names{s: ed.ReservedName} +} + +func (e *enumDescriptor) EnumDescriptorProto() *descriptorpb.EnumDescriptorProto { + return e.proto +} + +func (e *enumDescriptor) AsProto() proto.Message { + return e.proto +} + +func (e *enumDescriptor) ParentFile() protoreflect.FileDescriptor { + return e.file +} + +func (e *enumDescriptor) Parent() protoreflect.Descriptor { + return e.parent +} + +func (e *enumDescriptor) Index() int { + return e.index +} + +func (e *enumDescriptor) Syntax() protoreflect.Syntax { + return e.file.Syntax() +} + +func (e *enumDescriptor) Name() protoreflect.Name { + return protoreflect.Name(e.proto.GetName()) +} + +func (e *enumDescriptor) FullName() protoreflect.FullName { + return protoreflect.FullName(e.fqn) +} + +func (e *enumDescriptor) IsPlaceholder() bool { + return false +} + +func (e *enumDescriptor) Options() protoreflect.ProtoMessage { + return e.proto.Options +} + +func (e *enumDescriptor) Values() protoreflect.EnumValueDescriptors { + return &e.values +} + +func (e *enumDescriptor) ReservedNames() protoreflect.Names { + return e.rsvdNames +} + +func (e *enumDescriptor) ReservedRanges() protoreflect.EnumRanges { + return e.rsvdRanges +} + +func (e *enumDescriptor) IsClosed() bool { + enumType := resolveFeature(e, enumTypeField) + return descriptorpb.FeatureSet_EnumType(enumType.Enum()) == descriptorpb.FeatureSet_CLOSED +} + +type enumRanges struct { + protoreflect.EnumRanges + ranges [][2]protoreflect.EnumNumber +} + +func createEnumRanges(rangeProtos []*descriptorpb.EnumDescriptorProto_EnumReservedRange) enumRanges { + ranges := make([][2]protoreflect.EnumNumber, len(rangeProtos)) + for i, r := range rangeProtos { + ranges[i] = [2]protoreflect.EnumNumber{ + protoreflect.EnumNumber(r.GetStart()), + protoreflect.EnumNumber(r.GetEnd()), + } + } + return enumRanges{ranges: ranges} +} + +func (e enumRanges) Len() int { + return len(e.ranges) +} + +func (e enumRanges) Get(i int) [2]protoreflect.EnumNumber { + return e.ranges[i] +} + +func (e enumRanges) Has(n protoreflect.EnumNumber) bool { + for _, r := range e.ranges { + if r[0] <= n && r[1] >= n { + return true + } + } + return false +} + +type enValDescriptors struct { + protoreflect.EnumValueDescriptors + vals []enValDescriptor +} + +func (r *result) createEnumValues(prefix string, parent *enumDescriptor, enValProtos []*descriptorpb.EnumValueDescriptorProto, pool *allocPool) enValDescriptors { + vals := pool.getEnumValues(len(enValProtos)) + for i, enValProto := range enValProtos { + r.createEnumValueDescriptor(&vals[i], enValProto, parent, i, prefix+enValProto.GetName()) + } + return enValDescriptors{vals: vals} +} + +func (e *enValDescriptors) Len() int { + return len(e.vals) +} + +func (e *enValDescriptors) Get(i int) protoreflect.EnumValueDescriptor { + return &e.vals[i] +} + +func (e *enValDescriptors) ByName(s protoreflect.Name) protoreflect.EnumValueDescriptor { + for i := range e.vals { + val := &e.vals[i] + if val.Name() == s { + return val + } + } + return nil +} + +func (e *enValDescriptors) ByNumber(n protoreflect.EnumNumber) protoreflect.EnumValueDescriptor { + for i := range e.vals { + val := &e.vals[i] + if val.Number() == n { + return val + } + } + return nil +} + +type enValDescriptor struct { + protoreflect.EnumValueDescriptor + file *result + parent *enumDescriptor + index int + proto *descriptorpb.EnumValueDescriptorProto + fqn string +} + +var _ protoreflect.EnumValueDescriptor = (*enValDescriptor)(nil) +var _ protoutil.DescriptorProtoWrapper = (*enValDescriptor)(nil) + +func (r *result) createEnumValueDescriptor(ret *enValDescriptor, ed *descriptorpb.EnumValueDescriptorProto, parent *enumDescriptor, index int, fqn string) { + r.descriptors[fqn] = ret + ret.EnumValueDescriptor = noOpEnumValue + ret.file = r + ret.parent = parent + ret.index = index + ret.proto = ed + ret.fqn = fqn +} + +func (e *enValDescriptor) EnumValueDescriptorProto() *descriptorpb.EnumValueDescriptorProto { + return e.proto +} + +func (e *enValDescriptor) AsProto() proto.Message { + return e.proto +} + +func (e *enValDescriptor) ParentFile() protoreflect.FileDescriptor { + return e.file +} + +func (e *enValDescriptor) Parent() protoreflect.Descriptor { + return e.parent +} + +func (e *enValDescriptor) Index() int { + return e.index +} + +func (e *enValDescriptor) Syntax() protoreflect.Syntax { + return e.file.Syntax() +} + +func (e *enValDescriptor) Name() protoreflect.Name { + return protoreflect.Name(e.proto.GetName()) +} + +func (e *enValDescriptor) FullName() protoreflect.FullName { + return protoreflect.FullName(e.fqn) +} + +func (e *enValDescriptor) IsPlaceholder() bool { + return false +} + +func (e *enValDescriptor) Options() protoreflect.ProtoMessage { + return e.proto.Options +} + +func (e *enValDescriptor) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(e.proto.GetNumber()) +} + +type extDescriptors struct { + protoreflect.ExtensionDescriptors + exts []extTypeDescriptor +} + +func (r *result) createExtensions(prefix string, parent protoreflect.Descriptor, extProtos []*descriptorpb.FieldDescriptorProto, pool *allocPool) extDescriptors { + exts := pool.getExtensions(len(extProtos)) + for i, extProto := range extProtos { + r.createExtTypeDescriptor(&exts[i], extProto, parent, i, prefix+extProto.GetName()) + } + return extDescriptors{exts: exts} +} + +func (e *extDescriptors) Len() int { + return len(e.exts) +} + +func (e *extDescriptors) Get(i int) protoreflect.ExtensionDescriptor { + return &e.exts[i] +} + +func (e *extDescriptors) ByName(s protoreflect.Name) protoreflect.ExtensionDescriptor { + for i := range e.exts { + ext := &e.exts[i] + if ext.Name() == s { + return ext + } + } + return nil +} + +type extTypeDescriptor struct { + protoreflect.ExtensionTypeDescriptor + field fldDescriptor +} + +var _ protoutil.DescriptorProtoWrapper = &extTypeDescriptor{} + +func (r *result) createExtTypeDescriptor(ret *extTypeDescriptor, fd *descriptorpb.FieldDescriptorProto, parent protoreflect.Descriptor, index int, fqn string) { + r.descriptors[fqn] = ret + ret.field = fldDescriptor{FieldDescriptor: noOpExtension, file: r, parent: parent, index: index, proto: fd, fqn: fqn} + ret.ExtensionTypeDescriptor = dynamicpb.NewExtensionType(&ret.field).TypeDescriptor() +} + +func (e *extTypeDescriptor) FieldDescriptorProto() *descriptorpb.FieldDescriptorProto { + return e.field.proto +} + +func (e *extTypeDescriptor) AsProto() proto.Message { + return e.field.proto +} + +type fldDescriptors struct { + protoreflect.FieldDescriptors + // We use pointers here, instead of flattened slice, because oneofs + // also have fields, but need to point to values in the parent + // message's fields. Even though they are pointers, in the containing + // message, we always allocate a flattened slice and then point into + // that, so we're still doing fewer allocations (2 per set of fields + // instead of 1 per each field). + fields []*fldDescriptor +} + +func (r *result) createFields(prefix string, parent *msgDescriptor, fldProtos []*descriptorpb.FieldDescriptorProto, pool *allocPool) fldDescriptors { + fields := pool.getFields(len(fldProtos)) + fieldPtrs := make([]*fldDescriptor, len(fldProtos)) + for i, fldProto := range fldProtos { + r.createFieldDescriptor(&fields[i], fldProto, parent, i, prefix+fldProto.GetName()) + fieldPtrs[i] = &fields[i] + } + return fldDescriptors{fields: fieldPtrs} +} + +func (f *fldDescriptors) Len() int { + return len(f.fields) +} + +func (f *fldDescriptors) Get(i int) protoreflect.FieldDescriptor { + return f.fields[i] +} + +func (f *fldDescriptors) ByName(s protoreflect.Name) protoreflect.FieldDescriptor { + for _, fld := range f.fields { + if fld.Name() == s { + return fld + } + } + return nil +} + +func (f *fldDescriptors) ByJSONName(s string) protoreflect.FieldDescriptor { + for _, fld := range f.fields { + if fld.JSONName() == s { + return fld + } + } + return nil +} + +func (f *fldDescriptors) ByTextName(s string) protoreflect.FieldDescriptor { + fld := f.ByName(protoreflect.Name(s)) + if fld != nil { + return fld + } + // Groups use type name instead, so we fallback to slow search + for _, fld := range f.fields { + if fld.TextName() == s { + return fld + } + } + return nil +} + +func (f *fldDescriptors) ByNumber(n protoreflect.FieldNumber) protoreflect.FieldDescriptor { + for _, fld := range f.fields { + if fld.Number() == n { + return fld + } + } + return nil +} + +type fldDescriptor struct { + protoreflect.FieldDescriptor + file *result + parent protoreflect.Descriptor + index int + proto *descriptorpb.FieldDescriptorProto + fqn string + + msgType protoreflect.MessageDescriptor + extendee protoreflect.MessageDescriptor + enumType protoreflect.EnumDescriptor + oneof protoreflect.OneofDescriptor +} + +var _ protoreflect.FieldDescriptor = (*fldDescriptor)(nil) +var _ protoutil.DescriptorProtoWrapper = (*fldDescriptor)(nil) + +func (r *result) createFieldDescriptor(ret *fldDescriptor, fd *descriptorpb.FieldDescriptorProto, parent *msgDescriptor, index int, fqn string) { + r.descriptors[fqn] = ret + ret.FieldDescriptor = noOpField + ret.file = r + ret.parent = parent + ret.index = index + ret.proto = fd + ret.fqn = fqn +} + +func (f *fldDescriptor) FieldDescriptorProto() *descriptorpb.FieldDescriptorProto { + return f.proto +} + +func (f *fldDescriptor) AsProto() proto.Message { + return f.proto +} + +func (f *fldDescriptor) ParentFile() protoreflect.FileDescriptor { + return f.file +} + +func (f *fldDescriptor) Parent() protoreflect.Descriptor { + return f.parent +} + +func (f *fldDescriptor) Index() int { + return f.index +} + +func (f *fldDescriptor) Syntax() protoreflect.Syntax { + return f.file.Syntax() +} + +func (f *fldDescriptor) Name() protoreflect.Name { + return protoreflect.Name(f.proto.GetName()) +} + +func (f *fldDescriptor) FullName() protoreflect.FullName { + return protoreflect.FullName(f.fqn) +} + +func (f *fldDescriptor) IsPlaceholder() bool { + return false +} + +func (f *fldDescriptor) Options() protoreflect.ProtoMessage { + return f.proto.Options +} + +func (f *fldDescriptor) Number() protoreflect.FieldNumber { + return protoreflect.FieldNumber(f.proto.GetNumber()) +} + +func (f *fldDescriptor) Cardinality() protoreflect.Cardinality { + switch f.proto.GetLabel() { + case descriptorpb.FieldDescriptorProto_LABEL_REPEATED: + return protoreflect.Repeated + case descriptorpb.FieldDescriptorProto_LABEL_REQUIRED: + return protoreflect.Required + case descriptorpb.FieldDescriptorProto_LABEL_OPTIONAL: + if f.Syntax() == protoreflect.Editions { + // Editions does not use label to indicate required. It instead + // uses a feature, and label is always optional. + fieldPresence := descriptorpb.FeatureSet_FieldPresence(resolveFeature(f, fieldPresenceField).Enum()) + if fieldPresence == descriptorpb.FeatureSet_LEGACY_REQUIRED { + return protoreflect.Required + } + } + return protoreflect.Optional + default: + return 0 + } +} + +func (f *fldDescriptor) Kind() protoreflect.Kind { + if f.proto.GetType() == descriptorpb.FieldDescriptorProto_TYPE_MESSAGE && f.Syntax() == protoreflect.Editions && + !f.IsMap() && !f.parentIsMap() { + // In editions, "group encoding" (aka "delimited encoding") is toggled + // via a feature. So we report group kind when that feature is enabled. + messageEncoding := resolveFeature(f, messageEncodingField) + if descriptorpb.FeatureSet_MessageEncoding(messageEncoding.Enum()) == descriptorpb.FeatureSet_DELIMITED { + return protoreflect.GroupKind + } + } + return protoreflect.Kind(f.proto.GetType()) +} + +func (f *fldDescriptor) HasJSONName() bool { + return f.proto.JsonName != nil +} + +func (f *fldDescriptor) JSONName() string { + if f.IsExtension() { + return f.TextName() + } + return f.proto.GetJsonName() +} + +func (f *fldDescriptor) TextName() string { + if f.IsExtension() { + return fmt.Sprintf("[%s]", f.FullName()) + } + if f.looksLikeGroup() { + // groups use the type name + return string(protoreflect.FullName(f.proto.GetTypeName()).Name()) + } + return string(f.Name()) +} + +func (f *fldDescriptor) looksLikeGroup() bool { + // It looks like a group if it uses group/delimited encoding (checked via f.Kind) + // and the message type is a sibling whose name is a mixed-case version of the field name. + return f.Kind() == protoreflect.GroupKind && + f.Message().FullName().Parent() == f.FullName().Parent() && + string(f.Name()) == strings.ToLower(string(f.Message().Name())) +} + +func (f *fldDescriptor) HasPresence() bool { + if f.proto.GetLabel() == descriptorpb.FieldDescriptorProto_LABEL_REPEATED { + return false + } + if f.IsExtension() || + f.Kind() == protoreflect.MessageKind || f.Kind() == protoreflect.GroupKind || + f.proto.OneofIndex != nil { + return true + } + fieldPresence := descriptorpb.FeatureSet_FieldPresence(resolveFeature(f, fieldPresenceField).Enum()) + return fieldPresence == descriptorpb.FeatureSet_EXPLICIT || fieldPresence == descriptorpb.FeatureSet_LEGACY_REQUIRED +} + +func (f *fldDescriptor) IsExtension() bool { + return f.proto.GetExtendee() != "" +} + +func (f *fldDescriptor) HasOptionalKeyword() bool { + if f.proto.GetLabel() != descriptorpb.FieldDescriptorProto_LABEL_OPTIONAL { + return false + } + if f.proto.GetProto3Optional() { + // NB: This smells weird to return false here. If the proto3_optional field + // is set, it's because the keyword WAS present. However, the Go runtime + // returns false for this case, so we mirror that behavior. + return !f.IsExtension() + } + // If it's optional, but not a proto3 optional, then the keyword is only + // present for proto2 files, for fields that are not part of a oneof. + return f.file.Syntax() == protoreflect.Proto2 && f.proto.OneofIndex == nil +} + +func (f *fldDescriptor) IsWeak() bool { + return f.proto.Options.GetWeak() +} + +func (f *fldDescriptor) IsPacked() bool { + if f.Cardinality() != protoreflect.Repeated || !internal.CanPack(f.Kind()) { + return false + } + opts := f.proto.GetOptions() + if opts != nil && opts.Packed != nil { + // packed option is set explicitly + return *opts.Packed + } + fieldEncoding := resolveFeature(f, repeatedFieldEncodingField) + return descriptorpb.FeatureSet_RepeatedFieldEncoding(fieldEncoding.Enum()) == descriptorpb.FeatureSet_PACKED +} + +func (f *fldDescriptor) IsList() bool { + if f.proto.GetLabel() != descriptorpb.FieldDescriptorProto_LABEL_REPEATED { + return false + } + return !f.isMapEntry() +} + +func (f *fldDescriptor) IsMap() bool { + if f.proto.GetLabel() != descriptorpb.FieldDescriptorProto_LABEL_REPEATED { + return false + } + if f.IsExtension() { + return false + } + return f.isMapEntry() +} + +func (f *fldDescriptor) isMapEntry() bool { + if f.proto.GetType() != descriptorpb.FieldDescriptorProto_TYPE_MESSAGE { + return false + } + return f.Message().IsMapEntry() +} + +func (f *fldDescriptor) parentIsMap() bool { + parent, ok := f.parent.(protoreflect.MessageDescriptor) + return ok && parent.IsMapEntry() +} + +func (f *fldDescriptor) MapKey() protoreflect.FieldDescriptor { + if !f.IsMap() { + return nil + } + return f.Message().Fields().ByNumber(1) +} + +func (f *fldDescriptor) MapValue() protoreflect.FieldDescriptor { + if !f.IsMap() { + return nil + } + return f.Message().Fields().ByNumber(2) +} + +func (f *fldDescriptor) HasDefault() bool { + return f.proto.DefaultValue != nil +} + +func (f *fldDescriptor) Default() protoreflect.Value { + // We only return a valid value for scalar fields + if f.proto.GetLabel() == descriptorpb.FieldDescriptorProto_LABEL_REPEATED || + f.Kind() == protoreflect.GroupKind || f.Kind() == protoreflect.MessageKind { + return protoreflect.Value{} + } + + if f.proto.DefaultValue != nil { + defVal := f.parseDefaultValue(f.proto.GetDefaultValue()) + if defVal.IsValid() { + return defVal + } + // if we cannot parse a valid value, fall back to zero value below + } + + // No custom default value, so return the zero value for the type + switch f.Kind() { + case protoreflect.Int32Kind, protoreflect.Sint32Kind, protoreflect.Sfixed32Kind: + return protoreflect.ValueOfInt32(0) + case protoreflect.Int64Kind, protoreflect.Sint64Kind, protoreflect.Sfixed64Kind: + return protoreflect.ValueOfInt64(0) + case protoreflect.Uint32Kind, protoreflect.Fixed32Kind: + return protoreflect.ValueOfUint32(0) + case protoreflect.Uint64Kind, protoreflect.Fixed64Kind: + return protoreflect.ValueOfUint64(0) + case protoreflect.FloatKind: + return protoreflect.ValueOfFloat32(0) + case protoreflect.DoubleKind: + return protoreflect.ValueOfFloat64(0) + case protoreflect.BoolKind: + return protoreflect.ValueOfBool(false) + case protoreflect.BytesKind: + return protoreflect.ValueOfBytes(nil) + case protoreflect.StringKind: + return protoreflect.ValueOfString("") + case protoreflect.EnumKind: + return protoreflect.ValueOfEnum(f.Enum().Values().Get(0).Number()) + case protoreflect.GroupKind, protoreflect.MessageKind: + return protoreflect.ValueOfMessage(dynamicpb.NewMessage(f.Message())) + default: + panic(fmt.Sprintf("unknown kind: %v", f.Kind())) + } +} + +func (f *fldDescriptor) parseDefaultValue(val string) protoreflect.Value { + switch f.Kind() { + case protoreflect.EnumKind: + vd := f.Enum().Values().ByName(protoreflect.Name(val)) + if vd != nil { + return protoreflect.ValueOfEnum(vd.Number()) + } + return protoreflect.Value{} + case protoreflect.BoolKind: + switch val { + case "true": + return protoreflect.ValueOfBool(true) + case "false": + return protoreflect.ValueOfBool(false) + default: + return protoreflect.Value{} + } + case protoreflect.BytesKind: + return protoreflect.ValueOfBytes([]byte(unescape(val))) + case protoreflect.StringKind: + return protoreflect.ValueOfString(val) + case protoreflect.FloatKind: + if f, err := strconv.ParseFloat(val, 32); err == nil { + return protoreflect.ValueOfFloat32(float32(f)) + } + return protoreflect.Value{} + case protoreflect.DoubleKind: + if f, err := strconv.ParseFloat(val, 64); err == nil { + return protoreflect.ValueOfFloat64(f) + } + return protoreflect.Value{} + case protoreflect.Int32Kind, protoreflect.Sint32Kind, protoreflect.Sfixed32Kind: + if i, err := strconv.ParseInt(val, 10, 32); err == nil { + return protoreflect.ValueOfInt32(int32(i)) + } + return protoreflect.Value{} + case protoreflect.Uint32Kind, protoreflect.Fixed32Kind: + if i, err := strconv.ParseUint(val, 10, 32); err == nil { + return protoreflect.ValueOfUint32(uint32(i)) + } + return protoreflect.Value{} + case protoreflect.Int64Kind, protoreflect.Sint64Kind, protoreflect.Sfixed64Kind: + if i, err := strconv.ParseInt(val, 10, 64); err == nil { + return protoreflect.ValueOfInt64(i) + } + return protoreflect.Value{} + case protoreflect.Uint64Kind, protoreflect.Fixed64Kind: + if i, err := strconv.ParseUint(val, 10, 64); err == nil { + return protoreflect.ValueOfUint64(i) + } + return protoreflect.Value{} + default: + return protoreflect.Value{} + } +} + +func unescape(s string) string { + // protoc encodes default values for 'bytes' fields using C escaping, + // so this function reverses that escaping + out := make([]byte, 0, len(s)) + var buf [4]byte + for len(s) > 0 { + if s[0] != '\\' || len(s) < 2 { + // not escape sequence, or too short to be well-formed escape + out = append(out, s[0]) + s = s[1:] + continue + } + nextIndex := 2 // by default, skip '\' + escaped character + switch s[1] { + case 'x', 'X': + n := matchPrefix(s[2:], 2, isHex) + if n == 0 { + // bad escape + out = append(out, s[:2]...) + } else { + c, err := strconv.ParseUint(s[2:2+n], 16, 8) + if err != nil { + // shouldn't really happen... + out = append(out, s[:2+n]...) + } else { + out = append(out, byte(c)) + } + nextIndex = 2 + n + } + case '0', '1', '2', '3', '4', '5', '6', '7': + n := 1 + matchPrefix(s[2:], 2, isOctal) + c, err := strconv.ParseUint(s[1:1+n], 8, 8) + if err != nil || c > 0xff { + out = append(out, s[:1+n]...) + } else { + out = append(out, byte(c)) + } + nextIndex = 1 + n + case 'u': + if len(s) < 6 { + // bad escape + out = append(out, s...) + nextIndex = len(s) + } else { + c, err := strconv.ParseUint(s[2:6], 16, 16) + if err != nil { + // bad escape + out = append(out, s[:6]...) + } else { + w := utf8.EncodeRune(buf[:], rune(c)) + out = append(out, buf[:w]...) + } + nextIndex = 6 + } + case 'U': + if len(s) < 10 { + // bad escape + out = append(out, s...) + nextIndex = len(s) + } else { + c, err := strconv.ParseUint(s[2:10], 16, 32) + if err != nil || c > 0x10ffff { + // bad escape + out = append(out, s[:10]...) + } else { + w := utf8.EncodeRune(buf[:], rune(c)) + out = append(out, buf[:w]...) + } + nextIndex = 10 + } + case 'a': + out = append(out, '\a') + case 'b': + out = append(out, '\b') + case 'f': + out = append(out, '\f') + case 'n': + out = append(out, '\n') + case 'r': + out = append(out, '\r') + case 't': + out = append(out, '\t') + case 'v': + out = append(out, '\v') + case '\\', '\'', '"', '?': + out = append(out, s[1]) + default: + // invalid escape, just copy it as-is + out = append(out, s[:2]...) + } + s = s[nextIndex:] + } + return string(out) +} + +func isOctal(b byte) bool { return b >= '0' && b <= '7' } +func isHex(b byte) bool { + return (b >= '0' && b <= '9') || (b >= 'a' && b <= 'f') || (b >= 'A' && b <= 'F') +} +func matchPrefix(s string, limit int, fn func(byte) bool) int { + l := len(s) + if l > limit { + l = limit + } + i := 0 + for ; i < l; i++ { + if !fn(s[i]) { + return i + } + } + return i +} + +func (f *fldDescriptor) DefaultEnumValue() protoreflect.EnumValueDescriptor { + ed := f.Enum() + if ed == nil { + return nil + } + if f.proto.DefaultValue != nil { + if val := ed.Values().ByName(protoreflect.Name(f.proto.GetDefaultValue())); val != nil { + return val + } + } + // if no default specified in source, return nil + return nil +} + +func (f *fldDescriptor) ContainingOneof() protoreflect.OneofDescriptor { + return f.oneof +} + +func (f *fldDescriptor) ContainingMessage() protoreflect.MessageDescriptor { + if f.extendee != nil { + return f.extendee + } + return f.parent.(protoreflect.MessageDescriptor) +} + +func (f *fldDescriptor) Enum() protoreflect.EnumDescriptor { + return f.enumType +} + +func (f *fldDescriptor) Message() protoreflect.MessageDescriptor { + return f.msgType +} + +type oneofDescriptors struct { + protoreflect.OneofDescriptors + oneofs []oneofDescriptor +} + +func (r *result) createOneofs(prefix string, parent *msgDescriptor, ooProtos []*descriptorpb.OneofDescriptorProto, pool *allocPool) oneofDescriptors { + oos := pool.getOneofs(len(ooProtos)) + for i, fldProto := range ooProtos { + r.createOneofDescriptor(&oos[i], fldProto, parent, i, prefix+fldProto.GetName()) + } + return oneofDescriptors{oneofs: oos} +} + +func (o *oneofDescriptors) Len() int { + return len(o.oneofs) +} + +func (o *oneofDescriptors) Get(i int) protoreflect.OneofDescriptor { + return &o.oneofs[i] +} + +func (o *oneofDescriptors) ByName(s protoreflect.Name) protoreflect.OneofDescriptor { + for i := range o.oneofs { + oo := &o.oneofs[i] + if oo.Name() == s { + return oo + } + } + return nil +} + +type oneofDescriptor struct { + protoreflect.OneofDescriptor + file *result + parent *msgDescriptor + index int + proto *descriptorpb.OneofDescriptorProto + fqn string + + fields fldDescriptors +} + +var _ protoreflect.OneofDescriptor = (*oneofDescriptor)(nil) +var _ protoutil.DescriptorProtoWrapper = (*oneofDescriptor)(nil) + +func (r *result) createOneofDescriptor(ret *oneofDescriptor, ood *descriptorpb.OneofDescriptorProto, parent *msgDescriptor, index int, fqn string) { + r.descriptors[fqn] = ret + ret.OneofDescriptor = noOpOneof + ret.file = r + ret.parent = parent + ret.index = index + ret.proto = ood + ret.fqn = fqn + + var fields []*fldDescriptor + for _, fld := range parent.fields.fields { + if fld.proto.OneofIndex != nil && int(fld.proto.GetOneofIndex()) == index { + fields = append(fields, fld) + } + } + ret.fields = fldDescriptors{fields: fields} +} + +func (o *oneofDescriptor) OneofDescriptorProto() *descriptorpb.OneofDescriptorProto { + return o.proto +} + +func (o *oneofDescriptor) AsProto() proto.Message { + return o.proto +} + +func (o *oneofDescriptor) ParentFile() protoreflect.FileDescriptor { + return o.file +} + +func (o *oneofDescriptor) Parent() protoreflect.Descriptor { + return o.parent +} + +func (o *oneofDescriptor) Index() int { + return o.index +} + +func (o *oneofDescriptor) Syntax() protoreflect.Syntax { + return o.file.Syntax() +} + +func (o *oneofDescriptor) Name() protoreflect.Name { + return protoreflect.Name(o.proto.GetName()) +} + +func (o *oneofDescriptor) FullName() protoreflect.FullName { + return protoreflect.FullName(o.fqn) +} + +func (o *oneofDescriptor) IsPlaceholder() bool { + return false +} + +func (o *oneofDescriptor) Options() protoreflect.ProtoMessage { + return o.proto.Options +} + +func (o *oneofDescriptor) IsSynthetic() bool { + for _, fld := range o.parent.proto.GetField() { + if fld.OneofIndex != nil && int(fld.GetOneofIndex()) == o.index { + return fld.GetProto3Optional() + } + } + return false // NB: we should never get here +} + +func (o *oneofDescriptor) Fields() protoreflect.FieldDescriptors { + return &o.fields +} + +type svcDescriptors struct { + protoreflect.ServiceDescriptors + svcs []svcDescriptor +} + +func (r *result) createServices(prefix string, svcProtos []*descriptorpb.ServiceDescriptorProto, pool *allocPool) svcDescriptors { + svcs := pool.getServices(len(svcProtos)) + for i, svcProto := range svcProtos { + r.createServiceDescriptor(&svcs[i], svcProto, i, prefix+svcProto.GetName(), pool) + } + return svcDescriptors{svcs: svcs} +} + +func (s *svcDescriptors) Len() int { + return len(s.svcs) +} + +func (s *svcDescriptors) Get(i int) protoreflect.ServiceDescriptor { + return &s.svcs[i] +} + +func (s *svcDescriptors) ByName(n protoreflect.Name) protoreflect.ServiceDescriptor { + for i := range s.svcs { + svc := &s.svcs[i] + if svc.Name() == n { + return svc + } + } + return nil +} + +type svcDescriptor struct { + protoreflect.ServiceDescriptor + file *result + index int + proto *descriptorpb.ServiceDescriptorProto + fqn string + + methods mtdDescriptors +} + +var _ protoreflect.ServiceDescriptor = (*svcDescriptor)(nil) +var _ protoutil.DescriptorProtoWrapper = (*svcDescriptor)(nil) + +func (r *result) createServiceDescriptor(ret *svcDescriptor, sd *descriptorpb.ServiceDescriptorProto, index int, fqn string, pool *allocPool) { + r.descriptors[fqn] = ret + ret.ServiceDescriptor = noOpService + ret.file = r + ret.index = index + ret.proto = sd + ret.fqn = fqn + + prefix := fqn + "." + ret.methods = r.createMethods(prefix, ret, sd.Method, pool) +} + +func (s *svcDescriptor) ServiceDescriptorProto() *descriptorpb.ServiceDescriptorProto { + return s.proto +} + +func (s *svcDescriptor) AsProto() proto.Message { + return s.proto +} + +func (s *svcDescriptor) ParentFile() protoreflect.FileDescriptor { + return s.file +} + +func (s *svcDescriptor) Parent() protoreflect.Descriptor { + return s.file +} + +func (s *svcDescriptor) Index() int { + return s.index +} + +func (s *svcDescriptor) Syntax() protoreflect.Syntax { + return s.file.Syntax() +} + +func (s *svcDescriptor) Name() protoreflect.Name { + return protoreflect.Name(s.proto.GetName()) +} + +func (s *svcDescriptor) FullName() protoreflect.FullName { + return protoreflect.FullName(s.fqn) +} + +func (s *svcDescriptor) IsPlaceholder() bool { + return false +} + +func (s *svcDescriptor) Options() protoreflect.ProtoMessage { + return s.proto.Options +} + +func (s *svcDescriptor) Methods() protoreflect.MethodDescriptors { + return &s.methods +} + +type mtdDescriptors struct { + protoreflect.MethodDescriptors + mtds []mtdDescriptor +} + +func (r *result) createMethods(prefix string, parent *svcDescriptor, mtdProtos []*descriptorpb.MethodDescriptorProto, pool *allocPool) mtdDescriptors { + mtds := pool.getMethods(len(mtdProtos)) + for i, mtdProto := range mtdProtos { + r.createMethodDescriptor(&mtds[i], mtdProto, parent, i, prefix+mtdProto.GetName()) + } + return mtdDescriptors{mtds: mtds} +} + +func (m *mtdDescriptors) Len() int { + return len(m.mtds) +} + +func (m *mtdDescriptors) Get(i int) protoreflect.MethodDescriptor { + return &m.mtds[i] +} + +func (m *mtdDescriptors) ByName(n protoreflect.Name) protoreflect.MethodDescriptor { + for i := range m.mtds { + mtd := &m.mtds[i] + if mtd.Name() == n { + return mtd + } + } + return nil +} + +type mtdDescriptor struct { + protoreflect.MethodDescriptor + file *result + parent *svcDescriptor + index int + proto *descriptorpb.MethodDescriptorProto + fqn string + + inputType, outputType protoreflect.MessageDescriptor +} + +var _ protoreflect.MethodDescriptor = (*mtdDescriptor)(nil) +var _ protoutil.DescriptorProtoWrapper = (*mtdDescriptor)(nil) + +func (r *result) createMethodDescriptor(ret *mtdDescriptor, mtd *descriptorpb.MethodDescriptorProto, parent *svcDescriptor, index int, fqn string) { + r.descriptors[fqn] = ret + ret.MethodDescriptor = noOpMethod + ret.file = r + ret.parent = parent + ret.index = index + ret.proto = mtd + ret.fqn = fqn +} + +func (m *mtdDescriptor) MethodDescriptorProto() *descriptorpb.MethodDescriptorProto { + return m.proto +} + +func (m *mtdDescriptor) AsProto() proto.Message { + return m.proto +} + +func (m *mtdDescriptor) ParentFile() protoreflect.FileDescriptor { + return m.file +} + +func (m *mtdDescriptor) Parent() protoreflect.Descriptor { + return m.parent +} + +func (m *mtdDescriptor) Index() int { + return m.index +} + +func (m *mtdDescriptor) Syntax() protoreflect.Syntax { + return m.file.Syntax() +} + +func (m *mtdDescriptor) Name() protoreflect.Name { + return protoreflect.Name(m.proto.GetName()) +} + +func (m *mtdDescriptor) FullName() protoreflect.FullName { + return protoreflect.FullName(m.fqn) +} + +func (m *mtdDescriptor) IsPlaceholder() bool { + return false +} + +func (m *mtdDescriptor) Options() protoreflect.ProtoMessage { + return m.proto.Options +} + +func (m *mtdDescriptor) Input() protoreflect.MessageDescriptor { + return m.inputType +} + +func (m *mtdDescriptor) Output() protoreflect.MessageDescriptor { + return m.outputType +} + +func (m *mtdDescriptor) IsStreamingClient() bool { + return m.proto.GetClientStreaming() +} + +func (m *mtdDescriptor) IsStreamingServer() bool { + return m.proto.GetServerStreaming() +} + +func (r *result) FindImportByPath(path string) File { + return r.deps.FindFileByPath(path) +} + +func (r *result) FindExtensionByNumber(msg protoreflect.FullName, tag protoreflect.FieldNumber) protoreflect.ExtensionTypeDescriptor { + return findExtension(r, msg, tag) +} + +func (r *result) FindDescriptorByName(name protoreflect.FullName) protoreflect.Descriptor { + fqn := strings.TrimPrefix(string(name), ".") + return r.descriptors[fqn] +} + +func (r *result) hasSource() bool { + n := r.FileNode() + _, ok := n.(*ast.FileNode) + return ok +} + +// resolveFeature resolves a feature for the given descriptor. If the given element +// is in a proto2 or proto3 syntax file, this skips resolution and just returns the +// relevant default (since such files are not allowed to override features). +// +// If neither the given element nor any of its ancestors override the given feature, +// the relevant default is returned. +func resolveFeature(element protoreflect.Descriptor, feature protoreflect.FieldDescriptor) protoreflect.Value { + edition := editions.GetEdition(element) + if edition == descriptorpb.Edition_EDITION_PROTO2 || edition == descriptorpb.Edition_EDITION_PROTO3 { + // these syntax levels can't specify features, so we can short-circuit the search + // through the descriptor hierarchy for feature overrides + defaults := editions.GetEditionDefaults(edition) + return defaults.ProtoReflect().Get(feature) // returns default value if field is not present + } + val, err := editions.ResolveFeature(element, feature) + if err == nil && val.IsValid() { + return val + } + defaults := editions.GetEditionDefaults(edition) + return defaults.ProtoReflect().Get(feature) +} + +func isJSONCompliant(d protoreflect.Descriptor) bool { + jsonFormat := resolveFeature(d, jsonFormatField) + return descriptorpb.FeatureSet_JsonFormat(jsonFormat.Enum()) == descriptorpb.FeatureSet_ALLOW +} diff --git a/vendor/github.com/bufbuild/protocompile/linker/doc.go b/vendor/github.com/bufbuild/protocompile/linker/doc.go new file mode 100644 index 0000000..93e68d9 --- /dev/null +++ b/vendor/github.com/bufbuild/protocompile/linker/doc.go @@ -0,0 +1,48 @@ +// Copyright 2020-2024 Buf Technologies, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Package linker contains logic and APIs related to linking a protobuf file. +// The process of linking involves resolving all symbol references to the +// referenced descriptor. The result of linking is a "rich" descriptor that +// is more useful than just a descriptor proto since the links allow easy +// traversal of a protobuf type schema and the relationships between elements. +// +// # Files +// +// This package uses an augmentation to protoreflect.FileDescriptor instances +// in the form of the File interface. There are also factory functions for +// promoting a FileDescriptor into a linker.File. This new interface provides +// additional methods for resolving symbols in the file. +// +// This interface is both the result of linking but also an input to the linking +// process, as all dependencies of a file to be linked must be provided in this +// form. The actual result of the Link function, a Result, is an even broader +// interface than File: The linker.Result interface provides even more functions, +// which are needed for subsequent compilation steps: interpreting options and +// generating source code info. +// +// # Symbols +// +// This package has a type named Symbols which represents a symbol table. This +// is usually an internal detail when linking, but callers can provide an +// instance so that symbols across multiple compile/link operations all have +// access to the same table. This allows for detection of cases where multiple +// files try to declare elements with conflicting fully-qualified names or +// declare extensions for a particular extendable message that have conflicting +// tag numbers. +// +// The calling code simply uses the same Symbols instance across all compile +// operations and if any files processed have such conflicts, they can be +// reported. +package linker diff --git a/vendor/github.com/bufbuild/protocompile/linker/files.go b/vendor/github.com/bufbuild/protocompile/linker/files.go new file mode 100644 index 0000000..51ce3a8 --- /dev/null +++ b/vendor/github.com/bufbuild/protocompile/linker/files.go @@ -0,0 +1,366 @@ +// Copyright 2020-2024 Buf Technologies, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package linker + +import ( + "fmt" + "strings" + + "google.golang.org/protobuf/reflect/protodesc" + "google.golang.org/protobuf/reflect/protoreflect" + "google.golang.org/protobuf/reflect/protoregistry" + "google.golang.org/protobuf/types/dynamicpb" + + "github.com/bufbuild/protocompile/walk" +) + +// File is like a super-powered protoreflect.FileDescriptor. It includes helpful +// methods for looking up elements in the descriptor and can be used to create a +// resolver for the entire transitive closure of the file's dependencies. (See +// ResolverFromFile.) +type File interface { + protoreflect.FileDescriptor + // FindDescriptorByName returns the given named element that is defined in + // this file. If no such element exists, nil is returned. + FindDescriptorByName(name protoreflect.FullName) protoreflect.Descriptor + // FindImportByPath returns the File corresponding to the given import path. + // If this file does not import the given path, nil is returned. + FindImportByPath(path string) File + // FindExtensionByNumber returns the extension descriptor for the given tag + // that extends the given message name. If no such extension is defined in this + // file, nil is returned. + FindExtensionByNumber(message protoreflect.FullName, tag protoreflect.FieldNumber) protoreflect.ExtensionTypeDescriptor +} + +// NewFile converts a protoreflect.FileDescriptor to a File. The given deps must +// contain all dependencies/imports of f. Also see NewFileRecursive. +func NewFile(f protoreflect.FileDescriptor, deps Files) (File, error) { + if asFile, ok := f.(File); ok { + return asFile, nil + } + checkedDeps := make(Files, f.Imports().Len()) + for i := 0; i < f.Imports().Len(); i++ { + imprt := f.Imports().Get(i) + dep := deps.FindFileByPath(imprt.Path()) + if dep == nil { + return nil, fmt.Errorf("cannot create File for %q: missing dependency for %q", f.Path(), imprt.Path()) + } + checkedDeps[i] = dep + } + return newFile(f, checkedDeps) +} + +func newFile(f protoreflect.FileDescriptor, deps Files) (File, error) { + descs := map[protoreflect.FullName]protoreflect.Descriptor{} + err := walk.Descriptors(f, func(d protoreflect.Descriptor) error { + if _, ok := descs[d.FullName()]; ok { + return fmt.Errorf("file %q contains multiple elements with the name %s", f.Path(), d.FullName()) + } + descs[d.FullName()] = d + return nil + }) + if err != nil { + return nil, err + } + return &file{ + FileDescriptor: f, + descs: descs, + deps: deps, + }, nil +} + +// NewFileRecursive recursively converts a protoreflect.FileDescriptor to a File. +// If f has any dependencies/imports, they are converted, too, including any and +// all transitive dependencies. +// +// If f already implements File, it is returned unchanged. +func NewFileRecursive(f protoreflect.FileDescriptor) (File, error) { + if asFile, ok := f.(File); ok { + return asFile, nil + } + return newFileRecursive(f, map[protoreflect.FileDescriptor]File{}) +} + +func newFileRecursive(fd protoreflect.FileDescriptor, seen map[protoreflect.FileDescriptor]File) (File, error) { + if res, ok := seen[fd]; ok { + if res == nil { + return nil, fmt.Errorf("import cycle encountered: file %s transitively imports itself", fd.Path()) + } + return res, nil + } + + if f, ok := fd.(File); ok { + seen[fd] = f + return f, nil + } + + seen[fd] = nil + deps := make([]File, fd.Imports().Len()) + for i := 0; i < fd.Imports().Len(); i++ { + imprt := fd.Imports().Get(i) + dep, err := newFileRecursive(imprt, seen) + if err != nil { + return nil, err + } + deps[i] = dep + } + + f, err := newFile(fd, deps) + if err != nil { + return nil, err + } + seen[fd] = f + return f, nil +} + +type file struct { + protoreflect.FileDescriptor + descs map[protoreflect.FullName]protoreflect.Descriptor + deps Files +} + +var _ File = (*file)(nil) + +func (f *file) FindDescriptorByName(name protoreflect.FullName) protoreflect.Descriptor { + return f.descs[name] +} + +func (f *file) FindImportByPath(path string) File { + return f.deps.FindFileByPath(path) +} + +func (f *file) FindExtensionByNumber(msg protoreflect.FullName, tag protoreflect.FieldNumber) protoreflect.ExtensionTypeDescriptor { + return findExtension(f, msg, tag) +} + +func (f *file) Unwrap() protoreflect.FileDescriptor { + return f.FileDescriptor +} + +// Files represents a set of protobuf files. It is a slice of File values, but +// also provides a method for easily looking up files by path and name. +type Files []File + +// FindFileByPath finds a file in f that has the given path and name. If f +// contains no such file, nil is returned. +func (f Files) FindFileByPath(path string) File { + for _, file := range f { + if file.Path() == path { + return file + } + } + return nil +} + +// AsResolver returns a Resolver that uses f as the source of descriptors. If +// a given query cannot be answered with the files in f, the query will fail +// with a protoregistry.NotFound error. The implementation just delegates calls +// to each file until a result is found. +// +// Also see ResolverFromFile. +func (f Files) AsResolver() Resolver { + return filesResolver(f) +} + +// Resolver is an interface that can resolve various kinds of queries about +// descriptors. It satisfies the resolver interfaces defined in protodesc +// and protoregistry packages. +type Resolver interface { + protodesc.Resolver + protoregistry.MessageTypeResolver + protoregistry.ExtensionTypeResolver +} + +// ResolverFromFile returns a Resolver that can resolve any element that is +// visible to the given file. It will search the given file, its imports, and +// any transitive public imports. +// +// Note that this function does not compute any additional indexes for efficient +// search, so queries generally take linear time, O(n) where n is the number of +// files whose elements are visible to the given file. Queries for an extension +// by number have runtime complexity that is linear with the number of messages +// and extensions defined across those files. +func ResolverFromFile(f File) Resolver { + return fileResolver{f: f} +} + +type fileResolver struct { + f File +} + +func (r fileResolver) FindFileByPath(path string) (protoreflect.FileDescriptor, error) { + return resolveInFile(r.f, false, nil, func(f File) (protoreflect.FileDescriptor, error) { + if f.Path() == path { + return f, nil + } + return nil, protoregistry.NotFound + }) +} + +func (r fileResolver) FindDescriptorByName(name protoreflect.FullName) (protoreflect.Descriptor, error) { + return resolveInFile(r.f, false, nil, func(f File) (protoreflect.Descriptor, error) { + if d := f.FindDescriptorByName(name); d != nil { + return d, nil + } + return nil, protoregistry.NotFound + }) +} + +func (r fileResolver) FindMessageByName(message protoreflect.FullName) (protoreflect.MessageType, error) { + return resolveInFile(r.f, false, nil, func(f File) (protoreflect.MessageType, error) { + d := f.FindDescriptorByName(message) + if d != nil { + md, ok := d.(protoreflect.MessageDescriptor) + if !ok { + return nil, fmt.Errorf("%q is %s, not a message", message, descriptorTypeWithArticle(d)) + } + return dynamicpb.NewMessageType(md), nil + } + return nil, protoregistry.NotFound + }) +} + +func (r fileResolver) FindMessageByURL(url string) (protoreflect.MessageType, error) { + fullName := messageNameFromURL(url) + return r.FindMessageByName(protoreflect.FullName(fullName)) +} + +func messageNameFromURL(url string) string { + lastSlash := strings.LastIndexByte(url, '/') + return url[lastSlash+1:] +} + +func (r fileResolver) FindExtensionByName(field protoreflect.FullName) (protoreflect.ExtensionType, error) { + return resolveInFile(r.f, false, nil, func(f File) (protoreflect.ExtensionType, error) { + d := f.FindDescriptorByName(field) + if d != nil { + fld, ok := d.(protoreflect.FieldDescriptor) + if !ok || !fld.IsExtension() { + return nil, fmt.Errorf("%q is %s, not an extension", field, descriptorTypeWithArticle(d)) + } + if extd, ok := fld.(protoreflect.ExtensionTypeDescriptor); ok { + return extd.Type(), nil + } + return dynamicpb.NewExtensionType(fld), nil + } + return nil, protoregistry.NotFound + }) +} + +func (r fileResolver) FindExtensionByNumber(message protoreflect.FullName, field protoreflect.FieldNumber) (protoreflect.ExtensionType, error) { + return resolveInFile(r.f, false, nil, func(f File) (protoreflect.ExtensionType, error) { + ext := findExtension(f, message, field) + if ext != nil { + return ext.Type(), nil + } + return nil, protoregistry.NotFound + }) +} + +type filesResolver []File + +func (r filesResolver) FindFileByPath(path string) (protoreflect.FileDescriptor, error) { + for _, f := range r { + if f.Path() == path { + return f, nil + } + } + return nil, protoregistry.NotFound +} + +func (r filesResolver) FindDescriptorByName(name protoreflect.FullName) (protoreflect.Descriptor, error) { + for _, f := range r { + result := f.FindDescriptorByName(name) + if result != nil { + return result, nil + } + } + return nil, protoregistry.NotFound +} + +func (r filesResolver) FindMessageByName(message protoreflect.FullName) (protoreflect.MessageType, error) { + for _, f := range r { + d := f.FindDescriptorByName(message) + if d != nil { + if md, ok := d.(protoreflect.MessageDescriptor); ok { + return dynamicpb.NewMessageType(md), nil + } + return nil, protoregistry.NotFound + } + } + return nil, protoregistry.NotFound +} + +func (r filesResolver) FindMessageByURL(url string) (protoreflect.MessageType, error) { + name := messageNameFromURL(url) + return r.FindMessageByName(protoreflect.FullName(name)) +} + +func (r filesResolver) FindExtensionByName(field protoreflect.FullName) (protoreflect.ExtensionType, error) { + for _, f := range r { + d := f.FindDescriptorByName(field) + if d != nil { + if extd, ok := d.(protoreflect.ExtensionTypeDescriptor); ok { + return extd.Type(), nil + } + if fld, ok := d.(protoreflect.FieldDescriptor); ok && fld.IsExtension() { + return dynamicpb.NewExtensionType(fld), nil + } + return nil, protoregistry.NotFound + } + } + return nil, protoregistry.NotFound +} + +func (r filesResolver) FindExtensionByNumber(message protoreflect.FullName, field protoreflect.FieldNumber) (protoreflect.ExtensionType, error) { + for _, f := range r { + ext := findExtension(f, message, field) + if ext != nil { + return ext.Type(), nil + } + } + return nil, protoregistry.NotFound +} + +type hasExtensionsAndMessages interface { + Messages() protoreflect.MessageDescriptors + Extensions() protoreflect.ExtensionDescriptors +} + +func findExtension(d hasExtensionsAndMessages, message protoreflect.FullName, field protoreflect.FieldNumber) protoreflect.ExtensionTypeDescriptor { + for i := 0; i < d.Extensions().Len(); i++ { + if extType := isExtensionMatch(d.Extensions().Get(i), message, field); extType != nil { + return extType + } + } + + for i := 0; i < d.Messages().Len(); i++ { + if extType := findExtension(d.Messages().Get(i), message, field); extType != nil { + return extType + } + } + + return nil // could not be found +} + +func isExtensionMatch(ext protoreflect.ExtensionDescriptor, message protoreflect.FullName, field protoreflect.FieldNumber) protoreflect.ExtensionTypeDescriptor { + if ext.Number() != field || ext.ContainingMessage().FullName() != message { + return nil + } + if extType, ok := ext.(protoreflect.ExtensionTypeDescriptor); ok { + return extType + } + return dynamicpb.NewExtensionType(ext).TypeDescriptor() +} diff --git a/vendor/github.com/bufbuild/protocompile/linker/linker.go b/vendor/github.com/bufbuild/protocompile/linker/linker.go new file mode 100644 index 0000000..6d87883 --- /dev/null +++ b/vendor/github.com/bufbuild/protocompile/linker/linker.go @@ -0,0 +1,153 @@ +// Copyright 2020-2024 Buf Technologies, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package linker + +import ( + "fmt" + + "google.golang.org/protobuf/reflect/protoreflect" + + "github.com/bufbuild/protocompile/ast" + "github.com/bufbuild/protocompile/parser" + "github.com/bufbuild/protocompile/reporter" +) + +// Link handles linking a parsed descriptor proto into a fully-linked descriptor. +// If the given parser.Result has imports, they must all be present in the given +// dependencies. +// +// The symbols value is optional and may be nil. If it is not nil, it must be the +// same instance used to create and link all of the given result's dependencies +// (or otherwise already have all dependencies imported). Otherwise, linking may +// fail with spurious errors resolving symbols. +// +// The handler value is used to report any link errors. If any such errors are +// reported, this function returns a non-nil error. The Result value returned +// also implements protoreflect.FileDescriptor. +// +// Note that linking does NOT interpret options. So options messages in the +// returned value have all values stored in UninterpretedOptions fields. +func Link(parsed parser.Result, dependencies Files, symbols *Symbols, handler *reporter.Handler) (Result, error) { + if symbols == nil { + symbols = &Symbols{} + } + prefix := parsed.FileDescriptorProto().GetPackage() + if prefix != "" { + prefix += "." + } + + for _, imp := range parsed.FileDescriptorProto().Dependency { + dep := dependencies.FindFileByPath(imp) + if dep == nil { + return nil, fmt.Errorf("dependencies is missing import %q", imp) + } + if err := symbols.Import(dep, handler); err != nil { + return nil, err + } + } + + r := &result{ + FileDescriptor: noOpFile, + Result: parsed, + deps: dependencies, + descriptors: map[string]protoreflect.Descriptor{}, + usedImports: map[string]struct{}{}, + prefix: prefix, + optionQualifiedNames: map[ast.IdentValueNode]string{}, + } + // First, we create the hierarchy of descendant descriptors. + r.createDescendants() + + // Then we can put all symbols into a single pool, which lets us ensure there + // are no duplicate symbols and will also let us resolve and revise all type + // references in next step. + if err := symbols.importResult(r, handler); err != nil { + return nil, err + } + + // After we've populated the pool, we can now try to resolve all type + // references. All references must be checked for correct type, any fields + // with enum types must be corrected (since we parse them as if they are + // message references since we don't actually know message or enum until + // link time), and references will be re-written to be fully-qualified + // references (e.g. start with a dot "."). + if err := r.resolveReferences(handler, symbols); err != nil { + return nil, err + } + + return r, handler.Error() +} + +// Result is the result of linking. This is a protoreflect.FileDescriptor, but +// with some additional methods for exposing additional information, such as the +// for accessing the input AST or file descriptor. +// +// It also provides Resolve* methods, for looking up enums, messages, and +// extensions that are available to the protobuf source file this result +// represents. An element is "available" if it meets any of the following +// criteria: +// 1. The element is defined in this file itself. +// 2. The element is defined in a file that is directly imported by this file. +// 3. The element is "available" to a file that is directly imported by this +// file as a public import. +// +// Other elements, even if in the transitive closure of this file, are not +// available and thus won't be returned by these methods. +type Result interface { + File + parser.Result + + // ResolveMessageLiteralExtensionName returns the fully qualified name for + // an identifier for extension field names in message literals. + ResolveMessageLiteralExtensionName(ast.IdentValueNode) string + // ValidateOptions runs some validation checks on the descriptor that can only + // be done after options are interpreted. Any errors or warnings encountered + // will be reported via the given handler. If any error is reported, this + // function returns a non-nil error. + ValidateOptions(handler *reporter.Handler, symbols *Symbols) error + // CheckForUnusedImports is used to report warnings for unused imports. This + // should be called after options have been interpreted. Otherwise, the logic + // could incorrectly report imports as unused if the only symbol used were a + // custom option. + CheckForUnusedImports(handler *reporter.Handler) + // PopulateSourceCodeInfo is used to populate source code info for the file + // descriptor. This step requires that the underlying descriptor proto have + // its `source_code_info` field populated. This is typically a post-process + // step separate from linking, because computing source code info requires + // interpreting options (which is done after linking). + PopulateSourceCodeInfo() + + // RemoveAST drops the AST information from this result. + RemoveAST() +} + +// ErrorUnusedImport may be passed to a warning reporter when an unused +// import is detected. The error the reporter receives will be wrapped +// with source position that indicates the file and line where the import +// statement appeared. +type ErrorUnusedImport interface { + error + UnusedImport() string +} + +type errUnusedImport string + +func (e errUnusedImport) Error() string { + return fmt.Sprintf("import %q not used", string(e)) +} + +func (e errUnusedImport) UnusedImport() string { + return string(e) +} diff --git a/vendor/github.com/bufbuild/protocompile/linker/pathkey_no_unsafe.go b/vendor/github.com/bufbuild/protocompile/linker/pathkey_no_unsafe.go new file mode 100644 index 0000000..e00debc --- /dev/null +++ b/vendor/github.com/bufbuild/protocompile/linker/pathkey_no_unsafe.go @@ -0,0 +1,35 @@ +// Copyright 2020-2024 Buf Technologies, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//go:build appengine || gopherjs || purego +// +build appengine gopherjs purego + +// NB: other environments where unsafe is inappropriate should use "purego" build tag +// https://github.com/golang/go/issues/23172 + +package linker + +import ( + "reflect" + + "google.golang.org/protobuf/reflect/protoreflect" +) + +func pathKey(p protoreflect.SourcePath) interface{} { + rv := reflect.ValueOf(p) + arrayType := reflect.ArrayOf(rv.Len(), rv.Type().Elem()) + array := reflect.New(arrayType).Elem() + reflect.Copy(array, rv) + return array.Interface() +} diff --git a/vendor/github.com/bufbuild/protocompile/linker/pathkey_unsafe.go b/vendor/github.com/bufbuild/protocompile/linker/pathkey_unsafe.go new file mode 100644 index 0000000..aa33e74 --- /dev/null +++ b/vendor/github.com/bufbuild/protocompile/linker/pathkey_unsafe.go @@ -0,0 +1,40 @@ +// Copyright 2020-2024 Buf Technologies, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//go:build !appengine && !gopherjs && !purego +// +build !appengine,!gopherjs,!purego + +// NB: other environments where unsafe is inappropriate should use "purego" build tag +// https://github.com/golang/go/issues/23172 + +package linker + +import ( + "reflect" + "unsafe" + + "google.golang.org/protobuf/reflect/protoreflect" +) + +var pathElementType = reflect.TypeOf(protoreflect.SourcePath{}).Elem() + +func pathKey(p protoreflect.SourcePath) interface{} { + if p == nil { + // Reflection code below doesn't work with nil slices + return [0]int32{} + } + hdr := (*reflect.SliceHeader)(unsafe.Pointer(reflect.ValueOf(&p).Pointer())) + array := reflect.NewAt(reflect.ArrayOf(hdr.Len, pathElementType), unsafe.Pointer(hdr.Data)) + return array.Elem().Interface() +} diff --git a/vendor/github.com/bufbuild/protocompile/linker/pool.go b/vendor/github.com/bufbuild/protocompile/linker/pool.go new file mode 100644 index 0000000..3609edc --- /dev/null +++ b/vendor/github.com/bufbuild/protocompile/linker/pool.go @@ -0,0 +1,131 @@ +// Copyright 2020-2024 Buf Technologies, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package linker + +import "google.golang.org/protobuf/types/descriptorpb" + +// allocPool helps allocate descriptor instances. Instead of allocating +// them one at a time, we allocate a pool -- a large, flat slice to hold +// all descriptors of a particular kind for a file. We then use capacity +// in the pool when we need space for individual descriptors. +type allocPool struct { + numMessages int + numFields int + numOneofs int + numEnums int + numEnumValues int + numExtensions int + numServices int + numMethods int + + messages []msgDescriptor + fields []fldDescriptor + oneofs []oneofDescriptor + enums []enumDescriptor + enumVals []enValDescriptor + extensions []extTypeDescriptor + services []svcDescriptor + methods []mtdDescriptor +} + +func newAllocPool(file *descriptorpb.FileDescriptorProto) *allocPool { + var pool allocPool + pool.countElements(file) + pool.messages = make([]msgDescriptor, pool.numMessages) + pool.fields = make([]fldDescriptor, pool.numFields) + pool.oneofs = make([]oneofDescriptor, pool.numOneofs) + pool.enums = make([]enumDescriptor, pool.numEnums) + pool.enumVals = make([]enValDescriptor, pool.numEnumValues) + pool.extensions = make([]extTypeDescriptor, pool.numExtensions) + pool.services = make([]svcDescriptor, pool.numServices) + pool.methods = make([]mtdDescriptor, pool.numMethods) + return &pool +} + +func (p *allocPool) getMessages(count int) []msgDescriptor { + allocated := p.messages[:count] + p.messages = p.messages[count:] + return allocated +} + +func (p *allocPool) getFields(count int) []fldDescriptor { + allocated := p.fields[:count] + p.fields = p.fields[count:] + return allocated +} + +func (p *allocPool) getOneofs(count int) []oneofDescriptor { + allocated := p.oneofs[:count] + p.oneofs = p.oneofs[count:] + return allocated +} + +func (p *allocPool) getEnums(count int) []enumDescriptor { + allocated := p.enums[:count] + p.enums = p.enums[count:] + return allocated +} + +func (p *allocPool) getEnumValues(count int) []enValDescriptor { + allocated := p.enumVals[:count] + p.enumVals = p.enumVals[count:] + return allocated +} + +func (p *allocPool) getExtensions(count int) []extTypeDescriptor { + allocated := p.extensions[:count] + p.extensions = p.extensions[count:] + return allocated +} + +func (p *allocPool) getServices(count int) []svcDescriptor { + allocated := p.services[:count] + p.services = p.services[count:] + return allocated +} + +func (p *allocPool) getMethods(count int) []mtdDescriptor { + allocated := p.methods[:count] + p.methods = p.methods[count:] + return allocated +} + +func (p *allocPool) countElements(file *descriptorpb.FileDescriptorProto) { + p.countElementsInMessages(file.MessageType) + p.countElementsInEnums(file.EnumType) + p.numExtensions += len(file.Extension) + p.numServices += len(file.Service) + for _, svc := range file.Service { + p.numMethods += len(svc.Method) + } +} + +func (p *allocPool) countElementsInMessages(msgs []*descriptorpb.DescriptorProto) { + p.numMessages += len(msgs) + for _, msg := range msgs { + p.numFields += len(msg.Field) + p.numOneofs += len(msg.OneofDecl) + p.countElementsInMessages(msg.NestedType) + p.countElementsInEnums(msg.EnumType) + p.numExtensions += len(msg.Extension) + } +} + +func (p *allocPool) countElementsInEnums(enums []*descriptorpb.EnumDescriptorProto) { + p.numEnums += len(enums) + for _, enum := range enums { + p.numEnumValues += len(enum.Value) + } +} diff --git a/vendor/github.com/bufbuild/protocompile/linker/resolve.go b/vendor/github.com/bufbuild/protocompile/linker/resolve.go new file mode 100644 index 0000000..cf30148 --- /dev/null +++ b/vendor/github.com/bufbuild/protocompile/linker/resolve.go @@ -0,0 +1,835 @@ +// Copyright 2020-2024 Buf Technologies, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package linker + +import ( + "errors" + "fmt" + "strings" + + "google.golang.org/protobuf/proto" + "google.golang.org/protobuf/reflect/protoreflect" + "google.golang.org/protobuf/reflect/protoregistry" + "google.golang.org/protobuf/types/descriptorpb" + + "github.com/bufbuild/protocompile/ast" + "github.com/bufbuild/protocompile/internal" + "github.com/bufbuild/protocompile/reporter" + "github.com/bufbuild/protocompile/walk" +) + +func (r *result) ResolveMessageLiteralExtensionName(node ast.IdentValueNode) string { + return r.optionQualifiedNames[node] +} + +func (r *result) resolveElement(name protoreflect.FullName, checkedCache []string) protoreflect.Descriptor { + if len(name) > 0 && name[0] == '.' { + name = name[1:] + } + res, _ := resolveInFile(r, false, checkedCache[:0], func(f File) (protoreflect.Descriptor, error) { + d := resolveElementInFile(name, f) + if d != nil { + return d, nil + } + return nil, protoregistry.NotFound + }) + return res +} + +func resolveInFile[T any](f File, publicImportsOnly bool, checked []string, fn func(File) (T, error)) (T, error) { + var zero T + path := f.Path() + for _, str := range checked { + if str == path { + // already checked + return zero, protoregistry.NotFound + } + } + checked = append(checked, path) + + res, err := fn(f) + if err == nil { + // found it + return res, nil + } + if !errors.Is(err, protoregistry.NotFound) { + return zero, err + } + + imports := f.Imports() + for i, l := 0, imports.Len(); i < l; i++ { + imp := imports.Get(i) + if publicImportsOnly && !imp.IsPublic { + continue + } + res, err := resolveInFile(f.FindImportByPath(imp.Path()), true, checked, fn) + if errors.Is(err, protoregistry.NotFound) { + continue + } + if err != nil { + return zero, err + } + if !imp.IsPublic { + if r, ok := f.(*result); ok { + r.markUsed(imp.Path()) + } + } + return res, nil + } + return zero, err +} + +func (r *result) markUsed(importPath string) { + r.usedImports[importPath] = struct{}{} +} + +func (r *result) CheckForUnusedImports(handler *reporter.Handler) { + fd := r.FileDescriptorProto() + file, _ := r.FileNode().(*ast.FileNode) + for i, dep := range fd.Dependency { + if _, ok := r.usedImports[dep]; !ok { + isPublic := false + // it's fine if it's a public import + for _, j := range fd.PublicDependency { + if i == int(j) { + isPublic = true + break + } + } + if isPublic { + continue + } + span := ast.UnknownSpan(fd.GetName()) + if file != nil { + for _, decl := range file.Decls { + imp, ok := decl.(*ast.ImportNode) + if ok && imp.Name.AsString() == dep { + span = file.NodeInfo(imp) + } + } + } + handler.HandleWarningWithPos(span, errUnusedImport(dep)) + } + } +} + +func descriptorTypeWithArticle(d protoreflect.Descriptor) string { + switch d := d.(type) { + case protoreflect.MessageDescriptor: + return "a message" + case protoreflect.FieldDescriptor: + if d.IsExtension() { + return "an extension" + } + return "a field" + case protoreflect.OneofDescriptor: + return "a oneof" + case protoreflect.EnumDescriptor: + return "an enum" + case protoreflect.EnumValueDescriptor: + return "an enum value" + case protoreflect.ServiceDescriptor: + return "a service" + case protoreflect.MethodDescriptor: + return "a method" + case protoreflect.FileDescriptor: + return "a file" + default: + // shouldn't be possible + return fmt.Sprintf("a %T", d) + } +} + +func (r *result) createDescendants() { + fd := r.FileDescriptorProto() + pool := newAllocPool(fd) + prefix := "" + if fd.GetPackage() != "" { + prefix = fd.GetPackage() + "." + } + r.imports = r.createImports() + r.messages = r.createMessages(prefix, r, fd.MessageType, pool) + r.enums = r.createEnums(prefix, r, fd.EnumType, pool) + r.extensions = r.createExtensions(prefix, r, fd.Extension, pool) + r.services = r.createServices(prefix, fd.Service, pool) +} + +func (r *result) resolveReferences(handler *reporter.Handler, s *Symbols) error { + fd := r.FileDescriptorProto() + checkedCache := make([]string, 0, 16) + scopes := []scope{fileScope(r, checkedCache)} + if fd.Options != nil { + if err := r.resolveOptions(handler, "file", protoreflect.FullName(fd.GetName()), fd.Options.UninterpretedOption, scopes, checkedCache); err != nil { + return err + } + } + + // This is to de-dupe extendee-releated error messages when the same + // extendee is referenced from multiple extension field definitions. + // We leave it nil if there's no AST. + var extendeeNodes map[ast.Node]struct{} + + return walk.DescriptorsEnterAndExit(r, + func(d protoreflect.Descriptor) error { + fqn := d.FullName() + switch d := d.(type) { + case *msgDescriptor: + // Strangely, when protoc resolves extension names, it uses the *enclosing* scope + // instead of the message's scope. So if the message contains an extension named "i", + // an option cannot refer to it as simply "i" but must qualify it (at a minimum "Msg.i"). + // So we don't add this messages scope to our scopes slice until *after* we do options. + if d.proto.Options != nil { + if err := r.resolveOptions(handler, "message", fqn, d.proto.Options.UninterpretedOption, scopes, checkedCache); err != nil { + return err + } + } + scopes = append(scopes, messageScope(r, fqn)) // push new scope on entry + // walk only visits descriptors, so we need to loop over extension ranges ourselves + for _, er := range d.proto.ExtensionRange { + if er.Options != nil { + erName := protoreflect.FullName(fmt.Sprintf("%s:%d-%d", fqn, er.GetStart(), er.GetEnd()-1)) + if err := r.resolveOptions(handler, "extension range", erName, er.Options.UninterpretedOption, scopes, checkedCache); err != nil { + return err + } + } + } + case *extTypeDescriptor: + if d.field.proto.Options != nil { + if err := r.resolveOptions(handler, "extension", fqn, d.field.proto.Options.UninterpretedOption, scopes, checkedCache); err != nil { + return err + } + } + if extendeeNodes == nil && r.AST() != nil { + extendeeNodes = map[ast.Node]struct{}{} + } + if err := resolveFieldTypes(&d.field, handler, extendeeNodes, s, scopes, checkedCache); err != nil { + return err + } + if r.Syntax() == protoreflect.Proto3 && !allowedProto3Extendee(d.field.proto.GetExtendee()) { + file := r.FileNode() + node := r.FieldNode(d.field.proto).FieldExtendee() + if err := handler.HandleErrorf(file.NodeInfo(node), "extend blocks in proto3 can only be used to define custom options"); err != nil { + return err + } + } + case *fldDescriptor: + if d.proto.Options != nil { + if err := r.resolveOptions(handler, "field", fqn, d.proto.Options.UninterpretedOption, scopes, checkedCache); err != nil { + return err + } + } + if err := resolveFieldTypes(d, handler, nil, s, scopes, checkedCache); err != nil { + return err + } + case *oneofDescriptor: + if d.proto.Options != nil { + if err := r.resolveOptions(handler, "oneof", fqn, d.proto.Options.UninterpretedOption, scopes, checkedCache); err != nil { + return err + } + } + case *enumDescriptor: + if d.proto.Options != nil { + if err := r.resolveOptions(handler, "enum", fqn, d.proto.Options.UninterpretedOption, scopes, checkedCache); err != nil { + return err + } + } + case *enValDescriptor: + if d.proto.Options != nil { + if err := r.resolveOptions(handler, "enum value", fqn, d.proto.Options.UninterpretedOption, scopes, checkedCache); err != nil { + return err + } + } + case *svcDescriptor: + if d.proto.Options != nil { + if err := r.resolveOptions(handler, "service", fqn, d.proto.Options.UninterpretedOption, scopes, checkedCache); err != nil { + return err + } + } + // not a message, but same scoping rules for nested elements as if it were + scopes = append(scopes, messageScope(r, fqn)) // push new scope on entry + case *mtdDescriptor: + if d.proto.Options != nil { + if err := r.resolveOptions(handler, "method", fqn, d.proto.Options.UninterpretedOption, scopes, checkedCache); err != nil { + return err + } + } + if err := resolveMethodTypes(d, handler, scopes, checkedCache); err != nil { + return err + } + } + return nil + }, + func(d protoreflect.Descriptor) error { + switch d.(type) { + case protoreflect.MessageDescriptor, protoreflect.ServiceDescriptor: + // pop message scope on exit + scopes = scopes[:len(scopes)-1] + } + return nil + }) +} + +var allowedProto3Extendees = map[string]struct{}{ + ".google.protobuf.FileOptions": {}, + ".google.protobuf.MessageOptions": {}, + ".google.protobuf.FieldOptions": {}, + ".google.protobuf.OneofOptions": {}, + ".google.protobuf.ExtensionRangeOptions": {}, + ".google.protobuf.EnumOptions": {}, + ".google.protobuf.EnumValueOptions": {}, + ".google.protobuf.ServiceOptions": {}, + ".google.protobuf.MethodOptions": {}, +} + +func allowedProto3Extendee(n string) bool { + if n == "" { + // not an extension, allowed + return true + } + _, ok := allowedProto3Extendees[n] + return ok +} + +func resolveFieldTypes(f *fldDescriptor, handler *reporter.Handler, extendees map[ast.Node]struct{}, s *Symbols, scopes []scope, checkedCache []string) error { + r := f.file + fld := f.proto + file := r.FileNode() + node := r.FieldNode(fld) + kind := "field" + if fld.GetExtendee() != "" { + kind = "extension" + var alreadyReported bool + if extendees != nil { + _, alreadyReported = extendees[node.FieldExtendee()] + if !alreadyReported { + extendees[node.FieldExtendee()] = struct{}{} + } + } + dsc := r.resolve(fld.GetExtendee(), false, scopes, checkedCache) + if dsc == nil { + if alreadyReported { + return nil + } + var extendeePrefix string + if extendees == nil { + extendeePrefix = kind + " " + f.fqn + ": " + } + return handler.HandleErrorf(file.NodeInfo(node.FieldExtendee()), "%sunknown extendee type %s", extendeePrefix, fld.GetExtendee()) + } + if isSentinelDescriptor(dsc) { + if alreadyReported { + return nil + } + var extendeePrefix string + if extendees == nil { + extendeePrefix = kind + " " + f.fqn + ": " + } + return handler.HandleErrorf(file.NodeInfo(node.FieldExtendee()), "%sunknown extendee type %s; resolved to %s which is not defined; consider using a leading dot", extendeePrefix, fld.GetExtendee(), dsc.FullName()) + } + extd, ok := dsc.(protoreflect.MessageDescriptor) + if !ok { + if alreadyReported { + return nil + } + var extendeePrefix string + if extendees == nil { + extendeePrefix = kind + " " + f.fqn + ": " + } + return handler.HandleErrorf(file.NodeInfo(node.FieldExtendee()), "%sextendee is invalid: %s is %s, not a message", extendeePrefix, dsc.FullName(), descriptorTypeWithArticle(dsc)) + } + + f.extendee = extd + extendeeName := "." + string(dsc.FullName()) + if fld.GetExtendee() != extendeeName { + fld.Extendee = proto.String(extendeeName) + } + // make sure the tag number is in range + found := false + tag := protoreflect.FieldNumber(fld.GetNumber()) + for i := 0; i < extd.ExtensionRanges().Len(); i++ { + rng := extd.ExtensionRanges().Get(i) + if tag >= rng[0] && tag < rng[1] { + found = true + break + } + } + if !found { + if err := handler.HandleErrorf(file.NodeInfo(node.FieldTag()), "%s %s: tag %d is not in valid range for extended type %s", kind, f.fqn, tag, dsc.FullName()); err != nil { + return err + } + } else { + // make sure tag is not a duplicate + if err := s.AddExtension(packageFor(dsc), dsc.FullName(), tag, file.NodeInfo(node.FieldTag()), handler); err != nil { + return err + } + } + } else if f.proto.OneofIndex != nil { + parent := f.parent.(protoreflect.MessageDescriptor) //nolint:errcheck + index := int(f.proto.GetOneofIndex()) + f.oneof = parent.Oneofs().Get(index) + } + + if fld.GetTypeName() == "" { + // scalar type; no further resolution required + return nil + } + + dsc := r.resolve(fld.GetTypeName(), true, scopes, checkedCache) + if dsc == nil { + return handler.HandleErrorf(file.NodeInfo(node.FieldType()), "%s %s: unknown type %s", kind, f.fqn, fld.GetTypeName()) + } + if isSentinelDescriptor(dsc) { + return handler.HandleErrorf(file.NodeInfo(node.FieldType()), "%s %s: unknown type %s; resolved to %s which is not defined; consider using a leading dot", kind, f.fqn, fld.GetTypeName(), dsc.FullName()) + } + switch dsc := dsc.(type) { + case protoreflect.MessageDescriptor: + if dsc.IsMapEntry() { + isValid := false + switch node.(type) { + case *ast.MapFieldNode: + // We have an AST for this file and can see this field is from a map declaration + isValid = true + case *ast.NoSourceNode: + // We don't have an AST for the file (it came from a provided descriptor). So we + // need to validate that it's not an illegal reference. To be valid, the field + // must be repeated and the entry type must be nested in the same enclosing + // message as the field. + isValid = isValidMap(f, dsc) + if isValid && f.index > 0 { + // also make sure there are no earlier fields that are valid for this map entry + flds := f.Parent().(protoreflect.MessageDescriptor).Fields() + for i := 0; i < f.index; i++ { + if isValidMap(flds.Get(i), dsc) { + isValid = false + break + } + } + } + } + if !isValid { + return handler.HandleErrorf(file.NodeInfo(node.FieldType()), "%s %s: %s is a synthetic map entry and may not be referenced explicitly", kind, f.fqn, dsc.FullName()) + } + } + typeName := "." + string(dsc.FullName()) + if fld.GetTypeName() != typeName { + fld.TypeName = proto.String(typeName) + } + if fld.Type == nil { + // if type was tentatively unset, we now know it's actually a message + fld.Type = descriptorpb.FieldDescriptorProto_TYPE_MESSAGE.Enum() + } else if fld.GetType() != descriptorpb.FieldDescriptorProto_TYPE_MESSAGE && fld.GetType() != descriptorpb.FieldDescriptorProto_TYPE_GROUP { + return handler.HandleErrorf(file.NodeInfo(node.FieldType()), "%s %s: descriptor proto indicates type %v but should be %v", kind, f.fqn, fld.GetType(), descriptorpb.FieldDescriptorProto_TYPE_MESSAGE) + } + f.msgType = dsc + case protoreflect.EnumDescriptor: + typeName := "." + string(dsc.FullName()) + if fld.GetTypeName() != typeName { + fld.TypeName = proto.String(typeName) + } + if fld.Type == nil { + // the type was tentatively unset, but now we know it's actually an enum + fld.Type = descriptorpb.FieldDescriptorProto_TYPE_ENUM.Enum() + } else if fld.GetType() != descriptorpb.FieldDescriptorProto_TYPE_ENUM { + return handler.HandleErrorf(file.NodeInfo(node.FieldType()), "%s %s: descriptor proto indicates type %v but should be %v", kind, f.fqn, fld.GetType(), descriptorpb.FieldDescriptorProto_TYPE_ENUM) + } + f.enumType = dsc + default: + return handler.HandleErrorf(file.NodeInfo(node.FieldType()), "%s %s: invalid type: %s is %s, not a message or enum", kind, f.fqn, dsc.FullName(), descriptorTypeWithArticle(dsc)) + } + return nil +} + +func packageFor(dsc protoreflect.Descriptor) protoreflect.FullName { + if dsc.ParentFile() != nil { + return dsc.ParentFile().Package() + } + // Can't access package? Make a best effort guess. + return dsc.FullName().Parent() +} + +func isValidMap(mapField protoreflect.FieldDescriptor, mapEntry protoreflect.MessageDescriptor) bool { + return !mapField.IsExtension() && + mapEntry.Parent() == mapField.ContainingMessage() && + mapField.Cardinality() == protoreflect.Repeated && + string(mapEntry.Name()) == internal.InitCap(internal.JSONName(string(mapField.Name())))+"Entry" +} + +func resolveMethodTypes(m *mtdDescriptor, handler *reporter.Handler, scopes []scope, checkedCache []string) error { + scope := "method " + m.fqn + r := m.file + mtd := m.proto + file := r.FileNode() + node := r.MethodNode(mtd) + dsc := r.resolve(mtd.GetInputType(), false, scopes, checkedCache) + if dsc == nil { + if err := handler.HandleErrorf(file.NodeInfo(node.GetInputType()), "%s: unknown request type %s", scope, mtd.GetInputType()); err != nil { + return err + } + } else if isSentinelDescriptor(dsc) { + if err := handler.HandleErrorf(file.NodeInfo(node.GetInputType()), "%s: unknown request type %s; resolved to %s which is not defined; consider using a leading dot", scope, mtd.GetInputType(), dsc.FullName()); err != nil { + return err + } + } else if msg, ok := dsc.(protoreflect.MessageDescriptor); !ok { + if err := handler.HandleErrorf(file.NodeInfo(node.GetInputType()), "%s: invalid request type: %s is %s, not a message", scope, dsc.FullName(), descriptorTypeWithArticle(dsc)); err != nil { + return err + } + } else { + typeName := "." + string(dsc.FullName()) + if mtd.GetInputType() != typeName { + mtd.InputType = proto.String(typeName) + } + m.inputType = msg + } + + // TODO: make input and output type resolution more DRY + dsc = r.resolve(mtd.GetOutputType(), false, scopes, checkedCache) + if dsc == nil { + if err := handler.HandleErrorf(file.NodeInfo(node.GetOutputType()), "%s: unknown response type %s", scope, mtd.GetOutputType()); err != nil { + return err + } + } else if isSentinelDescriptor(dsc) { + if err := handler.HandleErrorf(file.NodeInfo(node.GetOutputType()), "%s: unknown response type %s; resolved to %s which is not defined; consider using a leading dot", scope, mtd.GetOutputType(), dsc.FullName()); err != nil { + return err + } + } else if msg, ok := dsc.(protoreflect.MessageDescriptor); !ok { + if err := handler.HandleErrorf(file.NodeInfo(node.GetOutputType()), "%s: invalid response type: %s is %s, not a message", scope, dsc.FullName(), descriptorTypeWithArticle(dsc)); err != nil { + return err + } + } else { + typeName := "." + string(dsc.FullName()) + if mtd.GetOutputType() != typeName { + mtd.OutputType = proto.String(typeName) + } + m.outputType = msg + } + + return nil +} + +func (r *result) resolveOptions(handler *reporter.Handler, elemType string, elemName protoreflect.FullName, opts []*descriptorpb.UninterpretedOption, scopes []scope, checkedCache []string) error { + mc := &internal.MessageContext{ + File: r, + ElementName: string(elemName), + ElementType: elemType, + } + file := r.FileNode() +opts: + for _, opt := range opts { + // resolve any extension names found in option names + for _, nm := range opt.Name { + if nm.GetIsExtension() { + node := r.OptionNamePartNode(nm) + fqn, err := r.resolveExtensionName(nm.GetNamePart(), scopes, checkedCache) + if err != nil { + if err := handler.HandleErrorf(file.NodeInfo(node), "%v%v", mc, err); err != nil { + return err + } + continue opts + } + nm.NamePart = proto.String(fqn) + } + } + // also resolve any extension names found inside message literals in option values + mc.Option = opt + optVal := r.OptionNode(opt).GetValue() + if err := r.resolveOptionValue(handler, mc, optVal, scopes, checkedCache); err != nil { + return err + } + mc.Option = nil + } + return nil +} + +func (r *result) resolveOptionValue(handler *reporter.Handler, mc *internal.MessageContext, val ast.ValueNode, scopes []scope, checkedCache []string) error { + optVal := val.Value() + switch optVal := optVal.(type) { + case []ast.ValueNode: + origPath := mc.OptAggPath + defer func() { + mc.OptAggPath = origPath + }() + for i, v := range optVal { + mc.OptAggPath = fmt.Sprintf("%s[%d]", origPath, i) + if err := r.resolveOptionValue(handler, mc, v, scopes, checkedCache); err != nil { + return err + } + } + case []*ast.MessageFieldNode: + origPath := mc.OptAggPath + defer func() { + mc.OptAggPath = origPath + }() + for _, fld := range optVal { + // check for extension name + if fld.Name.IsExtension() { + // Confusingly, an extension reference inside a message literal cannot refer to + // elements in the same enclosing message without a qualifier. Basically, we + // treat this as if there were no message scopes, so only the package name is + // used for resolving relative references. (Inconsistent protoc behavior, but + // likely due to how it re-uses C++ text format implementation, and normal text + // format doesn't expect that kind of relative reference.) + scopes := scopes[:1] // first scope is file, the rest are enclosing messages + fqn, err := r.resolveExtensionName(string(fld.Name.Name.AsIdentifier()), scopes, checkedCache) + if err != nil { + if err := handler.HandleErrorf(r.FileNode().NodeInfo(fld.Name.Name), "%v%v", mc, err); err != nil { + return err + } + } else { + r.optionQualifiedNames[fld.Name.Name] = fqn + } + } + + // recurse into value + mc.OptAggPath = origPath + if origPath != "" { + mc.OptAggPath += "." + } + if fld.Name.IsExtension() { + mc.OptAggPath = fmt.Sprintf("%s[%s]", mc.OptAggPath, string(fld.Name.Name.AsIdentifier())) + } else { + mc.OptAggPath = fmt.Sprintf("%s%s", mc.OptAggPath, string(fld.Name.Name.AsIdentifier())) + } + + if err := r.resolveOptionValue(handler, mc, fld.Val, scopes, checkedCache); err != nil { + return err + } + } + } + return nil +} + +func (r *result) resolveExtensionName(name string, scopes []scope, checkedCache []string) (string, error) { + dsc := r.resolve(name, false, scopes, checkedCache) + if dsc == nil { + return "", fmt.Errorf("unknown extension %s", name) + } + if isSentinelDescriptor(dsc) { + return "", fmt.Errorf("unknown extension %s; resolved to %s which is not defined; consider using a leading dot", name, dsc.FullName()) + } + if ext, ok := dsc.(protoreflect.FieldDescriptor); !ok { + return "", fmt.Errorf("invalid extension: %s is %s, not an extension", name, descriptorTypeWithArticle(dsc)) + } else if !ext.IsExtension() { + return "", fmt.Errorf("invalid extension: %s is a field but not an extension", name) + } + return string("." + dsc.FullName()), nil +} + +func (r *result) resolve(name string, onlyTypes bool, scopes []scope, checkedCache []string) protoreflect.Descriptor { + if strings.HasPrefix(name, ".") { + // already fully-qualified + return r.resolveElement(protoreflect.FullName(name[1:]), checkedCache) + } + // unqualified, so we look in the enclosing (last) scope first and move + // towards outermost (first) scope, trying to resolve the symbol + pos := strings.IndexByte(name, '.') + firstName := name + if pos > 0 { + firstName = name[:pos] + } + var bestGuess protoreflect.Descriptor + for i := len(scopes) - 1; i >= 0; i-- { + d := scopes[i](firstName, name) + if d != nil { + // In `protoc`, it will skip a match of the wrong type and move on + // to the next scope, but only if the reference is unqualified. So + // we mirror that behavior here. When we skip and move on, we go + // ahead and save the match of the wrong type so we can at least use + // it to construct a better error in the event that we don't find + // any match of the right type. + if !onlyTypes || isType(d) || firstName != name { + return d + } + if bestGuess == nil { + bestGuess = d + } + } + } + // we return best guess, even though it was not an allowed kind of + // descriptor, so caller can print a better error message (e.g. + // indicating that the name was found but that it's the wrong type) + return bestGuess +} + +func isType(d protoreflect.Descriptor) bool { + switch d.(type) { + case protoreflect.MessageDescriptor, protoreflect.EnumDescriptor: + return true + } + return false +} + +// scope represents a lexical scope in a proto file in which messages and enums +// can be declared. +type scope func(firstName, fullName string) protoreflect.Descriptor + +func fileScope(r *result, checkedCache []string) scope { + // we search symbols in this file, but also symbols in other files that have + // the same package as this file or a "parent" package (in protobuf, + // packages are a hierarchy like C++ namespaces) + prefixes := internal.CreatePrefixList(r.FileDescriptorProto().GetPackage()) + querySymbol := func(n string) protoreflect.Descriptor { + return r.resolveElement(protoreflect.FullName(n), checkedCache) + } + return func(firstName, fullName string) protoreflect.Descriptor { + for _, prefix := range prefixes { + var n1, n string + if prefix == "" { + // exhausted all prefixes, so it must be in this one + n1, n = fullName, fullName + } else { + n = prefix + "." + fullName + n1 = prefix + "." + firstName + } + d := resolveElementRelative(n1, n, querySymbol) + if d != nil { + return d + } + } + return nil + } +} + +func messageScope(r *result, messageName protoreflect.FullName) scope { + querySymbol := func(n string) protoreflect.Descriptor { + return resolveElementInFile(protoreflect.FullName(n), r) + } + return func(firstName, fullName string) protoreflect.Descriptor { + n1 := string(messageName) + "." + firstName + n := string(messageName) + "." + fullName + return resolveElementRelative(n1, n, querySymbol) + } +} + +func resolveElementRelative(firstName, fullName string, query func(name string) protoreflect.Descriptor) protoreflect.Descriptor { + d := query(firstName) + if d == nil { + return nil + } + if firstName == fullName { + return d + } + if !isAggregateDescriptor(d) { + // can't possibly find the rest of full name if + // the first name indicated a leaf descriptor + return nil + } + d = query(fullName) + if d == nil { + return newSentinelDescriptor(fullName) + } + return d +} + +func resolveElementInFile(name protoreflect.FullName, f File) protoreflect.Descriptor { + d := f.FindDescriptorByName(name) + if d != nil { + return d + } + + if matchesPkgNamespace(name, f.Package()) { + // this sentinel means the name is a valid namespace but + // does not refer to a descriptor + return newSentinelDescriptor(string(name)) + } + return nil +} + +func matchesPkgNamespace(fqn, pkg protoreflect.FullName) bool { + if pkg == "" { + return false + } + if fqn == pkg { + return true + } + if len(pkg) > len(fqn) && strings.HasPrefix(string(pkg), string(fqn)) { + // if char after fqn is a dot, then fqn is a namespace + if pkg[len(fqn)] == '.' { + return true + } + } + return false +} + +func isAggregateDescriptor(d protoreflect.Descriptor) bool { + if isSentinelDescriptor(d) { + // this indicates the name matched a package, not a + // descriptor, but a package is an aggregate, so + // we return true + return true + } + switch d.(type) { + case protoreflect.MessageDescriptor, protoreflect.EnumDescriptor, protoreflect.ServiceDescriptor: + return true + default: + return false + } +} + +func isSentinelDescriptor(d protoreflect.Descriptor) bool { + _, ok := d.(*sentinelDescriptor) + return ok +} + +func newSentinelDescriptor(name string) protoreflect.Descriptor { + return &sentinelDescriptor{name: name} +} + +// sentinelDescriptor is a placeholder descriptor. It is used instead of nil to +// distinguish between two situations: +// 1. The given name could not be found. +// 2. The given name *cannot* be a valid result so stop searching. +// +// In these cases, attempts to resolve an element name will return nil for the +// first case and will return a sentinelDescriptor in the second. The sentinel +// contains the fully-qualified name which caused the search to stop (which may +// be a prefix of the actual name being resolved). +type sentinelDescriptor struct { + protoreflect.Descriptor + name string +} + +func (p *sentinelDescriptor) ParentFile() protoreflect.FileDescriptor { + return nil +} + +func (p *sentinelDescriptor) Parent() protoreflect.Descriptor { + return nil +} + +func (p *sentinelDescriptor) Index() int { + return 0 +} + +func (p *sentinelDescriptor) Syntax() protoreflect.Syntax { + return 0 +} + +func (p *sentinelDescriptor) Name() protoreflect.Name { + return protoreflect.Name(p.name) +} + +func (p *sentinelDescriptor) FullName() protoreflect.FullName { + return protoreflect.FullName(p.name) +} + +func (p *sentinelDescriptor) IsPlaceholder() bool { + return false +} + +func (p *sentinelDescriptor) Options() protoreflect.ProtoMessage { + return nil +} + +var _ protoreflect.Descriptor = (*sentinelDescriptor)(nil) diff --git a/vendor/github.com/bufbuild/protocompile/linker/symbols.go b/vendor/github.com/bufbuild/protocompile/linker/symbols.go new file mode 100644 index 0000000..c8db762 --- /dev/null +++ b/vendor/github.com/bufbuild/protocompile/linker/symbols.go @@ -0,0 +1,635 @@ +// Copyright 2020-2024 Buf Technologies, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package linker + +import ( + "strings" + "sync" + + "google.golang.org/protobuf/reflect/protoreflect" + + "github.com/bufbuild/protocompile/ast" + "github.com/bufbuild/protocompile/internal" + "github.com/bufbuild/protocompile/protoutil" + "github.com/bufbuild/protocompile/reporter" + "github.com/bufbuild/protocompile/walk" +) + +const unknownFilePath = "<unknown file>" + +// Symbols is a symbol table that maps names for all program elements to their +// location in source. It also tracks extension tag numbers. This can be used +// to enforce uniqueness for symbol names and tag numbers across many files and +// many link operations. +// +// This type is thread-safe. +type Symbols struct { + pkgTrie packageSymbols + + // We don't know the packages for these symbols, so we can't + // keep them in the pkgTrie. In vast majority of cases, this + // will always be empty/unused. When used, it ensures that + // multiple extension declarations don't refer to the same + // extension. + extDeclsMu sync.Mutex + extDecls map[protoreflect.FullName]extDecl +} + +type packageSymbols struct { + mu sync.RWMutex + children map[protoreflect.FullName]*packageSymbols + files map[protoreflect.FileDescriptor]struct{} + symbols map[protoreflect.FullName]symbolEntry + exts map[extNumber]ast.SourceSpan +} + +type extNumber struct { + extendee protoreflect.FullName + tag protoreflect.FieldNumber +} + +type symbolEntry struct { + span ast.SourceSpan + isEnumValue bool + isPackage bool +} + +type extDecl struct { + span ast.SourceSpan + extendee protoreflect.FullName + tag protoreflect.FieldNumber +} + +// Import populates the symbol table with all symbols/elements and extension +// tags present in the given file descriptor. If s is nil or if fd has already +// been imported into s, this returns immediately without doing anything. If any +// collisions in symbol names or extension tags are identified, an error will be +// returned and the symbol table will not be updated. +func (s *Symbols) Import(fd protoreflect.FileDescriptor, handler *reporter.Handler) error { + if s == nil { + return nil + } + + if f, ok := fd.(protoreflect.FileImport); ok { + // unwrap any import instance + fd = f.FileDescriptor + } + if f, ok := fd.(*file); ok { + // unwrap any file instance + fd = f.FileDescriptor + } + + var pkgSpan ast.SourceSpan + if res, ok := fd.(*result); ok { + pkgSpan = packageNameSpan(res) + } else { + pkgSpan = sourceSpanForPackage(fd) + } + pkg, err := s.importPackages(pkgSpan, fd.Package(), handler) + if err != nil || pkg == nil { + return err + } + + pkg.mu.RLock() + _, alreadyImported := pkg.files[fd] + pkg.mu.RUnlock() + + if alreadyImported { + return nil + } + + for i := 0; i < fd.Imports().Len(); i++ { + if err := s.Import(fd.Imports().Get(i).FileDescriptor, handler); err != nil { + return err + } + } + + if res, ok := fd.(*result); ok && res.hasSource() { + return s.importResultWithExtensions(pkg, res, handler) + } + + return s.importFileWithExtensions(pkg, fd, handler) +} + +func (s *Symbols) importFileWithExtensions(pkg *packageSymbols, fd protoreflect.FileDescriptor, handler *reporter.Handler) error { + imported, err := pkg.importFile(fd, handler) + if err != nil { + return err + } + if !imported { + // nothing else to do + return nil + } + + return walk.Descriptors(fd, func(d protoreflect.Descriptor) error { + fld, ok := d.(protoreflect.FieldDescriptor) + if !ok || !fld.IsExtension() { + return nil + } + span := sourceSpanForNumber(fld) + extendee := fld.ContainingMessage() + return s.AddExtension(packageFor(extendee), extendee.FullName(), fld.Number(), span, handler) + }) +} + +func (s *packageSymbols) importFile(fd protoreflect.FileDescriptor, handler *reporter.Handler) (bool, error) { + s.mu.Lock() + defer s.mu.Unlock() + + if _, ok := s.files[fd]; ok { + // have to double-check if it's already imported, in case + // it was added after above read-locked check + return false, nil + } + + // first pass: check for conflicts + if err := s.checkFileLocked(fd, handler); err != nil { + return false, err + } + if err := handler.Error(); err != nil { + return false, err + } + + // second pass: commit all symbols + s.commitFileLocked(fd) + + return true, nil +} + +func (s *Symbols) importPackages(pkgSpan ast.SourceSpan, pkg protoreflect.FullName, handler *reporter.Handler) (*packageSymbols, error) { + if pkg == "" { + return &s.pkgTrie, nil + } + + cur := &s.pkgTrie + enumerator := nameEnumerator{name: pkg} + for { + p, ok := enumerator.next() + if !ok { + return cur, nil + } + var err error + cur, err = cur.importPackage(pkgSpan, p, handler) + if err != nil { + return nil, err + } + if cur == nil { + return nil, nil + } + } +} + +func (s *packageSymbols) importPackage(pkgSpan ast.SourceSpan, pkg protoreflect.FullName, handler *reporter.Handler) (*packageSymbols, error) { + s.mu.RLock() + existing, ok := s.symbols[pkg] + var child *packageSymbols + if ok && existing.isPackage { + child = s.children[pkg] + } + s.mu.RUnlock() + + if ok && existing.isPackage { + // package already exists + return child, nil + } else if ok { + return nil, reportSymbolCollision(pkgSpan, pkg, false, existing, handler) + } + + s.mu.Lock() + defer s.mu.Unlock() + // have to double-check in case it was added while upgrading to write lock + existing, ok = s.symbols[pkg] + if ok && existing.isPackage { + // package already exists + return s.children[pkg], nil + } else if ok { + return nil, reportSymbolCollision(pkgSpan, pkg, false, existing, handler) + } + if s.symbols == nil { + s.symbols = map[protoreflect.FullName]symbolEntry{} + } + s.symbols[pkg] = symbolEntry{span: pkgSpan, isPackage: true} + child = &packageSymbols{} + if s.children == nil { + s.children = map[protoreflect.FullName]*packageSymbols{} + } + s.children[pkg] = child + return child, nil +} + +func (s *Symbols) getPackage(pkg protoreflect.FullName, exact bool) *packageSymbols { + if pkg == "" { + return &s.pkgTrie + } + cur := &s.pkgTrie + enumerator := nameEnumerator{name: pkg} + for { + p, ok := enumerator.next() + if !ok { + return cur + } + cur.mu.RLock() + next := cur.children[p] + cur.mu.RUnlock() + + if next == nil { + if exact { + return nil + } + return cur + } + cur = next + } +} + +func reportSymbolCollision(span ast.SourceSpan, fqn protoreflect.FullName, additionIsEnumVal bool, existing symbolEntry, handler *reporter.Handler) error { + // because of weird scoping for enum values, provide more context in error message + // if this conflict is with an enum value + var isPkg, suffix string + if additionIsEnumVal || existing.isEnumValue { + suffix = "; protobuf uses C++ scoping rules for enum values, so they exist in the scope enclosing the enum" + } + if existing.isPackage { + isPkg = " as a package" + } + orig := existing.span + conflict := span + if posLess(conflict.Start(), orig.Start()) { + orig, conflict = conflict, orig + } + return handler.HandleErrorf(conflict, "symbol %q already defined%s at %v%s", fqn, isPkg, orig.Start(), suffix) +} + +func posLess(a, b ast.SourcePos) bool { + if a.Filename == b.Filename { + if a.Line == b.Line { + return a.Col < b.Col + } + return a.Line < b.Line + } + return false +} + +func (s *packageSymbols) checkFileLocked(f protoreflect.FileDescriptor, handler *reporter.Handler) error { + return walk.Descriptors(f, func(d protoreflect.Descriptor) error { + span := sourceSpanFor(d) + if existing, ok := s.symbols[d.FullName()]; ok { + _, isEnumVal := d.(protoreflect.EnumValueDescriptor) + if err := reportSymbolCollision(span, d.FullName(), isEnumVal, existing, handler); err != nil { + return err + } + } + return nil + }) +} + +func sourceSpanForPackage(fd protoreflect.FileDescriptor) ast.SourceSpan { + loc := fd.SourceLocations().ByPath([]int32{internal.FilePackageTag}) + if internal.IsZeroLocation(loc) { + return ast.UnknownSpan(fd.Path()) + } + return ast.NewSourceSpan( + ast.SourcePos{ + Filename: fd.Path(), + Line: loc.StartLine, + Col: loc.StartColumn, + }, + ast.SourcePos{ + Filename: fd.Path(), + Line: loc.EndLine, + Col: loc.EndColumn, + }, + ) +} + +func sourceSpanFor(d protoreflect.Descriptor) ast.SourceSpan { + file := d.ParentFile() + if file == nil { + return ast.UnknownSpan(unknownFilePath) + } + if result, ok := file.(*result); ok { + return nameSpan(result.FileNode(), result.Node(protoutil.ProtoFromDescriptor(d))) + } + path, ok := internal.ComputePath(d) + if !ok { + return ast.UnknownSpan(file.Path()) + } + namePath := path + switch d.(type) { + case protoreflect.FieldDescriptor: + namePath = append(namePath, internal.FieldNameTag) + case protoreflect.MessageDescriptor: + namePath = append(namePath, internal.MessageNameTag) + case protoreflect.OneofDescriptor: + namePath = append(namePath, internal.OneofNameTag) + case protoreflect.EnumDescriptor: + namePath = append(namePath, internal.EnumNameTag) + case protoreflect.EnumValueDescriptor: + namePath = append(namePath, internal.EnumValNameTag) + case protoreflect.ServiceDescriptor: + namePath = append(namePath, internal.ServiceNameTag) + case protoreflect.MethodDescriptor: + namePath = append(namePath, internal.MethodNameTag) + default: + // NB: shouldn't really happen, but just in case fall back to path to + // descriptor, sans name field + } + loc := file.SourceLocations().ByPath(namePath) + if internal.IsZeroLocation(loc) { + loc = file.SourceLocations().ByPath(path) + if internal.IsZeroLocation(loc) { + return ast.UnknownSpan(file.Path()) + } + } + + return ast.NewSourceSpan( + ast.SourcePos{ + Filename: file.Path(), + Line: loc.StartLine, + Col: loc.StartColumn, + }, + ast.SourcePos{ + Filename: file.Path(), + Line: loc.EndLine, + Col: loc.EndColumn, + }, + ) +} + +func sourceSpanForNumber(fd protoreflect.FieldDescriptor) ast.SourceSpan { + file := fd.ParentFile() + if file == nil { + return ast.UnknownSpan(unknownFilePath) + } + path, ok := internal.ComputePath(fd) + if !ok { + return ast.UnknownSpan(file.Path()) + } + numberPath := path + numberPath = append(numberPath, internal.FieldNumberTag) + loc := file.SourceLocations().ByPath(numberPath) + if internal.IsZeroLocation(loc) { + loc = file.SourceLocations().ByPath(path) + if internal.IsZeroLocation(loc) { + return ast.UnknownSpan(file.Path()) + } + } + return ast.NewSourceSpan( + ast.SourcePos{ + Filename: file.Path(), + Line: loc.StartLine, + Col: loc.StartColumn, + }, + ast.SourcePos{ + Filename: file.Path(), + Line: loc.EndLine, + Col: loc.EndColumn, + }, + ) +} + +func (s *packageSymbols) commitFileLocked(f protoreflect.FileDescriptor) { + if s.symbols == nil { + s.symbols = map[protoreflect.FullName]symbolEntry{} + } + if s.exts == nil { + s.exts = map[extNumber]ast.SourceSpan{} + } + _ = walk.Descriptors(f, func(d protoreflect.Descriptor) error { + span := sourceSpanFor(d) + name := d.FullName() + _, isEnumValue := d.(protoreflect.EnumValueDescriptor) + s.symbols[name] = symbolEntry{span: span, isEnumValue: isEnumValue} + return nil + }) + + if s.files == nil { + s.files = map[protoreflect.FileDescriptor]struct{}{} + } + s.files[f] = struct{}{} +} + +func (s *Symbols) importResultWithExtensions(pkg *packageSymbols, r *result, handler *reporter.Handler) error { + imported, err := pkg.importResult(r, handler) + if err != nil { + return err + } + if !imported { + // nothing else to do + return nil + } + + return walk.Descriptors(r, func(d protoreflect.Descriptor) error { + fd, ok := d.(*extTypeDescriptor) + if !ok { + return nil + } + file := r.FileNode() + node := r.FieldNode(fd.FieldDescriptorProto()) + info := file.NodeInfo(node.FieldTag()) + extendee := fd.ContainingMessage() + return s.AddExtension(packageFor(extendee), extendee.FullName(), fd.Number(), info, handler) + }) +} + +func (s *Symbols) importResult(r *result, handler *reporter.Handler) error { + pkg, err := s.importPackages(packageNameSpan(r), r.Package(), handler) + if err != nil || pkg == nil { + return err + } + _, err = pkg.importResult(r, handler) + return err +} + +func (s *packageSymbols) importResult(r *result, handler *reporter.Handler) (bool, error) { + s.mu.Lock() + defer s.mu.Unlock() + + if _, ok := s.files[r]; ok { + // already imported + return false, nil + } + + // first pass: check for conflicts + if err := s.checkResultLocked(r, handler); err != nil { + return false, err + } + if err := handler.Error(); err != nil { + return false, err + } + + // second pass: commit all symbols + s.commitFileLocked(r) + + return true, nil +} + +func (s *packageSymbols) checkResultLocked(r *result, handler *reporter.Handler) error { + resultSyms := map[protoreflect.FullName]symbolEntry{} + return walk.Descriptors(r, func(d protoreflect.Descriptor) error { + _, isEnumVal := d.(protoreflect.EnumValueDescriptor) + file := r.FileNode() + name := d.FullName() + node := r.Node(protoutil.ProtoFromDescriptor(d)) + span := nameSpan(file, node) + // check symbols already in this symbol table + if existing, ok := s.symbols[name]; ok { + if err := reportSymbolCollision(span, name, isEnumVal, existing, handler); err != nil { + return err + } + } + + // also check symbols from this result (that are not yet in symbol table) + if existing, ok := resultSyms[name]; ok { + if err := reportSymbolCollision(span, name, isEnumVal, existing, handler); err != nil { + return err + } + } + resultSyms[name] = symbolEntry{ + span: span, + isEnumValue: isEnumVal, + } + + return nil + }) +} + +func packageNameSpan(r *result) ast.SourceSpan { + if node, ok := r.FileNode().(*ast.FileNode); ok { + for _, decl := range node.Decls { + if pkgNode, ok := decl.(*ast.PackageNode); ok { + return r.FileNode().NodeInfo(pkgNode.Name) + } + } + } + return ast.UnknownSpan(r.Path()) +} + +func nameSpan(file ast.FileDeclNode, n ast.Node) ast.SourceSpan { + // TODO: maybe ast package needs a NamedNode interface to simplify this? + switch n := n.(type) { + case ast.FieldDeclNode: + return file.NodeInfo(n.FieldName()) + case ast.MessageDeclNode: + return file.NodeInfo(n.MessageName()) + case ast.OneofDeclNode: + return file.NodeInfo(n.OneofName()) + case ast.EnumValueDeclNode: + return file.NodeInfo(n.GetName()) + case *ast.EnumNode: + return file.NodeInfo(n.Name) + case *ast.ServiceNode: + return file.NodeInfo(n.Name) + case ast.RPCDeclNode: + return file.NodeInfo(n.GetName()) + default: + return file.NodeInfo(n) + } +} + +// AddExtension records the given extension, which is used to ensure that no two files +// attempt to extend the same message using the same tag. The given pkg should be the +// package that defines extendee. +func (s *Symbols) AddExtension(pkg, extendee protoreflect.FullName, tag protoreflect.FieldNumber, span ast.SourceSpan, handler *reporter.Handler) error { + if pkg != "" { + if !strings.HasPrefix(string(extendee), string(pkg)+".") { + return handler.HandleErrorf(span, "could not register extension: extendee %q does not match package %q", extendee, pkg) + } + } + pkgSyms := s.getPackage(pkg, true) + if pkgSyms == nil { + // should never happen + return handler.HandleErrorf(span, "could not register extension: missing package symbols for %q", pkg) + } + return pkgSyms.addExtension(extendee, tag, span, handler) +} + +func (s *packageSymbols) addExtension(extendee protoreflect.FullName, tag protoreflect.FieldNumber, span ast.SourceSpan, handler *reporter.Handler) error { + s.mu.Lock() + defer s.mu.Unlock() + + extNum := extNumber{extendee: extendee, tag: tag} + if existing, ok := s.exts[extNum]; ok { + return handler.HandleErrorf(span, "extension with tag %d for message %s already defined at %v", tag, extendee, existing.Start()) + } + + if s.exts == nil { + s.exts = map[extNumber]ast.SourceSpan{} + } + s.exts[extNum] = span + return nil +} + +// AddExtensionDeclaration records the given extension declaration, which is used to +// ensure that no two declarations refer to the same extension. +func (s *Symbols) AddExtensionDeclaration(extension, extendee protoreflect.FullName, tag protoreflect.FieldNumber, span ast.SourceSpan, handler *reporter.Handler) error { + s.extDeclsMu.Lock() + defer s.extDeclsMu.Unlock() + existing, ok := s.extDecls[extension] + if ok { + if existing.extendee == extendee && existing.tag == tag { + // This is a declaration that has already been added. Ignore. + return nil + } + return handler.HandleErrorf(span, "extension %s already declared as extending %s with tag %d at %v", extension, existing.extendee, existing.tag, existing.span.Start()) + } + if s.extDecls == nil { + s.extDecls = map[protoreflect.FullName]extDecl{} + } + s.extDecls[extension] = extDecl{ + span: span, + extendee: extendee, + tag: tag, + } + return nil +} + +// Lookup finds the registered location of the given name. If the given name has +// not been seen/registered, nil is returned. +func (s *Symbols) Lookup(name protoreflect.FullName) ast.SourceSpan { + // note: getPackage never returns nil when exact=false + pkgSyms := s.getPackage(name, false) + if entry, ok := pkgSyms.symbols[name]; ok { + return entry.span + } + return nil +} + +// LookupExtension finds the registered location of the given extension. If the given +// extension has not been seen/registered, nil is returned. +func (s *Symbols) LookupExtension(messageName protoreflect.FullName, extensionNumber protoreflect.FieldNumber) ast.SourceSpan { + // note: getPackage never returns nil when exact=false + pkgSyms := s.getPackage(messageName, false) + return pkgSyms.exts[extNumber{messageName, extensionNumber}] +} + +type nameEnumerator struct { + name protoreflect.FullName + start int +} + +func (e *nameEnumerator) next() (protoreflect.FullName, bool) { + if e.start < 0 { + return "", false + } + pos := strings.IndexByte(string(e.name[e.start:]), '.') + if pos == -1 { + e.start = -1 + return e.name, true + } + pos += e.start + e.start = pos + 1 + return e.name[:pos], true +} diff --git a/vendor/github.com/bufbuild/protocompile/linker/validate.go b/vendor/github.com/bufbuild/protocompile/linker/validate.go new file mode 100644 index 0000000..6633a9f --- /dev/null +++ b/vendor/github.com/bufbuild/protocompile/linker/validate.go @@ -0,0 +1,1153 @@ +// Copyright 2020-2024 Buf Technologies, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package linker + +import ( + "fmt" + "math" + "strings" + "unicode" + "unicode/utf8" + + "google.golang.org/protobuf/reflect/protoreflect" + "google.golang.org/protobuf/types/descriptorpb" + + "github.com/bufbuild/protocompile/ast" + "github.com/bufbuild/protocompile/internal" + "github.com/bufbuild/protocompile/protoutil" + "github.com/bufbuild/protocompile/reporter" + "github.com/bufbuild/protocompile/walk" +) + +// ValidateOptions runs some validation checks on the result that can only +// be done after options are interpreted. +func (r *result) ValidateOptions(handler *reporter.Handler, symbols *Symbols) error { + if err := r.validateFile(handler); err != nil { + return err + } + return walk.Descriptors(r, func(d protoreflect.Descriptor) error { + switch d := d.(type) { + case protoreflect.FieldDescriptor: + if err := r.validateField(d, handler); err != nil { + return err + } + case protoreflect.MessageDescriptor: + if symbols == nil { + symbols = &Symbols{} + } + if err := r.validateMessage(d, handler, symbols); err != nil { + return err + } + case protoreflect.EnumDescriptor: + if err := r.validateEnum(d, handler); err != nil { + return err + } + } + return nil + }) +} + +func (r *result) validateFile(handler *reporter.Handler) error { + opts := r.FileDescriptorProto().GetOptions() + if opts.GetOptimizeFor() != descriptorpb.FileOptions_LITE_RUNTIME { + // Non-lite files may not import lite files. + imports := r.Imports() + for i, length := 0, imports.Len(); i < length; i++ { + dep := imports.Get(i) + depOpts, ok := dep.Options().(*descriptorpb.FileOptions) + if !ok { + continue // what else to do? + } + if depOpts.GetOptimizeFor() == descriptorpb.FileOptions_LITE_RUNTIME { + err := handler.HandleErrorf(r.getImportLocation(dep.Path()), "a file that does not use optimize_for=LITE_RUNTIME may not import file %q that does", dep.Path()) + if err != nil { + return err + } + } + } + } + if isEditions(r) { + // Validate features + if opts.GetFeatures().GetFieldPresence() == descriptorpb.FeatureSet_LEGACY_REQUIRED { + span := r.findOptionSpan(r, internal.FileOptionsFeaturesTag, internal.FeatureSetFieldPresenceTag) + err := handler.HandleErrorf(span, "LEGACY_REQUIRED field presence cannot be set as the default for a file") + if err != nil { + return err + } + } + if opts != nil && opts.JavaStringCheckUtf8 != nil { + span := r.findOptionSpan(r, internal.FileOptionsJavaStringCheckUTF8Tag) + err := handler.HandleErrorf(span, `file option java_string_check_utf8 is not allowed with editions; import "google/protobuf/java_features.proto" and use (pb.java).utf8_validation instead`) + if err != nil { + return err + } + } + } + return nil +} + +func (r *result) validateField(fld protoreflect.FieldDescriptor, handler *reporter.Handler) error { + if xtd, ok := fld.(protoreflect.ExtensionTypeDescriptor); ok { + fld = xtd.Descriptor() + } + fd, ok := fld.(*fldDescriptor) + if !ok { + // should not be possible + return fmt.Errorf("field descriptor is wrong type: expecting %T, got %T", (*fldDescriptor)(nil), fld) + } + + if err := r.validatePacked(fd, handler); err != nil { + return err + } + if fd.Kind() == protoreflect.EnumKind { + requiresOpen := !fd.IsList() && !fd.HasPresence() + if requiresOpen && fd.Enum().IsClosed() { + // Fields in a proto3 message cannot refer to proto2 enums. + // In editions, this translates to implicit presence fields + // not being able to refer to closed enums. + // TODO: This really should be based solely on whether the enum's first + // value is zero, NOT based on if it's open vs closed. + // https://github.com/protocolbuffers/protobuf/issues/16249 + file := r.FileNode() + info := file.NodeInfo(r.FieldNode(fd.proto).FieldType()) + if err := handler.HandleErrorf(info, "cannot use closed enum %s in a field with implicit presence", fd.Enum().FullName()); err != nil { + return err + } + } + } + if fd.HasDefault() && !fd.HasPresence() { + span := r.findScalarOptionSpan(r.FieldNode(fd.proto), "default") + err := handler.HandleErrorf(span, "default value is not allowed on fields with implicit presence") + if err != nil { + return err + } + } + if fd.proto.Options != nil && fd.proto.Options.Ctype != nil { + if descriptorpb.Edition(r.Edition()) >= descriptorpb.Edition_EDITION_2024 { + // We don't support edition 2024 yet, but we went ahead and mimic'ed this check + // from protoc, which currently has experimental support for 2024. + span := r.findOptionSpan(fd, internal.FieldOptionsCTypeTag) + if err := handler.HandleErrorf(span, "ctype option cannot be used as of edition 2024; use features.string_type instead"); err != nil { + return err + } + } else if descriptorpb.Edition(r.Edition()) == descriptorpb.Edition_EDITION_2023 { + if fld.Kind() != protoreflect.StringKind && fld.Kind() != protoreflect.BytesKind { + span := r.findOptionSpan(fd, internal.FieldOptionsCTypeTag) + if err := handler.HandleErrorf(span, "ctype option can only be used on string and bytes fields"); err != nil { + return err + } + } + if fd.proto.Options.GetCtype() == descriptorpb.FieldOptions_CORD && fd.IsExtension() { + span := r.findOptionSpan(fd, internal.FieldOptionsCTypeTag) + if err := handler.HandleErrorf(span, "ctype option cannot be CORD for extension fields"); err != nil { + return err + } + } + } + } + if (fd.proto.Options.GetLazy() || fd.proto.Options.GetUnverifiedLazy()) && fd.Kind() != protoreflect.MessageKind { + var span ast.SourceSpan + var optionName string + if fd.proto.Options.GetLazy() { + span = r.findOptionSpan(fd, internal.FieldOptionsLazyTag) + optionName = "lazy" + } else { + span = r.findOptionSpan(fd, internal.FieldOptionsUnverifiedLazyTag) + optionName = "unverified_lazy" + } + var suffix string + if fd.Kind() == protoreflect.GroupKind { + if isEditions(r) { + suffix = " that use length-prefixed encoding" + } else { + suffix = ", not groups" + } + } + if err := handler.HandleErrorf(span, "%s option can only be used with message fields%s", optionName, suffix); err != nil { + return err + } + } + if fd.proto.Options.GetJstype() != descriptorpb.FieldOptions_JS_NORMAL { + switch fd.Kind() { + case protoreflect.Int64Kind, protoreflect.Uint64Kind, protoreflect.Sint64Kind, + protoreflect.Fixed64Kind, protoreflect.Sfixed64Kind: + // allowed only for 64-bit integer types + default: + span := r.findOptionSpan(fd, internal.FieldOptionsJSTypeTag) + err := handler.HandleErrorf(span, "only 64-bit integer fields (int64, uint64, sint64, fixed64, and sfixed64) can specify a jstype other than JS_NORMAL") + if err != nil { + return err + } + } + } + if isEditions(r) { + if err := r.validateFieldFeatures(fd, handler); err != nil { + return err + } + } + + if fld.IsExtension() { + // More checks if this is an extension field. + if err := r.validateExtension(fd, handler); err != nil { + return err + } + } + + return nil +} + +func (r *result) validateExtension(fd *fldDescriptor, handler *reporter.Handler) error { + // NB: It's a little gross that we don't enforce these in validateBasic(). + // But it requires linking to resolve the extendee, so we can interrogate + // its descriptor. + msg := fd.ContainingMessage() + if msg.Options().(*descriptorpb.MessageOptions).GetMessageSetWireFormat() { + // Message set wire format requires that all extensions be messages + // themselves (no scalar extensions) + if fd.Kind() != protoreflect.MessageKind { + file := r.FileNode() + info := file.NodeInfo(r.FieldNode(fd.proto).FieldType()) + err := handler.HandleErrorf(info, "messages with message-set wire format cannot contain scalar extensions, only messages") + if err != nil { + return err + } + } + if fd.Cardinality() == protoreflect.Repeated { + file := r.FileNode() + info := file.NodeInfo(r.FieldNode(fd.proto).FieldLabel()) + err := handler.HandleErrorf(info, "messages with message-set wire format cannot contain repeated extensions, only optional") + if err != nil { + return err + } + } + } else if fd.Number() > internal.MaxNormalTag { + // In validateBasic() we just made sure these were within bounds for any message. But + // now that things are linked, we can check if the extendee is messageset wire format + // and, if not, enforce tighter limit. + file := r.FileNode() + info := file.NodeInfo(r.FieldNode(fd.proto).FieldTag()) + err := handler.HandleErrorf(info, "tag number %d is higher than max allowed tag number (%d)", fd.Number(), internal.MaxNormalTag) + if err != nil { + return err + } + } + + fileOpts := r.FileDescriptorProto().GetOptions() + if fileOpts.GetOptimizeFor() == descriptorpb.FileOptions_LITE_RUNTIME { + extendeeFileOpts, _ := msg.ParentFile().Options().(*descriptorpb.FileOptions) + if extendeeFileOpts.GetOptimizeFor() != descriptorpb.FileOptions_LITE_RUNTIME { + file := r.FileNode() + info := file.NodeInfo(r.FieldNode(fd.proto)) + err := handler.HandleErrorf(info, "extensions in a file that uses optimize_for=LITE_RUNTIME may not extend messages in file %q which does not", msg.ParentFile().Path()) + if err != nil { + return err + } + } + } + + // If the extendee uses extension declarations, make sure this extension matches. + md := protoutil.ProtoFromMessageDescriptor(msg) + for i, extRange := range md.ExtensionRange { + if int32(fd.Number()) < extRange.GetStart() || int32(fd.Number()) >= extRange.GetEnd() { + continue + } + extRangeOpts := extRange.GetOptions() + if extRangeOpts == nil { + break + } + if len(extRangeOpts.Declaration) == 0 && extRangeOpts.GetVerification() != descriptorpb.ExtensionRangeOptions_DECLARATION { + break + } + var found bool + for j, extDecl := range extRangeOpts.Declaration { + if extDecl.GetNumber() != int32(fd.Number()) { + continue + } + found = true + if extDecl.GetReserved() { + file := r.FileNode() + info := file.NodeInfo(r.FieldNode(fd.proto).FieldTag()) + span, _ := findExtensionRangeOptionSpan(msg.ParentFile(), msg, i, extRange, + internal.ExtensionRangeOptionsDeclarationTag, int32(j), internal.ExtensionRangeOptionsDeclarationReservedTag) + err := handler.HandleErrorf(info, "cannot use field number %d for an extension because it is reserved in declaration at %v", + fd.Number(), span.Start()) + if err != nil { + return err + } + break + } + if extDecl.GetFullName() != "."+string(fd.FullName()) { + file := r.FileNode() + info := file.NodeInfo(r.FieldNode(fd.proto).FieldName()) + span, _ := findExtensionRangeOptionSpan(msg.ParentFile(), msg, i, extRange, + internal.ExtensionRangeOptionsDeclarationTag, int32(j), internal.ExtensionRangeOptionsDeclarationFullNameTag) + err := handler.HandleErrorf(info, "expected extension with number %d to be named %s, not %s, per declaration at %v", + fd.Number(), strings.TrimPrefix(extDecl.GetFullName(), "."), fd.FullName(), span.Start()) + if err != nil { + return err + } + } + if extDecl.GetType() != getTypeName(fd) { + file := r.FileNode() + info := file.NodeInfo(r.FieldNode(fd.proto).FieldType()) + span, _ := findExtensionRangeOptionSpan(msg.ParentFile(), msg, i, extRange, + internal.ExtensionRangeOptionsDeclarationTag, int32(j), internal.ExtensionRangeOptionsDeclarationTypeTag) + err := handler.HandleErrorf(info, "expected extension with number %d to have type %s, not %s, per declaration at %v", + fd.Number(), strings.TrimPrefix(extDecl.GetType(), "."), getTypeName(fd), span.Start()) + if err != nil { + return err + } + } + if extDecl.GetRepeated() != (fd.Cardinality() == protoreflect.Repeated) { + expected, actual := "repeated", "optional" + if !extDecl.GetRepeated() { + expected, actual = actual, expected + } + file := r.FileNode() + info := file.NodeInfo(r.FieldNode(fd.proto).FieldLabel()) + span, _ := findExtensionRangeOptionSpan(msg.ParentFile(), msg, i, extRange, + internal.ExtensionRangeOptionsDeclarationTag, int32(j), internal.ExtensionRangeOptionsDeclarationRepeatedTag) + err := handler.HandleErrorf(info, "expected extension with number %d to be %s, not %s, per declaration at %v", + fd.Number(), expected, actual, span.Start()) + if err != nil { + return err + } + } + break + } + if !found { + file := r.FileNode() + info := file.NodeInfo(r.FieldNode(fd.proto).FieldTag()) + span, _ := findExtensionRangeOptionSpan(fd.ParentFile(), msg, i, extRange, + internal.ExtensionRangeOptionsVerificationTag) + err := handler.HandleErrorf(info, "expected extension with number %d to be declared in type %s, but no declaration found at %v", + fd.Number(), fd.ContainingMessage().FullName(), span.Start()) + if err != nil { + return err + } + } + } + + return nil +} + +func (r *result) validatePacked(fd *fldDescriptor, handler *reporter.Handler) error { + if fd.proto.Options != nil && fd.proto.Options.Packed != nil && isEditions(r) { + span := r.findOptionSpan(fd, internal.FieldOptionsPackedTag) + err := handler.HandleErrorf(span, "packed option cannot be used with editions; use features.repeated_field_encoding=PACKED instead") + if err != nil { + return err + } + } + if !fd.proto.GetOptions().GetPacked() { + // if packed isn't true, nothing to validate + return nil + } + if fd.proto.GetLabel() != descriptorpb.FieldDescriptorProto_LABEL_REPEATED { + file := r.FileNode() + info := file.NodeInfo(r.FieldNode(fd.proto).FieldLabel()) + err := handler.HandleErrorf(info, "packed option is only allowed on repeated fields") + if err != nil { + return err + } + } + switch fd.proto.GetType() { + case descriptorpb.FieldDescriptorProto_TYPE_STRING, descriptorpb.FieldDescriptorProto_TYPE_BYTES, + descriptorpb.FieldDescriptorProto_TYPE_MESSAGE, descriptorpb.FieldDescriptorProto_TYPE_GROUP: + file := r.FileNode() + info := file.NodeInfo(r.FieldNode(fd.proto).FieldType()) + err := handler.HandleErrorf(info, "packed option is only allowed on numeric, boolean, and enum fields") + if err != nil { + return err + } + } + return nil +} + +func (r *result) validateFieldFeatures(fld *fldDescriptor, handler *reporter.Handler) error { + if msg, ok := fld.Parent().(*msgDescriptor); ok && msg.proto.GetOptions().GetMapEntry() { + // Skip validating features on fields of synthetic map entry messages. + // We blindly propagate them from the map field's features, but some may + // really only apply to the map field and not to a key or value entry field. + return nil + } + features := fld.proto.GetOptions().GetFeatures() + if features == nil { + // No features to validate. + return nil + } + if features.FieldPresence != nil { + switch { + case fld.proto.OneofIndex != nil: + span := r.findOptionSpan(fld, internal.FieldOptionsFeaturesTag, internal.FeatureSetFieldPresenceTag) + if err := handler.HandleErrorf(span, "oneof fields may not specify field presence"); err != nil { + return err + } + case fld.Cardinality() == protoreflect.Repeated: + span := r.findOptionSpan(fld, internal.FieldOptionsFeaturesTag, internal.FeatureSetFieldPresenceTag) + if err := handler.HandleErrorf(span, "repeated fields may not specify field presence"); err != nil { + return err + } + case fld.IsExtension(): + span := r.findOptionSpan(fld, internal.FieldOptionsFeaturesTag, internal.FeatureSetFieldPresenceTag) + if err := handler.HandleErrorf(span, "extension fields may not specify field presence"); err != nil { + return err + } + case fld.Message() != nil && features.GetFieldPresence() == descriptorpb.FeatureSet_IMPLICIT: + span := r.findOptionSpan(fld, internal.FieldOptionsFeaturesTag, internal.FeatureSetFieldPresenceTag) + if err := handler.HandleErrorf(span, "message fields may not specify implicit presence"); err != nil { + return err + } + } + } + if features.RepeatedFieldEncoding != nil { + if fld.Cardinality() != protoreflect.Repeated { + span := r.findOptionSpan(fld, internal.FieldOptionsFeaturesTag, internal.FeatureSetRepeatedFieldEncodingTag) + if err := handler.HandleErrorf(span, "only repeated fields may specify repeated field encoding"); err != nil { + return err + } + } else if !internal.CanPack(fld.Kind()) && features.GetRepeatedFieldEncoding() == descriptorpb.FeatureSet_PACKED { + span := r.findOptionSpan(fld, internal.FieldOptionsFeaturesTag, internal.FeatureSetRepeatedFieldEncodingTag) + if err := handler.HandleErrorf(span, "only repeated primitive fields may specify packed encoding"); err != nil { + return err + } + } + } + if features.Utf8Validation != nil { + isMap := fld.IsMap() + if (!isMap && fld.Kind() != protoreflect.StringKind) || + (isMap && + fld.MapKey().Kind() != protoreflect.StringKind && + fld.MapValue().Kind() != protoreflect.StringKind) { + span := r.findOptionSpan(fld, internal.FieldOptionsFeaturesTag, internal.FeatureSetUTF8ValidationTag) + if err := handler.HandleErrorf(span, "only string fields may specify UTF8 validation"); err != nil { + return err + } + } + } + if features.MessageEncoding != nil { + if fld.Message() == nil || fld.IsMap() { + span := r.findOptionSpan(fld, internal.FieldOptionsFeaturesTag, internal.FeatureSetMessageEncodingTag) + if err := handler.HandleErrorf(span, "only message fields may specify message encoding"); err != nil { + return err + } + } + } + return nil +} + +func (r *result) validateMessage(d protoreflect.MessageDescriptor, handler *reporter.Handler, symbols *Symbols) error { + md, ok := d.(*msgDescriptor) + if !ok { + // should not be possible + return fmt.Errorf("message descriptor is wrong type: expecting %T, got %T", (*msgDescriptor)(nil), d) + } + + if err := r.validateJSONNamesInMessage(md, handler); err != nil { + return err + } + + return r.validateExtensionDeclarations(md, handler, symbols) +} + +func (r *result) validateJSONNamesInMessage(md *msgDescriptor, handler *reporter.Handler) error { + if err := r.validateFieldJSONNames(md, false, handler); err != nil { + return err + } + if err := r.validateFieldJSONNames(md, true, handler); err != nil { + return err + } + return nil +} + +func (r *result) validateEnum(d protoreflect.EnumDescriptor, handler *reporter.Handler) error { + ed, ok := d.(*enumDescriptor) + if !ok { + // should not be possible + return fmt.Errorf("enum descriptor is wrong type: expecting %T, got %T", (*enumDescriptor)(nil), d) + } + + firstValue := ed.Values().Get(0) + if !ed.IsClosed() && firstValue.Number() != 0 { + // TODO: This check doesn't really belong here. Whether the + // first value is zero s/b orthogonal to whether the + // allowed values are open or closed. + // https://github.com/protocolbuffers/protobuf/issues/16249 + file := r.FileNode() + evd, ok := firstValue.(*enValDescriptor) + if !ok { + // should not be possible + return fmt.Errorf("enum value descriptor is wrong type: expecting %T, got %T", (*enValDescriptor)(nil), firstValue) + } + info := file.NodeInfo(r.EnumValueNode(evd.proto).GetNumber()) + if err := handler.HandleErrorf(info, "first value of open enum %s must have numeric value zero", ed.FullName()); err != nil { + return err + } + } + + if err := r.validateJSONNamesInEnum(ed, handler); err != nil { + return err + } + + return nil +} + +func (r *result) validateJSONNamesInEnum(ed *enumDescriptor, handler *reporter.Handler) error { + seen := map[string]*descriptorpb.EnumValueDescriptorProto{} + for _, evd := range ed.proto.GetValue() { + scope := "enum value " + ed.proto.GetName() + "." + evd.GetName() + + name := canonicalEnumValueName(evd.GetName(), ed.proto.GetName()) + if existing, ok := seen[name]; ok && evd.GetNumber() != existing.GetNumber() { + fldNode := r.EnumValueNode(evd) + existingNode := r.EnumValueNode(existing) + conflictErr := fmt.Errorf("%s: camel-case name (with optional enum name prefix removed) %q conflicts with camel-case name of enum value %s, defined at %v", + scope, name, existing.GetName(), r.FileNode().NodeInfo(existingNode).Start()) + + // Since proto2 did not originally have a JSON format, we report conflicts as just warnings. + // With editions, not fully supporting JSON is allowed via feature: json_format == BEST_EFFORT + if !isJSONCompliant(ed) { + handler.HandleWarningWithPos(r.FileNode().NodeInfo(fldNode), conflictErr) + } else if err := handler.HandleErrorWithPos(r.FileNode().NodeInfo(fldNode), conflictErr); err != nil { + return err + } + } else { + seen[name] = evd + } + } + return nil +} + +func (r *result) validateFieldJSONNames(md *msgDescriptor, useCustom bool, handler *reporter.Handler) error { + type jsonName struct { + source *descriptorpb.FieldDescriptorProto + // true if orig is a custom JSON name (vs. the field's default JSON name) + custom bool + } + seen := map[string]jsonName{} + + for _, fd := range md.proto.GetField() { + scope := "field " + md.proto.GetName() + "." + fd.GetName() + defaultName := internal.JSONName(fd.GetName()) + name := defaultName + custom := false + if useCustom { + n := fd.GetJsonName() + if n != defaultName || r.hasCustomJSONName(fd) { + name = n + custom = true + } + } + if existing, ok := seen[name]; ok { + // When useCustom is true, we'll only report an issue when a conflict is + // due to a custom name. That way, we don't double report conflicts on + // non-custom names. + if !useCustom || custom || existing.custom { + fldNode := r.FieldNode(fd) + customStr, srcCustomStr := "custom", "custom" + if !custom { + customStr = "default" + } + if !existing.custom { + srcCustomStr = "default" + } + info := r.FileNode().NodeInfo(fldNode) + conflictErr := reporter.Errorf(info, "%s: %s JSON name %q conflicts with %s JSON name of field %s, defined at %v", + scope, customStr, name, srcCustomStr, existing.source.GetName(), r.FileNode().NodeInfo(r.FieldNode(existing.source)).Start()) + + // Since proto2 did not originally have default JSON names, we report conflicts + // between default names (neither is a custom name) as just warnings. + // With editions, not fully supporting JSON is allowed via feature: json_format == BEST_EFFORT + if !isJSONCompliant(md) && !custom && !existing.custom { + handler.HandleWarning(conflictErr) + } else if err := handler.HandleError(conflictErr); err != nil { + return err + } + } + } else { + seen[name] = jsonName{source: fd, custom: custom} + } + } + return nil +} + +func (r *result) validateExtensionDeclarations(md *msgDescriptor, handler *reporter.Handler, symbols *Symbols) error { + for i, extRange := range md.proto.ExtensionRange { + opts := extRange.GetOptions() + if len(opts.GetDeclaration()) == 0 { + // nothing to check + continue + } + // If any declarations are present, verification is assumed to be + // DECLARATION. It's an error for declarations to be present but the + // verification field explicitly set to something other than that. + if opts.Verification != nil && opts.GetVerification() != descriptorpb.ExtensionRangeOptions_DECLARATION { + span, ok := findExtensionRangeOptionSpan(r, md, i, extRange, internal.ExtensionRangeOptionsVerificationTag) + if !ok { + span, _ = findExtensionRangeOptionSpan(r, md, i, extRange, internal.ExtensionRangeOptionsDeclarationTag, 0) + } + if err := handler.HandleErrorf(span, "extension range cannot have declarations and have verification of %s", opts.GetVerification()); err != nil { + return err + } + } + declsByTag := map[int32]ast.SourcePos{} + for i, extDecl := range extRange.GetOptions().GetDeclaration() { + if extDecl.Number == nil { + span, _ := findExtensionRangeOptionSpan(r, md, i, extRange, internal.ExtensionRangeOptionsDeclarationTag, int32(i)) + if err := handler.HandleErrorf(span, "extension declaration is missing required field number"); err != nil { + return err + } + } else { + extensionNumberSpan, _ := findExtensionRangeOptionSpan(r, md, i, extRange, + internal.ExtensionRangeOptionsDeclarationTag, int32(i), internal.ExtensionRangeOptionsDeclarationNumberTag) + if extDecl.GetNumber() < extRange.GetStart() || extDecl.GetNumber() >= extRange.GetEnd() { + // Number is out of range. + // See if one of the other ranges on the same extends statement includes the number, + // so we can provide a helpful message. + var suffix string + if extRange, ok := r.ExtensionsNode(extRange).(*ast.ExtensionRangeNode); ok { + for _, rng := range extRange.Ranges { + start, _ := rng.StartVal.AsInt64() + var end int64 + switch { + case rng.Max != nil: + end = math.MaxInt64 + case rng.EndVal != nil: + end, _ = rng.EndVal.AsInt64() + default: + end = start + } + if int64(extDecl.GetNumber()) >= start && int64(extDecl.GetNumber()) <= end { + // Found another range that matches + suffix = "; when using declarations, extends statements should indicate only a single span of field numbers" + break + } + } + } + err := handler.HandleErrorf(extensionNumberSpan, "extension declaration has number outside the range: %d not in [%d,%d]%s", + extDecl.GetNumber(), extRange.GetStart(), extRange.GetEnd()-1, suffix) + if err != nil { + return err + } + } else { + // Valid number; make sure it's not a duplicate + if existing, ok := declsByTag[extDecl.GetNumber()]; ok { + err := handler.HandleErrorf(extensionNumberSpan, "extension for tag number %d already declared at %v", + extDecl.GetNumber(), existing) + if err != nil { + return err + } + } else { + declsByTag[extDecl.GetNumber()] = extensionNumberSpan.Start() + } + } + } + + if extDecl.FullName == nil && !extDecl.GetReserved() { + span, _ := findExtensionRangeOptionSpan(r, md, i, extRange, internal.ExtensionRangeOptionsDeclarationTag, int32(i)) + if err := handler.HandleErrorf(span, "extension declaration that is not marked reserved must have a full_name"); err != nil { + return err + } + } else if extDecl.FullName != nil { + var extensionFullName protoreflect.FullName + extensionNameSpan, _ := findExtensionRangeOptionSpan(r, md, i, extRange, + internal.ExtensionRangeOptionsDeclarationTag, int32(i), internal.ExtensionRangeOptionsDeclarationFullNameTag) + if !strings.HasPrefix(extDecl.GetFullName(), ".") { + if err := handler.HandleErrorf(extensionNameSpan, "extension declaration full name %q should start with a leading dot (.)", extDecl.GetFullName()); err != nil { + return err + } + extensionFullName = protoreflect.FullName(extDecl.GetFullName()) + } else { + extensionFullName = protoreflect.FullName(extDecl.GetFullName()[1:]) + } + if !extensionFullName.IsValid() { + if err := handler.HandleErrorf(extensionNameSpan, "extension declaration full name %q is not a valid qualified name", extDecl.GetFullName()); err != nil { + return err + } + } + if err := symbols.AddExtensionDeclaration(extensionFullName, md.FullName(), protoreflect.FieldNumber(extDecl.GetNumber()), extensionNameSpan, handler); err != nil { + return err + } + } + + if extDecl.Type == nil && !extDecl.GetReserved() { + span, _ := findExtensionRangeOptionSpan(r, md, i, extRange, internal.ExtensionRangeOptionsDeclarationTag, int32(i)) + if err := handler.HandleErrorf(span, "extension declaration that is not marked reserved must have a type"); err != nil { + return err + } + } else if extDecl.Type != nil { + if strings.HasPrefix(extDecl.GetType(), ".") { + if !protoreflect.FullName(extDecl.GetType()[1:]).IsValid() { + span, _ := findExtensionRangeOptionSpan(r, md, i, extRange, + internal.ExtensionRangeOptionsDeclarationTag, int32(i), internal.ExtensionRangeOptionsDeclarationTypeTag) + if err := handler.HandleErrorf(span, "extension declaration type %q is not a valid qualified name", extDecl.GetType()); err != nil { + return err + } + } + } else if !isBuiltinTypeName(extDecl.GetType()) { + span, _ := findExtensionRangeOptionSpan(r, md, i, extRange, + internal.ExtensionRangeOptionsDeclarationTag, int32(i), internal.ExtensionRangeOptionsDeclarationTypeTag) + if err := handler.HandleErrorf(span, "extension declaration type %q must be a builtin type or start with a leading dot (.)", extDecl.GetType()); err != nil { + return err + } + } + } + + if extDecl.GetReserved() && (extDecl.FullName == nil) != (extDecl.Type == nil) { + var fieldTag int32 + if extDecl.FullName != nil { + fieldTag = internal.ExtensionRangeOptionsDeclarationFullNameTag + } else { + fieldTag = internal.ExtensionRangeOptionsDeclarationTypeTag + } + span, _ := findExtensionRangeOptionSpan(r, md, i, extRange, + internal.ExtensionRangeOptionsDeclarationTag, int32(i), fieldTag) + if err := handler.HandleErrorf(span, "extension declarations that are reserved should specify both full_name and type or neither"); err != nil { + return err + } + } + } + } + return nil +} + +func (r *result) hasCustomJSONName(fdProto *descriptorpb.FieldDescriptorProto) bool { + // if we have the AST, we can more precisely determine if there was a custom + // JSON named defined, even if it is explicitly configured to tbe the same + // as the default JSON name for the field. + opts := r.FieldNode(fdProto).GetOptions() + if opts == nil { + return false + } + for _, opt := range opts.Options { + if len(opt.Name.Parts) == 1 && + opt.Name.Parts[0].Name.AsIdentifier() == "json_name" && + !opt.Name.Parts[0].IsExtension() { + return true + } + } + return false +} + +func canonicalEnumValueName(enumValueName, enumName string) string { + return enumValCamelCase(removePrefix(enumValueName, enumName)) +} + +// removePrefix is used to remove the given prefix from the given str. It does not require +// an exact match and ignores case and underscores. If the all non-underscore characters +// would be removed from str, str is returned unchanged. If str does not have the given +// prefix (even with the very lenient matching, in regard to case and underscores), then +// str is returned unchanged. +// +// The algorithm is adapted from the protoc source: +// +// https://github.com/protocolbuffers/protobuf/blob/v21.3/src/google/protobuf/descriptor.cc#L922 +func removePrefix(str, prefix string) string { + j := 0 + for i, r := range str { + if r == '_' { + // skip underscores in the input + continue + } + + p, sz := utf8.DecodeRuneInString(prefix[j:]) + for p == '_' { + j += sz // consume/skip underscore + p, sz = utf8.DecodeRuneInString(prefix[j:]) + } + + if j == len(prefix) { + // matched entire prefix; return rest of str + // but skipping any leading underscores + result := strings.TrimLeft(str[i:], "_") + if len(result) == 0 { + // result can't be empty string + return str + } + return result + } + if unicode.ToLower(r) != unicode.ToLower(p) { + // does not match prefix + return str + } + j += sz // consume matched rune of prefix + } + return str +} + +// enumValCamelCase converts the given string to upper-camel-case. +// +// The algorithm is adapted from the protoc source: +// +// https://github.com/protocolbuffers/protobuf/blob/v21.3/src/google/protobuf/descriptor.cc#L887 +func enumValCamelCase(name string) string { + var js []rune + nextUpper := true + for _, r := range name { + if r == '_' { + nextUpper = true + continue + } + if nextUpper { + nextUpper = false + js = append(js, unicode.ToUpper(r)) + } else { + js = append(js, unicode.ToLower(r)) + } + } + return string(js) +} + +func isBuiltinTypeName(typeName string) bool { + switch typeName { + case "int32", "int64", "uint32", "uint64", "sint32", "sint64", + "fixed32", "fixed64", "sfixed32", "sfixed64", + "bool", "double", "float", "string", "bytes": + return true + default: + return false + } +} + +func getTypeName(fd protoreflect.FieldDescriptor) string { + switch fd.Kind() { + case protoreflect.MessageKind, protoreflect.GroupKind: + return "." + string(fd.Message().FullName()) + case protoreflect.EnumKind: + return "." + string(fd.Enum().FullName()) + default: + return fd.Kind().String() + } +} + +func findExtensionRangeOptionSpan( + file protoreflect.FileDescriptor, + extended protoreflect.MessageDescriptor, + extRangeIndex int, + extRange *descriptorpb.DescriptorProto_ExtensionRange, + path ...int32, +) (ast.SourceSpan, bool) { + // NB: Typically, we have an AST for a file and NOT source code info, because the + // compiler validates options before computing source code info. However, we might + // be validating an extension (whose source/AST we have), but whose extendee (and + // thus extension range options for declarations) could be in some other file, which + // could be provided to the compiler as an already-compiled descriptor. So this + // function can fallback to using source code info if an AST is not available. + + if r, ok := file.(Result); ok && r.AST() != nil { + // Find the location using the AST, which will generally be higher fidelity + // than what we might find in a file descriptor's source code info. + exts := r.ExtensionsNode(extRange) + return findOptionSpan(r.FileNode(), exts, extRange.Options.ProtoReflect().Descriptor(), path...) + } + + srcLocs := file.SourceLocations() + if srcLocs.Len() == 0 { + // no source code info, can't do any better than the filename. We + // return true as the boolean so the caller doesn't try again with + // an alternate path, since we won't be able to do any better. + return ast.UnknownSpan(file.Path()), true + } + msgPath, ok := internal.ComputePath(extended) + if !ok { + // Same as above: return true since no subsequent query can do better. + return ast.UnknownSpan(file.Path()), true + } + + //nolint:gocritic // intentionally assigning to different slice variables + extRangePath := append(msgPath, internal.MessageExtensionRangesTag, int32(extRangeIndex)) + optsPath := append(extRangePath, internal.ExtensionRangeOptionsTag) //nolint:gocritic + fullPath := append(optsPath, path...) //nolint:gocritic + srcLoc := srcLocs.ByPath(fullPath) + if srcLoc.Path != nil { + // found it + return asSpan(file.Path(), srcLoc), true + } + + // Slow path to find closest match :/ + // We look for longest matching path that is at least len(extRangePath) + // long. If we find a path that is longer (meaning a path that points INSIDE + // the request element), accept the first such location. + var bestMatch protoreflect.SourceLocation + var bestMatchPathLen int + for i, length := 0, srcLocs.Len(); i < length; i++ { + srcLoc := srcLocs.Get(i) + if len(srcLoc.Path) >= len(extRangePath) && + isDescendantPath(fullPath, srcLoc.Path) && + len(srcLoc.Path) > bestMatchPathLen { + bestMatch = srcLoc + bestMatchPathLen = len(srcLoc.Path) + } else if isDescendantPath(srcLoc.Path, path) { + return asSpan(file.Path(), srcLoc), false + } + } + if bestMatchPathLen > 0 { + return asSpan(file.Path(), bestMatch), false + } + return ast.UnknownSpan(file.Path()), false +} + +func (r *result) findScalarOptionSpan( + root ast.NodeWithOptions, + name string, +) ast.SourceSpan { + match := ast.Node(root) + root.RangeOptions(func(n *ast.OptionNode) bool { + if len(n.Name.Parts) == 1 && !n.Name.Parts[0].IsExtension() && + string(n.Name.Parts[0].Name.AsIdentifier()) == name { + match = n + return false + } + return true + }) + return r.FileNode().NodeInfo(match) +} + +func (r *result) findOptionSpan( + d protoutil.DescriptorProtoWrapper, + path ...int32, +) ast.SourceSpan { + node := r.Node(d.AsProto()) + nodeWithOpts, ok := node.(ast.NodeWithOptions) + if !ok { + return r.FileNode().NodeInfo(node) + } + span, _ := findOptionSpan(r.FileNode(), nodeWithOpts, d.Options().ProtoReflect().Descriptor(), path...) + return span +} + +func findOptionSpan( + file ast.FileDeclNode, + root ast.NodeWithOptions, + md protoreflect.MessageDescriptor, + path ...int32, +) (ast.SourceSpan, bool) { + bestMatch := ast.Node(root) + var bestMatchLen int + var repeatedIndices []int + root.RangeOptions(func(n *ast.OptionNode) bool { + desc := md + limit := len(n.Name.Parts) + if limit > len(path) { + limit = len(path) + } + var nextIsIndex bool + for i := 0; i < limit; i++ { + if desc == nil || nextIsIndex { + // Can't match anymore. Try next option. + return true + } + wantField := desc.Fields().ByNumber(protoreflect.FieldNumber(path[i])) + if wantField == nil { + // Should not be possible... next option won't fare any better since + // it's a disagreement between given path and given descriptor so bail. + return false + } + if n.Name.Parts[i].Open != nil || + string(n.Name.Parts[i].Name.AsIdentifier()) != string(wantField.Name()) { + // This is an extension/custom option or indicates the wrong name. + // Try the next one. + return true + } + desc = wantField.Message() + nextIsIndex = wantField.Cardinality() == protoreflect.Repeated + } + // If we made it this far, we've matched everything so far. + if len(n.Name.Parts) >= len(path) { + // Either an exact match (if equal) or this option points *inside* the + // item we care about (if greater). Either way, the first such result + // is a keeper. + bestMatch = n.Name.Parts[len(path)-1] + bestMatchLen = len(n.Name.Parts) + return false + } + // We've got more path elements to try to match with the value. + match, matchLen := findMatchingValueNode( + desc, + path[len(n.Name.Parts):], + nextIsIndex, + 0, + &repeatedIndices, + n, + n.Val) + if match != nil { + totalMatchLen := matchLen + len(n.Name.Parts) + if totalMatchLen > bestMatchLen { + bestMatch, bestMatchLen = match, totalMatchLen + } + } + return bestMatchLen != len(path) // no exact match, so keep looking + }) + return file.NodeInfo(bestMatch), bestMatchLen == len(path) +} + +func findMatchingValueNode( + md protoreflect.MessageDescriptor, + path protoreflect.SourcePath, + currIsRepeated bool, + repeatedCount int, + repeatedIndices *[]int, + node ast.Node, + val ast.ValueNode, +) (ast.Node, int) { + var matchLen int + var index int + if currIsRepeated { + // Compute the index of the current value (or, if an array literal, the + // index of the first value in the array). + if len(*repeatedIndices) > repeatedCount { + (*repeatedIndices)[repeatedCount]++ + index = (*repeatedIndices)[repeatedCount] + } else { + *repeatedIndices = append(*repeatedIndices, 0) + index = 0 + } + repeatedCount++ + } + + if arrayVal, ok := val.(*ast.ArrayLiteralNode); ok { + if !currIsRepeated { + // This should not happen. + return nil, 0 + } + offset := int(path[0]) - index + if offset >= len(arrayVal.Elements) { + // The index we are looking for is not in this array. + return nil, 0 + } + elem := arrayVal.Elements[offset] + // We've matched the index! + matchLen++ + path = path[1:] + // Recurse into array element. + nextMatch, nextMatchLen := findMatchingValueNode( + md, + path, + false, + repeatedCount, + repeatedIndices, + elem, + elem, + ) + return nextMatch, nextMatchLen + matchLen + } + + if currIsRepeated { + if index != int(path[0]) { + // Not a match! + return nil, 0 + } + // We've matched the index! + matchLen++ + path = path[1:] + if len(path) == 0 { + // We're done matching! + return node, matchLen + } + } + + msgValue, ok := val.(*ast.MessageLiteralNode) + if !ok { + // We can't go any further + return node, matchLen + } + + var wantField protoreflect.FieldDescriptor + if md != nil { + wantField = md.Fields().ByNumber(protoreflect.FieldNumber(path[0])) + } + if wantField == nil { + // Should not be possible... next option won't fare any better since + // it's a disagreement between given path and given descriptor so bail. + return nil, 0 + } + for _, field := range msgValue.Elements { + if field.Name.Open != nil || + string(field.Name.Name.AsIdentifier()) != string(wantField.Name()) { + // This is an extension/custom option or indicates the wrong name. + // Try the next one. + continue + } + // We've matched this field. + matchLen++ + path = path[1:] + if len(path) == 0 { + // Perfect match! + return field, matchLen + } + nextMatch, nextMatchLen := findMatchingValueNode( + wantField.Message(), + path, + wantField.Cardinality() == protoreflect.Repeated, + repeatedCount, + repeatedIndices, + field, + field.Val, + ) + return nextMatch, nextMatchLen + matchLen + } + + // If we didn't find the right field, just return what we have so far. + return node, matchLen +} + +func isDescendantPath(descendant, ancestor protoreflect.SourcePath) bool { + if len(descendant) < len(ancestor) { + return false + } + for i := range ancestor { + if descendant[i] != ancestor[i] { + return false + } + } + return true +} + +func asSpan(file string, srcLoc protoreflect.SourceLocation) ast.SourceSpan { + return ast.NewSourceSpan( + ast.SourcePos{ + Filename: file, + Line: srcLoc.StartLine + 1, + Col: srcLoc.StartColumn + 1, + }, + ast.SourcePos{ + Filename: file, + Line: srcLoc.EndLine + 1, + Col: srcLoc.EndColumn + 1, + }, + ) +} + +func (r *result) getImportLocation(path string) ast.SourceSpan { + node, ok := r.FileNode().(*ast.FileNode) + if !ok { + return ast.UnknownSpan(path) + } + for _, decl := range node.Decls { + imp, ok := decl.(*ast.ImportNode) + if !ok { + continue + } + if imp.Name.AsString() == path { + return node.NodeInfo(imp.Name) + } + } + // Couldn't find it? Should never happen... + return ast.UnknownSpan(path) +} + +func isEditions(r *result) bool { + return descriptorpb.Edition(r.Edition()) >= descriptorpb.Edition_EDITION_2023 +} diff --git a/vendor/github.com/bufbuild/protocompile/options/options.go b/vendor/github.com/bufbuild/protocompile/options/options.go new file mode 100644 index 0000000..e22bb2a --- /dev/null +++ b/vendor/github.com/bufbuild/protocompile/options/options.go @@ -0,0 +1,2267 @@ +// Copyright 2020-2024 Buf Technologies, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Package options contains the logic for interpreting options. The parse step +// of compilation stores the options in uninterpreted form, which contains raw +// identifiers and literal values. +// +// The process of interpreting an option is to resolve identifiers, by examining +// descriptors for the google.protobuf.*Options types and their available +// extensions (custom options). As field names are resolved, the values can be +// type-checked against the types indicated in field descriptors. +// +// On success, the various fields and extensions of the options message are +// populated and the field holding the uninterpreted form is cleared. +package options + +import ( + "bytes" + "errors" + "fmt" + "math" + "strings" + + "google.golang.org/protobuf/encoding/prototext" + "google.golang.org/protobuf/proto" + "google.golang.org/protobuf/reflect/protoreflect" + "google.golang.org/protobuf/reflect/protoregistry" + "google.golang.org/protobuf/types/descriptorpb" + "google.golang.org/protobuf/types/dynamicpb" + + "github.com/bufbuild/protocompile/ast" + "github.com/bufbuild/protocompile/internal" + "github.com/bufbuild/protocompile/internal/messageset" + "github.com/bufbuild/protocompile/linker" + "github.com/bufbuild/protocompile/parser" + "github.com/bufbuild/protocompile/reporter" + "github.com/bufbuild/protocompile/sourceinfo" +) + +type interpreter struct { + file file + resolver linker.Resolver + overrideDescriptorProto linker.File + + index sourceinfo.OptionIndex + pathBuffer []int32 + + reporter *reporter.Handler + lenient bool + + // lenienceEnabled is set to true when errors reported to reporter + // should be lenient + lenienceEnabled bool + lenientErrReported bool +} + +type file interface { + parser.Result + ResolveMessageLiteralExtensionName(ast.IdentValueNode) string +} + +type noResolveFile struct { + parser.Result +} + +func (n noResolveFile) ResolveMessageLiteralExtensionName(ast.IdentValueNode) string { + return "" +} + +// InterpreterOption is an option that can be passed to InterpretOptions and +// its variants. +type InterpreterOption func(*interpreter) + +// WithOverrideDescriptorProto returns an option that indicates that the given file +// should be consulted when looking up a definition for an option type. The given +// file should usually have the path "google/protobuf/descriptor.proto". The given +// file will only be consulted if the option type is otherwise not visible to the +// file whose options are being interpreted. +func WithOverrideDescriptorProto(f linker.File) InterpreterOption { + return func(interp *interpreter) { + interp.overrideDescriptorProto = f + } +} + +// InterpretOptions interprets options in the given linked result, returning +// an index that can be used to generate source code info. This step mutates +// the linked result's underlying proto to move option elements out of the +// "uninterpreted_option" fields and into proper option fields and extensions. +// +// The given handler is used to report errors and warnings. If any errors are +// reported, this function returns a non-nil error. +func InterpretOptions(linked linker.Result, handler *reporter.Handler, opts ...InterpreterOption) (sourceinfo.OptionIndex, error) { + return interpretOptions(false, linked, linker.ResolverFromFile(linked), handler, opts) +} + +// InterpretOptionsLenient interprets options in a lenient/best-effort way in +// the given linked result, returning an index that can be used to generate +// source code info. This step mutates the linked result's underlying proto to +// move option elements out of the "uninterpreted_option" fields and into proper +// option fields and extensions. +// +// In lenient more, errors resolving option names and type errors are ignored. +// Any options that are uninterpretable (due to such errors) will remain in the +// "uninterpreted_option" fields. +func InterpretOptionsLenient(linked linker.Result, opts ...InterpreterOption) (sourceinfo.OptionIndex, error) { + return interpretOptions(true, linked, linker.ResolverFromFile(linked), reporter.NewHandler(nil), opts) +} + +// InterpretUnlinkedOptions does a best-effort attempt to interpret options in +// the given parsed result, returning an index that can be used to generate +// source code info. This step mutates the parsed result's underlying proto to +// move option elements out of the "uninterpreted_option" fields and into proper +// option fields and extensions. +// +// This is the same as InterpretOptionsLenient except that it accepts an +// unlinked result. Because the file is unlinked, custom options cannot be +// interpreted. Other errors resolving option names or type errors will be +// effectively ignored. Any options that are uninterpretable (due to such +// errors) will remain in the "uninterpreted_option" fields. +func InterpretUnlinkedOptions(parsed parser.Result, opts ...InterpreterOption) (sourceinfo.OptionIndex, error) { + return interpretOptions(true, noResolveFile{parsed}, nil, reporter.NewHandler(nil), opts) +} + +func interpretOptions(lenient bool, file file, res linker.Resolver, handler *reporter.Handler, interpOpts []InterpreterOption) (sourceinfo.OptionIndex, error) { + interp := &interpreter{ + file: file, + resolver: res, + lenient: lenient, + reporter: handler, + index: sourceinfo.OptionIndex{}, + pathBuffer: make([]int32, 0, 16), + } + for _, opt := range interpOpts { + opt(interp) + } + // We have to do this in two phases. First we interpret non-custom options. + // This allows us to handle standard options and features that may needed to + // correctly reference the custom options in the second phase. + if err := interp.interpretFileOptions(file, false); err != nil { + return nil, err + } + // Now we can do custom options. + if err := interp.interpretFileOptions(file, true); err != nil { + return nil, err + } + return interp.index, nil +} + +func (interp *interpreter) handleErrorf(span ast.SourceSpan, msg string, args ...interface{}) error { + if interp.lenienceEnabled { + interp.lenientErrReported = true + return nil + } + return interp.reporter.HandleErrorf(span, msg, args...) +} + +func (interp *interpreter) handleErrorWithPos(span ast.SourceSpan, err error) error { + if interp.lenienceEnabled { + interp.lenientErrReported = true + return nil + } + return interp.reporter.HandleErrorWithPos(span, err) +} + +func (interp *interpreter) handleError(err error) error { + if interp.lenienceEnabled { + interp.lenientErrReported = true + return nil + } + return interp.reporter.HandleError(err) +} + +func (interp *interpreter) interpretFileOptions(file file, customOpts bool) error { + fd := file.FileDescriptorProto() + prefix := fd.GetPackage() + if prefix != "" { + prefix += "." + } + err := interpretElementOptions(interp, fd.GetName(), targetTypeFile, fd, customOpts) + if err != nil { + return err + } + for _, md := range fd.GetMessageType() { + fqn := prefix + md.GetName() + if err := interp.interpretMessageOptions(fqn, md, customOpts); err != nil { + return err + } + } + for _, fld := range fd.GetExtension() { + fqn := prefix + fld.GetName() + if err := interp.interpretFieldOptions(fqn, fld, customOpts); err != nil { + return err + } + } + for _, ed := range fd.GetEnumType() { + fqn := prefix + ed.GetName() + if err := interp.interpretEnumOptions(fqn, ed, customOpts); err != nil { + return err + } + } + for _, sd := range fd.GetService() { + fqn := prefix + sd.GetName() + err := interpretElementOptions(interp, fqn, targetTypeService, sd, customOpts) + if err != nil { + return err + } + for _, mtd := range sd.GetMethod() { + mtdFqn := fqn + "." + mtd.GetName() + err := interpretElementOptions(interp, mtdFqn, targetTypeMethod, mtd, customOpts) + if err != nil { + return err + } + } + } + return nil +} + +func resolveDescriptor[T protoreflect.Descriptor](res linker.Resolver, name string) T { + var zero T + if res == nil { + return zero + } + if len(name) > 0 && name[0] == '.' { + name = name[1:] + } + desc, _ := res.FindDescriptorByName(protoreflect.FullName(name)) + typedDesc, ok := desc.(T) + if ok { + return typedDesc + } + return zero +} + +func (interp *interpreter) resolveExtensionType(name string) (protoreflect.ExtensionTypeDescriptor, error) { + if interp.resolver == nil { + return nil, protoregistry.NotFound + } + if len(name) > 0 && name[0] == '.' { + name = name[1:] + } + ext, err := interp.resolver.FindExtensionByName(protoreflect.FullName(name)) + if err != nil { + return nil, err + } + return ext.TypeDescriptor(), nil +} + +func (interp *interpreter) resolveOptionsType(name string) protoreflect.MessageDescriptor { + md := resolveDescriptor[protoreflect.MessageDescriptor](interp.resolver, name) + if md != nil { + return md + } + if interp.overrideDescriptorProto == nil { + return nil + } + if len(name) > 0 && name[0] == '.' { + name = name[1:] + } + desc := interp.overrideDescriptorProto.FindDescriptorByName(protoreflect.FullName(name)) + if md, ok := desc.(protoreflect.MessageDescriptor); ok { + return md + } + return nil +} + +func (interp *interpreter) nodeInfo(n ast.Node) ast.NodeInfo { + return interp.file.FileNode().NodeInfo(n) +} + +func (interp *interpreter) interpretMessageOptions(fqn string, md *descriptorpb.DescriptorProto, customOpts bool) error { + err := interpretElementOptions(interp, fqn, targetTypeMessage, md, customOpts) + if err != nil { + return err + } + for _, fld := range md.GetField() { + fldFqn := fqn + "." + fld.GetName() + if err := interp.interpretFieldOptions(fldFqn, fld, customOpts); err != nil { + return err + } + } + for _, ood := range md.GetOneofDecl() { + oodFqn := fqn + "." + ood.GetName() + err := interpretElementOptions(interp, oodFqn, targetTypeOneof, ood, customOpts) + if err != nil { + return err + } + } + for _, fld := range md.GetExtension() { + fldFqn := fqn + "." + fld.GetName() + if err := interp.interpretFieldOptions(fldFqn, fld, customOpts); err != nil { + return err + } + } + for _, er := range md.GetExtensionRange() { + erFqn := fmt.Sprintf("%s.%d-%d", fqn, er.GetStart(), er.GetEnd()) + err := interpretElementOptions(interp, erFqn, targetTypeExtensionRange, er, customOpts) + if err != nil { + return err + } + } + for _, nmd := range md.GetNestedType() { + nmdFqn := fqn + "." + nmd.GetName() + if err := interp.interpretMessageOptions(nmdFqn, nmd, customOpts); err != nil { + return err + } + } + for _, ed := range md.GetEnumType() { + edFqn := fqn + "." + ed.GetName() + if err := interp.interpretEnumOptions(edFqn, ed, customOpts); err != nil { + return err + } + } + + // We also copy features for map fields down to their synthesized key and value fields. + for _, fld := range md.GetField() { + entryName := internal.InitCap(internal.JSONName(fld.GetName())) + "Entry" + if fld.GetLabel() != descriptorpb.FieldDescriptorProto_LABEL_REPEATED || + fld.GetType() != descriptorpb.FieldDescriptorProto_TYPE_MESSAGE && + fld.GetTypeName() != "."+fqn+"."+entryName { + // can't be a map field + continue + } + if fld.Options == nil || fld.Options.Features == nil { + // no features to propagate + continue + } + for _, nmd := range md.GetNestedType() { + if nmd.GetName() == entryName { + // found the entry message + if !nmd.GetOptions().GetMapEntry() { + break // not a map + } + for _, mapField := range nmd.Field { + if mapField.Options == nil { + mapField.Options = &descriptorpb.FieldOptions{} + } + features := proto.Clone(fld.Options.Features).(*descriptorpb.FeatureSet) //nolint:errcheck + if mapField.Options.Features != nil { + proto.Merge(features, mapField.Options.Features) + } + mapField.Options.Features = features + } + break + } + } + } + + return nil +} + +var emptyFieldOptions = &descriptorpb.FieldOptions{} + +func (interp *interpreter) interpretFieldOptions(fqn string, fld *descriptorpb.FieldDescriptorProto, customOpts bool) error { + opts := fld.GetOptions() + emptyOptionsAlreadyPresent := opts != nil && len(opts.GetUninterpretedOption()) == 0 + + // For non-custom phase, first process pseudo-options + if len(opts.GetUninterpretedOption()) > 0 && !customOpts { + interp.enableLenience(true) + err := interp.interpretFieldPseudoOptions(fqn, fld, opts) + interp.enableLenience(false) + if err != nil { + return err + } + } + + // Must re-check length of uninterpreted options since above step could remove some. + if len(opts.GetUninterpretedOption()) == 0 { + // If the message has no other interpreted options, we clear it out. But don't + // do that if the descriptor came in with empty options or if it already has + // interpreted option fields. + if opts != nil && !emptyOptionsAlreadyPresent && proto.Equal(fld.Options, emptyFieldOptions) { + fld.Options = nil + } + return nil + } + + // Then process actual options. + return interpretElementOptions(interp, fqn, targetTypeField, fld, customOpts) +} + +func (interp *interpreter) interpretFieldPseudoOptions(fqn string, fld *descriptorpb.FieldDescriptorProto, opts *descriptorpb.FieldOptions) error { + scope := "field " + fqn + uo := opts.UninterpretedOption + + // process json_name pseudo-option + if index, err := internal.FindOption(interp.file, interp.handleErrorf, scope, uo, "json_name"); err != nil { + return err + } else if index >= 0 { + opt := uo[index] + optNode := interp.file.OptionNode(opt) + if opt.StringValue == nil { + return interp.handleErrorf(interp.nodeInfo(optNode.GetValue()), "%s: expecting string value for json_name option", scope) + } + jsonName := string(opt.StringValue) + // Extensions don't support custom json_name values. + // If the value is already set (via the descriptor) and doesn't match the default value, return an error. + if fld.GetExtendee() != "" && jsonName != "" && jsonName != internal.JSONName(fld.GetName()) { + return interp.handleErrorf(interp.nodeInfo(optNode.GetName()), "%s: option json_name is not allowed on extensions", scope) + } + // attribute source code info + if on, ok := optNode.(*ast.OptionNode); ok { + interp.index[on] = &sourceinfo.OptionSourceInfo{Path: []int32{-1, internal.FieldJSONNameTag}} + } + uo = internal.RemoveOption(uo, index) + if strings.HasPrefix(jsonName, "[") && strings.HasSuffix(jsonName, "]") { + return interp.handleErrorf(interp.nodeInfo(optNode.GetValue()), "%s: option json_name value cannot start with '[' and end with ']'; that is reserved for representing extensions", scope) + } + fld.JsonName = proto.String(jsonName) + } + + // and process default pseudo-option + if index, err := interp.processDefaultOption(scope, fqn, fld, uo); err != nil { + return err + } else if index >= 0 { + // attribute source code info + optNode := interp.file.OptionNode(uo[index]) + if on, ok := optNode.(*ast.OptionNode); ok { + interp.index[on] = &sourceinfo.OptionSourceInfo{Path: []int32{-1, internal.FieldDefaultTag}} + } + uo = internal.RemoveOption(uo, index) + } + + opts.UninterpretedOption = uo + return nil +} + +func (interp *interpreter) processDefaultOption(scope string, fqn string, fld *descriptorpb.FieldDescriptorProto, uos []*descriptorpb.UninterpretedOption) (defaultIndex int, err error) { + found, err := internal.FindOption(interp.file, interp.handleErrorf, scope, uos, "default") + if err != nil || found == -1 { + return -1, err + } + opt := uos[found] + optNode := interp.file.OptionNode(opt) + if fld.GetLabel() == descriptorpb.FieldDescriptorProto_LABEL_REPEATED { + return -1, interp.handleErrorf(interp.nodeInfo(optNode.GetName()), "%s: default value cannot be set because field is repeated", scope) + } + if fld.GetType() == descriptorpb.FieldDescriptorProto_TYPE_GROUP || fld.GetType() == descriptorpb.FieldDescriptorProto_TYPE_MESSAGE { + return -1, interp.handleErrorf(interp.nodeInfo(optNode.GetName()), "%s: default value cannot be set because field is a message", scope) + } + mc := &internal.MessageContext{ + File: interp.file, + ElementName: fqn, + ElementType: descriptorType(fld), + Option: opt, + } + + val := optNode.GetValue() + var v interface{} + if val.Value() == nil { + // no value in the AST, so we dig the value out of the uninterpreted option proto + v, err = interp.defaultValueFromProto(mc, fld, opt, val) + } else { + // compute value from AST + v, err = interp.defaultValue(mc, fld, val) + } + if err != nil { + return -1, interp.handleError(err) + } + + if str, ok := v.(string); ok { + fld.DefaultValue = proto.String(str) + } else if b, ok := v.([]byte); ok { + fld.DefaultValue = proto.String(encodeDefaultBytes(b)) + } else { + var flt float64 + var ok bool + if flt, ok = v.(float64); !ok { + var flt32 float32 + if flt32, ok = v.(float32); ok { + flt = float64(flt32) + } + } + if ok { + switch { + case math.IsInf(flt, 1): + fld.DefaultValue = proto.String("inf") + case math.IsInf(flt, -1): + fld.DefaultValue = proto.String("-inf") + case math.IsNaN(flt): + fld.DefaultValue = proto.String("nan") + default: + fld.DefaultValue = proto.String(fmt.Sprintf("%v", v)) + } + } else { + fld.DefaultValue = proto.String(fmt.Sprintf("%v", v)) + } + } + return found, nil +} + +func (interp *interpreter) defaultValue(mc *internal.MessageContext, fld *descriptorpb.FieldDescriptorProto, val ast.ValueNode) (interface{}, error) { + if _, ok := val.(*ast.MessageLiteralNode); ok { + return -1, reporter.Errorf(interp.nodeInfo(val), "%vdefault value cannot be a message", mc) + } + if fld.GetType() == descriptorpb.FieldDescriptorProto_TYPE_ENUM { + ed := resolveDescriptor[protoreflect.EnumDescriptor](interp.resolver, fld.GetTypeName()) + if ed == nil { + return -1, reporter.Errorf(interp.nodeInfo(val), "%vunable to resolve enum type %q for field %q", mc, fld.GetTypeName(), fld.GetName()) + } + _, name, err := interp.enumFieldValue(mc, ed, val, false) + if err != nil { + return -1, err + } + return string(name), nil + } + return interp.scalarFieldValue(mc, fld.GetType(), val, false) +} + +func (interp *interpreter) defaultValueFromProto(mc *internal.MessageContext, fld *descriptorpb.FieldDescriptorProto, opt *descriptorpb.UninterpretedOption, node ast.Node) (interface{}, error) { + if opt.AggregateValue != nil { + return -1, reporter.Errorf(interp.nodeInfo(node), "%vdefault value cannot be a message", mc) + } + if fld.GetType() == descriptorpb.FieldDescriptorProto_TYPE_ENUM { + ed := resolveDescriptor[protoreflect.EnumDescriptor](interp.resolver, fld.GetTypeName()) + if ed == nil { + return -1, reporter.Errorf(interp.nodeInfo(node), "%vunable to resolve enum type %q for field %q", mc, fld.GetTypeName(), fld.GetName()) + } + _, name, err := interp.enumFieldValueFromProto(mc, ed, opt, node) + if err != nil { + return nil, err + } + return string(name), nil + } + return interp.scalarFieldValueFromProto(mc, fld.GetType(), opt, node) +} + +func encodeDefaultBytes(b []byte) string { + var buf bytes.Buffer + internal.WriteEscapedBytes(&buf, b) + return buf.String() +} + +func (interp *interpreter) interpretEnumOptions(fqn string, ed *descriptorpb.EnumDescriptorProto, customOpts bool) error { + err := interpretElementOptions(interp, fqn, targetTypeEnum, ed, customOpts) + if err != nil { + return err + } + for _, evd := range ed.GetValue() { + evdFqn := fqn + "." + evd.GetName() + err := interpretElementOptions(interp, evdFqn, targetTypeEnumValue, evd, customOpts) + if err != nil { + return err + } + } + return nil +} + +func interpretElementOptions[Elem elementType[OptsStruct, Opts], OptsStruct any, Opts optionsType[OptsStruct]]( + interp *interpreter, + fqn string, + target *targetType[Elem, OptsStruct, Opts], + elem Elem, + customOpts bool, +) error { + opts := elem.GetOptions() + uninterpreted := opts.GetUninterpretedOption() + if len(uninterpreted) > 0 { + remain, err := interp.interpretOptions(fqn, target.t, elem, opts, uninterpreted, customOpts) + if err != nil { + return err + } + target.setUninterpretedOptions(opts, remain) + } else if customOpts { + // If customOpts is true, we are in second pass of interpreting. + // For second pass, even if there are no options to interpret, we still + // need to verify feature usage. + features := opts.GetFeatures() + var msg protoreflect.Message + if len(features.ProtoReflect().GetUnknown()) > 0 { + // We need to first convert to a message that uses the sources' definition + // of FeatureSet. + optsDesc := opts.ProtoReflect().Descriptor() + optsFqn := string(optsDesc.FullName()) + if md := interp.resolveOptionsType(optsFqn); md != nil { + dm := dynamicpb.NewMessage(md) + if err := cloneInto(dm, opts, interp.resolver); err != nil { + node := interp.file.Node(elem) + return interp.handleError(reporter.Error(interp.nodeInfo(node), err)) + } + msg = dm + } + } + if msg == nil { + msg = opts.ProtoReflect() + } + err := interp.validateRecursive(false, msg, "", elem, nil, false, false, false) + if err != nil { + return err + } + } + return nil +} + +// interpretOptions processes the options in uninterpreted, which are interpreted as fields +// of the given opts message. The first return value is the features to use for child elements. +// On success, the latter two return values will usually be nil, nil. But if the current +// operation is lenient, it may return a non-nil slice of uninterpreted options on success. +// In such a case, the returned slice contains the options which could not be interpreted. +func (interp *interpreter) interpretOptions( + fqn string, + targetType descriptorpb.FieldOptions_OptionTargetType, + element, opts proto.Message, + uninterpreted []*descriptorpb.UninterpretedOption, + customOpts bool, +) ([]*descriptorpb.UninterpretedOption, error) { + optsDesc := opts.ProtoReflect().Descriptor() + optsFqn := string(optsDesc.FullName()) + var msg protoreflect.Message + // see if the parse included an override copy for these options + if md := interp.resolveOptionsType(optsFqn); md != nil { + dm := dynamicpb.NewMessage(md) + if err := cloneInto(dm, opts, interp.resolver); err != nil { + node := interp.file.Node(element) + return nil, interp.handleError(reporter.Error(interp.nodeInfo(node), err)) + } + msg = dm + } else { + msg = proto.Clone(opts).ProtoReflect() + } + + mc := &internal.MessageContext{ + File: interp.file, + ElementName: fqn, + ElementType: descriptorType(element), + } + var remain []*descriptorpb.UninterpretedOption + for _, uo := range uninterpreted { + isCustom := uo.Name[0].GetIsExtension() + if isCustom != customOpts { + // We're not looking at these this phase. + remain = append(remain, uo) + continue + } + firstName := uo.Name[0].GetNamePart() + if targetType == descriptorpb.FieldOptions_TARGET_TYPE_FIELD && + !isCustom && (firstName == "default" || firstName == "json_name") { + // Field pseudo-option that we can skip and is handled elsewhere. + remain = append(remain, uo) + continue + } + node := interp.file.OptionNode(uo) + if !isCustom && firstName == "uninterpreted_option" { + if interp.lenient { + remain = append(remain, uo) + continue + } + // uninterpreted_option might be found reflectively, but is not actually valid for use + if err := interp.handleErrorf(interp.nodeInfo(node.GetName()), "%vinvalid option 'uninterpreted_option'", mc); err != nil { + return nil, err + } + } + mc.Option = uo + interp.enableLenience(true) + srcInfo, err := interp.interpretField(targetType, mc, msg, uo, 0, interp.pathBuffer) + interp.enableLenience(false) + if err != nil { + return nil, err + } + if interp.lenientErrReported { + remain = append(remain, uo) + continue + } + + if srcInfo != nil { + if optn, ok := node.(*ast.OptionNode); ok { + interp.index[optn] = srcInfo + } + } + } + + // customOpts is true for the second pass, which is also when we want to validate feature usage. + doValidation := customOpts + if doValidation { + validateRequiredFields := !interp.lenient + err := interp.validateRecursive(validateRequiredFields, msg, "", element, nil, false, false, false) + if err != nil { + return nil, err + } + } + + if interp.lenient { + // If we're lenient, then we don't want to clobber the passed in message + // and leave it partially populated. So we convert into a copy first + optsClone := opts.ProtoReflect().New().Interface() + if err := cloneInto(optsClone, msg.Interface(), interp.resolver); err != nil { + // TODO: do this in a more granular way, so we can convert individual + // fields and leave bad ones uninterpreted instead of skipping all of + // the work we've done so far. + return uninterpreted, nil + } + if doValidation { + if err := proto.CheckInitialized(optsClone); err != nil { + // Conversion from dynamic message failed to set some required fields. + // TODO above applies here as well... + return uninterpreted, nil + } + } + // conversion from dynamic message above worked, so now + // it is safe to overwrite the passed in message + proto.Reset(opts) + proto.Merge(opts, optsClone) + + return remain, nil + } + + // now try to convert into the passed in message and fail if not successful + if err := cloneInto(opts, msg.Interface(), interp.resolver); err != nil { + node := interp.file.Node(element) + return nil, interp.handleError(reporter.Error(interp.nodeInfo(node), err)) + } + + return remain, nil +} + +// checkFieldUsage verifies that the given option field can be used +// for the given target type. It reports an error if not and returns +// a non-nil error if the handler returned a non-nil error. +func (interp *interpreter) checkFieldUsage( + targetType descriptorpb.FieldOptions_OptionTargetType, + fld protoreflect.FieldDescriptor, + node ast.Node, +) error { + msgOpts, _ := fld.ContainingMessage().Options().(*descriptorpb.MessageOptions) + if msgOpts.GetMessageSetWireFormat() && !messageset.CanSupportMessageSets() { + err := interp.handleErrorf(interp.nodeInfo(node), "field %q may not be used in an option: it uses 'message set wire format' legacy proto1 feature which is not supported", fld.FullName()) + if err != nil { + return err + } + } + + opts, ok := fld.Options().(*descriptorpb.FieldOptions) + if !ok { + return nil + } + targetTypes := opts.GetTargets() + if len(targetTypes) == 0 { + return nil + } + for _, allowedType := range targetTypes { + if allowedType == targetType { + return nil + } + } + allowedTypes := make([]string, len(targetTypes)) + for i, t := range targetTypes { + allowedTypes[i] = targetTypeString(t) + } + if len(targetTypes) == 1 && targetTypes[0] == descriptorpb.FieldOptions_TARGET_TYPE_UNKNOWN { + return interp.handleErrorf(interp.nodeInfo(node), "field %q may not be used in an option (it declares no allowed target types)", fld.FullName()) + } + return interp.handleErrorf(interp.nodeInfo(node), "field %q is allowed on [%s], not on %s", fld.FullName(), strings.Join(allowedTypes, ","), targetTypeString(targetType)) +} + +func targetTypeString(t descriptorpb.FieldOptions_OptionTargetType) string { + return strings.ToLower(strings.ReplaceAll(strings.TrimPrefix(t.String(), "TARGET_TYPE_"), "_", " ")) +} + +func editionString(t descriptorpb.Edition) string { + return strings.ToLower(strings.ReplaceAll(strings.TrimPrefix(t.String(), "EDITION_"), "_", "-")) +} + +func cloneInto(dest proto.Message, src proto.Message, res linker.Resolver) error { + if dest.ProtoReflect().Descriptor() == src.ProtoReflect().Descriptor() { + proto.Reset(dest) + proto.Merge(dest, src) + return nil + } + + // If descriptors are not the same, we could have field descriptors in src that + // don't match the ones in dest. There's no easy/sane way to handle that. So we + // just marshal to bytes and back to do this + marshaler := proto.MarshalOptions{ + // We've already validated required fields before this point, + // so we can allow partial here. + AllowPartial: true, + } + data, err := marshaler.Marshal(src) + if err != nil { + return err + } + unmarshaler := proto.UnmarshalOptions{AllowPartial: true} + if res != nil { + unmarshaler.Resolver = res + } else { + // Use a typed nil, which returns "not found" to all queries + // and prevents fallback to protoregistry.GlobalTypes. + unmarshaler.Resolver = (*protoregistry.Types)(nil) + } + return unmarshaler.Unmarshal(data, dest) +} + +func (interp *interpreter) validateRecursive( + validateRequiredFields bool, + msg protoreflect.Message, + prefix string, + element proto.Message, + path []int32, + isFeatures bool, + inFeatures bool, + inMap bool, +) error { + if validateRequiredFields { + flds := msg.Descriptor().Fields() + var missingFields []string + for i := 0; i < flds.Len(); i++ { + fld := flds.Get(i) + if fld.Cardinality() == protoreflect.Required && !msg.Has(fld) { + missingFields = append(missingFields, fmt.Sprintf("%s%s", prefix, fld.Name())) + } + } + if len(missingFields) > 0 { + node := interp.findOptionNode(path, element) + err := interp.handleErrorf(interp.nodeInfo(node), "error in %s options: some required fields missing: %v", descriptorType(element), strings.Join(missingFields, ", ")) + if err != nil { + return err + } + } + } + + var err error + msg.Range(func(fld protoreflect.FieldDescriptor, val protoreflect.Value) bool { + chpath := path + if !inMap { + chpath = append(chpath, int32(fld.Number())) + } + chInFeatures := isFeatures || inFeatures + chIsFeatures := !chInFeatures && len(path) == 0 && fld.Name() == "features" + + if (isFeatures || (inFeatures && fld.IsExtension())) && + interp.file.FileNode().Name() == fld.ParentFile().Path() { + var what, name string + if fld.IsExtension() { + what = "custom feature" + name = "(" + string(fld.FullName()) + ")" + } else { + what = "feature" + name = string(fld.Name()) + } + node := interp.findOptionNode(path, element) + err = interp.handleErrorf(interp.nodeInfo(node), "%s %s cannot be used from the same file in which it is defined", what, name) + if err != nil { + return false + } + } + + if chInFeatures { + // Validate feature usage against feature settings. + + // First, check the feature support settings of the field. + opts, _ := fld.Options().(*descriptorpb.FieldOptions) + edition := interp.file.FileDescriptorProto().GetEdition() + if opts != nil && opts.FeatureSupport != nil { + err = interp.validateFeatureSupport(edition, opts.FeatureSupport, "field", string(fld.FullName()), chpath, element) + if err != nil { + return false + } + } + // Then, if it's an enum or has an enum, check the feature support settings of the enum values. + var enum protoreflect.EnumDescriptor + if fld.Enum() != nil { + enum = fld.Enum() + } else if fld.IsMap() && fld.MapValue().Enum() != nil { + enum = fld.MapValue().Enum() + } + if enum != nil { + switch { + case fld.IsMap(): + val.Map().Range(func(_ protoreflect.MapKey, v protoreflect.Value) bool { + // Can't construct path to particular map entry since we don't this entry's index. + // So we leave chpath alone, and it will have to point to the whole map value (or + // the first entry if the map is de-structured across multiple option statements). + err = interp.validateEnumValueFeatureSupport(edition, enum, v.Enum(), chpath, element) + return err == nil + }) + if err != nil { + return false + } + case fld.IsList(): + sl := val.List() + for i := 0; i < sl.Len(); i++ { + v := sl.Get(i) + err = interp.validateEnumValueFeatureSupport(edition, enum, v.Enum(), append(chpath, int32(i)), element) + if err != nil { + return false + } + } + default: + err = interp.validateEnumValueFeatureSupport(edition, enum, val.Enum(), chpath, element) + if err != nil { + return false + } + } + } + } + + // If it's a message or contains a message, recursively validate fields in those messages. + switch { + case fld.IsMap() && fld.MapValue().Message() != nil: + val.Map().Range(func(k protoreflect.MapKey, v protoreflect.Value) bool { + chprefix := fmt.Sprintf("%s%s[%v].", prefix, fieldName(fld), k) + err = interp.validateRecursive(validateRequiredFields, v.Message(), chprefix, element, chpath, chIsFeatures, chInFeatures, true) + return err == nil + }) + if err != nil { + return false + } + case fld.IsList() && fld.Message() != nil: + sl := val.List() + for i := 0; i < sl.Len(); i++ { + v := sl.Get(i) + chprefix := fmt.Sprintf("%s%s[%d].", prefix, fieldName(fld), i) + if !inMap { + chpath = append(chpath, int32(i)) + } + err = interp.validateRecursive(validateRequiredFields, v.Message(), chprefix, element, chpath, chIsFeatures, chInFeatures, inMap) + if err != nil { + return false + } + } + case !fld.IsMap() && fld.Message() != nil: + chprefix := fmt.Sprintf("%s%s.", prefix, fieldName(fld)) + err = interp.validateRecursive(validateRequiredFields, val.Message(), chprefix, element, chpath, chIsFeatures, chInFeatures, inMap) + if err != nil { + return false + } + } + return true + }) + return err +} + +func (interp *interpreter) validateEnumValueFeatureSupport( + edition descriptorpb.Edition, + enum protoreflect.EnumDescriptor, + number protoreflect.EnumNumber, + path []int32, + element proto.Message, +) error { + enumVal := enum.Values().ByNumber(number) + if enumVal == nil { + return nil + } + enumValOpts, _ := enumVal.Options().(*descriptorpb.EnumValueOptions) + if enumValOpts == nil || enumValOpts.FeatureSupport == nil { + return nil + } + return interp.validateFeatureSupport(edition, enumValOpts.FeatureSupport, "enum value", string(enumVal.Name()), path, element) +} + +func (interp *interpreter) validateFeatureSupport( + edition descriptorpb.Edition, + featureSupport *descriptorpb.FieldOptions_FeatureSupport, + what string, + name string, + path []int32, + element proto.Message, +) error { + if featureSupport.EditionIntroduced != nil && edition < featureSupport.GetEditionIntroduced() { + node := interp.findOptionNode(path, element) + err := interp.handleErrorf(interp.nodeInfo(node), "%s %q was not introduced until edition %s", what, name, editionString(featureSupport.GetEditionIntroduced())) + if err != nil { + return err + } + } + if featureSupport.EditionRemoved != nil && edition >= featureSupport.GetEditionRemoved() { + node := interp.findOptionNode(path, element) + err := interp.handleErrorf(interp.nodeInfo(node), "%s %q was removed in edition %s", what, name, editionString(featureSupport.GetEditionRemoved())) + if err != nil { + return err + } + } + if featureSupport.EditionDeprecated != nil && edition >= featureSupport.GetEditionDeprecated() { + node := interp.findOptionNode(path, element) + var suffix string + if featureSupport.GetDeprecationWarning() != "" { + suffix = ": " + featureSupport.GetDeprecationWarning() + } + interp.reporter.HandleWarningf(interp.nodeInfo(node), "%s %q is deprecated as of edition %s%s", what, name, editionString(featureSupport.GetEditionDeprecated()), suffix) + } + return nil +} + +func (interp *interpreter) findOptionNode( + path []int32, + element proto.Message, +) ast.Node { + elementNode := interp.file.Node(element) + nodeWithOpts, _ := elementNode.(ast.NodeWithOptions) + if nodeWithOpts == nil { + return elementNode + } + node, _ := findOptionNode[*ast.OptionNode]( + path, + optionsRanger{nodeWithOpts}, + func(n *ast.OptionNode) *sourceinfo.OptionSourceInfo { + return interp.index[n] + }, + ) + if node != nil { + return node + } + return elementNode +} + +func findOptionNode[N ast.Node]( + path []int32, + nodes interface { + Range(func(N, ast.ValueNode) bool) + }, + srcInfoAccessor func(N) *sourceinfo.OptionSourceInfo, +) (ast.Node, int) { + var bestMatch ast.Node + var bestMatchLen int + nodes.Range(func(node N, val ast.ValueNode) bool { + srcInfo := srcInfoAccessor(node) + if srcInfo == nil { + // can happen if we are lenient when interpreting -- this node + // could not be interpreted and thus has no source info; skip + return true + } + if srcInfo.Path[0] < 0 { + // negative first value means it's a field pseudo-option; skip + return true + } + match, matchLen := findOptionValueNode(path, node, val, srcInfo) + if matchLen > bestMatchLen { + bestMatch = match + bestMatchLen = matchLen + if matchLen >= len(path) { + // not going to find a better one + return false + } + } + return true + }) + return bestMatch, bestMatchLen +} + +type optionsRanger struct { + node ast.NodeWithOptions +} + +func (r optionsRanger) Range(f func(*ast.OptionNode, ast.ValueNode) bool) { + r.node.RangeOptions(func(optNode *ast.OptionNode) bool { + return f(optNode, optNode.Val) + }) +} + +type valueRanger []ast.ValueNode + +func (r valueRanger) Range(f func(ast.ValueNode, ast.ValueNode) bool) { + for _, elem := range r { + if !f(elem, elem) { + return + } + } +} + +type fieldRanger map[*ast.MessageFieldNode]*sourceinfo.OptionSourceInfo + +func (r fieldRanger) Range(f func(*ast.MessageFieldNode, ast.ValueNode) bool) { + for elem := range r { + if !f(elem, elem.Val) { + return + } + } +} + +func isPathMatch(a, b []int32) bool { + length := len(a) + if len(b) < length { + length = len(b) + } + for i := 0; i < length; i++ { + if a[i] != b[i] { + return false + } + } + return true +} + +func findOptionValueNode( + path []int32, + node ast.Node, + value ast.ValueNode, + srcInfo *sourceinfo.OptionSourceInfo, +) (ast.Node, int) { + srcInfoPath := srcInfo.Path + if _, ok := srcInfo.Children.(*sourceinfo.ArrayLiteralSourceInfo); ok { + // Last path element for array source info is the index of the + // first element. So exclude in the comparison, since path could + // indicate a later index, which is present in the array. + srcInfoPath = srcInfo.Path[:len(srcInfo.Path)-1] + } + + if !isPathMatch(path, srcInfoPath) { + return nil, 0 + } + if len(srcInfoPath) >= len(path) { + return node, len(path) + } + + switch children := srcInfo.Children.(type) { + case *sourceinfo.ArrayLiteralSourceInfo: + array, ok := value.(*ast.ArrayLiteralNode) + if !ok { + break // should never happen + } + var i int + match, matchLen := findOptionNode[ast.ValueNode]( + path, + valueRanger(array.Elements), + func(_ ast.ValueNode) *sourceinfo.OptionSourceInfo { + val := &children.Elements[i] + i++ + return val + }, + ) + if match != nil { + return match, matchLen + } + + case *sourceinfo.MessageLiteralSourceInfo: + match, matchLen := findOptionNode[*ast.MessageFieldNode]( + path, + fieldRanger(children.Fields), + func(n *ast.MessageFieldNode) *sourceinfo.OptionSourceInfo { + return children.Fields[n] + }, + ) + if match != nil { + return match, matchLen + } + } + + return node, len(srcInfoPath) +} + +// interpretField interprets the option described by opt, as a field inside the given msg. This +// interprets components of the option name starting at nameIndex. When nameIndex == 0, then +// msg must be an options message. For nameIndex > 0, msg is a nested message inside of the +// options message. The given pathPrefix is the path (sequence of field numbers and indices +// with a FileDescriptorProto as the start) up to but not including the given nameIndex. +// +// Any errors encountered will be handled, so the returned error will only be non-nil if +// the handler returned non-nil. Callers must check that the source info is non-nil before +// using it since it can be nil (in the event of a problem) even if the error is nil. +func (interp *interpreter) interpretField( + targetType descriptorpb.FieldOptions_OptionTargetType, + mc *internal.MessageContext, + msg protoreflect.Message, + opt *descriptorpb.UninterpretedOption, + nameIndex int, + pathPrefix []int32, +) (*sourceinfo.OptionSourceInfo, error) { + var fld protoreflect.FieldDescriptor + nm := opt.GetName()[nameIndex] + node := interp.file.OptionNamePartNode(nm) + if nm.GetIsExtension() { + extName := nm.GetNamePart() + if extName[0] == '.' { + extName = extName[1:] /* skip leading dot */ + } + var err error + fld, err = interp.resolveExtensionType(extName) + if errors.Is(err, protoregistry.NotFound) { + return nil, interp.handleErrorf(interp.nodeInfo(node), + "%vunrecognized extension %s of %s", + mc, extName, msg.Descriptor().FullName()) + } else if err != nil { + return nil, interp.handleErrorWithPos(interp.nodeInfo(node), err) + } + if fld.ContainingMessage().FullName() != msg.Descriptor().FullName() { + return nil, interp.handleErrorf(interp.nodeInfo(node), + "%vextension %s should extend %s but instead extends %s", + mc, extName, msg.Descriptor().FullName(), fld.ContainingMessage().FullName()) + } + } else { + fld = msg.Descriptor().Fields().ByName(protoreflect.Name(nm.GetNamePart())) + if fld == nil { + return nil, interp.handleErrorf(interp.nodeInfo(node), + "%vfield %s of %s does not exist", + mc, nm.GetNamePart(), msg.Descriptor().FullName()) + } + } + pathPrefix = append(pathPrefix, int32(fld.Number())) + + if err := interp.checkFieldUsage(targetType, fld, node); err != nil { + return nil, err + } + + if len(opt.GetName()) > nameIndex+1 { + nextnm := opt.GetName()[nameIndex+1] + nextnode := interp.file.OptionNamePartNode(nextnm) + k := fld.Kind() + if k != protoreflect.MessageKind && k != protoreflect.GroupKind { + return nil, interp.handleErrorf(interp.nodeInfo(nextnode), + "%vcannot set field %s because %s is not a message", + mc, nextnm.GetNamePart(), nm.GetNamePart()) + } + if fld.Cardinality() == protoreflect.Repeated { + return nil, interp.handleErrorf(interp.nodeInfo(nextnode), + "%vcannot set field %s because %s is repeated (must use an aggregate)", + mc, nextnm.GetNamePart(), nm.GetNamePart()) + } + var fdm protoreflect.Message + if msg.Has(fld) { + v := msg.Mutable(fld) + fdm = v.Message() + } else { + if ood := fld.ContainingOneof(); ood != nil { + existingFld := msg.WhichOneof(ood) + if existingFld != nil && existingFld.Number() != fld.Number() { + return nil, interp.handleErrorf(interp.nodeInfo(node), + "%voneof %q already has field %q set", + mc, ood.Name(), fieldName(existingFld)) + } + } + fldVal := msg.NewField(fld) + fdm = fldVal.Message() + msg.Set(fld, fldVal) + } + // recurse to set next part of name + return interp.interpretField(targetType, mc, fdm, opt, nameIndex+1, pathPrefix) + } + + optNode := interp.file.OptionNode(opt) + optValNode := optNode.GetValue() + var srcInfo *sourceinfo.OptionSourceInfo + var err error + if optValNode.Value() == nil { + err = interp.setOptionFieldFromProto(targetType, mc, msg, fld, node, opt, optValNode) + srcInfoVal := newSrcInfo(pathPrefix, nil) + srcInfo = &srcInfoVal + } else { + srcInfo, err = interp.setOptionField(targetType, mc, msg, fld, node, optValNode, false, pathPrefix) + } + if err != nil { + return nil, err + } + + return srcInfo, nil +} + +// setOptionField sets the value for field fld in the given message msg to the value represented +// by AST node val. The given name is the AST node that corresponds to the name of fld. On success, +// it returns additional metadata about the field that was set. +func (interp *interpreter) setOptionField( + targetType descriptorpb.FieldOptions_OptionTargetType, + mc *internal.MessageContext, + msg protoreflect.Message, + fld protoreflect.FieldDescriptor, + name ast.Node, + val ast.ValueNode, + insideMsgLiteral bool, + pathPrefix []int32, +) (*sourceinfo.OptionSourceInfo, error) { + v := val.Value() + if sl, ok := v.([]ast.ValueNode); ok { + // handle slices a little differently than the others + if fld.Cardinality() != protoreflect.Repeated { + return nil, interp.handleErrorf(interp.nodeInfo(val), "%vvalue is an array but field is not repeated", mc) + } + origPath := mc.OptAggPath + defer func() { + mc.OptAggPath = origPath + }() + childVals := make([]sourceinfo.OptionSourceInfo, len(sl)) + var firstIndex int + if fld.IsMap() { + firstIndex = msg.Get(fld).Map().Len() + } else { + firstIndex = msg.Get(fld).List().Len() + } + for index, item := range sl { + mc.OptAggPath = fmt.Sprintf("%s[%d]", origPath, index) + value, srcInfo, err := interp.fieldValue(targetType, mc, msg, fld, item, insideMsgLiteral, append(pathPrefix, int32(firstIndex+index))) + if err != nil || !value.IsValid() { + return nil, err + } + if fld.IsMap() { + mv := msg.Mutable(fld).Map() + setMapEntry(fld, msg, mv, value.Message()) + } else { + lv := msg.Mutable(fld).List() + lv.Append(value) + } + childVals[index] = srcInfo + } + srcInfo := newSrcInfo(append(pathPrefix, int32(firstIndex)), &sourceinfo.ArrayLiteralSourceInfo{Elements: childVals}) + return &srcInfo, nil + } + + if fld.IsMap() { + pathPrefix = append(pathPrefix, int32(msg.Get(fld).Map().Len())) + } else if fld.IsList() { + pathPrefix = append(pathPrefix, int32(msg.Get(fld).List().Len())) + } + + value, srcInfo, err := interp.fieldValue(targetType, mc, msg, fld, val, insideMsgLiteral, pathPrefix) + if err != nil || !value.IsValid() { + return nil, err + } + + if ood := fld.ContainingOneof(); ood != nil { + existingFld := msg.WhichOneof(ood) + if existingFld != nil && existingFld.Number() != fld.Number() { + return nil, interp.handleErrorf(interp.nodeInfo(name), "%voneof %q already has field %q set", mc, ood.Name(), fieldName(existingFld)) + } + } + + switch { + case fld.IsMap(): + mv := msg.Mutable(fld).Map() + setMapEntry(fld, msg, mv, value.Message()) + case fld.IsList(): + lv := msg.Mutable(fld).List() + lv.Append(value) + default: + if msg.Has(fld) { + return nil, interp.handleErrorf(interp.nodeInfo(name), "%vnon-repeated option field %s already set", mc, fieldName(fld)) + } + msg.Set(fld, value) + } + return &srcInfo, nil +} + +// setOptionFieldFromProto sets the value for field fld in the given message msg to the value +// represented by the given uninterpreted option. The given ast.Node, if non-nil, will be used +// to report source positions in error messages. On success, it returns additional metadata +// about the field that was set. +func (interp *interpreter) setOptionFieldFromProto( + targetType descriptorpb.FieldOptions_OptionTargetType, + mc *internal.MessageContext, + msg protoreflect.Message, + fld protoreflect.FieldDescriptor, + name ast.Node, + opt *descriptorpb.UninterpretedOption, + node ast.Node, +) error { + k := fld.Kind() + var value protoreflect.Value + switch k { + case protoreflect.EnumKind: + num, _, err := interp.enumFieldValueFromProto(mc, fld.Enum(), opt, node) + if err != nil { + return interp.handleError(err) + } + value = protoreflect.ValueOfEnum(num) + + case protoreflect.MessageKind, protoreflect.GroupKind: + if opt.AggregateValue == nil { + return interp.handleErrorf(interp.nodeInfo(node), "%vexpecting message, got %s", mc, optionValueKind(opt)) + } + // We must parse the text format from the aggregate value string + var elem protoreflect.Message + switch { + case fld.IsMap(): + elem = dynamicpb.NewMessage(fld.Message()) + case fld.IsList(): + elem = msg.Get(fld).List().NewElement().Message() + default: + elem = msg.NewField(fld).Message() + } + err := prototext.UnmarshalOptions{ + Resolver: &msgLiteralResolver{interp: interp, pkg: fld.ParentFile().Package()}, + AllowPartial: true, + }.Unmarshal([]byte(opt.GetAggregateValue()), elem.Interface()) + if err != nil { + return interp.handleErrorf(interp.nodeInfo(node), "%vfailed to parse message literal %w", mc, err) + } + if err := interp.checkFieldUsagesInMessage(targetType, elem, node); err != nil { + return err + } + value = protoreflect.ValueOfMessage(elem) + + default: + v, err := interp.scalarFieldValueFromProto(mc, descriptorpb.FieldDescriptorProto_Type(k), opt, node) + if err != nil { + return interp.handleError(err) + } + value = protoreflect.ValueOf(v) + } + + if ood := fld.ContainingOneof(); ood != nil { + existingFld := msg.WhichOneof(ood) + if existingFld != nil && existingFld.Number() != fld.Number() { + return interp.handleErrorf(interp.nodeInfo(name), "%voneof %q already has field %q set", mc, ood.Name(), fieldName(existingFld)) + } + } + + switch { + case fld.IsMap(): + mv := msg.Mutable(fld).Map() + setMapEntry(fld, msg, mv, value.Message()) + case fld.IsList(): + msg.Mutable(fld).List().Append(value) + default: + if msg.Has(fld) { + return interp.handleErrorf(interp.nodeInfo(name), "%vnon-repeated option field %s already set", mc, fieldName(fld)) + } + msg.Set(fld, value) + } + return nil +} + +// checkFieldUsagesInMessage verifies that all fields present in the given +// message can be used for the given target type. When an AST is +// present, we validate each field as it is processed. But without +// an AST, we unmarshal a message from an uninterpreted option's +// aggregate value string, and then must make sure that all fields +// set in that message are valid. This reports an error for each +// invalid field it encounters and returns a non-nil error if/when +// the handler returns a non-nil error. +func (interp *interpreter) checkFieldUsagesInMessage( + targetType descriptorpb.FieldOptions_OptionTargetType, + msg protoreflect.Message, + node ast.Node, +) error { + var err error + msg.Range(func(fld protoreflect.FieldDescriptor, val protoreflect.Value) bool { + err = interp.checkFieldUsage(targetType, fld, node) + if err != nil { + return false + } + switch { + case fld.IsList() && fld.Message() != nil: + listVal := val.List() + for i, length := 0, listVal.Len(); i < length; i++ { + err = interp.checkFieldUsagesInMessage(targetType, listVal.Get(i).Message(), node) + if err != nil { + return false + } + } + case fld.IsMap() && fld.MapValue().Message() != nil: + mapVal := val.Map() + mapVal.Range(func(_ protoreflect.MapKey, val protoreflect.Value) bool { + err = interp.checkFieldUsagesInMessage(targetType, val.Message(), node) + return err == nil + }) + case !fld.IsMap() && fld.Message() != nil: + err = interp.checkFieldUsagesInMessage(targetType, val.Message(), node) + } + return err == nil + }) + return err +} + +func (interp *interpreter) enableLenience(enable bool) { + if !interp.lenient { + return // nothing to do + } + if enable { + // reset the flag that tracks if an error has been reported + interp.lenientErrReported = false + } + interp.lenienceEnabled = enable +} + +func setMapEntry( + fld protoreflect.FieldDescriptor, + msg protoreflect.Message, + mapVal protoreflect.Map, + entry protoreflect.Message, +) { + keyFld, valFld := fld.MapKey(), fld.MapValue() + key := entry.Get(keyFld) + val := entry.Get(valFld) + if fld.MapValue().Kind() == protoreflect.MessageKind { + // Replace any nil/invalid values with an empty message + dm, valIsDynamic := val.Interface().(*dynamicpb.Message) + if (valIsDynamic && dm == nil) || !val.Message().IsValid() { + val = protoreflect.ValueOfMessage(dynamicpb.NewMessage(valFld.Message())) + } + _, containerIsDynamic := msg.Interface().(*dynamicpb.Message) + if valIsDynamic && !containerIsDynamic { + // This happens because we create dynamic messages to represent map entries, + // but the container of the map may expect a non-dynamic, generated type. + dest := mapVal.NewValue() + _, destIsDynamic := dest.Message().Interface().(*dynamicpb.Message) + if !destIsDynamic { + // reflection Set methods do not support cases where destination is + // generated but source is dynamic (or vice versa). But proto.Merge + // *DOES* support that, as long as dest and source use the same + // descriptor. + proto.Merge(dest.Message().Interface(), val.Message().Interface()) + val = dest + } + } + } + // TODO: error if key is already present + mapVal.Set(key.MapKey(), val) +} + +type msgLiteralResolver struct { + interp *interpreter + pkg protoreflect.FullName +} + +func (r *msgLiteralResolver) FindMessageByName(message protoreflect.FullName) (protoreflect.MessageType, error) { + if r.interp.resolver == nil { + return nil, protoregistry.NotFound + } + return r.interp.resolver.FindMessageByName(message) +} + +func (r *msgLiteralResolver) FindMessageByURL(url string) (protoreflect.MessageType, error) { + // In a message literal, we don't allow arbitrary URL prefixes + pos := strings.LastIndexByte(url, '/') + var urlPrefix string + if pos > 0 { + urlPrefix = url[:pos] + } + if urlPrefix != "type.googleapis.com" && urlPrefix != "type.googleprod.com" { + return nil, fmt.Errorf("could not resolve type reference %s", url) + } + return r.FindMessageByName(protoreflect.FullName(url[pos+1:])) +} + +func (r *msgLiteralResolver) FindExtensionByName(field protoreflect.FullName) (protoreflect.ExtensionType, error) { + if r.interp.resolver == nil { + return nil, protoregistry.NotFound + } + // In a message literal, extension name may be partially qualified, relative to package. + // So we have to search through package scopes. + pkg := r.pkg + for { + // TODO: This does not *fully* implement the insane logic of protoc with regards + // to resolving relative references. + // https://protobuf.com/docs/language-spec#reference-resolution + name := pkg.Append(protoreflect.Name(field)) + ext, err := r.interp.resolver.FindExtensionByName(name) + if err == nil { + return ext, nil + } + if pkg == "" { + // no more namespaces to check + return nil, err + } + pkg = pkg.Parent() + } +} + +func (r *msgLiteralResolver) FindExtensionByNumber(message protoreflect.FullName, field protoreflect.FieldNumber) (protoreflect.ExtensionType, error) { + if r.interp.resolver == nil { + return nil, protoregistry.NotFound + } + return r.interp.resolver.FindExtensionByNumber(message, field) +} + +func fieldName(fld protoreflect.FieldDescriptor) string { + if fld.IsExtension() { + return fmt.Sprintf("(%s)", fld.FullName()) + } + return string(fld.Name()) +} + +func valueKind(val interface{}) string { + switch val := val.(type) { + case ast.Identifier: + return "identifier" + case bool: + return "bool" + case int64: + if val < 0 { + return "negative integer" + } + return "integer" + case uint64: + return "integer" + case float64: + return "double" + case string, []byte: + return "string" + case []*ast.MessageFieldNode: + return "message" + case []ast.ValueNode: + return "array" + default: + return fmt.Sprintf("%T", val) + } +} + +func optionValueKind(opt *descriptorpb.UninterpretedOption) string { + switch { + case opt.IdentifierValue != nil: + return "identifier" + case opt.PositiveIntValue != nil: + return "integer" + case opt.NegativeIntValue != nil: + return "negative integer" + case opt.DoubleValue != nil: + return "double" + case opt.StringValue != nil: + return "string" + case opt.AggregateValue != nil: + return "message" + default: + // should not be possible + return "<nil>" + } +} + +// fieldValue computes a compile-time value (constant or list or message literal) for the given +// AST node val. The value in val must be assignable to the field fld. +// +// If the returned value is not valid, then an error occurred during processing. +// The returned err may be nil, however, as any errors will already have been +// handled (so the resulting error could be nil if the handler returned nil). +func (interp *interpreter) fieldValue( + targetType descriptorpb.FieldOptions_OptionTargetType, + mc *internal.MessageContext, + msg protoreflect.Message, + fld protoreflect.FieldDescriptor, + val ast.ValueNode, + insideMsgLiteral bool, + pathPrefix []int32, +) (protoreflect.Value, sourceinfo.OptionSourceInfo, error) { + k := fld.Kind() + switch k { + case protoreflect.EnumKind: + num, _, err := interp.enumFieldValue(mc, fld.Enum(), val, insideMsgLiteral) + if err != nil { + return protoreflect.Value{}, sourceinfo.OptionSourceInfo{}, interp.handleError(err) + } + return protoreflect.ValueOfEnum(num), newSrcInfo(pathPrefix, nil), nil + + case protoreflect.MessageKind, protoreflect.GroupKind: + v := val.Value() + if aggs, ok := v.([]*ast.MessageFieldNode); ok { + var childMsg protoreflect.Message + switch { + case fld.IsList(): + // List of messages + val := msg.NewField(fld) + childMsg = val.List().NewElement().Message() + case fld.IsMap(): + // No generated type for map entries, so we use a dynamic type + childMsg = dynamicpb.NewMessage(fld.Message()) + default: + // Normal message field + childMsg = msg.NewField(fld).Message() + } + return interp.messageLiteralValue(targetType, mc, aggs, childMsg, pathPrefix) + } + return protoreflect.Value{}, sourceinfo.OptionSourceInfo{}, + interp.handleErrorf(interp.nodeInfo(val), "%vexpecting message, got %s", mc, valueKind(v)) + + default: + v, err := interp.scalarFieldValue(mc, descriptorpb.FieldDescriptorProto_Type(k), val, insideMsgLiteral) + if err != nil { + return protoreflect.Value{}, sourceinfo.OptionSourceInfo{}, interp.handleError(err) + } + return protoreflect.ValueOf(v), newSrcInfo(pathPrefix, nil), nil + } +} + +// enumFieldValue resolves the given AST node val as an enum value descriptor. If the given +// value is not a valid identifier (or number if allowed), an error is returned instead. +func (interp *interpreter) enumFieldValue( + mc *internal.MessageContext, + ed protoreflect.EnumDescriptor, + val ast.ValueNode, + allowNumber bool, +) (protoreflect.EnumNumber, protoreflect.Name, error) { + v := val.Value() + var num protoreflect.EnumNumber + switch v := v.(type) { + case ast.Identifier: + name := protoreflect.Name(v) + ev := ed.Values().ByName(name) + if ev == nil { + return 0, "", reporter.Errorf(interp.nodeInfo(val), "%venum %s has no value named %s", mc, ed.FullName(), v) + } + return ev.Number(), name, nil + case int64: + if !allowNumber { + return 0, "", reporter.Errorf(interp.nodeInfo(val), "%vexpecting enum name, got %s", mc, valueKind(v)) + } + if v > math.MaxInt32 || v < math.MinInt32 { + return 0, "", reporter.Errorf(interp.nodeInfo(val), "%vvalue %d is out of range for an enum", mc, v) + } + num = protoreflect.EnumNumber(v) + case uint64: + if !allowNumber { + return 0, "", reporter.Errorf(interp.nodeInfo(val), "%vexpecting enum name, got %s", mc, valueKind(v)) + } + if v > math.MaxInt32 { + return 0, "", reporter.Errorf(interp.nodeInfo(val), "%vvalue %d is out of range for an enum", mc, v) + } + num = protoreflect.EnumNumber(v) + default: + return 0, "", reporter.Errorf(interp.nodeInfo(val), "%vexpecting enum, got %s", mc, valueKind(v)) + } + ev := ed.Values().ByNumber(num) + if ev != nil { + return num, ev.Name(), nil + } + if ed.IsClosed() { + return num, "", reporter.Errorf(interp.nodeInfo(val), "%vclosed enum %s has no value with number %d", mc, ed.FullName(), num) + } + // unknown value, but enum is open, so we allow it and return blank name + return num, "", nil +} + +// enumFieldValueFromProto resolves the given uninterpreted option value as an enum value descriptor. +// If the given value is not a valid identifier, an error is returned instead. +func (interp *interpreter) enumFieldValueFromProto( + mc *internal.MessageContext, + ed protoreflect.EnumDescriptor, + opt *descriptorpb.UninterpretedOption, + node ast.Node, +) (protoreflect.EnumNumber, protoreflect.Name, error) { + // We don't have to worry about allowing numbers because numbers are never allowed + // in uninterpreted values; they are only allowed inside aggregate values (i.e. + // message literals). + switch { + case opt.IdentifierValue != nil: + name := protoreflect.Name(opt.GetIdentifierValue()) + ev := ed.Values().ByName(name) + if ev == nil { + return 0, "", reporter.Errorf(interp.nodeInfo(node), "%venum %s has no value named %s", mc, ed.FullName(), name) + } + return ev.Number(), name, nil + default: + return 0, "", reporter.Errorf(interp.nodeInfo(node), "%vexpecting enum, got %s", mc, optionValueKind(opt)) + } +} + +// scalarFieldValue resolves the given AST node val as a value whose type is assignable to a +// field with the given fldType. +func (interp *interpreter) scalarFieldValue( + mc *internal.MessageContext, + fldType descriptorpb.FieldDescriptorProto_Type, + val ast.ValueNode, + insideMsgLiteral bool, +) (interface{}, error) { + v := val.Value() + switch fldType { + case descriptorpb.FieldDescriptorProto_TYPE_BOOL: + if b, ok := v.(bool); ok { + return b, nil + } + if id, ok := v.(ast.Identifier); ok { + if insideMsgLiteral { + // inside a message literal, values use the protobuf text format, + // which is lenient in that it accepts "t" and "f" or "True" and "False" + switch id { + case "t", "true", "True": + return true, nil + case "f", "false", "False": + return false, nil + } + } else { + // options with simple scalar values (no message literal) are stricter + switch id { + case "true": + return true, nil + case "false": + return false, nil + } + } + } + return nil, reporter.Errorf(interp.nodeInfo(val), "%vexpecting bool, got %s", mc, valueKind(v)) + case descriptorpb.FieldDescriptorProto_TYPE_BYTES: + if str, ok := v.(string); ok { + return []byte(str), nil + } + return nil, reporter.Errorf(interp.nodeInfo(val), "%vexpecting bytes, got %s", mc, valueKind(v)) + case descriptorpb.FieldDescriptorProto_TYPE_STRING: + if str, ok := v.(string); ok { + return str, nil + } + return nil, reporter.Errorf(interp.nodeInfo(val), "%vexpecting string, got %s", mc, valueKind(v)) + case descriptorpb.FieldDescriptorProto_TYPE_INT32, descriptorpb.FieldDescriptorProto_TYPE_SINT32, descriptorpb.FieldDescriptorProto_TYPE_SFIXED32: + if i, ok := v.(int64); ok { + if i > math.MaxInt32 || i < math.MinInt32 { + return nil, reporter.Errorf(interp.nodeInfo(val), "%vvalue %d is out of range for int32", mc, i) + } + return int32(i), nil + } + if ui, ok := v.(uint64); ok { + if ui > math.MaxInt32 { + return nil, reporter.Errorf(interp.nodeInfo(val), "%vvalue %d is out of range for int32", mc, ui) + } + return int32(ui), nil + } + return nil, reporter.Errorf(interp.nodeInfo(val), "%vexpecting int32, got %s", mc, valueKind(v)) + case descriptorpb.FieldDescriptorProto_TYPE_UINT32, descriptorpb.FieldDescriptorProto_TYPE_FIXED32: + if i, ok := v.(int64); ok { + if i > math.MaxUint32 || i < 0 { + return nil, reporter.Errorf(interp.nodeInfo(val), "%vvalue %d is out of range for uint32", mc, i) + } + return uint32(i), nil + } + if ui, ok := v.(uint64); ok { + if ui > math.MaxUint32 { + return nil, reporter.Errorf(interp.nodeInfo(val), "%vvalue %d is out of range for uint32", mc, ui) + } + return uint32(ui), nil + } + return nil, reporter.Errorf(interp.nodeInfo(val), "%vexpecting uint32, got %s", mc, valueKind(v)) + case descriptorpb.FieldDescriptorProto_TYPE_INT64, descriptorpb.FieldDescriptorProto_TYPE_SINT64, descriptorpb.FieldDescriptorProto_TYPE_SFIXED64: + if i, ok := v.(int64); ok { + return i, nil + } + if ui, ok := v.(uint64); ok { + if ui > math.MaxInt64 { + return nil, reporter.Errorf(interp.nodeInfo(val), "%vvalue %d is out of range for int64", mc, ui) + } + return int64(ui), nil + } + return nil, reporter.Errorf(interp.nodeInfo(val), "%vexpecting int64, got %s", mc, valueKind(v)) + case descriptorpb.FieldDescriptorProto_TYPE_UINT64, descriptorpb.FieldDescriptorProto_TYPE_FIXED64: + if i, ok := v.(int64); ok { + if i < 0 { + return nil, reporter.Errorf(interp.nodeInfo(val), "%vvalue %d is out of range for uint64", mc, i) + } + return uint64(i), nil + } + if ui, ok := v.(uint64); ok { + return ui, nil + } + return nil, reporter.Errorf(interp.nodeInfo(val), "%vexpecting uint64, got %s", mc, valueKind(v)) + case descriptorpb.FieldDescriptorProto_TYPE_DOUBLE: + if id, ok := v.(ast.Identifier); ok { + switch id { + case "inf": + return math.Inf(1), nil + case "nan": + return math.NaN(), nil + } + } + if d, ok := v.(float64); ok { + return d, nil + } + if i, ok := v.(int64); ok { + return float64(i), nil + } + if u, ok := v.(uint64); ok { + return float64(u), nil + } + return nil, reporter.Errorf(interp.nodeInfo(val), "%vexpecting double, got %s", mc, valueKind(v)) + case descriptorpb.FieldDescriptorProto_TYPE_FLOAT: + if id, ok := v.(ast.Identifier); ok { + switch id { + case "inf": + return float32(math.Inf(1)), nil + case "nan": + return float32(math.NaN()), nil + } + } + if d, ok := v.(float64); ok { + return float32(d), nil + } + if i, ok := v.(int64); ok { + return float32(i), nil + } + if u, ok := v.(uint64); ok { + return float32(u), nil + } + return nil, reporter.Errorf(interp.nodeInfo(val), "%vexpecting float, got %s", mc, valueKind(v)) + default: + return nil, reporter.Errorf(interp.nodeInfo(val), "%vunrecognized field type: %s", mc, fldType) + } +} + +// scalarFieldValue resolves the given uninterpreted option value as a value whose type is +// assignable to a field with the given fldType. +func (interp *interpreter) scalarFieldValueFromProto( + mc *internal.MessageContext, + fldType descriptorpb.FieldDescriptorProto_Type, + opt *descriptorpb.UninterpretedOption, + node ast.Node, +) (interface{}, error) { + switch fldType { + case descriptorpb.FieldDescriptorProto_TYPE_BOOL: + if opt.IdentifierValue != nil { + switch opt.GetIdentifierValue() { + case "true": + return true, nil + case "false": + return false, nil + } + } + return nil, reporter.Errorf(interp.nodeInfo(node), "%vexpecting bool, got %s", mc, optionValueKind(opt)) + case descriptorpb.FieldDescriptorProto_TYPE_BYTES: + if opt.StringValue != nil { + return opt.GetStringValue(), nil + } + return nil, reporter.Errorf(interp.nodeInfo(node), "%vexpecting bytes, got %s", mc, optionValueKind(opt)) + case descriptorpb.FieldDescriptorProto_TYPE_STRING: + if opt.StringValue != nil { + return string(opt.GetStringValue()), nil + } + return nil, reporter.Errorf(interp.nodeInfo(node), "%vexpecting string, got %s", mc, optionValueKind(opt)) + case descriptorpb.FieldDescriptorProto_TYPE_INT32, descriptorpb.FieldDescriptorProto_TYPE_SINT32, descriptorpb.FieldDescriptorProto_TYPE_SFIXED32: + if opt.NegativeIntValue != nil { + i := opt.GetNegativeIntValue() + if i > math.MaxInt32 || i < math.MinInt32 { + return nil, reporter.Errorf(interp.nodeInfo(node), "%vvalue %d is out of range for int32", mc, i) + } + return int32(i), nil + } + if opt.PositiveIntValue != nil { + ui := opt.GetPositiveIntValue() + if ui > math.MaxInt32 { + return nil, reporter.Errorf(interp.nodeInfo(node), "%vvalue %d is out of range for int32", mc, ui) + } + return int32(ui), nil + } + return nil, reporter.Errorf(interp.nodeInfo(node), "%vexpecting int32, got %s", mc, optionValueKind(opt)) + case descriptorpb.FieldDescriptorProto_TYPE_UINT32, descriptorpb.FieldDescriptorProto_TYPE_FIXED32: + if opt.NegativeIntValue != nil { + i := opt.GetNegativeIntValue() + if i > math.MaxUint32 || i < 0 { + return nil, reporter.Errorf(interp.nodeInfo(node), "%vvalue %d is out of range for uint32", mc, i) + } + return uint32(i), nil + } + if opt.PositiveIntValue != nil { + ui := opt.GetPositiveIntValue() + if ui > math.MaxUint32 { + return nil, reporter.Errorf(interp.nodeInfo(node), "%vvalue %d is out of range for uint32", mc, ui) + } + return uint32(ui), nil + } + return nil, reporter.Errorf(interp.nodeInfo(node), "%vexpecting uint32, got %s", mc, optionValueKind(opt)) + case descriptorpb.FieldDescriptorProto_TYPE_INT64, descriptorpb.FieldDescriptorProto_TYPE_SINT64, descriptorpb.FieldDescriptorProto_TYPE_SFIXED64: + if opt.NegativeIntValue != nil { + return opt.GetNegativeIntValue(), nil + } + if opt.PositiveIntValue != nil { + ui := opt.GetPositiveIntValue() + if ui > math.MaxInt64 { + return nil, reporter.Errorf(interp.nodeInfo(node), "%vvalue %d is out of range for int64", mc, ui) + } + return int64(ui), nil + } + return nil, reporter.Errorf(interp.nodeInfo(node), "%vexpecting int64, got %s", mc, optionValueKind(opt)) + case descriptorpb.FieldDescriptorProto_TYPE_UINT64, descriptorpb.FieldDescriptorProto_TYPE_FIXED64: + if opt.NegativeIntValue != nil { + i := opt.GetNegativeIntValue() + if i < 0 { + return nil, reporter.Errorf(interp.nodeInfo(node), "%vvalue %d is out of range for uint64", mc, i) + } + // should not be possible since i should always be negative... + return uint64(i), nil + } + if opt.PositiveIntValue != nil { + return opt.GetPositiveIntValue(), nil + } + return nil, reporter.Errorf(interp.nodeInfo(node), "%vexpecting uint64, got %s", mc, optionValueKind(opt)) + case descriptorpb.FieldDescriptorProto_TYPE_DOUBLE: + if opt.IdentifierValue != nil { + switch opt.GetIdentifierValue() { + case "inf": + return math.Inf(1), nil + case "nan": + return math.NaN(), nil + } + } + if opt.DoubleValue != nil { + return opt.GetDoubleValue(), nil + } + if opt.NegativeIntValue != nil { + return float64(opt.GetNegativeIntValue()), nil + } + if opt.PositiveIntValue != nil { + return float64(opt.GetPositiveIntValue()), nil + } + return nil, reporter.Errorf(interp.nodeInfo(node), "%vexpecting double, got %s", mc, optionValueKind(opt)) + case descriptorpb.FieldDescriptorProto_TYPE_FLOAT: + if opt.IdentifierValue != nil { + switch opt.GetIdentifierValue() { + case "inf": + return float32(math.Inf(1)), nil + case "nan": + return float32(math.NaN()), nil + } + } + if opt.DoubleValue != nil { + return float32(opt.GetDoubleValue()), nil + } + if opt.NegativeIntValue != nil { + return float32(opt.GetNegativeIntValue()), nil + } + if opt.PositiveIntValue != nil { + return float32(opt.GetPositiveIntValue()), nil + } + return nil, reporter.Errorf(interp.nodeInfo(node), "%vexpecting float, got %s", mc, optionValueKind(opt)) + default: + return nil, reporter.Errorf(interp.nodeInfo(node), "%vunrecognized field type: %s", mc, fldType) + } +} + +func descriptorType(m proto.Message) string { + switch m := m.(type) { + case *descriptorpb.DescriptorProto: + return "message" + case *descriptorpb.DescriptorProto_ExtensionRange: + return "extension range" + case *descriptorpb.FieldDescriptorProto: + if m.GetExtendee() == "" { + return "field" + } + return "extension" + case *descriptorpb.EnumDescriptorProto: + return "enum" + case *descriptorpb.EnumValueDescriptorProto: + return "enum value" + case *descriptorpb.ServiceDescriptorProto: + return "service" + case *descriptorpb.MethodDescriptorProto: + return "method" + case *descriptorpb.FileDescriptorProto: + return "file" + default: + // shouldn't be possible + return fmt.Sprintf("%T", m) + } +} + +// messageLiteralValue processes a message literal value. +// +// If the returned value is not valid, then an error occurred during processing. +// The returned err may be nil, however, as any errors will already have been +// handled (so the resulting error could be nil if the handler returned nil). +func (interp *interpreter) messageLiteralValue( + targetType descriptorpb.FieldOptions_OptionTargetType, + mc *internal.MessageContext, + fieldNodes []*ast.MessageFieldNode, + msg protoreflect.Message, + pathPrefix []int32, +) (protoreflect.Value, sourceinfo.OptionSourceInfo, error) { + fmd := msg.Descriptor() + origPath := mc.OptAggPath + defer func() { + mc.OptAggPath = origPath + }() + flds := make(map[*ast.MessageFieldNode]*sourceinfo.OptionSourceInfo, len(fieldNodes)) + var hadError bool + for _, fieldNode := range fieldNodes { + if origPath == "" { + mc.OptAggPath = fieldNode.Name.Value() + } else { + mc.OptAggPath = origPath + "." + fieldNode.Name.Value() + } + if fieldNode.Name.IsAnyTypeReference() { + if len(fieldNodes) > 1 { + err := interp.handleErrorf(interp.nodeInfo(fieldNode.Name.URLPrefix), "%vany type references cannot be repeated or mixed with other fields", mc) + if err != nil { + return protoreflect.Value{}, sourceinfo.OptionSourceInfo{}, err + } + hadError = true + } + + if fmd.FullName() != "google.protobuf.Any" { + err := interp.handleErrorf(interp.nodeInfo(fieldNode.Name.URLPrefix), "%vtype references are only allowed for google.protobuf.Any, but this type is %s", mc, fmd.FullName()) + if err != nil { + return protoreflect.Value{}, sourceinfo.OptionSourceInfo{}, err + } + hadError = true + continue + } + typeURLDescriptor := fmd.Fields().ByNumber(internal.AnyTypeURLTag) + var err error + switch { + case typeURLDescriptor == nil: + err = fmt.Errorf("message schema is missing type_url field (number %d)", internal.AnyTypeURLTag) + case typeURLDescriptor.IsList(): + err = fmt.Errorf("message schema has type_url field (number %d) that is a list but should be singular", internal.AnyTypeURLTag) + case typeURLDescriptor.Kind() != protoreflect.StringKind: + err = fmt.Errorf("message schema has type_url field (number %d) that is %s but should be string", internal.AnyTypeURLTag, typeURLDescriptor.Kind()) + } + if err != nil { + err := interp.handleErrorf(interp.nodeInfo(fieldNode.Name), "%v%w", mc, err) + if err != nil { + return protoreflect.Value{}, sourceinfo.OptionSourceInfo{}, err + } + hadError = true + continue + } + valueDescriptor := fmd.Fields().ByNumber(internal.AnyValueTag) + switch { + case valueDescriptor == nil: + err = fmt.Errorf("message schema is missing value field (number %d)", internal.AnyValueTag) + case valueDescriptor.IsList(): + err = fmt.Errorf("message schema has value field (number %d) that is a list but should be singular", internal.AnyValueTag) + case valueDescriptor.Kind() != protoreflect.BytesKind: + err = fmt.Errorf("message schema has value field (number %d) that is %s but should be bytes", internal.AnyValueTag, valueDescriptor.Kind()) + } + if err != nil { + err := interp.handleErrorf(interp.nodeInfo(fieldNode.Name), "%v%w", mc, err) + if err != nil { + return protoreflect.Value{}, sourceinfo.OptionSourceInfo{}, err + } + hadError = true + continue + } + + urlPrefix := fieldNode.Name.URLPrefix.AsIdentifier() + msgName := fieldNode.Name.Name.AsIdentifier() + fullURL := fmt.Sprintf("%s/%s", urlPrefix, msgName) + // TODO: Support other URLs dynamically -- the caller of protocompile + // should be able to provide a custom resolver that can resolve type + // URLs into message descriptors. The default resolver would be + // implemented as below, only accepting "type.googleapis.com" and + // "type.googleprod.com" as hosts/prefixes and using the compiled + // file's transitive closure to find the named message, since that + // is what protoc does. + if urlPrefix != "type.googleapis.com" && urlPrefix != "type.googleprod.com" { + err := interp.handleErrorf(interp.nodeInfo(fieldNode.Name.URLPrefix), "%vcould not resolve type reference %s", mc, fullURL) + if err != nil { + return protoreflect.Value{}, sourceinfo.OptionSourceInfo{}, err + } + hadError = true + continue + } + anyFields, ok := fieldNode.Val.Value().([]*ast.MessageFieldNode) + if !ok { + err := interp.handleErrorf(interp.nodeInfo(fieldNode.Val), "%vtype references for google.protobuf.Any must have message literal value", mc) + if err != nil { + return protoreflect.Value{}, sourceinfo.OptionSourceInfo{}, err + } + hadError = true + continue + } + anyMd := resolveDescriptor[protoreflect.MessageDescriptor](interp.resolver, string(msgName)) + if anyMd == nil { + err := interp.handleErrorf(interp.nodeInfo(fieldNode.Name.URLPrefix), "%vcould not resolve type reference %s", mc, fullURL) + if err != nil { + return protoreflect.Value{}, sourceinfo.OptionSourceInfo{}, err + } + hadError = true + continue + } + // parse the message value + msgVal, valueSrcInfo, err := interp.messageLiteralValue(targetType, mc, anyFields, dynamicpb.NewMessage(anyMd), append(pathPrefix, internal.AnyValueTag)) + if err != nil { + return protoreflect.Value{}, sourceinfo.OptionSourceInfo{}, err + } else if !msgVal.IsValid() { + hadError = true + continue + } + + b, err := (proto.MarshalOptions{Deterministic: true}).Marshal(msgVal.Message().Interface()) + if err != nil { + err := interp.handleErrorf(interp.nodeInfo(fieldNode.Val), "%vfailed to serialize message value: %w", mc, err) + if err != nil { + return protoreflect.Value{}, sourceinfo.OptionSourceInfo{}, err + } + hadError = true + continue + } + + // Success! + if !hadError { + msg.Set(typeURLDescriptor, protoreflect.ValueOfString(fullURL)) + msg.Set(valueDescriptor, protoreflect.ValueOfBytes(b)) + flds[fieldNode] = &valueSrcInfo + } + continue + } + + // Not expanded Any syntax; handle normal field. + var ffld protoreflect.FieldDescriptor + var err error + if fieldNode.Name.IsExtension() { + n := interp.file.ResolveMessageLiteralExtensionName(fieldNode.Name.Name) + if n == "" { + // this should not be possible! + n = string(fieldNode.Name.Name.AsIdentifier()) + } + ffld, err = interp.resolveExtensionType(n) + if errors.Is(err, protoregistry.NotFound) { + // may need to qualify with package name + // (this should not be necessary!) + pkg := mc.File.FileDescriptorProto().GetPackage() + if pkg != "" { + ffld, err = interp.resolveExtensionType(pkg + "." + n) + } + } + } else { + ffld = fmd.Fields().ByName(protoreflect.Name(fieldNode.Name.Value())) + if ffld == nil { + err = protoregistry.NotFound + // It could be a proto2 group, where the text format refers to the group type + // name, and the field name is the lower-cased form of that. + ffld = fmd.Fields().ByName(protoreflect.Name(strings.ToLower(fieldNode.Name.Value()))) + if ffld != nil { + // In editions, we support using the group type name only for fields that + // "look like" proto2 groups. + if protoreflect.Name(fieldNode.Name.Value()) == ffld.Message().Name() && // text format uses type name + ffld.Message().FullName().Parent() == ffld.FullName().Parent() && // message and field declared in same scope + ffld.Kind() == protoreflect.GroupKind /* uses delimited encoding */ { + // This one looks like a proto2 group, so it's a keeper. + err = nil + } else { + // It doesn't look like a proto2 group, so this is not a match. + ffld = nil + } + } + } + } + if errors.Is(err, protoregistry.NotFound) { + err := interp.handleErrorf(interp.nodeInfo(fieldNode.Name), "%vfield %s not found", mc, string(fieldNode.Name.Name.AsIdentifier())) + if err != nil { + return protoreflect.Value{}, sourceinfo.OptionSourceInfo{}, err + } + hadError = true + continue + } else if err != nil { + err := interp.handleErrorWithPos(interp.nodeInfo(fieldNode.Name), err) + if err != nil { + return protoreflect.Value{}, sourceinfo.OptionSourceInfo{}, err + } + hadError = true + continue + } + if err := interp.checkFieldUsage(targetType, ffld, fieldNode.Name); err != nil { + return protoreflect.Value{}, sourceinfo.OptionSourceInfo{}, err + } + if fieldNode.Sep == nil && ffld.Message() == nil { + // If there is no separator, the field type should be a message. + // Otherwise, it is an error in the text format. + err := interp.handleErrorf(interp.nodeInfo(fieldNode.Val), "syntax error: unexpected value, expecting ':'") + if err != nil { + return protoreflect.Value{}, sourceinfo.OptionSourceInfo{}, err + } + hadError = true + continue + } + srcInfo, err := interp.setOptionField(targetType, mc, msg, ffld, fieldNode.Name, fieldNode.Val, true, append(pathPrefix, int32(ffld.Number()))) + if err != nil { + return protoreflect.Value{}, sourceinfo.OptionSourceInfo{}, err + } + if srcInfo != nil { + flds[fieldNode] = srcInfo + } + } + if hadError { + return protoreflect.Value{}, sourceinfo.OptionSourceInfo{}, nil + } + return protoreflect.ValueOfMessage(msg), + newSrcInfo(pathPrefix, &sourceinfo.MessageLiteralSourceInfo{Fields: flds}), + nil +} + +func newSrcInfo(path []int32, children sourceinfo.OptionChildrenSourceInfo) sourceinfo.OptionSourceInfo { + return sourceinfo.OptionSourceInfo{ + Path: internal.ClonePath(path), + Children: children, + } +} diff --git a/vendor/github.com/bufbuild/protocompile/options/source_retention_options.go b/vendor/github.com/bufbuild/protocompile/options/source_retention_options.go new file mode 100644 index 0000000..05c3e29 --- /dev/null +++ b/vendor/github.com/bufbuild/protocompile/options/source_retention_options.go @@ -0,0 +1,539 @@ +// Copyright 2020-2024 Buf Technologies, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package options + +import ( + "fmt" + + "google.golang.org/protobuf/proto" + "google.golang.org/protobuf/reflect/protoreflect" + "google.golang.org/protobuf/types/descriptorpb" + + "github.com/bufbuild/protocompile/internal" +) + +// StripSourceRetentionOptionsFromFile returns a file descriptor proto that omits any +// options in file that are defined to be retained only in source. If file has no +// such options, then it is returned as is. If it does have such options, a copy is +// made; the given file will not be mutated. +// +// Even when a copy is returned, it is not a deep copy: it may share data with the +// original file. So callers should not mutate the returned file unless mutating the +// input file is also safe. +func StripSourceRetentionOptionsFromFile(file *descriptorpb.FileDescriptorProto) (*descriptorpb.FileDescriptorProto, error) { + var path sourcePath + var removedPaths *sourcePathTrie + if file.SourceCodeInfo != nil && len(file.SourceCodeInfo.Location) > 0 { + path = make(sourcePath, 0, 16) + removedPaths = &sourcePathTrie{} + } + var dirty bool + optionsPath := path.push(internal.FileOptionsTag) + newOpts, err := stripSourceRetentionOptions(file.GetOptions(), optionsPath, removedPaths) + if err != nil { + return nil, err + } + if newOpts != file.GetOptions() { + dirty = true + } + msgsPath := path.push(internal.FileMessagesTag) + newMsgs, changed, err := stripOptionsFromAll(file.GetMessageType(), stripSourceRetentionOptionsFromMessage, msgsPath, removedPaths) + if err != nil { + return nil, err + } + if changed { + dirty = true + } + enumsPath := path.push(internal.FileEnumsTag) + newEnums, changed, err := stripOptionsFromAll(file.GetEnumType(), stripSourceRetentionOptionsFromEnum, enumsPath, removedPaths) + if err != nil { + return nil, err + } + if changed { + dirty = true + } + extsPath := path.push(internal.FileExtensionsTag) + newExts, changed, err := stripOptionsFromAll(file.GetExtension(), stripSourceRetentionOptionsFromField, extsPath, removedPaths) + if err != nil { + return nil, err + } + if changed { + dirty = true + } + svcsPath := path.push(internal.FileServicesTag) + newSvcs, changed, err := stripOptionsFromAll(file.GetService(), stripSourceRetentionOptionsFromService, svcsPath, removedPaths) + if err != nil { + return nil, err + } + if changed { + dirty = true + } + + if !dirty { + return file, nil + } + + newFile, err := shallowCopy(file) + if err != nil { + return nil, err + } + newFile.Options = newOpts + newFile.MessageType = newMsgs + newFile.EnumType = newEnums + newFile.Extension = newExts + newFile.Service = newSvcs + newFile.SourceCodeInfo = stripSourcePathsForSourceRetentionOptions(newFile.SourceCodeInfo, removedPaths) + return newFile, nil +} + +type sourcePath protoreflect.SourcePath + +func (p sourcePath) push(element int32) sourcePath { + if p == nil { + return nil + } + return append(p, element) +} + +type sourcePathTrie struct { + removed bool + children map[int32]*sourcePathTrie +} + +func (t *sourcePathTrie) addPath(p sourcePath) { + if t == nil { + return + } + if len(p) == 0 { + t.removed = true + return + } + child := t.children[p[0]] + if child == nil { + if t.children == nil { + t.children = map[int32]*sourcePathTrie{} + } + child = &sourcePathTrie{} + t.children[p[0]] = child + } + child.addPath(p[1:]) +} + +func (t *sourcePathTrie) isRemoved(p []int32) bool { + if t == nil { + return false + } + if t.removed { + return true + } + if len(p) == 0 { + return false + } + child := t.children[p[0]] + if child == nil { + return false + } + return child.isRemoved(p[1:]) +} + +func stripSourceRetentionOptions[M proto.Message]( + options M, + path sourcePath, + removedPaths *sourcePathTrie, +) (M, error) { + optionsRef := options.ProtoReflect() + // See if there are any options to strip. + var hasFieldToStrip bool + var numFieldsToKeep int + var err error + optionsRef.Range(func(field protoreflect.FieldDescriptor, _ protoreflect.Value) bool { + fieldOpts, ok := field.Options().(*descriptorpb.FieldOptions) + if !ok { + err = fmt.Errorf("field options is unexpected type: got %T, want %T", field.Options(), fieldOpts) + return false + } + if fieldOpts.GetRetention() == descriptorpb.FieldOptions_RETENTION_SOURCE { + hasFieldToStrip = true + } else { + numFieldsToKeep++ + } + return true + }) + var zero M + if err != nil { + return zero, err + } + if !hasFieldToStrip { + return options, nil + } + + if numFieldsToKeep == 0 { + // Stripping the message would remove *all* options. In that case, + // we'll clear out the options by returning the zero value (i.e. nil). + removedPaths.addPath(path) // clear out all source locations, too + return zero, nil + } + + // There is at least one option to remove. So we need to make a copy that does not have those options. + newOptions := optionsRef.New() + ret, ok := newOptions.Interface().(M) + if !ok { + return zero, fmt.Errorf("creating new message of same type resulted in unexpected type; got %T, want %T", newOptions.Interface(), zero) + } + optionsRef.Range(func(field protoreflect.FieldDescriptor, val protoreflect.Value) bool { + fieldOpts, ok := field.Options().(*descriptorpb.FieldOptions) + if !ok { + err = fmt.Errorf("field options is unexpected type: got %T, want %T", field.Options(), fieldOpts) + return false + } + if fieldOpts.GetRetention() != descriptorpb.FieldOptions_RETENTION_SOURCE { + newOptions.Set(field, val) + } else { + removedPaths.addPath(path.push(int32(field.Number()))) + } + return true + }) + if err != nil { + return zero, err + } + return ret, nil +} + +func stripSourceRetentionOptionsFromMessage( + msg *descriptorpb.DescriptorProto, + path sourcePath, + removedPaths *sourcePathTrie, +) (*descriptorpb.DescriptorProto, error) { + var dirty bool + optionsPath := path.push(internal.MessageOptionsTag) + newOpts, err := stripSourceRetentionOptions(msg.Options, optionsPath, removedPaths) + if err != nil { + return nil, err + } + if newOpts != msg.Options { + dirty = true + } + fieldsPath := path.push(internal.MessageFieldsTag) + newFields, changed, err := stripOptionsFromAll(msg.Field, stripSourceRetentionOptionsFromField, fieldsPath, removedPaths) + if err != nil { + return nil, err + } + if changed { + dirty = true + } + oneofsPath := path.push(internal.MessageOneofsTag) + newOneofs, changed, err := stripOptionsFromAll(msg.OneofDecl, stripSourceRetentionOptionsFromOneof, oneofsPath, removedPaths) + if err != nil { + return nil, err + } + if changed { + dirty = true + } + extRangesPath := path.push(internal.MessageExtensionRangesTag) + newExtRanges, changed, err := stripOptionsFromAll(msg.ExtensionRange, stripSourceRetentionOptionsFromExtensionRange, extRangesPath, removedPaths) + if err != nil { + return nil, err + } + if changed { + dirty = true + } + msgsPath := path.push(internal.MessageNestedMessagesTag) + newMsgs, changed, err := stripOptionsFromAll(msg.NestedType, stripSourceRetentionOptionsFromMessage, msgsPath, removedPaths) + if err != nil { + return nil, err + } + if changed { + dirty = true + } + enumsPath := path.push(internal.MessageEnumsTag) + newEnums, changed, err := stripOptionsFromAll(msg.EnumType, stripSourceRetentionOptionsFromEnum, enumsPath, removedPaths) + if err != nil { + return nil, err + } + if changed { + dirty = true + } + extsPath := path.push(internal.MessageExtensionsTag) + newExts, changed, err := stripOptionsFromAll(msg.Extension, stripSourceRetentionOptionsFromField, extsPath, removedPaths) + if err != nil { + return nil, err + } + if changed { + dirty = true + } + + if !dirty { + return msg, nil + } + + newMsg, err := shallowCopy(msg) + if err != nil { + return nil, err + } + newMsg.Options = newOpts + newMsg.Field = newFields + newMsg.OneofDecl = newOneofs + newMsg.ExtensionRange = newExtRanges + newMsg.NestedType = newMsgs + newMsg.EnumType = newEnums + newMsg.Extension = newExts + return newMsg, nil +} + +func stripSourceRetentionOptionsFromField( + field *descriptorpb.FieldDescriptorProto, + path sourcePath, + removedPaths *sourcePathTrie, +) (*descriptorpb.FieldDescriptorProto, error) { + optionsPath := path.push(internal.FieldOptionsTag) + newOpts, err := stripSourceRetentionOptions(field.Options, optionsPath, removedPaths) + if err != nil { + return nil, err + } + if newOpts == field.Options { + return field, nil + } + newField, err := shallowCopy(field) + if err != nil { + return nil, err + } + newField.Options = newOpts + return newField, nil +} + +func stripSourceRetentionOptionsFromOneof( + oneof *descriptorpb.OneofDescriptorProto, + path sourcePath, + removedPaths *sourcePathTrie, +) (*descriptorpb.OneofDescriptorProto, error) { + optionsPath := path.push(internal.OneofOptionsTag) + newOpts, err := stripSourceRetentionOptions(oneof.Options, optionsPath, removedPaths) + if err != nil { + return nil, err + } + if newOpts == oneof.Options { + return oneof, nil + } + newOneof, err := shallowCopy(oneof) + if err != nil { + return nil, err + } + newOneof.Options = newOpts + return newOneof, nil +} + +func stripSourceRetentionOptionsFromExtensionRange( + extRange *descriptorpb.DescriptorProto_ExtensionRange, + path sourcePath, + removedPaths *sourcePathTrie, +) (*descriptorpb.DescriptorProto_ExtensionRange, error) { + optionsPath := path.push(internal.ExtensionRangeOptionsTag) + newOpts, err := stripSourceRetentionOptions(extRange.Options, optionsPath, removedPaths) + if err != nil { + return nil, err + } + if newOpts == extRange.Options { + return extRange, nil + } + newExtRange, err := shallowCopy(extRange) + if err != nil { + return nil, err + } + newExtRange.Options = newOpts + return newExtRange, nil +} + +func stripSourceRetentionOptionsFromEnum( + enum *descriptorpb.EnumDescriptorProto, + path sourcePath, + removedPaths *sourcePathTrie, +) (*descriptorpb.EnumDescriptorProto, error) { + var dirty bool + optionsPath := path.push(internal.EnumOptionsTag) + newOpts, err := stripSourceRetentionOptions(enum.Options, optionsPath, removedPaths) + if err != nil { + return nil, err + } + if newOpts != enum.Options { + dirty = true + } + valsPath := path.push(internal.EnumValuesTag) + newVals, changed, err := stripOptionsFromAll(enum.Value, stripSourceRetentionOptionsFromEnumValue, valsPath, removedPaths) + if err != nil { + return nil, err + } + if changed { + dirty = true + } + + if !dirty { + return enum, nil + } + + newEnum, err := shallowCopy(enum) + if err != nil { + return nil, err + } + newEnum.Options = newOpts + newEnum.Value = newVals + return newEnum, nil +} + +func stripSourceRetentionOptionsFromEnumValue( + enumVal *descriptorpb.EnumValueDescriptorProto, + path sourcePath, + removedPaths *sourcePathTrie, +) (*descriptorpb.EnumValueDescriptorProto, error) { + optionsPath := path.push(internal.EnumValOptionsTag) + newOpts, err := stripSourceRetentionOptions(enumVal.Options, optionsPath, removedPaths) + if err != nil { + return nil, err + } + if newOpts == enumVal.Options { + return enumVal, nil + } + newEnumVal, err := shallowCopy(enumVal) + if err != nil { + return nil, err + } + newEnumVal.Options = newOpts + return newEnumVal, nil +} + +func stripSourceRetentionOptionsFromService( + svc *descriptorpb.ServiceDescriptorProto, + path sourcePath, + removedPaths *sourcePathTrie, +) (*descriptorpb.ServiceDescriptorProto, error) { + var dirty bool + optionsPath := path.push(internal.ServiceOptionsTag) + newOpts, err := stripSourceRetentionOptions(svc.Options, optionsPath, removedPaths) + if err != nil { + return nil, err + } + if newOpts != svc.Options { + dirty = true + } + methodsPath := path.push(internal.ServiceMethodsTag) + newMethods, changed, err := stripOptionsFromAll(svc.Method, stripSourceRetentionOptionsFromMethod, methodsPath, removedPaths) + if err != nil { + return nil, err + } + if changed { + dirty = true + } + + if !dirty { + return svc, nil + } + + newSvc, err := shallowCopy(svc) + if err != nil { + return nil, err + } + newSvc.Options = newOpts + newSvc.Method = newMethods + return newSvc, nil +} + +func stripSourceRetentionOptionsFromMethod( + method *descriptorpb.MethodDescriptorProto, + path sourcePath, + removedPaths *sourcePathTrie, +) (*descriptorpb.MethodDescriptorProto, error) { + optionsPath := path.push(internal.MethodOptionsTag) + newOpts, err := stripSourceRetentionOptions(method.Options, optionsPath, removedPaths) + if err != nil { + return nil, err + } + if newOpts == method.Options { + return method, nil + } + newMethod, err := shallowCopy(method) + if err != nil { + return nil, err + } + newMethod.Options = newOpts + return newMethod, nil +} + +func stripSourcePathsForSourceRetentionOptions( + sourceInfo *descriptorpb.SourceCodeInfo, + removedPaths *sourcePathTrie, +) *descriptorpb.SourceCodeInfo { + if sourceInfo == nil || len(sourceInfo.Location) == 0 || removedPaths == nil { + // nothing to do + return sourceInfo + } + newLocations := make([]*descriptorpb.SourceCodeInfo_Location, len(sourceInfo.Location)) + var i int + for _, loc := range sourceInfo.Location { + if removedPaths.isRemoved(loc.Path) { + continue + } + newLocations[i] = loc + i++ + } + newLocations = newLocations[:i] + return &descriptorpb.SourceCodeInfo{Location: newLocations} +} + +func shallowCopy[M proto.Message](msg M) (M, error) { + msgRef := msg.ProtoReflect() + other := msgRef.New() + ret, ok := other.Interface().(M) + if !ok { + return ret, fmt.Errorf("creating new message of same type resulted in unexpected type; got %T, want %T", other.Interface(), ret) + } + msgRef.Range(func(field protoreflect.FieldDescriptor, val protoreflect.Value) bool { + other.Set(field, val) + return true + }) + return ret, nil +} + +// stripOptionsFromAll applies the given function to each element in the given +// slice in order to remove source-retention options from it. It returns the new +// slice and a bool indicating whether anything was actually changed. If the +// second value is false, then the returned slice is the same slice as the input +// slice. Usually, T is a pointer type, in which case the given updateFunc should +// NOT mutate the input value. Instead, it should return the input value if only +// if there is no update needed. If a mutation is needed, it should return a new +// value. +func stripOptionsFromAll[T comparable]( + slice []T, + updateFunc func(T, sourcePath, *sourcePathTrie) (T, error), + path sourcePath, + removedPaths *sourcePathTrie, +) ([]T, bool, error) { + var updated []T // initialized lazily, only when/if a copy is needed + for i, item := range slice { + newItem, err := updateFunc(item, path.push(int32(i)), removedPaths) + if err != nil { + return nil, false, err + } + if updated != nil { + updated[i] = newItem + } else if newItem != item { + updated = make([]T, len(slice)) + copy(updated[:i], slice) + updated[i] = newItem + } + } + if updated != nil { + return updated, true, nil + } + return slice, false, nil +} diff --git a/vendor/github.com/bufbuild/protocompile/options/target_types.go b/vendor/github.com/bufbuild/protocompile/options/target_types.go new file mode 100644 index 0000000..0d78075 --- /dev/null +++ b/vendor/github.com/bufbuild/protocompile/options/target_types.go @@ -0,0 +1,152 @@ +// Copyright 2020-2024 Buf Technologies, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package options + +import ( + "google.golang.org/protobuf/proto" + "google.golang.org/protobuf/types/descriptorpb" +) + +type optionsType[T any] interface { + *T + proto.Message + GetFeatures() *descriptorpb.FeatureSet + GetUninterpretedOption() []*descriptorpb.UninterpretedOption +} + +type elementType[OptsStruct any, Opts optionsType[OptsStruct]] interface { + proto.Message + GetOptions() Opts +} + +type targetType[Elem elementType[OptsStruct, Opts], OptsStruct any, Opts optionsType[OptsStruct]] struct { + t descriptorpb.FieldOptions_OptionTargetType + setUninterpretedOptions func(opts Opts, uninterpreted []*descriptorpb.UninterpretedOption) + setOptions func(elem Elem, opts Opts) +} + +var ( + targetTypeFile = newTargetType[*descriptorpb.FileDescriptorProto]( + descriptorpb.FieldOptions_TARGET_TYPE_FILE, setUninterpretedFileOptions, setFileOptions, + ) + targetTypeMessage = newTargetType[*descriptorpb.DescriptorProto]( + descriptorpb.FieldOptions_TARGET_TYPE_MESSAGE, setUninterpretedMessageOptions, setMessageOptions, + ) + targetTypeField = newTargetType[*descriptorpb.FieldDescriptorProto]( + descriptorpb.FieldOptions_TARGET_TYPE_FIELD, setUninterpretedFieldOptions, setFieldOptions, + ) + targetTypeOneof = newTargetType[*descriptorpb.OneofDescriptorProto]( + descriptorpb.FieldOptions_TARGET_TYPE_ONEOF, setUninterpretedOneofOptions, setOneofOptions, + ) + targetTypeExtensionRange = newTargetType[*descriptorpb.DescriptorProto_ExtensionRange]( + descriptorpb.FieldOptions_TARGET_TYPE_EXTENSION_RANGE, setUninterpretedExtensionRangeOptions, setExtensionRangeOptions, + ) + targetTypeEnum = newTargetType[*descriptorpb.EnumDescriptorProto]( + descriptorpb.FieldOptions_TARGET_TYPE_ENUM, setUninterpretedEnumOptions, setEnumOptions, + ) + targetTypeEnumValue = newTargetType[*descriptorpb.EnumValueDescriptorProto]( + descriptorpb.FieldOptions_TARGET_TYPE_ENUM_ENTRY, setUninterpretedEnumValueOptions, setEnumValueOptions, + ) + targetTypeService = newTargetType[*descriptorpb.ServiceDescriptorProto]( + descriptorpb.FieldOptions_TARGET_TYPE_SERVICE, setUninterpretedServiceOptions, setServiceOptions, + ) + targetTypeMethod = newTargetType[*descriptorpb.MethodDescriptorProto]( + descriptorpb.FieldOptions_TARGET_TYPE_METHOD, setUninterpretedMethodOptions, setMethodOptions, + ) +) + +func newTargetType[Elem elementType[OptsStruct, Opts], OptsStruct any, Opts optionsType[OptsStruct]]( + t descriptorpb.FieldOptions_OptionTargetType, + setUninterpretedOptions func(opts Opts, uninterpreted []*descriptorpb.UninterpretedOption), + setOptions func(elem Elem, opts Opts), +) *targetType[Elem, OptsStruct, Opts] { + return &targetType[Elem, OptsStruct, Opts]{ + t: t, + setUninterpretedOptions: setUninterpretedOptions, + setOptions: setOptions, + } +} + +func setUninterpretedFileOptions(opts *descriptorpb.FileOptions, uninterpreted []*descriptorpb.UninterpretedOption) { + opts.UninterpretedOption = uninterpreted +} + +func setUninterpretedMessageOptions(opts *descriptorpb.MessageOptions, uninterpreted []*descriptorpb.UninterpretedOption) { + opts.UninterpretedOption = uninterpreted +} + +func setUninterpretedFieldOptions(opts *descriptorpb.FieldOptions, uninterpreted []*descriptorpb.UninterpretedOption) { + opts.UninterpretedOption = uninterpreted +} + +func setUninterpretedOneofOptions(opts *descriptorpb.OneofOptions, uninterpreted []*descriptorpb.UninterpretedOption) { + opts.UninterpretedOption = uninterpreted +} + +func setUninterpretedExtensionRangeOptions(opts *descriptorpb.ExtensionRangeOptions, uninterpreted []*descriptorpb.UninterpretedOption) { + opts.UninterpretedOption = uninterpreted +} + +func setUninterpretedEnumOptions(opts *descriptorpb.EnumOptions, uninterpreted []*descriptorpb.UninterpretedOption) { + opts.UninterpretedOption = uninterpreted +} + +func setUninterpretedEnumValueOptions(opts *descriptorpb.EnumValueOptions, uninterpreted []*descriptorpb.UninterpretedOption) { + opts.UninterpretedOption = uninterpreted +} + +func setUninterpretedServiceOptions(opts *descriptorpb.ServiceOptions, uninterpreted []*descriptorpb.UninterpretedOption) { + opts.UninterpretedOption = uninterpreted +} + +func setUninterpretedMethodOptions(opts *descriptorpb.MethodOptions, uninterpreted []*descriptorpb.UninterpretedOption) { + opts.UninterpretedOption = uninterpreted +} + +func setFileOptions(desc *descriptorpb.FileDescriptorProto, opts *descriptorpb.FileOptions) { + desc.Options = opts +} + +func setMessageOptions(desc *descriptorpb.DescriptorProto, opts *descriptorpb.MessageOptions) { + desc.Options = opts +} + +func setFieldOptions(desc *descriptorpb.FieldDescriptorProto, opts *descriptorpb.FieldOptions) { + desc.Options = opts +} + +func setOneofOptions(desc *descriptorpb.OneofDescriptorProto, opts *descriptorpb.OneofOptions) { + desc.Options = opts +} + +func setExtensionRangeOptions(desc *descriptorpb.DescriptorProto_ExtensionRange, opts *descriptorpb.ExtensionRangeOptions) { + desc.Options = opts +} + +func setEnumOptions(desc *descriptorpb.EnumDescriptorProto, opts *descriptorpb.EnumOptions) { + desc.Options = opts +} + +func setEnumValueOptions(desc *descriptorpb.EnumValueDescriptorProto, opts *descriptorpb.EnumValueOptions) { + desc.Options = opts +} + +func setServiceOptions(desc *descriptorpb.ServiceDescriptorProto, opts *descriptorpb.ServiceOptions) { + desc.Options = opts +} + +func setMethodOptions(desc *descriptorpb.MethodDescriptorProto, opts *descriptorpb.MethodOptions) { + desc.Options = opts +} diff --git a/vendor/github.com/bufbuild/protocompile/parser/.gitignore b/vendor/github.com/bufbuild/protocompile/parser/.gitignore new file mode 100644 index 0000000..2652053 --- /dev/null +++ b/vendor/github.com/bufbuild/protocompile/parser/.gitignore @@ -0,0 +1 @@ +y.output diff --git a/vendor/github.com/bufbuild/protocompile/parser/ast.go b/vendor/github.com/bufbuild/protocompile/parser/ast.go new file mode 100644 index 0000000..f58f7ae --- /dev/null +++ b/vendor/github.com/bufbuild/protocompile/parser/ast.go @@ -0,0 +1,144 @@ +// Copyright 2020-2024 Buf Technologies, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package parser + +import ( + "github.com/bufbuild/protocompile/ast" +) + +// the types below are accumulator types, just used in intermediate productions +// to accumulate slices that will get stored in AST nodes + +type compactOptionSlices struct { + options []*ast.OptionNode + commas []*ast.RuneNode +} + +func toStringValueNode(strs []*ast.StringLiteralNode) ast.StringValueNode { + if len(strs) == 1 { + return strs[0] + } + return ast.NewCompoundLiteralStringNode(strs...) +} + +type nameSlices struct { + // only names or idents will be set, never both + names []ast.StringValueNode + idents []*ast.IdentNode + commas []*ast.RuneNode +} + +type rangeSlices struct { + ranges []*ast.RangeNode + commas []*ast.RuneNode +} + +type valueSlices struct { + vals []ast.ValueNode + commas []*ast.RuneNode +} + +type fieldRefSlices struct { + refs []*ast.FieldReferenceNode + dots []*ast.RuneNode +} + +type identSlices struct { + idents []*ast.IdentNode + dots []*ast.RuneNode +} + +func (s *identSlices) toIdentValueNode(leadingDot *ast.RuneNode) ast.IdentValueNode { + if len(s.idents) == 1 && leadingDot == nil { + // single simple name + return s.idents[0] + } + return ast.NewCompoundIdentNode(leadingDot, s.idents, s.dots) +} + +type messageFieldList struct { + field *ast.MessageFieldNode + delimiter *ast.RuneNode + next *messageFieldList +} + +func (list *messageFieldList) toNodes() ([]*ast.MessageFieldNode, []*ast.RuneNode) { + if list == nil { + return nil, nil + } + l := 0 + for cur := list; cur != nil; cur = cur.next { + l++ + } + fields := make([]*ast.MessageFieldNode, l) + delimiters := make([]*ast.RuneNode, l) + for cur, i := list, 0; cur != nil; cur, i = cur.next, i+1 { + fields[i] = cur.field + if cur.delimiter != nil { + delimiters[i] = cur.delimiter + } + } + return fields, delimiters +} + +func prependRunes[T ast.Node](convert func(*ast.RuneNode) T, runes []*ast.RuneNode, elements []T) []T { + elems := make([]T, 0, len(runes)+len(elements)) + for _, rune := range runes { + elems = append(elems, convert(rune)) + } + elems = append(elems, elements...) + return elems +} + +func toServiceElement(semi *ast.RuneNode) ast.ServiceElement { + return ast.NewEmptyDeclNode(semi) +} + +func toMethodElement(semi *ast.RuneNode) ast.RPCElement { + return ast.NewEmptyDeclNode(semi) +} + +func toFileElement(semi *ast.RuneNode) ast.FileElement { + return ast.NewEmptyDeclNode(semi) +} + +func toEnumElement(semi *ast.RuneNode) ast.EnumElement { + return ast.NewEmptyDeclNode(semi) +} + +func toMessageElement(semi *ast.RuneNode) ast.MessageElement { + return ast.NewEmptyDeclNode(semi) +} + +type nodeWithRunes[T ast.Node] struct { + Node T + Runes []*ast.RuneNode +} + +func newNodeWithRunes[T ast.Node](node T, trailingRunes ...*ast.RuneNode) nodeWithRunes[T] { + return nodeWithRunes[T]{ + Node: node, + Runes: trailingRunes, + } +} + +func toElements[T ast.Node](convert func(*ast.RuneNode) T, node T, runes []*ast.RuneNode) []T { + elements := make([]T, 1+len(runes)) + elements[0] = node + for i, rune := range runes { + elements[i+1] = convert(rune) + } + return elements +} diff --git a/vendor/github.com/bufbuild/protocompile/parser/clone.go b/vendor/github.com/bufbuild/protocompile/parser/clone.go new file mode 100644 index 0000000..0432248 --- /dev/null +++ b/vendor/github.com/bufbuild/protocompile/parser/clone.go @@ -0,0 +1,183 @@ +// Copyright 2020-2024 Buf Technologies, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package parser + +import ( + "google.golang.org/protobuf/proto" + "google.golang.org/protobuf/types/descriptorpb" + + "github.com/bufbuild/protocompile/ast" + "github.com/bufbuild/protocompile/reporter" +) + +// Clone returns a copy of the given result. Since descriptor protos may be +// mutated during linking, this can return a defensive copy so that mutations +// don't impact concurrent operations in an unsafe way. This is called if the +// parse result could be re-used across concurrent operations and has unresolved +// references and options which will require mutation by the linker. +// +// If the given value has a method with the following signature, it will be +// called to perform the operation: +// +// Clone() Result +// +// If the given value does not provide a Clone method and is not the implementation +// provided by this package, it is possible for an error to occur in creating the +// copy, which may result in a panic. This can happen if the AST of the given result +// is not actually valid and a file descriptor proto cannot be successfully derived +// from it. +func Clone(r Result) Result { + if cl, ok := r.(interface{ Clone() Result }); ok { + return cl.Clone() + } + if res, ok := r.(*result); ok { + newProto := proto.Clone(res.proto).(*descriptorpb.FileDescriptorProto) //nolint:errcheck + newNodes := make(map[proto.Message]ast.Node, len(res.nodes)) + newResult := &result{ + file: res.file, + proto: newProto, + nodes: newNodes, + } + recreateNodeIndexForFile(res, newResult, res.proto, newProto) + return newResult + } + + // Can't do the deep-copy we know how to do. So we have to take a + // different tactic. + if r.AST() == nil { + // no AST? all we have to do is copy the proto + fileProto := proto.Clone(r.FileDescriptorProto()).(*descriptorpb.FileDescriptorProto) //nolint:errcheck + return ResultWithoutAST(fileProto) + } + // Otherwise, we have an AST, but no way to clone the result's + // internals. So just re-create them from scratch. + res, err := ResultFromAST(r.AST(), false, reporter.NewHandler(nil)) + if err != nil { + panic(err) + } + return res +} + +func recreateNodeIndexForFile(orig, clone *result, origProto, cloneProto *descriptorpb.FileDescriptorProto) { + updateNodeIndexWithOptions[*descriptorpb.FileOptions](orig, clone, origProto, cloneProto) + for i, origMd := range origProto.MessageType { + cloneMd := cloneProto.MessageType[i] + recreateNodeIndexForMessage(orig, clone, origMd, cloneMd) + } + for i, origEd := range origProto.EnumType { + cloneEd := cloneProto.EnumType[i] + recreateNodeIndexForEnum(orig, clone, origEd, cloneEd) + } + for i, origExtd := range origProto.Extension { + cloneExtd := cloneProto.Extension[i] + updateNodeIndexWithOptions[*descriptorpb.FieldOptions](orig, clone, origExtd, cloneExtd) + } + for i, origSd := range origProto.Service { + cloneSd := cloneProto.Service[i] + updateNodeIndexWithOptions[*descriptorpb.ServiceOptions](orig, clone, origSd, cloneSd) + for j, origMtd := range origSd.Method { + cloneMtd := cloneSd.Method[j] + updateNodeIndexWithOptions[*descriptorpb.MethodOptions](orig, clone, origMtd, cloneMtd) + } + } +} + +func recreateNodeIndexForMessage(orig, clone *result, origProto, cloneProto *descriptorpb.DescriptorProto) { + updateNodeIndexWithOptions[*descriptorpb.MessageOptions](orig, clone, origProto, cloneProto) + for i, origFld := range origProto.Field { + cloneFld := cloneProto.Field[i] + updateNodeIndexWithOptions[*descriptorpb.FieldOptions](orig, clone, origFld, cloneFld) + } + for i, origOod := range origProto.OneofDecl { + cloneOod := cloneProto.OneofDecl[i] + updateNodeIndexWithOptions[*descriptorpb.OneofOptions](orig, clone, origOod, cloneOod) + } + for i, origExtr := range origProto.ExtensionRange { + cloneExtr := cloneProto.ExtensionRange[i] + updateNodeIndex(orig, clone, asExtsNode(origExtr), asExtsNode(cloneExtr)) + updateNodeIndexWithOptions[*descriptorpb.ExtensionRangeOptions](orig, clone, origExtr, cloneExtr) + } + for i, origRr := range origProto.ReservedRange { + cloneRr := cloneProto.ReservedRange[i] + updateNodeIndex(orig, clone, origRr, cloneRr) + } + for i, origNmd := range origProto.NestedType { + cloneNmd := cloneProto.NestedType[i] + recreateNodeIndexForMessage(orig, clone, origNmd, cloneNmd) + } + for i, origEd := range origProto.EnumType { + cloneEd := cloneProto.EnumType[i] + recreateNodeIndexForEnum(orig, clone, origEd, cloneEd) + } + for i, origExtd := range origProto.Extension { + cloneExtd := cloneProto.Extension[i] + updateNodeIndexWithOptions[*descriptorpb.FieldOptions](orig, clone, origExtd, cloneExtd) + } +} + +func recreateNodeIndexForEnum(orig, clone *result, origProto, cloneProto *descriptorpb.EnumDescriptorProto) { + updateNodeIndexWithOptions[*descriptorpb.EnumOptions](orig, clone, origProto, cloneProto) + for i, origEvd := range origProto.Value { + cloneEvd := cloneProto.Value[i] + updateNodeIndexWithOptions[*descriptorpb.EnumValueOptions](orig, clone, origEvd, cloneEvd) + } + for i, origRr := range origProto.ReservedRange { + cloneRr := cloneProto.ReservedRange[i] + updateNodeIndex(orig, clone, origRr, cloneRr) + } +} + +func recreateNodeIndexForOptions(orig, clone *result, origProtos, cloneProtos []*descriptorpb.UninterpretedOption) { + for i, origOpt := range origProtos { + cloneOpt := cloneProtos[i] + updateNodeIndex(orig, clone, origOpt, cloneOpt) + for j, origName := range origOpt.Name { + cloneName := cloneOpt.Name[j] + updateNodeIndex(orig, clone, origName, cloneName) + } + } +} + +func updateNodeIndex[M proto.Message](orig, clone *result, origProto, cloneProto M) { + node := orig.nodes[origProto] + if node != nil { + clone.nodes[cloneProto] = node + } +} + +type pointerMessage[T any] interface { + *T + proto.Message +} + +type options[T any] interface { + // need this type instead of just proto.Message so we can check for nil pointer + pointerMessage[T] + GetUninterpretedOption() []*descriptorpb.UninterpretedOption +} + +type withOptions[O options[T], T any] interface { + proto.Message + GetOptions() O +} + +func updateNodeIndexWithOptions[O options[T], M withOptions[O, T], T any](orig, clone *result, origProto, cloneProto M) { + updateNodeIndex(orig, clone, origProto, cloneProto) + origOpts := origProto.GetOptions() + cloneOpts := cloneProto.GetOptions() + if origOpts != nil { + recreateNodeIndexForOptions(orig, clone, origOpts.GetUninterpretedOption(), cloneOpts.GetUninterpretedOption()) + } +} diff --git a/vendor/github.com/bufbuild/protocompile/parser/doc.go b/vendor/github.com/bufbuild/protocompile/parser/doc.go new file mode 100644 index 0000000..4055554 --- /dev/null +++ b/vendor/github.com/bufbuild/protocompile/parser/doc.go @@ -0,0 +1,25 @@ +// Copyright 2020-2024 Buf Technologies, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Package parser contains the logic for parsing protobuf source code into an +// AST (abstract syntax tree) and also for converting an AST into a descriptor +// proto. +// +// A FileDescriptorProto is very similar to an AST, but the AST this package +// uses is more useful because it contains more information about the source +// code, including details about whitespace and comments, that cannot be +// represented by a descriptor proto. This makes it ideal for things like +// code formatters, which may want to preserve things like whitespace and +// comment format. +package parser diff --git a/vendor/github.com/bufbuild/protocompile/parser/errors.go b/vendor/github.com/bufbuild/protocompile/parser/errors.go new file mode 100644 index 0000000..e78bdda --- /dev/null +++ b/vendor/github.com/bufbuild/protocompile/parser/errors.go @@ -0,0 +1,22 @@ +// Copyright 2020-2024 Buf Technologies, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package parser + +import "errors" + +// ErrNoSyntax is a sentinel error that may be passed to a warning reporter. +// The error the reporter receives will be wrapped with source position that +// indicates the file that had no syntax statement. +var ErrNoSyntax = errors.New("no syntax specified; defaulting to proto2 syntax") diff --git a/vendor/github.com/bufbuild/protocompile/parser/lexer.go b/vendor/github.com/bufbuild/protocompile/parser/lexer.go new file mode 100644 index 0000000..71cbc7a --- /dev/null +++ b/vendor/github.com/bufbuild/protocompile/parser/lexer.go @@ -0,0 +1,771 @@ +// Copyright 2020-2024 Buf Technologies, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package parser + +import ( + "bufio" + "bytes" + "errors" + "fmt" + "io" + "math" + "strconv" + "strings" + "unicode/utf8" + + "github.com/bufbuild/protocompile/ast" + "github.com/bufbuild/protocompile/reporter" +) + +type runeReader struct { + data []byte + pos int + err error + mark int + // Enable this check to make input required to be valid UTF-8. + // For now, since protoc allows invalid UTF-8, default to false. + utf8Strict bool +} + +func (rr *runeReader) readRune() (r rune, size int, err error) { + if rr.err != nil { + return 0, 0, rr.err + } + if rr.pos == len(rr.data) { + rr.err = io.EOF + return 0, 0, rr.err + } + r, sz := utf8.DecodeRune(rr.data[rr.pos:]) + if rr.utf8Strict && r == utf8.RuneError { + rr.err = fmt.Errorf("invalid UTF8 at offset %d: %x", rr.pos, rr.data[rr.pos]) + return 0, 0, rr.err + } + rr.pos += sz + return r, sz, nil +} + +func (rr *runeReader) offset() int { + return rr.pos +} + +func (rr *runeReader) unreadRune(sz int) { + newPos := rr.pos - sz + if newPos < rr.mark { + panic("unread past mark") + } + rr.pos = newPos +} + +func (rr *runeReader) setMark() { + rr.mark = rr.pos +} + +func (rr *runeReader) getMark() string { + return string(rr.data[rr.mark:rr.pos]) +} + +type protoLex struct { + input *runeReader + info *ast.FileInfo + handler *reporter.Handler + res *ast.FileNode + + prevSym ast.TerminalNode + prevOffset int + eof ast.Token + + comments []ast.Token +} + +var utf8Bom = []byte{0xEF, 0xBB, 0xBF} + +func newLexer(in io.Reader, filename string, handler *reporter.Handler) (*protoLex, error) { + br := bufio.NewReader(in) + + // if file has UTF8 byte order marker preface, consume it + marker, err := br.Peek(3) + if err == nil && bytes.Equal(marker, utf8Bom) { + _, _ = br.Discard(3) + } + + contents, err := io.ReadAll(br) + if err != nil { + return nil, err + } + return &protoLex{ + input: &runeReader{data: contents}, + info: ast.NewFileInfo(filename, contents), + handler: handler, + }, nil +} + +var keywords = map[string]int{ + "syntax": _SYNTAX, + "edition": _EDITION, + "import": _IMPORT, + "weak": _WEAK, + "public": _PUBLIC, + "package": _PACKAGE, + "option": _OPTION, + "true": _TRUE, + "false": _FALSE, + "inf": _INF, + "nan": _NAN, + "repeated": _REPEATED, + "optional": _OPTIONAL, + "required": _REQUIRED, + "double": _DOUBLE, + "float": _FLOAT, + "int32": _INT32, + "int64": _INT64, + "uint32": _UINT32, + "uint64": _UINT64, + "sint32": _SINT32, + "sint64": _SINT64, + "fixed32": _FIXED32, + "fixed64": _FIXED64, + "sfixed32": _SFIXED32, + "sfixed64": _SFIXED64, + "bool": _BOOL, + "string": _STRING, + "bytes": _BYTES, + "group": _GROUP, + "oneof": _ONEOF, + "map": _MAP, + "extensions": _EXTENSIONS, + "to": _TO, + "max": _MAX, + "reserved": _RESERVED, + "enum": _ENUM, + "message": _MESSAGE, + "extend": _EXTEND, + "service": _SERVICE, + "rpc": _RPC, + "stream": _STREAM, + "returns": _RETURNS, +} + +func (l *protoLex) maybeNewLine(r rune) { + if r == '\n' { + l.info.AddLine(l.input.offset()) + } +} + +func (l *protoLex) prev() ast.SourcePos { + return l.info.SourcePos(l.prevOffset) +} + +func (l *protoLex) Lex(lval *protoSymType) int { + if l.handler.ReporterError() != nil { + // if error reporter already returned non-nil error, + // we can skip the rest of the input + return 0 + } + + l.comments = nil + + for { + l.input.setMark() + + l.prevOffset = l.input.offset() + c, _, err := l.input.readRune() + if err == io.EOF { + // we're not actually returning a rune, but this will associate + // accumulated comments as a trailing comment on last symbol + // (if appropriate) + l.setRune(lval, 0) + l.eof = lval.b.Token() + return 0 + } + if err != nil { + l.setError(lval, err) + return _ERROR + } + + if strings.ContainsRune("\n\r\t\f\v ", c) { + // skip whitespace + l.maybeNewLine(c) + continue + } + + if c == '.' { + // decimal literals could start with a dot + cn, szn, err := l.input.readRune() + if err != nil { + l.setRune(lval, c) + return int(c) + } + if cn >= '0' && cn <= '9' { + l.readNumber() + token := l.input.getMark() + f, err := parseFloat(token) + if err != nil { + l.setError(lval, numError(err, "float", token)) + return _ERROR + } + l.setFloat(lval, f) + return _FLOAT_LIT + } + l.input.unreadRune(szn) + l.setRune(lval, c) + return int(c) + } + + if c == '_' || (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') { + // identifier + l.readIdentifier() + str := l.input.getMark() + if t, ok := keywords[str]; ok { + l.setIdent(lval, str) + return t + } + l.setIdent(lval, str) + return _NAME + } + + if c >= '0' && c <= '9' { + // integer or float literal + l.readNumber() + token := l.input.getMark() + if strings.HasPrefix(token, "0x") || strings.HasPrefix(token, "0X") { + // hexadecimal + ui, err := strconv.ParseUint(token[2:], 16, 64) + if err != nil { + l.setError(lval, numError(err, "hexadecimal integer", token[2:])) + return _ERROR + } + l.setInt(lval, ui) + return _INT_LIT + } + if strings.ContainsAny(token, ".eE") { + // floating point! + f, err := parseFloat(token) + if err != nil { + l.setError(lval, numError(err, "float", token)) + return _ERROR + } + l.setFloat(lval, f) + return _FLOAT_LIT + } + // integer! (decimal or octal) + base := 10 + if token[0] == '0' { + base = 8 + } + ui, err := strconv.ParseUint(token, base, 64) + if err != nil { + kind := "integer" + if base == 8 { + kind = "octal integer" + } else if numErr, ok := err.(*strconv.NumError); ok && numErr.Err == strconv.ErrRange { + // if it's too big to be an int, parse it as a float + var f float64 + kind = "float" + f, err = parseFloat(token) + if err == nil { + l.setFloat(lval, f) + return _FLOAT_LIT + } + } + l.setError(lval, numError(err, kind, token)) + return _ERROR + } + l.setInt(lval, ui) + return _INT_LIT + } + + if c == '\'' || c == '"' { + // string literal + str, err := l.readStringLiteral(c) + if err != nil { + l.setError(lval, err) + return _ERROR + } + l.setString(lval, str) + return _STRING_LIT + } + + if c == '/' { + // comment + cn, szn, err := l.input.readRune() + if err != nil { + l.setRune(lval, '/') + return int(c) + } + if cn == '/' { + if hasErr := l.skipToEndOfLineComment(lval); hasErr { + return _ERROR + } + l.comments = append(l.comments, l.newToken()) + continue + } + if cn == '*' { + ok, hasErr := l.skipToEndOfBlockComment(lval) + if hasErr { + return _ERROR + } + if !ok { + l.setError(lval, errors.New("block comment never terminates, unexpected EOF")) + return _ERROR + } + l.comments = append(l.comments, l.newToken()) + continue + } + l.input.unreadRune(szn) + } + + if c < 32 || c == 127 { + l.setError(lval, errors.New("invalid control character")) + return _ERROR + } + if !strings.ContainsRune(";,.:=-+(){}[]<>/", c) { + l.setError(lval, errors.New("invalid character")) + return _ERROR + } + l.setRune(lval, c) + return int(c) + } +} + +func parseFloat(token string) (float64, error) { + // strconv.ParseFloat allows _ to separate digits, but protobuf does not + if strings.ContainsRune(token, '_') { + return 0, &strconv.NumError{ + Func: "parseFloat", + Num: token, + Err: strconv.ErrSyntax, + } + } + f, err := strconv.ParseFloat(token, 64) + if err == nil { + return f, nil + } + if numErr, ok := err.(*strconv.NumError); ok && numErr.Err == strconv.ErrRange && math.IsInf(f, 1) { + // protoc doesn't complain about float overflow and instead just uses "infinity" + // so we mirror that behavior by just returning infinity and ignoring the error + return f, nil + } + return f, err +} + +func (l *protoLex) newToken() ast.Token { + offset := l.input.mark + length := l.input.pos - l.input.mark + return l.info.AddToken(offset, length) +} + +func (l *protoLex) setPrevAndAddComments(n ast.TerminalNode) { + comments := l.comments + l.comments = nil + var prevTrailingComments []ast.Token + if l.prevSym != nil && len(comments) > 0 { + prevEnd := l.info.NodeInfo(l.prevSym).End().Line + info := l.info.NodeInfo(n) + nStart := info.Start().Line + if nStart == prevEnd { + if rn, ok := n.(*ast.RuneNode); ok && rn.Rune == 0 { + // if current token is EOF, pretend its on separate line + // so that the logic below can attribute a final trailing + // comment to the previous token + nStart++ + } + } + c := comments[0] + commentInfo := l.info.TokenInfo(c) + commentStart := commentInfo.Start().Line + if nStart > prevEnd && commentStart == prevEnd { + // Comment starts right after the previous token. If it's a + // line comment, we record that as a trailing comment. + // + // But if it's a block comment, it is only a trailing comment + // if there are multiple comments or if the block comment ends + // on a line before n. + canDonate := strings.HasPrefix(commentInfo.RawText(), "//") || + len(comments) > 1 || commentInfo.End().Line < nStart + + if canDonate { + prevTrailingComments = comments[:1] + comments = comments[1:] + } + } + } + + // now we can associate comments + for _, c := range prevTrailingComments { + l.info.AddComment(c, l.prevSym.Token()) + } + for _, c := range comments { + l.info.AddComment(c, n.Token()) + } + + l.prevSym = n +} + +func (l *protoLex) setString(lval *protoSymType, val string) { + lval.s = ast.NewStringLiteralNode(val, l.newToken()) + l.setPrevAndAddComments(lval.s) +} + +func (l *protoLex) setIdent(lval *protoSymType, val string) { + lval.id = ast.NewIdentNode(val, l.newToken()) + l.setPrevAndAddComments(lval.id) +} + +func (l *protoLex) setInt(lval *protoSymType, val uint64) { + lval.i = ast.NewUintLiteralNode(val, l.newToken()) + l.setPrevAndAddComments(lval.i) +} + +func (l *protoLex) setFloat(lval *protoSymType, val float64) { + lval.f = ast.NewFloatLiteralNode(val, l.newToken()) + l.setPrevAndAddComments(lval.f) +} + +func (l *protoLex) setRune(lval *protoSymType, val rune) { + lval.b = ast.NewRuneNode(val, l.newToken()) + l.setPrevAndAddComments(lval.b) +} + +func (l *protoLex) setError(lval *protoSymType, err error) { + lval.err, _ = l.addSourceError(err) +} + +func (l *protoLex) readNumber() { + allowExpSign := false + for { + c, sz, err := l.input.readRune() + if err != nil { + break + } + if (c == '-' || c == '+') && !allowExpSign { + l.input.unreadRune(sz) + break + } + allowExpSign = false + if c != '.' && c != '_' && (c < '0' || c > '9') && + (c < 'a' || c > 'z') && (c < 'A' || c > 'Z') && + c != '-' && c != '+' { + // no more chars in the number token + l.input.unreadRune(sz) + break + } + if c == 'e' || c == 'E' { + // scientific notation char can be followed by + // an exponent sign + allowExpSign = true + } + } +} + +func numError(err error, kind, s string) error { + ne, ok := err.(*strconv.NumError) + if !ok { + return err + } + if ne.Err == strconv.ErrRange { + return fmt.Errorf("value out of range for %s: %s", kind, s) + } + // syntax error + return fmt.Errorf("invalid syntax in %s value: %s", kind, s) +} + +func (l *protoLex) readIdentifier() { + for { + c, sz, err := l.input.readRune() + if err != nil { + break + } + if c != '_' && (c < 'a' || c > 'z') && (c < 'A' || c > 'Z') && (c < '0' || c > '9') { + l.input.unreadRune(sz) + break + } + } +} + +func (l *protoLex) readStringLiteral(quote rune) (string, error) { + var buf bytes.Buffer + var escapeError reporter.ErrorWithPos + var noMoreErrors bool + reportErr := func(msg, badEscape string) { + if noMoreErrors { + return + } + if escapeError != nil { + // report previous one + _, ok := l.addSourceError(escapeError) + if !ok { + noMoreErrors = true + } + } + var err error + if strings.HasSuffix(msg, "%s") { + err = fmt.Errorf(msg, badEscape) + } else { + err = errors.New(msg) + } + // we've now consumed the bad escape and lexer position is after it, so we need + // to back up to the beginning of the escape to report the correct position + escapeError = l.errWithCurrentPos(err, -len(badEscape)) + } + for { + c, _, err := l.input.readRune() + if err != nil { + if err == io.EOF { + err = io.ErrUnexpectedEOF + } + return "", err + } + if c == '\n' { + return "", errors.New("encountered end-of-line before end of string literal") + } + if c == quote { + break + } + if c == 0 { + reportErr("null character ('\\0') not allowed in string literal", string(rune(0))) + continue + } + if c == '\\' { + // escape sequence + c, _, err = l.input.readRune() + if err != nil { + return "", err + } + switch { + case c == 'x' || c == 'X': + // hex escape + c1, sz1, err := l.input.readRune() + if err != nil { + return "", err + } + if c1 == quote || c1 == '\\' { + l.input.unreadRune(sz1) + reportErr("invalid hex escape: %s", "\\"+string(c)) + continue + } + c2, sz2, err := l.input.readRune() + if err != nil { + return "", err + } + var hex string + if (c2 < '0' || c2 > '9') && (c2 < 'a' || c2 > 'f') && (c2 < 'A' || c2 > 'F') { + l.input.unreadRune(sz2) + hex = string(c1) + } else { + hex = string([]rune{c1, c2}) + } + i, err := strconv.ParseInt(hex, 16, 32) + if err != nil { + reportErr("invalid hex escape: %s", "\\"+string(c)+hex) + continue + } + buf.WriteByte(byte(i)) + case c >= '0' && c <= '7': + // octal escape + c2, sz2, err := l.input.readRune() + if err != nil { + return "", err + } + var octal string + if c2 < '0' || c2 > '7' { + l.input.unreadRune(sz2) + octal = string(c) + } else { + c3, sz3, err := l.input.readRune() + if err != nil { + return "", err + } + if c3 < '0' || c3 > '7' { + l.input.unreadRune(sz3) + octal = string([]rune{c, c2}) + } else { + octal = string([]rune{c, c2, c3}) + } + } + i, err := strconv.ParseInt(octal, 8, 32) + if err != nil { + reportErr("invalid octal escape: %s", "\\"+octal) + continue + } + if i > 0xff { + reportErr("octal escape is out range, must be between 0 and 377: %s", "\\"+octal) + continue + } + buf.WriteByte(byte(i)) + case c == 'u': + // short unicode escape + u := make([]rune, 4) + for i := range u { + c2, sz2, err := l.input.readRune() + if err != nil { + return "", err + } + if c2 == quote || c2 == '\\' { + l.input.unreadRune(sz2) + u = u[:i] + break + } + u[i] = c2 + } + codepointStr := string(u) + if len(u) < 4 { + reportErr("invalid unicode escape: %s", "\\u"+codepointStr) + continue + } + i, err := strconv.ParseInt(codepointStr, 16, 32) + if err != nil { + reportErr("invalid unicode escape: %s", "\\u"+codepointStr) + continue + } + buf.WriteRune(rune(i)) + case c == 'U': + // long unicode escape + u := make([]rune, 8) + for i := range u { + c2, sz2, err := l.input.readRune() + if err != nil { + return "", err + } + if c2 == quote || c2 == '\\' { + l.input.unreadRune(sz2) + u = u[:i] + break + } + u[i] = c2 + } + codepointStr := string(u) + if len(u) < 8 { + reportErr("invalid unicode escape: %s", "\\U"+codepointStr) + continue + } + i, err := strconv.ParseInt(string(u), 16, 32) + if err != nil { + reportErr("invalid unicode escape: %s", "\\U"+codepointStr) + continue + } + if i > 0x10ffff || i < 0 { + reportErr("unicode escape is out of range, must be between 0 and 0x10ffff: %s", "\\U"+codepointStr) + continue + } + buf.WriteRune(rune(i)) + case c == 'a': + buf.WriteByte('\a') + case c == 'b': + buf.WriteByte('\b') + case c == 'f': + buf.WriteByte('\f') + case c == 'n': + buf.WriteByte('\n') + case c == 'r': + buf.WriteByte('\r') + case c == 't': + buf.WriteByte('\t') + case c == 'v': + buf.WriteByte('\v') + case c == '\\': + buf.WriteByte('\\') + case c == '\'': + buf.WriteByte('\'') + case c == '"': + buf.WriteByte('"') + case c == '?': + buf.WriteByte('?') + default: + reportErr("invalid escape sequence: %s", "\\"+string(c)) + continue + } + } else { + buf.WriteRune(c) + } + } + if escapeError != nil { + return "", escapeError + } + return buf.String(), nil +} + +func (l *protoLex) skipToEndOfLineComment(lval *protoSymType) (hasErr bool) { + for { + c, sz, err := l.input.readRune() + if err != nil { + // eof + return false + } + switch c { + case '\n': + // don't include newline in the comment + l.input.unreadRune(sz) + return false + case 0: + l.setError(lval, errors.New("invalid control character")) + return true + } + } +} + +func (l *protoLex) skipToEndOfBlockComment(lval *protoSymType) (ok, hasErr bool) { + for { + c, _, err := l.input.readRune() + if err != nil { + return false, false + } + if c == 0 { + l.setError(lval, errors.New("invalid control character")) + return false, true + } + l.maybeNewLine(c) + if c == '*' { + c, sz, err := l.input.readRune() + if err != nil { + return false, false + } + if c == '/' { + return true, false + } + l.input.unreadRune(sz) + } + } +} + +func (l *protoLex) addSourceError(err error) (reporter.ErrorWithPos, bool) { + ewp, ok := err.(reporter.ErrorWithPos) + if !ok { + // TODO: Store the previous span instead of just the position. + ewp = reporter.Error(ast.NewSourceSpan(l.prev(), l.prev()), err) + } + handlerErr := l.handler.HandleError(ewp) + return ewp, handlerErr == nil +} + +func (l *protoLex) Error(s string) { + _, _ = l.addSourceError(errors.New(s)) +} + +// TODO: Accept both a start and end offset, and use that to create a span. +func (l *protoLex) errWithCurrentPos(err error, offset int) reporter.ErrorWithPos { + if ewp, ok := err.(reporter.ErrorWithPos); ok { + return ewp + } + pos := l.info.SourcePos(l.input.offset() + offset) + return reporter.Error(ast.NewSourceSpan(pos, pos), err) +} + +func (l *protoLex) requireSemicolon(semicolons []*ast.RuneNode) (*ast.RuneNode, []*ast.RuneNode) { + if len(semicolons) == 0 { + l.Error("syntax error: expecting ';'") + return nil, nil + } + return semicolons[0], semicolons[1:] +} diff --git a/vendor/github.com/bufbuild/protocompile/parser/parser.go b/vendor/github.com/bufbuild/protocompile/parser/parser.go new file mode 100644 index 0000000..21314d5 --- /dev/null +++ b/vendor/github.com/bufbuild/protocompile/parser/parser.go @@ -0,0 +1,201 @@ +// Copyright 2020-2024 Buf Technologies, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package parser + +import ( + "fmt" + "io" + + "google.golang.org/protobuf/proto" + "google.golang.org/protobuf/types/descriptorpb" + + "github.com/bufbuild/protocompile/ast" + "github.com/bufbuild/protocompile/reporter" +) + +// The path ../.tmp/bin/goyacc is built when using `make generate` from repo root. +//go:generate ../.tmp/bin/goyacc -o proto.y.go -l -p proto proto.y + +func init() { + protoErrorVerbose = true + + // fix up the generated "token name" array so that error messages are nicer + setTokenName(_STRING_LIT, "string literal") + setTokenName(_INT_LIT, "int literal") + setTokenName(_FLOAT_LIT, "float literal") + setTokenName(_NAME, "identifier") + setTokenName(_ERROR, "error") + // for keywords, just show the keyword itself wrapped in quotes + for str, i := range keywords { + setTokenName(i, fmt.Sprintf(`"%s"`, str)) + } +} + +func setTokenName(token int, text string) { + // NB: this is based on logic in generated parse code that translates the + // int returned from the lexer into an internal token number. + var intern int8 + if token < len(protoTok1) { + intern = protoTok1[token] + } else { + if token >= protoPrivate { + if token < protoPrivate+len(protoTok2) { + intern = protoTok2[token-protoPrivate] + } + } + if intern == 0 { + for i := 0; i+1 < len(protoTok3); i += 2 { + if int(protoTok3[i]) == token { + intern = protoTok3[i+1] + break + } + } + } + } + + if intern >= 1 && int(intern-1) < len(protoToknames) { + protoToknames[intern-1] = text + return + } + + panic(fmt.Sprintf("Unknown token value: %d", token)) +} + +// Parse parses the given source code info and returns an AST. The given filename +// is used to construct error messages and position information. The given reader +// supplies the source code. The given handler is used to report errors and +// warnings encountered while parsing. If any errors are reported, this function +// returns a non-nil error. +// +// If the error returned is due to a syntax error in the source, then a non-nil +// AST is also returned. If the handler chooses to not abort the parse (e.g. the +// underlying error reporter returns nil instead of an error), the parser will +// attempt to recover and keep going. This allows multiple syntax errors to be +// reported in a single pass. And it also means that more of the AST can be +// populated (erroneous productions around the syntax error will of course be +// absent). +// +// The degree to which the parser can recover from errors and populate the AST +// depends on the nature of the syntax error and if there are any tokens after the +// syntax error that can help the parser recover. This error recovery and partial +// AST production is best effort. +func Parse(filename string, r io.Reader, handler *reporter.Handler) (*ast.FileNode, error) { + lx, err := newLexer(r, filename, handler) + if err != nil { + return nil, err + } + protoParse(lx) + if lx.res == nil { + // nil AST means there was an error that prevented any parsing + // or the file was empty; synthesize empty non-nil AST + lx.res = ast.NewEmptyFileNode(filename) + } + return lx.res, handler.Error() +} + +// Result is the result of constructing a descriptor proto from a parsed AST. +// From this result, the AST and the file descriptor proto can be had. This +// also contains numerous lookup functions, for looking up AST nodes that +// correspond to various elements of the descriptor hierarchy. +// +// Results can be created without AST information, using the ResultWithoutAST() +// function. All functions other than AST() will still return non-nil values, +// allowing compile operations to work with files that have only intermediate +// descriptor protos and no source code. For such results, the function that +// return AST nodes will return placeholder nodes. The position information for +// placeholder nodes contains only the filename. +type Result interface { + // AST returns the parsed abstract syntax tree. This returns nil if the + // Result was created without an AST. + AST() *ast.FileNode + // FileDescriptorProto returns the file descriptor proto. + FileDescriptorProto() *descriptorpb.FileDescriptorProto + + // FileNode returns the root of the AST. If this result has no AST then a + // placeholder node is returned. + FileNode() ast.FileDeclNode + // Node returns the AST node from which the given message was created. This + // can return nil, such as if the given message is not part of the + // FileDescriptorProto hierarchy. If this result has no AST, this returns a + // placeholder node. + Node(proto.Message) ast.Node + // OptionNode returns the AST node corresponding to the given uninterpreted + // option. This can return nil, such as if the given option is not part of + // the FileDescriptorProto hierarchy. If this result has no AST, this + // returns a placeholder node. + OptionNode(*descriptorpb.UninterpretedOption) ast.OptionDeclNode + // OptionNamePartNode returns the AST node corresponding to the given name + // part for an uninterpreted option. This can return nil, such as if the + // given name part is not part of the FileDescriptorProto hierarchy. If this + // result has no AST, this returns a placeholder node. + OptionNamePartNode(*descriptorpb.UninterpretedOption_NamePart) ast.Node + // MessageNode returns the AST node corresponding to the given message. This + // can return nil, such as if the given message is not part of the + // FileDescriptorProto hierarchy. If this result has no AST, this returns a + // placeholder node. + MessageNode(*descriptorpb.DescriptorProto) ast.MessageDeclNode + // FieldNode returns the AST node corresponding to the given field. This can + // return nil, such as if the given field is not part of the + // FileDescriptorProto hierarchy. If this result has no AST, this returns a + // placeholder node. + FieldNode(*descriptorpb.FieldDescriptorProto) ast.FieldDeclNode + // OneofNode returns the AST node corresponding to the given oneof. This can + // return nil, such as if the given oneof is not part of the + // FileDescriptorProto hierarchy. If this result has no AST, this returns a + // placeholder node. + OneofNode(*descriptorpb.OneofDescriptorProto) ast.OneofDeclNode + // ExtensionRangeNode returns the AST node corresponding to the given + // extension range. This can return nil, such as if the given range is not + // part of the FileDescriptorProto hierarchy. If this result has no AST, + // this returns a placeholder node. + ExtensionRangeNode(*descriptorpb.DescriptorProto_ExtensionRange) ast.RangeDeclNode + + // ExtensionsNode returns the AST node corresponding to the "extensions" + // statement in a message that corresponds to the given range. This will be + // the parent of the node returned by ExtensionRangeNode, which contains the + // options that apply to all child ranges. + ExtensionsNode(*descriptorpb.DescriptorProto_ExtensionRange) ast.NodeWithOptions + + // MessageReservedRangeNode returns the AST node corresponding to the given + // reserved range. This can return nil, such as if the given range is not + // part of the FileDescriptorProto hierarchy. If this result has no AST, + // this returns a placeholder node. + MessageReservedRangeNode(*descriptorpb.DescriptorProto_ReservedRange) ast.RangeDeclNode + // EnumNode returns the AST node corresponding to the given enum. This can + // return nil, such as if the given enum is not part of the + // FileDescriptorProto hierarchy. If this result has no AST, this returns a + // placeholder node. + EnumNode(*descriptorpb.EnumDescriptorProto) ast.NodeWithOptions + // EnumValueNode returns the AST node corresponding to the given enum. This + // can return nil, such as if the given enum value is not part of the + // FileDescriptorProto hierarchy. If this result has no AST, this returns a + // placeholder node. + EnumValueNode(*descriptorpb.EnumValueDescriptorProto) ast.EnumValueDeclNode + // EnumReservedRangeNode returns the AST node corresponding to the given + // reserved range. This can return nil, such as if the given range is not + // part of the FileDescriptorProto hierarchy. If this result has no AST, + // this returns a placeholder node. + EnumReservedRangeNode(*descriptorpb.EnumDescriptorProto_EnumReservedRange) ast.RangeDeclNode + // ServiceNode returns the AST node corresponding to the given service. This + // can return nil, such as if the given service is not part of the + // FileDescriptorProto hierarchy. If this result has no AST, this returns a + // placeholder node. + ServiceNode(*descriptorpb.ServiceDescriptorProto) ast.NodeWithOptions + // MethodNode returns the AST node corresponding to the given method. This + // can return nil, such as if the given method is not part of the + // FileDescriptorProto hierarchy. If this result has no AST, this returns a + // placeholder node. + MethodNode(*descriptorpb.MethodDescriptorProto) ast.RPCDeclNode +} diff --git a/vendor/github.com/bufbuild/protocompile/parser/proto.y b/vendor/github.com/bufbuild/protocompile/parser/proto.y new file mode 100644 index 0000000..e66cabd --- /dev/null +++ b/vendor/github.com/bufbuild/protocompile/parser/proto.y @@ -0,0 +1,1498 @@ +%{ +package parser + +//lint:file-ignore SA4006 generated parser has unused values + +import ( + "math" + "strings" + + "github.com/bufbuild/protocompile/ast" +) + +%} + +// fields inside this union end up as the fields in a structure known +// as ${PREFIX}SymType, of which a reference is passed to the lexer. +%union{ + file *ast.FileNode + syn *ast.SyntaxNode + ed *ast.EditionNode + fileElements []ast.FileElement + pkg nodeWithRunes[*ast.PackageNode] + imprt nodeWithRunes[*ast.ImportNode] + msg nodeWithRunes[*ast.MessageNode] + msgElements []ast.MessageElement + fld *ast.FieldNode + msgFld nodeWithRunes[*ast.FieldNode] + mapFld nodeWithRunes[*ast.MapFieldNode] + mapType *ast.MapTypeNode + grp *ast.GroupNode + msgGrp nodeWithRunes[*ast.GroupNode] + oo nodeWithRunes[*ast.OneofNode] + ooElement ast.OneofElement + ooElements []ast.OneofElement + ext nodeWithRunes[*ast.ExtensionRangeNode] + resvd nodeWithRunes[*ast.ReservedNode] + en nodeWithRunes[*ast.EnumNode] + enElements []ast.EnumElement + env nodeWithRunes[*ast.EnumValueNode] + extend nodeWithRunes[*ast.ExtendNode] + extElement ast.ExtendElement + extElements []ast.ExtendElement + svc nodeWithRunes[*ast.ServiceNode] + svcElements []ast.ServiceElement + mtd nodeWithRunes[*ast.RPCNode] + mtdMsgType *ast.RPCTypeNode + mtdElements []ast.RPCElement + optRaw *ast.OptionNode + opt nodeWithRunes[*ast.OptionNode] + opts *compactOptionSlices + refRaw *ast.FieldReferenceNode + ref nodeWithRunes[*ast.FieldReferenceNode] + optNms *fieldRefSlices + cmpctOpts *ast.CompactOptionsNode + rng *ast.RangeNode + rngs *rangeSlices + names *nameSlices + cidPart nodeWithRunes[*ast.IdentNode] + cid *identSlices + tid ast.IdentValueNode + sl *valueSlices + msgLitFlds *messageFieldList + msgLitFld *ast.MessageFieldNode + v ast.ValueNode + il ast.IntValueNode + str []*ast.StringLiteralNode + s *ast.StringLiteralNode + i *ast.UintLiteralNode + f *ast.FloatLiteralNode + id *ast.IdentNode + b *ast.RuneNode + bs []*ast.RuneNode + err error +} + +// any non-terminal which returns a value needs a type, which is +// really a field name in the above union struct +%type <file> file +%type <syn> syntaxDecl +%type <ed> editionDecl +%type <fileElements> fileBody fileElement fileElements +%type <imprt> importDecl +%type <pkg> packageDecl +%type <optRaw> compactOption oneofOptionDecl +%type <opt> optionDecl compactOptionEntry compactOptionFinal +%type <opts> compactOptionDecls compactOptionLeadingDecls +%type <refRaw> extensionName messageLiteralFieldName optionNamePart +%type <ref> optionNameEntry optionNameFinal +%type <optNms> optionName optionNameLeading +%type <cmpctOpts> compactOptions +%type <v> fieldValue optionValue scalarValue fieldScalarValue messageLiteralWithBraces messageLiteral numLit specialFloatLit listLiteral listElement listOfMessagesLiteral messageValue +%type <il> enumValueNumber +%type <id> identifier mapKeyType msgElementName extElementName oneofElementName notGroupElementName mtdElementName enumValueName fieldCardinality +%type <cidPart> qualifiedIdentifierEntry qualifiedIdentifierFinal mtdElementIdentEntry mtdElementIdentFinal +%type <cid> qualifiedIdentifier msgElementIdent extElementIdent oneofElementIdent notGroupElementIdent mtdElementIdent qualifiedIdentifierDot qualifiedIdentifierLeading mtdElementIdentLeading +%type <tid> typeName msgElementTypeIdent extElementTypeIdent oneofElementTypeIdent notGroupElementTypeIdent mtdElementTypeIdent +%type <sl> listElements messageLiterals +%type <msgLitFlds> messageLiteralFieldEntry messageLiteralFields messageTextFormat +%type <msgLitFld> messageLiteralField +%type <msgFld> messageFieldDecl +%type <fld> oneofFieldDecl extensionFieldDecl +%type <oo> oneofDecl +%type <grp> groupDecl oneofGroupDecl +%type <msgGrp> messageGroupDecl +%type <mapFld> mapFieldDecl +%type <mapType> mapType +%type <msg> messageDecl +%type <msgElements> messageElement messageElements messageBody +%type <ooElement> oneofElement +%type <ooElements> oneofElements oneofBody +%type <names> fieldNameStrings fieldNameIdents +%type <resvd> msgReserved enumReserved reservedNames +%type <rng> tagRange enumValueRange +%type <rngs> tagRanges enumValueRanges +%type <ext> extensionRangeDecl +%type <en> enumDecl +%type <enElements> enumElement enumElements enumBody +%type <env> enumValueDecl +%type <extend> extensionDecl +%type <extElement> extensionElement +%type <extElements> extensionElements extensionBody +%type <str> stringLit +%type <svc> serviceDecl +%type <svcElements> serviceElement serviceElements serviceBody +%type <mtd> methodDecl +%type <mtdElements> methodElement methodElements methodBody +%type <mtdMsgType> methodMessageType +%type <b> semicolon +%type <bs> semicolons semicolonList + +// same for terminals +%token <s> _STRING_LIT +%token <i> _INT_LIT +%token <f> _FLOAT_LIT +%token <id> _NAME +%token <id> _SYNTAX _EDITION _IMPORT _WEAK _PUBLIC _PACKAGE _OPTION _TRUE _FALSE _INF _NAN _REPEATED _OPTIONAL _REQUIRED +%token <id> _DOUBLE _FLOAT _INT32 _INT64 _UINT32 _UINT64 _SINT32 _SINT64 _FIXED32 _FIXED64 _SFIXED32 _SFIXED64 +%token <id> _BOOL _STRING _BYTES _GROUP _ONEOF _MAP _EXTENSIONS _TO _MAX _RESERVED _ENUM _MESSAGE _EXTEND +%token <id> _SERVICE _RPC _STREAM _RETURNS +%token <err> _ERROR +// we define all of these, even ones that aren't used, to improve error messages +// so it shows the unexpected symbol instead of showing "$unk" +%token <b> '=' ';' ':' '{' '}' '\\' '/' '?' '.' ',' '>' '<' '+' '-' '(' ')' '[' ']' '*' '&' '^' '%' '$' '#' '@' '!' '~' '`' + +%% + +file : syntaxDecl { + lex := protolex.(*protoLex) + $$ = ast.NewFileNode(lex.info, $1, nil, lex.eof) + lex.res = $$ + } + | editionDecl { + lex := protolex.(*protoLex) + $$ = ast.NewFileNodeWithEdition(lex.info, $1, nil, lex.eof) + lex.res = $$ + } + | fileBody { + lex := protolex.(*protoLex) + $$ = ast.NewFileNode(lex.info, nil, $1, lex.eof) + lex.res = $$ + } + | syntaxDecl fileBody { + lex := protolex.(*protoLex) + $$ = ast.NewFileNode(lex.info, $1, $2, lex.eof) + lex.res = $$ + } + | editionDecl fileBody { + lex := protolex.(*protoLex) + $$ = ast.NewFileNodeWithEdition(lex.info, $1, $2, lex.eof) + lex.res = $$ + } + | { + lex := protolex.(*protoLex) + $$ = ast.NewFileNode(lex.info, nil, nil, lex.eof) + lex.res = $$ + } + +fileBody : semicolons fileElements { + $$ = prependRunes(toFileElement, $1, $2) + } + +fileElements : fileElements fileElement { + $$ = append($1, $2...) + } + | fileElement { + $$ = $1 + } + +fileElement : importDecl { + $$ = toElements[ast.FileElement](toFileElement, $1.Node, $1.Runes) + } + | packageDecl { + $$ = toElements[ast.FileElement](toFileElement, $1.Node, $1.Runes) + } + | optionDecl { + $$ = toElements[ast.FileElement](toFileElement, $1.Node, $1.Runes) + } + | messageDecl { + $$ = toElements[ast.FileElement](toFileElement, $1.Node, $1.Runes) + } + | enumDecl { + $$ = toElements[ast.FileElement](toFileElement, $1.Node, $1.Runes) + } + | extensionDecl { + $$ = toElements[ast.FileElement](toFileElement, $1.Node, $1.Runes) + } + | serviceDecl { + $$ = toElements[ast.FileElement](toFileElement, $1.Node, $1.Runes) + } + | error { + $$ = nil + } + +semicolonList : ';' { + $$ = []*ast.RuneNode{$1} + } + | semicolonList ';' { + $$ = append($1, $2) + } + +semicolons : semicolonList { + $$ = $1 + } + | { + $$ = nil + } + +semicolon : ';' { + $$ = $1 + } | + { + protolex.(*protoLex).Error("syntax error: expecting ';'") + $$ = nil + } + +syntaxDecl : _SYNTAX '=' stringLit ';' { + $$ = ast.NewSyntaxNode($1.ToKeyword(), $2, toStringValueNode($3), $4) + } + +editionDecl : _EDITION '=' stringLit ';' { + $$ = ast.NewEditionNode($1.ToKeyword(), $2, toStringValueNode($3), $4) + } + +importDecl : _IMPORT stringLit semicolons { + semi, extra := protolex.(*protoLex).requireSemicolon($3) + $$ = newNodeWithRunes(ast.NewImportNode($1.ToKeyword(), nil, nil, toStringValueNode($2), semi), extra...) + } + | _IMPORT _WEAK stringLit semicolons { + semi, extra := protolex.(*protoLex).requireSemicolon($4) + $$ = newNodeWithRunes(ast.NewImportNode($1.ToKeyword(), nil, $2.ToKeyword(), toStringValueNode($3), semi), extra...) + } + | _IMPORT _PUBLIC stringLit semicolons { + semi, extra := protolex.(*protoLex).requireSemicolon($4) + $$ = newNodeWithRunes(ast.NewImportNode($1.ToKeyword(), $2.ToKeyword(), nil, toStringValueNode($3), semi), extra...) + } + +packageDecl : _PACKAGE qualifiedIdentifier semicolons { + semi, extra := protolex.(*protoLex).requireSemicolon($3) + $$ = newNodeWithRunes(ast.NewPackageNode($1.ToKeyword(), $2.toIdentValueNode(nil), semi), extra...) + } + +qualifiedIdentifier : identifier { + $$ = &identSlices{idents: []*ast.IdentNode{$1}} + } + | qualifiedIdentifier '.' identifier { + $1.idents = append($1.idents, $3) + $1.dots = append($1.dots, $2) + $$ = $1 + } + +qualifiedIdentifierDot : qualifiedIdentifierFinal { + $$ = &identSlices{idents: []*ast.IdentNode{$1.Node}, dots: $1.Runes} + } + | qualifiedIdentifierLeading qualifiedIdentifierFinal { + $1.idents = append($1.idents, $2.Node) + $1.dots = append($1.dots, $2.Runes...) + $$ = $1 + } + +qualifiedIdentifierLeading : qualifiedIdentifierEntry { + $$ = &identSlices{idents: []*ast.IdentNode{$1.Node}, dots: $1.Runes} + } + | qualifiedIdentifierLeading qualifiedIdentifierEntry { + $1.idents = append($1.idents, $2.Node) + $1.dots = append($1.dots, $2.Runes...) + $$ = $1 + } + +qualifiedIdentifierFinal : identifier { + $$ = newNodeWithRunes($1) + } + | qualifiedIdentifierEntry { + protolex.(*protoLex).Error("syntax error: unexpected '.'") + $$ = $1 + } + +qualifiedIdentifierEntry : identifier '.' { + $$ = newNodeWithRunes($1, $2) + } + +// to mimic limitations of protoc recursive-descent parser, +// we don't allowed message statement keywords as identifiers +// (or oneof statement keywords [e.g. "option"] below) + +msgElementIdent : msgElementName { + $$ = &identSlices{idents: []*ast.IdentNode{$1}} + } + | msgElementIdent '.' identifier { + $1.idents = append($1.idents, $3) + $1.dots = append($1.dots, $2) + $$ = $1 + } + +extElementIdent : extElementName { + $$ = &identSlices{idents: []*ast.IdentNode{$1}} + } + | extElementIdent '.' identifier { + $1.idents = append($1.idents, $3) + $1.dots = append($1.dots, $2) + $$ = $1 + } + +oneofElementIdent : oneofElementName { + $$ = &identSlices{idents: []*ast.IdentNode{$1}} + } + | oneofElementIdent '.' identifier { + $1.idents = append($1.idents, $3) + $1.dots = append($1.dots, $2) + $$ = $1 + } + +notGroupElementIdent : notGroupElementName { + $$ = &identSlices{idents: []*ast.IdentNode{$1}} + } + | notGroupElementIdent '.' identifier { + $1.idents = append($1.idents, $3) + $1.dots = append($1.dots, $2) + $$ = $1 + } + +mtdElementIdent : mtdElementIdentFinal { + $$ = &identSlices{idents: []*ast.IdentNode{$1.Node}, dots: $1.Runes} + } + | mtdElementIdentLeading mtdElementIdentFinal { + $1.idents = append($1.idents, $2.Node) + $1.dots = append($1.dots, $2.Runes...) + $$ = $1 + } + +mtdElementIdentLeading : mtdElementIdentEntry { + $$ = &identSlices{idents: []*ast.IdentNode{$1.Node}, dots: $1.Runes} + } + | mtdElementIdentLeading mtdElementIdentEntry { + $1.idents = append($1.idents, $2.Node) + $1.dots = append($1.dots, $2.Runes...) + $$ = $1 + } + +mtdElementIdentFinal : mtdElementName { + $$ = newNodeWithRunes($1) + } + | mtdElementIdentEntry { + protolex.(*protoLex).Error("syntax error: unexpected '.'") + $$ = $1 + } + +mtdElementIdentEntry : mtdElementName '.' { + $$ = newNodeWithRunes($1, $2) + } + +oneofOptionDecl : _OPTION optionName '=' optionValue semicolon { + optName := ast.NewOptionNameNode($2.refs, $2.dots) + $$ = ast.NewOptionNode($1.ToKeyword(), optName, $3, $4, $5) + } + +optionDecl : _OPTION optionName '=' optionValue semicolons { + optName := ast.NewOptionNameNode($2.refs, $2.dots) + semi, extra := protolex.(*protoLex).requireSemicolon($5) + $$ = newNodeWithRunes(ast.NewOptionNode($1.ToKeyword(), optName, $3, $4, semi), extra...) + } + +optionNamePart : identifier { + $$ = ast.NewFieldReferenceNode($1) + } + | extensionName { + $$ = $1 + } + +optionNameEntry : optionNamePart '.' { + $$ = newNodeWithRunes($1, $2) + } + +optionNameFinal : optionNamePart { + $$ = newNodeWithRunes($1) + } + | optionNameEntry { + protolex.(*protoLex).Error("syntax error: unexpected '.'") + $$ = $1 + } + +optionNameLeading : optionNameEntry { + $$ = &fieldRefSlices{refs: []*ast.FieldReferenceNode{$1.Node}, dots: $1.Runes} + } + | optionNameLeading optionNameEntry { + $1.refs = append($1.refs, $2.Node) + $1.dots = append($1.dots, $2.Runes...) + $$ = $1 + } + +optionName : optionNameFinal { + $$ = &fieldRefSlices{refs: []*ast.FieldReferenceNode{$1.Node}, dots: $1.Runes} + } + | optionNameLeading optionNameFinal { + $1.refs = append($1.refs, $2.Node) + $1.dots = append($1.dots, $2.Runes...) + $$ = $1 + } + +extensionName : '(' typeName ')' { + $$ = ast.NewExtensionFieldReferenceNode($1, $2, $3) + } + +optionValue : scalarValue + | messageLiteralWithBraces + +scalarValue : stringLit { + $$ = toStringValueNode($1) + } + | numLit + | specialFloatLit + | identifier { + $$ = $1 + } + +numLit : _FLOAT_LIT { + $$ = $1 + } + | '-' _FLOAT_LIT { + $$ = ast.NewSignedFloatLiteralNode($1, $2) + } + | _INT_LIT { + $$ = $1 + } + | '-' _INT_LIT { + if $2.Val > math.MaxInt64 + 1 { + // can't represent as int so treat as float literal + $$ = ast.NewSignedFloatLiteralNode($1, $2) + } else { + $$ = ast.NewNegativeIntLiteralNode($1, $2) + } + } + +specialFloatLit : '-' _INF { + f := ast.NewSpecialFloatLiteralNode($2.ToKeyword()) + $$ = ast.NewSignedFloatLiteralNode($1, f) + } + | '-' _NAN { + f := ast.NewSpecialFloatLiteralNode($2.ToKeyword()) + $$ = ast.NewSignedFloatLiteralNode($1, f) + } + +stringLit : _STRING_LIT { + $$ = []*ast.StringLiteralNode{$1} + } + | stringLit _STRING_LIT { + $$ = append($1, $2) + } + +messageLiteralWithBraces : '{' messageTextFormat '}' { + if $2 == nil { + $$ = ast.NewMessageLiteralNode($1, nil, nil, $3) + } else { + fields, delimiters := $2.toNodes() + $$ = ast.NewMessageLiteralNode($1, fields, delimiters, $3) + } + } + | '{' '}' { + $$ = ast.NewMessageLiteralNode($1, nil, nil, $2) + } + +messageTextFormat : messageLiteralFields + +messageLiteralFields : messageLiteralFieldEntry + | messageLiteralFieldEntry messageLiteralFields { + if $1 != nil { + $1.next = $2 + $$ = $1 + } else { + $$ = $2 + } + } + +messageLiteralFieldEntry : messageLiteralField { + if $1 != nil { + $$ = &messageFieldList{field: $1} + } else { + $$ = nil + } + } + | messageLiteralField ',' { + if $1 != nil { + $$ = &messageFieldList{field: $1, delimiter: $2} + } else { + $$ = nil + } + } + | messageLiteralField ';' { + if $1 != nil { + $$ = &messageFieldList{field: $1, delimiter: $2} + } else { + $$ = nil + } + } + | error ',' { + $$ = nil + } + | error ';' { + $$ = nil + } + | error { + $$ = nil + } + +messageLiteralField : messageLiteralFieldName ':' fieldValue { + if $1 != nil && $2 != nil { + $$ = ast.NewMessageFieldNode($1, $2, $3) + } else { + $$ = nil + } + } + | messageLiteralFieldName messageValue { + if $1 != nil && $2 != nil { + $$ = ast.NewMessageFieldNode($1, nil, $2) + } else { + $$ = nil + } + } + | error ':' fieldValue { + $$ = nil + } + +messageLiteralFieldName : identifier { + $$ = ast.NewFieldReferenceNode($1) + } + | '[' qualifiedIdentifierDot ']' { + $$ = ast.NewExtensionFieldReferenceNode($1, $2.toIdentValueNode(nil), $3) + } + | '[' qualifiedIdentifierDot '/' qualifiedIdentifierDot ']' { + $$ = ast.NewAnyTypeReferenceNode($1, $2.toIdentValueNode(nil), $3, $4.toIdentValueNode(nil), $5) + } + | '[' error ']' { + $$ = nil + } + +fieldValue : fieldScalarValue + | messageLiteral + | listLiteral + +fieldScalarValue : stringLit { + $$ = toStringValueNode($1) + } + | numLit + | '-' identifier { + kw := $2.ToKeyword() + switch strings.ToLower(kw.Val) { + case "inf", "infinity", "nan": + // these are acceptable + default: + // anything else is not + protolex.(*protoLex).Error(`only identifiers "inf", "infinity", or "nan" may appear after negative sign`) + } + // we'll validate the identifier later + f := ast.NewSpecialFloatLiteralNode(kw) + $$ = ast.NewSignedFloatLiteralNode($1, f) + } + | identifier { + $$ = $1 + } + +messageValue : messageLiteral + | listOfMessagesLiteral + +messageLiteral : messageLiteralWithBraces + | '<' messageTextFormat '>' { + if $2 == nil { + $$ = ast.NewMessageLiteralNode($1, nil, nil, $3) + } else { + fields, delimiters := $2.toNodes() + $$ = ast.NewMessageLiteralNode($1, fields, delimiters, $3) + } + } + | '<' '>' { + $$ = ast.NewMessageLiteralNode($1, nil, nil, $2) + } + +listLiteral : '[' listElements ']' { + if $2 == nil { + $$ = ast.NewArrayLiteralNode($1, nil, nil, $3) + } else { + $$ = ast.NewArrayLiteralNode($1, $2.vals, $2.commas, $3) + } + } + | '[' ']' { + $$ = ast.NewArrayLiteralNode($1, nil, nil, $2) + } + | '[' error ']' { + $$ = ast.NewArrayLiteralNode($1, nil, nil, $3) + } + +listElements : listElement { + $$ = &valueSlices{vals: []ast.ValueNode{$1}} + } + | listElements ',' listElement { + $1.vals = append($1.vals, $3) + $1.commas = append($1.commas, $2) + $$ = $1 + } + +listElement : fieldScalarValue + | messageLiteral + +listOfMessagesLiteral : '[' messageLiterals ']' { + if $2 == nil { + $$ = ast.NewArrayLiteralNode($1, nil, nil, $3) + } else { + $$ = ast.NewArrayLiteralNode($1, $2.vals, $2.commas, $3) + } + } + | '[' ']' { + $$ = ast.NewArrayLiteralNode($1, nil, nil, $2) + } + | '[' error ']' { + $$ = ast.NewArrayLiteralNode($1, nil, nil, $3) + } + +messageLiterals : messageLiteral { + $$ = &valueSlices{vals: []ast.ValueNode{$1}} + } + | messageLiterals ',' messageLiteral { + $1.vals = append($1.vals, $3) + $1.commas = append($1.commas, $2) + $$ = $1 + } + +typeName : qualifiedIdentifierDot { + $$ = $1.toIdentValueNode(nil) + } + | '.' qualifiedIdentifierDot { + $$ = $2.toIdentValueNode($1) + } + +msgElementTypeIdent : msgElementIdent { + $$ = $1.toIdentValueNode(nil) + } + | '.' qualifiedIdentifier { + $$ = $2.toIdentValueNode($1) + } + +extElementTypeIdent : extElementIdent { + $$ = $1.toIdentValueNode(nil) + } + | '.' qualifiedIdentifier { + $$ = $2.toIdentValueNode($1) + } + +oneofElementTypeIdent : oneofElementIdent { + $$ = $1.toIdentValueNode(nil) + } + | '.' qualifiedIdentifier { + $$ = $2.toIdentValueNode($1) + } + +notGroupElementTypeIdent : notGroupElementIdent { + $$ = $1.toIdentValueNode(nil) + } + | '.' qualifiedIdentifier { + $$ = $2.toIdentValueNode($1) + } + +mtdElementTypeIdent : mtdElementIdent { + $$ = $1.toIdentValueNode(nil) + } + | '.' qualifiedIdentifierDot { + $$ = $2.toIdentValueNode($1) + } + +fieldCardinality : _REQUIRED + | _OPTIONAL + | _REPEATED + +compactOptions : '[' compactOptionDecls ']' { + $$ = ast.NewCompactOptionsNode($1, $2.options, $2.commas, $3) + } + | '[' ']' { + protolex.(*protoLex).Error("compact options must have at least one option") + $$ = ast.NewCompactOptionsNode($1, nil, nil, $2) + } + +compactOptionDecls : compactOptionFinal { + $$ = &compactOptionSlices{options: []*ast.OptionNode{$1.Node}, commas: $1.Runes} + } + | compactOptionLeadingDecls compactOptionFinal { + $1.options = append($1.options, $2.Node) + $1.commas = append($1.commas, $2.Runes...) + $$ = $1 + } + +compactOptionLeadingDecls : compactOptionEntry { + $$ = &compactOptionSlices{options: []*ast.OptionNode{$1.Node}, commas: $1.Runes} + } + | compactOptionLeadingDecls compactOptionEntry { + $1.options = append($1.options, $2.Node) + $1.commas = append($1.commas, $2.Runes...) + $$ = $1 + } + +compactOptionFinal : compactOption { + $$ = newNodeWithRunes($1) + } + | compactOptionEntry { + protolex.(*protoLex).Error("syntax error: unexpected ','") + $$ = $1 + } + +compactOptionEntry : compactOption ',' { + $$ = newNodeWithRunes($1, $2) + } + +compactOption : optionName '=' optionValue { + optName := ast.NewOptionNameNode($1.refs, $1.dots) + $$ = ast.NewCompactOptionNode(optName, $2, $3) + } + | optionName { + optName := ast.NewOptionNameNode($1.refs, $1.dots) + protolex.(*protoLex).Error("compact option must have a value") + $$ = ast.NewCompactOptionNode(optName, nil, nil) + } + + +groupDecl : fieldCardinality _GROUP identifier '=' _INT_LIT '{' messageBody '}' { + $$ = ast.NewGroupNode($1.ToKeyword(), $2.ToKeyword(), $3, $4, $5, nil, $6, $7, $8) + } + | fieldCardinality _GROUP identifier '=' _INT_LIT compactOptions '{' messageBody '}' { + $$ = ast.NewGroupNode($1.ToKeyword(), $2.ToKeyword(), $3, $4, $5, $6, $7, $8, $9) + } + +messageGroupDecl : fieldCardinality _GROUP identifier '=' _INT_LIT '{' messageBody '}' semicolons { + $$ = newNodeWithRunes(ast.NewGroupNode($1.ToKeyword(), $2.ToKeyword(), $3, $4, $5, nil, $6, $7, $8), $9...) + } + | fieldCardinality _GROUP identifier '=' _INT_LIT compactOptions '{' messageBody '}' semicolons { + $$ = newNodeWithRunes(ast.NewGroupNode($1.ToKeyword(), $2.ToKeyword(), $3, $4, $5, $6, $7, $8, $9), $10...) + } + | fieldCardinality _GROUP identifier '{' messageBody '}' semicolons { + $$ = newNodeWithRunes(ast.NewGroupNode($1.ToKeyword(), $2.ToKeyword(), $3, nil, nil, nil, $4, $5, $6), $7...) + } + | fieldCardinality _GROUP identifier compactOptions '{' messageBody '}' semicolons { + $$ = newNodeWithRunes(ast.NewGroupNode($1.ToKeyword(), $2.ToKeyword(), $3, nil, nil, $4, $5, $6, $7), $8...) + } + +oneofDecl : _ONEOF identifier '{' oneofBody '}' semicolons { + $$ = newNodeWithRunes(ast.NewOneofNode($1.ToKeyword(), $2, $3, $4, $5), $6...) + } + +oneofBody : { + $$ = nil + } + | oneofElements + +oneofElements : oneofElements oneofElement { + if $2 != nil { + $$ = append($1, $2) + } else { + $$ = $1 + } + } + | oneofElement { + if $1 != nil { + $$ = []ast.OneofElement{$1} + } else { + $$ = nil + } + } + +oneofElement : oneofOptionDecl { + $$ = $1 + } + | oneofFieldDecl { + $$ = $1 + } + | oneofGroupDecl { + $$ = $1 + } + | error ';' { + $$ = nil + } + | error { + $$ = nil + } + +oneofFieldDecl : oneofElementTypeIdent identifier '=' _INT_LIT semicolon { + $$ = ast.NewFieldNode(nil, $1, $2, $3, $4, nil, $5) + } + | oneofElementTypeIdent identifier '=' _INT_LIT compactOptions semicolon { + $$ = ast.NewFieldNode(nil, $1, $2, $3, $4, $5, $6) + } + | oneofElementTypeIdent identifier semicolon { + $$ = ast.NewFieldNode(nil, $1, $2, nil, nil, nil, $3) + } + | oneofElementTypeIdent identifier compactOptions semicolon { + $$ = ast.NewFieldNode(nil, $1, $2, nil, nil, $3, $4) + } + +oneofGroupDecl : _GROUP identifier '=' _INT_LIT '{' messageBody '}' { + $$ = ast.NewGroupNode(nil, $1.ToKeyword(), $2, $3, $4, nil, $5, $6, $7) + } + | _GROUP identifier '=' _INT_LIT compactOptions '{' messageBody '}' { + $$ = ast.NewGroupNode(nil, $1.ToKeyword(), $2, $3, $4, $5, $6, $7, $8) + } + | _GROUP identifier '{' messageBody '}' { + $$ = ast.NewGroupNode(nil, $1.ToKeyword(), $2, nil, nil, nil, $3, $4, $5) + } + | _GROUP identifier compactOptions '{' messageBody '}' { + $$ = ast.NewGroupNode(nil, $1.ToKeyword(), $2, nil, nil, $3, $4, $5, $6) + } + + +mapFieldDecl : mapType identifier '=' _INT_LIT semicolons { + semi, extra := protolex.(*protoLex).requireSemicolon($5) + $$ = newNodeWithRunes(ast.NewMapFieldNode($1, $2, $3, $4, nil, semi), extra...) + } + | mapType identifier '=' _INT_LIT compactOptions semicolons { + semi, extra := protolex.(*protoLex).requireSemicolon($6) + $$ = newNodeWithRunes(ast.NewMapFieldNode($1, $2, $3, $4, $5, semi), extra...) + } + | mapType identifier semicolons { + semi, extra := protolex.(*protoLex).requireSemicolon($3) + $$ = newNodeWithRunes(ast.NewMapFieldNode($1, $2, nil, nil, nil, semi), extra...) + } + | mapType identifier compactOptions semicolons { + semi, extra := protolex.(*protoLex).requireSemicolon($4) + $$ = newNodeWithRunes(ast.NewMapFieldNode($1, $2, nil, nil, $3, semi), extra...) + } + +mapType : _MAP '<' mapKeyType ',' typeName '>' { + $$ = ast.NewMapTypeNode($1.ToKeyword(), $2, $3, $4, $5, $6) + } + +mapKeyType : _INT32 + | _INT64 + | _UINT32 + | _UINT64 + | _SINT32 + | _SINT64 + | _FIXED32 + | _FIXED64 + | _SFIXED32 + | _SFIXED64 + | _BOOL + | _STRING + +extensionRangeDecl : _EXTENSIONS tagRanges ';' semicolons { + // TODO: Tolerate a missing semicolon here. This currnelty creates a shift/reduce conflict + // between `extensions 1 to 10` and `extensions 1` followed by `to = 10`. + $$ = newNodeWithRunes(ast.NewExtensionRangeNode($1.ToKeyword(), $2.ranges, $2.commas, nil, $3), $4...) + } + | _EXTENSIONS tagRanges compactOptions semicolons { + semi, extra := protolex.(*protoLex).requireSemicolon($4) + $$ = newNodeWithRunes(ast.NewExtensionRangeNode($1.ToKeyword(), $2.ranges, $2.commas, $3, semi), extra...) + } + +tagRanges : tagRange { + $$ = &rangeSlices{ranges: []*ast.RangeNode{$1}} + } + | tagRanges ',' tagRange { + $1.ranges = append($1.ranges, $3) + $1.commas = append($1.commas, $2) + $$ = $1 + } + +tagRange : _INT_LIT { + $$ = ast.NewRangeNode($1, nil, nil, nil) + } + | _INT_LIT _TO _INT_LIT { + $$ = ast.NewRangeNode($1, $2.ToKeyword(), $3, nil) + } + | _INT_LIT _TO _MAX { + $$ = ast.NewRangeNode($1, $2.ToKeyword(), nil, $3.ToKeyword()) + } + +enumValueRanges : enumValueRange { + $$ = &rangeSlices{ranges: []*ast.RangeNode{$1}} + } + | enumValueRanges ',' enumValueRange { + $1.ranges = append($1.ranges, $3) + $1.commas = append($1.commas, $2) + $$ = $1 + } + +enumValueRange : enumValueNumber { + $$ = ast.NewRangeNode($1, nil, nil, nil) + } + | enumValueNumber _TO enumValueNumber { + $$ = ast.NewRangeNode($1, $2.ToKeyword(), $3, nil) + } + | enumValueNumber _TO _MAX { + $$ = ast.NewRangeNode($1, $2.ToKeyword(), nil, $3.ToKeyword()) + } + +enumValueNumber : _INT_LIT { + $$ = $1 + } + | '-' _INT_LIT { + $$ = ast.NewNegativeIntLiteralNode($1, $2) + } + +msgReserved : _RESERVED tagRanges ';' semicolons { + // TODO: Tolerate a missing semicolon here. This currnelty creates a shift/reduce conflict + // between `reserved 1 to 10` and `reserved 1` followed by `to = 10`. + $$ = newNodeWithRunes(ast.NewReservedRangesNode($1.ToKeyword(), $2.ranges, $2.commas, $3), $4...) + } + | reservedNames + +enumReserved : _RESERVED enumValueRanges ';' semicolons { + // TODO: Tolerate a missing semicolon here. This currnelty creates a shift/reduce conflict + // between `reserved 1 to 10` and `reserved 1` followed by `to = 10`. + $$ = newNodeWithRunes(ast.NewReservedRangesNode($1.ToKeyword(), $2.ranges, $2.commas, $3), $4...) + } + | reservedNames + +reservedNames : _RESERVED fieldNameStrings semicolons { + semi, extra := protolex.(*protoLex).requireSemicolon($3) + $$ = newNodeWithRunes(ast.NewReservedNamesNode($1.ToKeyword(), $2.names, $2.commas, semi), extra...) + } + | _RESERVED fieldNameIdents semicolons { + semi, extra := protolex.(*protoLex).requireSemicolon($3) + $$ = newNodeWithRunes(ast.NewReservedIdentifiersNode($1.ToKeyword(), $2.idents, $2.commas, semi), extra...) + } + +fieldNameStrings : stringLit { + $$ = &nameSlices{names: []ast.StringValueNode{toStringValueNode($1)}} + } + | fieldNameStrings ',' stringLit { + $1.names = append($1.names, toStringValueNode($3)) + $1.commas = append($1.commas, $2) + $$ = $1 + } + +fieldNameIdents : identifier { + $$ = &nameSlices{idents: []*ast.IdentNode{$1}} + } + | fieldNameIdents ',' identifier { + $1.idents = append($1.idents, $3) + $1.commas = append($1.commas, $2) + $$ = $1 + } + +enumDecl : _ENUM identifier '{' enumBody '}' semicolons { + $$ = newNodeWithRunes(ast.NewEnumNode($1.ToKeyword(), $2, $3, $4, $5), $6...) + } + +enumBody : semicolons { + $$ = prependRunes(toEnumElement, $1, nil) + } + | semicolons enumElements { + $$ = prependRunes(toEnumElement, $1, $2) + } + +enumElements : enumElements enumElement { + $$ = append($1, $2...) + } + | enumElement { + $$ = $1 + } + +enumElement : optionDecl { + $$ = toElements[ast.EnumElement](toEnumElement, $1.Node, $1.Runes) + } + | enumValueDecl { + $$ = toElements[ast.EnumElement](toEnumElement, $1.Node, $1.Runes) + } + | enumReserved { + $$ = toElements[ast.EnumElement](toEnumElement, $1.Node, $1.Runes) + } + | error { + $$ = nil + } + +enumValueDecl : enumValueName '=' enumValueNumber semicolons { + semi, extra := protolex.(*protoLex).requireSemicolon($4) + $$ = newNodeWithRunes(ast.NewEnumValueNode($1, $2, $3, nil, semi), extra...) + } + | enumValueName '=' enumValueNumber compactOptions semicolons { + semi, extra := protolex.(*protoLex).requireSemicolon($5) + $$ = newNodeWithRunes(ast.NewEnumValueNode($1, $2, $3, $4, semi), extra...) + } + +messageDecl : _MESSAGE identifier '{' messageBody '}' semicolons { + $$ = newNodeWithRunes(ast.NewMessageNode($1.ToKeyword(), $2, $3, $4, $5), $6...) + } + +messageBody : semicolons { + $$ = prependRunes(toMessageElement, $1, nil) + } + | semicolons messageElements { + $$ = prependRunes(toMessageElement, $1, $2) + } + +messageElements : messageElements messageElement { + $$ = append($1, $2...) + } + | messageElement { + $$ = $1 + } + +messageElement : messageFieldDecl { + $$ = toElements[ast.MessageElement](toMessageElement, $1.Node, $1.Runes) + } + | enumDecl { + $$ = toElements[ast.MessageElement](toMessageElement, $1.Node, $1.Runes) + } + | messageDecl { + $$ = toElements[ast.MessageElement](toMessageElement, $1.Node, $1.Runes) + } + | extensionDecl { + $$ = toElements[ast.MessageElement](toMessageElement, $1.Node, $1.Runes) + } + | extensionRangeDecl { + $$ = toElements[ast.MessageElement](toMessageElement, $1.Node, $1.Runes) + } + | messageGroupDecl { + $$ = toElements[ast.MessageElement](toMessageElement, $1.Node, $1.Runes) + } + | optionDecl { + $$ = toElements[ast.MessageElement](toMessageElement, $1.Node, $1.Runes) + } + | oneofDecl { + $$ = toElements[ast.MessageElement](toMessageElement, $1.Node, $1.Runes) + } + | mapFieldDecl { + $$ = toElements[ast.MessageElement](toMessageElement, $1.Node, $1.Runes) + } + | msgReserved { + $$ = toElements[ast.MessageElement](toMessageElement, $1.Node, $1.Runes) + } + | error { + $$ = nil + } + +messageFieldDecl : fieldCardinality notGroupElementTypeIdent identifier '=' _INT_LIT semicolons { + semis, extra := protolex.(*protoLex).requireSemicolon($6) + $$ = newNodeWithRunes(ast.NewFieldNode($1.ToKeyword(), $2, $3, $4, $5, nil, semis), extra...) + } + | fieldCardinality notGroupElementTypeIdent identifier '=' _INT_LIT compactOptions semicolons { + semis, extra := protolex.(*protoLex).requireSemicolon($7) + $$ = newNodeWithRunes(ast.NewFieldNode($1.ToKeyword(), $2, $3, $4, $5, $6, semis), extra...) + } + | msgElementTypeIdent identifier '=' _INT_LIT semicolons { + semis, extra := protolex.(*protoLex).requireSemicolon($5) + $$ = newNodeWithRunes(ast.NewFieldNode(nil, $1, $2, $3, $4, nil, semis), extra...) + } + | msgElementTypeIdent identifier '=' _INT_LIT compactOptions semicolons { + semis, extra := protolex.(*protoLex).requireSemicolon($6) + $$ = newNodeWithRunes(ast.NewFieldNode(nil, $1, $2, $3, $4, $5, semis), extra...) + } + | fieldCardinality notGroupElementTypeIdent identifier semicolons { + semis, extra := protolex.(*protoLex).requireSemicolon($4) + $$ = newNodeWithRunes(ast.NewFieldNode($1.ToKeyword(), $2, $3, nil, nil, nil, semis), extra...) + } + | fieldCardinality notGroupElementTypeIdent identifier compactOptions semicolons { + semis, extra := protolex.(*protoLex).requireSemicolon($5) + $$ = newNodeWithRunes(ast.NewFieldNode($1.ToKeyword(), $2, $3, nil, nil, $4, semis), extra...) + } + | msgElementTypeIdent identifier semicolons { + semis, extra := protolex.(*protoLex).requireSemicolon($3) + $$ = newNodeWithRunes(ast.NewFieldNode(nil, $1, $2, nil, nil, nil, semis), extra...) + } + | msgElementTypeIdent identifier compactOptions semicolons { + semis, extra := protolex.(*protoLex).requireSemicolon($4) + $$ = newNodeWithRunes(ast.NewFieldNode(nil, $1, $2, nil, nil, $3, semis), extra...) + } + + +extensionDecl : _EXTEND typeName '{' extensionBody '}' semicolons { + $$ = newNodeWithRunes(ast.NewExtendNode($1.ToKeyword(), $2, $3, $4, $5), $6...) + } + +extensionBody : { + $$ = nil + } + | extensionElements + +extensionElements : extensionElements extensionElement { + if $2 != nil { + $$ = append($1, $2) + } else { + $$ = $1 + } + } + | extensionElement { + if $1 != nil { + $$ = []ast.ExtendElement{$1} + } else { + $$ = nil + } + } + +extensionElement : extensionFieldDecl { + $$ = $1 + } + | groupDecl { + $$ = $1 + } + | error ';' { + $$ = nil + } + | error { + $$ = nil + } + +extensionFieldDecl : fieldCardinality notGroupElementTypeIdent identifier '=' _INT_LIT semicolon { + $$ = ast.NewFieldNode($1.ToKeyword(), $2, $3, $4, $5, nil, $6) + } + | fieldCardinality notGroupElementTypeIdent identifier '=' _INT_LIT compactOptions semicolon { + $$ = ast.NewFieldNode($1.ToKeyword(), $2, $3, $4, $5, $6, $7) + } + | extElementTypeIdent identifier '=' _INT_LIT semicolon { + $$ = ast.NewFieldNode(nil, $1, $2, $3, $4, nil, $5) + } + | extElementTypeIdent identifier '=' _INT_LIT compactOptions semicolon { + $$ = ast.NewFieldNode(nil, $1, $2, $3, $4, $5, $6) + } + +serviceDecl : _SERVICE identifier '{' serviceBody '}' semicolons { + $$ = newNodeWithRunes(ast.NewServiceNode($1.ToKeyword(), $2, $3, $4, $5), $6...) + } + +serviceBody : semicolons { + $$ = prependRunes(toServiceElement, $1, nil) + } + | semicolons serviceElements { + $$ = prependRunes(toServiceElement, $1, $2) + } + +serviceElements : serviceElements serviceElement { + $$ = append($1, $2...) + } + | serviceElement { + $$ = $1 + } + +// NB: doc suggests support for "stream" declaration, separate from "rpc", but +// it does not appear to be supported in protoc (doc is likely from grammar for +// Google-internal version of protoc, with support for streaming stubby) +serviceElement : optionDecl { + $$ = toElements[ast.ServiceElement](toServiceElement, $1.Node, $1.Runes) + } + | methodDecl { + $$ = toElements[ast.ServiceElement](toServiceElement, $1.Node, $1.Runes) + } + | error { + $$ = nil + } + +methodDecl : _RPC identifier methodMessageType _RETURNS methodMessageType semicolons { + semi, extra := protolex.(*protoLex).requireSemicolon($6) + $$ = newNodeWithRunes(ast.NewRPCNode($1.ToKeyword(), $2, $3, $4.ToKeyword(), $5, semi), extra...) + } + | _RPC identifier methodMessageType _RETURNS methodMessageType '{' methodBody '}' semicolons { + $$ = newNodeWithRunes(ast.NewRPCNodeWithBody($1.ToKeyword(), $2, $3, $4.ToKeyword(), $5, $6, $7, $8), $9...) + } + +methodMessageType : '(' _STREAM typeName ')' { + $$ = ast.NewRPCTypeNode($1, $2.ToKeyword(), $3, $4) + } + | '(' mtdElementTypeIdent ')' { + $$ = ast.NewRPCTypeNode($1, nil, $2, $3) + } + +methodBody : semicolons { + $$ = prependRunes(toMethodElement, $1, nil) + } + | semicolons methodElements { + $$ = prependRunes(toMethodElement, $1, $2) + } + +methodElements : methodElements methodElement { + $$ = append($1, $2...) + } + | methodElement { + $$ = $1 + } + +methodElement : optionDecl { + $$ = toElements[ast.RPCElement](toMethodElement, $1.Node, $1.Runes) + } + | error { + $$ = nil + } + +// excludes message, enum, oneof, extensions, reserved, extend, +// option, group, optional, required, and repeated +msgElementName : _NAME + | _SYNTAX + | _EDITION + | _IMPORT + | _WEAK + | _PUBLIC + | _PACKAGE + | _TRUE + | _FALSE + | _INF + | _NAN + | _DOUBLE + | _FLOAT + | _INT32 + | _INT64 + | _UINT32 + | _UINT64 + | _SINT32 + | _SINT64 + | _FIXED32 + | _FIXED64 + | _SFIXED32 + | _SFIXED64 + | _BOOL + | _STRING + | _BYTES + | _MAP + | _TO + | _MAX + | _SERVICE + | _RPC + | _STREAM + | _RETURNS + +// excludes group, optional, required, and repeated +extElementName : _NAME + | _SYNTAX + | _EDITION + | _IMPORT + | _WEAK + | _PUBLIC + | _PACKAGE + | _OPTION + | _TRUE + | _FALSE + | _INF + | _NAN + | _DOUBLE + | _FLOAT + | _INT32 + | _INT64 + | _UINT32 + | _UINT64 + | _SINT32 + | _SINT64 + | _FIXED32 + | _FIXED64 + | _SFIXED32 + | _SFIXED64 + | _BOOL + | _STRING + | _BYTES + | _ONEOF + | _MAP + | _EXTENSIONS + | _TO + | _MAX + | _RESERVED + | _ENUM + | _MESSAGE + | _EXTEND + | _SERVICE + | _RPC + | _STREAM + | _RETURNS + +// excludes reserved, option +enumValueName : _NAME + | _SYNTAX + | _EDITION + | _IMPORT + | _WEAK + | _PUBLIC + | _PACKAGE + | _TRUE + | _FALSE + | _INF + | _NAN + | _REPEATED + | _OPTIONAL + | _REQUIRED + | _DOUBLE + | _FLOAT + | _INT32 + | _INT64 + | _UINT32 + | _UINT64 + | _SINT32 + | _SINT64 + | _FIXED32 + | _FIXED64 + | _SFIXED32 + | _SFIXED64 + | _BOOL + | _STRING + | _BYTES + | _GROUP + | _ONEOF + | _MAP + | _EXTENSIONS + | _TO + | _MAX + | _ENUM + | _MESSAGE + | _EXTEND + | _SERVICE + | _RPC + | _STREAM + | _RETURNS + +// excludes group, option, optional, required, and repeated +oneofElementName : _NAME + | _SYNTAX + | _EDITION + | _IMPORT + | _WEAK + | _PUBLIC + | _PACKAGE + | _TRUE + | _FALSE + | _INF + | _NAN + | _DOUBLE + | _FLOAT + | _INT32 + | _INT64 + | _UINT32 + | _UINT64 + | _SINT32 + | _SINT64 + | _FIXED32 + | _FIXED64 + | _SFIXED32 + | _SFIXED64 + | _BOOL + | _STRING + | _BYTES + | _ONEOF + | _MAP + | _EXTENSIONS + | _TO + | _MAX + | _RESERVED + | _ENUM + | _MESSAGE + | _EXTEND + | _SERVICE + | _RPC + | _STREAM + | _RETURNS + +// excludes group +notGroupElementName : _NAME + | _SYNTAX + | _EDITION + | _IMPORT + | _WEAK + | _PUBLIC + | _PACKAGE + | _OPTION + | _TRUE + | _FALSE + | _INF + | _NAN + | _REPEATED + | _OPTIONAL + | _REQUIRED + | _DOUBLE + | _FLOAT + | _INT32 + | _INT64 + | _UINT32 + | _UINT64 + | _SINT32 + | _SINT64 + | _FIXED32 + | _FIXED64 + | _SFIXED32 + | _SFIXED64 + | _BOOL + | _STRING + | _BYTES + | _ONEOF + | _MAP + | _EXTENSIONS + | _TO + | _MAX + | _RESERVED + | _ENUM + | _MESSAGE + | _EXTEND + | _SERVICE + | _RPC + | _STREAM + | _RETURNS + +// excludes stream +mtdElementName : _NAME + | _SYNTAX + | _EDITION + | _IMPORT + | _WEAK + | _PUBLIC + | _PACKAGE + | _OPTION + | _TRUE + | _FALSE + | _INF + | _NAN + | _REPEATED + | _OPTIONAL + | _REQUIRED + | _DOUBLE + | _FLOAT + | _INT32 + | _INT64 + | _UINT32 + | _UINT64 + | _SINT32 + | _SINT64 + | _FIXED32 + | _FIXED64 + | _SFIXED32 + | _SFIXED64 + | _BOOL + | _STRING + | _BYTES + | _GROUP + | _ONEOF + | _MAP + | _EXTENSIONS + | _TO + | _MAX + | _RESERVED + | _ENUM + | _MESSAGE + | _EXTEND + | _SERVICE + | _RPC + | _RETURNS + +identifier : _NAME + | _SYNTAX + | _EDITION + | _IMPORT + | _WEAK + | _PUBLIC + | _PACKAGE + | _OPTION + | _TRUE + | _FALSE + | _INF + | _NAN + | _REPEATED + | _OPTIONAL + | _REQUIRED + | _DOUBLE + | _FLOAT + | _INT32 + | _INT64 + | _UINT32 + | _UINT64 + | _SINT32 + | _SINT64 + | _FIXED32 + | _FIXED64 + | _SFIXED32 + | _SFIXED64 + | _BOOL + | _STRING + | _BYTES + | _GROUP + | _ONEOF + | _MAP + | _EXTENSIONS + | _TO + | _MAX + | _RESERVED + | _ENUM + | _MESSAGE + | _EXTEND + | _SERVICE + | _RPC + | _STREAM + | _RETURNS + +%% diff --git a/vendor/github.com/bufbuild/protocompile/parser/proto.y.go b/vendor/github.com/bufbuild/protocompile/parser/proto.y.go new file mode 100644 index 0000000..048e5cc --- /dev/null +++ b/vendor/github.com/bufbuild/protocompile/parser/proto.y.go @@ -0,0 +1,2659 @@ +// Copyright 2020-2024 Buf Technologies, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Code generated by goyacc -o proto.y.go -l -p proto proto.y. DO NOT EDIT. +package parser + +import __yyfmt__ "fmt" + +//lint:file-ignore SA4006 generated parser has unused values + +import ( + "math" + "strings" + + "github.com/bufbuild/protocompile/ast" +) + +type protoSymType struct { + yys int + file *ast.FileNode + syn *ast.SyntaxNode + ed *ast.EditionNode + fileElements []ast.FileElement + pkg nodeWithRunes[*ast.PackageNode] + imprt nodeWithRunes[*ast.ImportNode] + msg nodeWithRunes[*ast.MessageNode] + msgElements []ast.MessageElement + fld *ast.FieldNode + msgFld nodeWithRunes[*ast.FieldNode] + mapFld nodeWithRunes[*ast.MapFieldNode] + mapType *ast.MapTypeNode + grp *ast.GroupNode + msgGrp nodeWithRunes[*ast.GroupNode] + oo nodeWithRunes[*ast.OneofNode] + ooElement ast.OneofElement + ooElements []ast.OneofElement + ext nodeWithRunes[*ast.ExtensionRangeNode] + resvd nodeWithRunes[*ast.ReservedNode] + en nodeWithRunes[*ast.EnumNode] + enElements []ast.EnumElement + env nodeWithRunes[*ast.EnumValueNode] + extend nodeWithRunes[*ast.ExtendNode] + extElement ast.ExtendElement + extElements []ast.ExtendElement + svc nodeWithRunes[*ast.ServiceNode] + svcElements []ast.ServiceElement + mtd nodeWithRunes[*ast.RPCNode] + mtdMsgType *ast.RPCTypeNode + mtdElements []ast.RPCElement + optRaw *ast.OptionNode + opt nodeWithRunes[*ast.OptionNode] + opts *compactOptionSlices + refRaw *ast.FieldReferenceNode + ref nodeWithRunes[*ast.FieldReferenceNode] + optNms *fieldRefSlices + cmpctOpts *ast.CompactOptionsNode + rng *ast.RangeNode + rngs *rangeSlices + names *nameSlices + cidPart nodeWithRunes[*ast.IdentNode] + cid *identSlices + tid ast.IdentValueNode + sl *valueSlices + msgLitFlds *messageFieldList + msgLitFld *ast.MessageFieldNode + v ast.ValueNode + il ast.IntValueNode + str []*ast.StringLiteralNode + s *ast.StringLiteralNode + i *ast.UintLiteralNode + f *ast.FloatLiteralNode + id *ast.IdentNode + b *ast.RuneNode + bs []*ast.RuneNode + err error +} + +const _STRING_LIT = 57346 +const _INT_LIT = 57347 +const _FLOAT_LIT = 57348 +const _NAME = 57349 +const _SYNTAX = 57350 +const _EDITION = 57351 +const _IMPORT = 57352 +const _WEAK = 57353 +const _PUBLIC = 57354 +const _PACKAGE = 57355 +const _OPTION = 57356 +const _TRUE = 57357 +const _FALSE = 57358 +const _INF = 57359 +const _NAN = 57360 +const _REPEATED = 57361 +const _OPTIONAL = 57362 +const _REQUIRED = 57363 +const _DOUBLE = 57364 +const _FLOAT = 57365 +const _INT32 = 57366 +const _INT64 = 57367 +const _UINT32 = 57368 +const _UINT64 = 57369 +const _SINT32 = 57370 +const _SINT64 = 57371 +const _FIXED32 = 57372 +const _FIXED64 = 57373 +const _SFIXED32 = 57374 +const _SFIXED64 = 57375 +const _BOOL = 57376 +const _STRING = 57377 +const _BYTES = 57378 +const _GROUP = 57379 +const _ONEOF = 57380 +const _MAP = 57381 +const _EXTENSIONS = 57382 +const _TO = 57383 +const _MAX = 57384 +const _RESERVED = 57385 +const _ENUM = 57386 +const _MESSAGE = 57387 +const _EXTEND = 57388 +const _SERVICE = 57389 +const _RPC = 57390 +const _STREAM = 57391 +const _RETURNS = 57392 +const _ERROR = 57393 + +var protoToknames = [...]string{ + "$end", + "error", + "$unk", + "_STRING_LIT", + "_INT_LIT", + "_FLOAT_LIT", + "_NAME", + "_SYNTAX", + "_EDITION", + "_IMPORT", + "_WEAK", + "_PUBLIC", + "_PACKAGE", + "_OPTION", + "_TRUE", + "_FALSE", + "_INF", + "_NAN", + "_REPEATED", + "_OPTIONAL", + "_REQUIRED", + "_DOUBLE", + "_FLOAT", + "_INT32", + "_INT64", + "_UINT32", + "_UINT64", + "_SINT32", + "_SINT64", + "_FIXED32", + "_FIXED64", + "_SFIXED32", + "_SFIXED64", + "_BOOL", + "_STRING", + "_BYTES", + "_GROUP", + "_ONEOF", + "_MAP", + "_EXTENSIONS", + "_TO", + "_MAX", + "_RESERVED", + "_ENUM", + "_MESSAGE", + "_EXTEND", + "_SERVICE", + "_RPC", + "_STREAM", + "_RETURNS", + "_ERROR", + "'='", + "';'", + "':'", + "'{'", + "'}'", + "'\\\\'", + "'/'", + "'?'", + "'.'", + "','", + "'>'", + "'<'", + "'+'", + "'-'", + "'('", + "')'", + "'['", + "']'", + "'*'", + "'&'", + "'^'", + "'%'", + "'$'", + "'#'", + "'@'", + "'!'", + "'~'", + "'`'", +} + +var protoStatenames = [...]string{} + +const protoEofCode = 1 +const protoErrCode = 2 +const protoInitialStackSize = 16 + +var protoExca = [...]int16{ + -1, 0, + 1, 6, + -2, 21, + -1, 1, + 1, -1, + -2, 0, + -1, 2, + 1, 1, + -2, 21, + -1, 3, + 1, 2, + -2, 21, + -1, 14, + 1, 7, + -2, 0, + -1, 89, + 52, 60, + 61, 60, + 69, 60, + -2, 61, + -1, 101, + 55, 37, + 58, 37, + 62, 37, + 67, 37, + 69, 37, + -2, 34, + -1, 112, + 52, 60, + 61, 60, + 69, 60, + -2, 62, + -1, 118, + 56, 249, + -2, 0, + -1, 121, + 55, 37, + 58, 37, + 62, 37, + 67, 37, + 69, 37, + -2, 35, + -1, 140, + 56, 225, + -2, 0, + -1, 142, + 56, 214, + -2, 0, + -1, 144, + 56, 250, + -2, 0, + -1, 198, + 56, 262, + -2, 0, + -1, 203, + 56, 83, + 62, 83, + -2, 0, + -1, 214, + 56, 226, + -2, 0, + -1, 271, + 56, 215, + -2, 0, + -1, 377, + 56, 263, + -2, 0, + -1, 464, + 56, 155, + -2, 0, + -1, 523, + 69, 144, + -2, 141, + -1, 531, + 56, 156, + -2, 0, + -1, 607, + 67, 52, + -2, 49, + -1, 665, + 69, 144, + -2, 142, + -1, 690, + 67, 52, + -2, 50, + -1, 732, + 56, 273, + -2, 0, + -1, 745, + 56, 274, + -2, 0, +} + +const protoPrivate = 57344 + +const protoLast = 2053 + +var protoAct = [...]int16{ + 140, 7, 746, 7, 7, 100, 139, 18, 440, 394, + 604, 436, 607, 439, 502, 39, 524, 596, 95, 532, + 496, 127, 437, 422, 520, 200, 32, 34, 523, 233, + 421, 40, 90, 93, 94, 405, 102, 106, 36, 96, + 109, 435, 272, 85, 378, 458, 326, 404, 21, 20, + 19, 107, 108, 149, 215, 202, 145, 98, 101, 86, + 663, 89, 449, 390, 134, 706, 703, 598, 707, 513, + 9, 652, 395, 510, 465, 9, 511, 396, 717, 651, + 507, 459, 459, 460, 452, 459, 456, 9, 506, 459, + 459, 462, 739, 90, 693, 451, 655, 598, 459, 9, + 680, 653, 459, 687, 508, 459, 423, 459, 124, 125, + 453, 115, 459, 459, 459, 134, 126, 133, 142, 138, + 131, 129, 497, 395, 198, 130, 423, 134, 199, 448, + 416, 388, 389, 711, 489, 395, 505, 119, 9, 387, + 207, 666, 488, 593, 9, 468, 472, 113, 222, 112, + 273, 386, 470, 462, 587, 9, 373, 120, 121, 385, + 110, 40, 110, 691, 674, 428, 424, 414, 374, 122, + 114, 375, 279, 760, 758, 754, 750, 104, 744, 743, + 741, 733, 729, 721, 695, 9, 424, 716, 753, 219, + 217, 218, 668, 383, 227, 376, 322, 270, 213, 728, + 719, 323, 713, 658, 464, 123, 379, 118, 117, 207, + 116, 5, 6, 104, 399, 9, 598, 104, 670, 324, + 31, 702, 222, 667, 493, 490, 9, 492, 430, 392, + 419, 111, 13, 12, 403, 599, 407, 408, 413, 528, + 463, 40, 381, 748, 726, 8, 412, 724, 397, 659, + 33, 415, 15, 656, 26, 26, 9, 37, 38, 384, + 210, 209, 105, 219, 217, 218, 103, 35, 227, 400, + 595, 417, 211, 212, 402, 23, 529, 594, 104, 273, + 409, 582, 406, 24, 413, 516, 25, 26, 382, 495, + 491, 4, 412, 33, 10, 11, 731, 745, 380, 197, + 377, 279, 475, 476, 477, 478, 479, 480, 481, 482, + 483, 484, 485, 486, 418, 22, 143, 28, 27, 29, + 30, 144, 274, 425, 141, 271, 220, 420, 275, 225, + 411, 426, 427, 410, 40, 530, 531, 214, 231, 224, + 221, 535, 147, 223, 429, 146, 534, 216, 204, 203, + 447, 499, 601, 538, 150, 228, 605, 99, 602, 327, + 540, 154, 234, 277, 606, 329, 542, 156, 237, 474, + 391, 393, 438, 132, 128, 87, 88, 432, 206, 91, + 431, 521, 518, 533, 522, 379, 17, 16, 434, 14, + 3, 2, 1, 0, 0, 442, 442, 0, 0, 0, + 0, 207, 0, 0, 457, 0, 0, 454, 455, 466, + 0, 469, 471, 0, 0, 0, 0, 0, 0, 450, + 473, 445, 433, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 444, 0, 494, 0, 0, 0, 0, 0, + 0, 0, 0, 487, 0, 0, 0, 498, 0, 442, + 461, 0, 0, 0, 467, 503, 514, 0, 0, 517, + 0, 525, 526, 0, 0, 90, 504, 0, 583, 584, + 0, 0, 0, 0, 0, 0, 0, 0, 586, 0, + 0, 0, 0, 0, 585, 0, 0, 0, 588, 0, + 591, 0, 509, 0, 0, 0, 0, 0, 527, 0, + 512, 515, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 592, 0, 660, 661, 657, 590, + 0, 0, 0, 0, 0, 0, 0, 90, 0, 0, + 654, 0, 0, 589, 0, 0, 0, 0, 0, 0, + 0, 597, 0, 90, 672, 673, 664, 40, 0, 0, + 665, 669, 0, 0, 671, 0, 0, 675, 0, 0, + 0, 0, 662, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 676, 0, 0, 0, 0, 0, 0, + 679, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 678, 0, 0, 0, + 0, 0, 0, 682, 0, 684, 689, 0, 690, 686, + 685, 0, 0, 0, 0, 0, 0, 0, 677, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 681, 683, 0, 688, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 442, 0, + 697, 0, 0, 699, 503, 696, 0, 692, 0, 701, + 0, 0, 0, 133, 0, 504, 131, 129, 710, 0, + 709, 130, 0, 0, 0, 0, 715, 712, 0, 700, + 704, 0, 0, 0, 0, 0, 720, 0, 0, 722, + 718, 714, 694, 0, 0, 698, 0, 0, 133, 0, + 0, 131, 129, 0, 727, 0, 130, 732, 705, 708, + 730, 0, 735, 725, 723, 0, 734, 0, 0, 0, + 0, 0, 0, 0, 749, 742, 0, 0, 0, 0, + 747, 736, 737, 0, 0, 755, 752, 0, 756, 0, + 0, 757, 0, 747, 0, 0, 751, 0, 0, 0, + 759, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 738, 501, 740, 33, 137, 135, 41, 42, 43, 44, + 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, + 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, + 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, + 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, + 0, 0, 0, 0, 134, 0, 0, 0, 0, 0, + 0, 0, 395, 0, 441, 0, 0, 0, 500, 33, + 137, 135, 41, 42, 43, 44, 45, 46, 47, 48, + 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, + 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, + 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, + 79, 80, 81, 82, 83, 84, 0, 0, 0, 0, + 134, 0, 0, 0, 0, 0, 0, 0, 395, 0, + 441, 0, 0, 443, 33, 137, 135, 41, 42, 43, + 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, + 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, + 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, + 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, + 84, 0, 0, 0, 0, 134, 0, 0, 0, 0, + 0, 0, 0, 395, 0, 441, 41, 42, 43, 44, + 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, + 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, + 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, + 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 205, 92, 0, 0, 519, 41, + 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, + 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, + 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, + 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, + 82, 83, 84, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 446, 0, 205, 0, 0, 0, + 208, 41, 42, 43, 44, 45, 46, 47, 48, 49, + 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, + 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, + 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, + 80, 81, 82, 83, 84, 0, 0, 0, 0, 0, + 201, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 208, 33, 137, 135, 41, 42, 43, 44, + 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, + 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, + 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, + 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, + 0, 0, 0, 0, 134, 0, 0, 0, 0, 0, + 205, 0, 0, 0, 136, 41, 42, 43, 44, 45, + 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, + 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, + 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, + 76, 77, 78, 79, 80, 81, 82, 83, 84, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 33, 423, 208, 41, 42, 43, + 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, + 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, + 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, + 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, + 84, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 424, 41, 42, 43, 44, + 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, + 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, + 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, + 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 92, 41, 42, 43, 44, + 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, + 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, + 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, + 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 97, + 608, 609, 610, 611, 612, 613, 614, 615, 616, 617, + 618, 619, 620, 621, 622, 623, 624, 625, 626, 627, + 628, 629, 630, 631, 632, 633, 634, 635, 636, 637, + 638, 639, 640, 641, 642, 643, 644, 645, 646, 647, + 648, 649, 600, 650, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 603, 330, 331, 332, 333, 334, 335, + 336, 337, 338, 339, 340, 341, 342, 343, 344, 345, + 346, 347, 348, 349, 350, 351, 352, 353, 354, 355, + 356, 357, 358, 359, 401, 360, 361, 362, 363, 364, + 365, 366, 367, 368, 369, 370, 371, 372, 0, 0, + 0, 0, 0, 226, 0, 0, 0, 328, 238, 239, + 240, 241, 242, 243, 244, 26, 245, 246, 247, 248, + 153, 152, 151, 249, 250, 251, 252, 253, 254, 255, + 256, 257, 258, 259, 260, 261, 262, 263, 0, 230, + 236, 229, 264, 265, 232, 28, 27, 29, 266, 267, + 268, 269, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 235, 330, 331, 332, 333, 334, 335, 336, 337, + 338, 339, 340, 341, 342, 343, 344, 345, 346, 347, + 348, 349, 350, 351, 352, 353, 354, 355, 356, 357, + 358, 359, 325, 360, 361, 362, 363, 364, 365, 366, + 367, 368, 369, 370, 371, 372, 0, 0, 0, 0, + 0, 148, 0, 0, 0, 328, 157, 158, 159, 160, + 161, 162, 163, 164, 165, 166, 167, 168, 153, 152, + 151, 169, 170, 171, 172, 173, 174, 175, 176, 177, + 178, 179, 180, 181, 182, 183, 0, 184, 185, 186, + 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, + 0, 0, 0, 0, 0, 536, 0, 0, 0, 155, + 543, 544, 545, 546, 547, 548, 549, 537, 550, 551, + 552, 553, 0, 0, 0, 554, 555, 556, 557, 558, + 559, 560, 561, 562, 563, 564, 565, 566, 567, 568, + 539, 569, 570, 571, 572, 573, 574, 575, 576, 577, + 578, 579, 580, 581, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 541, 210, 209, 41, 42, 43, 44, + 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, + 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, + 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, + 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, + 33, 406, 0, 41, 42, 43, 44, 45, 46, 47, + 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, + 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, + 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, + 78, 79, 80, 81, 82, 83, 84, 276, 0, 0, + 0, 0, 280, 281, 282, 283, 284, 285, 286, 26, + 287, 288, 289, 290, 291, 292, 293, 294, 295, 296, + 297, 298, 299, 300, 301, 302, 303, 304, 305, 306, + 307, 308, 309, 310, 311, 312, 313, 314, 278, 315, + 316, 317, 318, 319, 320, 321, 398, 0, 0, 0, + 0, 41, 42, 43, 44, 45, 46, 47, 48, 49, + 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, + 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, + 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, + 80, 81, 82, 83, 84, 41, 42, 43, 44, 45, + 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, + 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, + 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, + 76, 77, 78, 79, 80, 81, 82, 83, 84, 608, + 609, 610, 611, 612, 613, 614, 615, 616, 617, 618, + 619, 620, 621, 622, 623, 624, 625, 626, 627, 628, + 629, 630, 631, 632, 633, 634, 635, 636, 637, 638, + 639, 640, 641, 642, 643, 644, 645, 646, 647, 648, + 649, 0, 650, +} + +var protoPact = [...]int16{ + 203, -1000, 162, 162, -1000, 181, 180, 273, 167, -1000, + -1000, -1000, 289, 289, 273, -1000, -1000, -1000, -1000, -1000, + -1000, -1000, -1000, -1000, 246, 1958, 1329, 1958, 1958, 1389, + 1958, -1000, 213, -1000, 209, -1000, 173, 289, 289, 102, + -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, + -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, + -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, + -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, + -1000, -1000, -1000, -1000, -1000, 179, -1000, 1329, 110, -1000, + -1000, -1000, 1389, 155, 153, 152, -1000, 1958, -1000, 1958, + 109, -1000, 150, -1000, -1000, -1000, -1000, 173, 173, -1000, + 1958, 1149, -1000, -1000, -1000, 52, 162, 162, 1659, -1000, + -1000, -1000, -1000, 162, -1000, -1000, -1000, 162, -1000, -1000, + 274, -1000, -1000, -1000, 1084, -1000, 255, -1000, -1000, 142, + 1551, 141, 1865, 140, 1659, -1000, -1000, -1000, 166, 1605, + 1958, -1000, -1000, -1000, 108, 1958, -1000, -1000, -1000, -1000, + -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, + -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, + -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, + -1000, -1000, -1000, -1000, -1000, -1000, -1000, 139, 240, -1000, + 137, -1000, -1000, 1208, 98, 78, 9, -1000, 1914, -1000, + -1000, -1000, -1000, 162, 1551, -1000, -1000, -1000, -1000, -1000, + -1000, -1000, -1000, -1000, -1000, -1000, -1000, 1497, 1958, 277, + 1958, 1958, 1816, -1000, 107, 1958, 67, -1000, -1000, -1000, + -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, + -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, + -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, + 162, 1865, -1000, -1000, -1000, -1000, -1000, 178, 1270, -1000, + -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, + -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, + -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, + -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, + -1000, -1000, 162, -1000, -1000, 1958, 1958, 105, 1958, -1000, + -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, + -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, + -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, + -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, + -1000, -1000, -1000, 176, 1958, 100, 162, 240, -1000, -1000, + -1000, -1000, 1958, -1000, -1000, -1000, -1000, -1000, -1000, 835, + 835, -1000, -1000, -1000, -1000, 1022, 60, 26, 41, -1000, + -1000, 1958, 1958, 34, 30, -1000, 199, 149, 22, 92, + 91, 85, 274, -1000, 1958, 100, 278, -1000, -1000, 121, + 81, -1000, 184, -1000, 285, -1000, 175, 172, 1958, 100, + 284, -1000, -1000, -1000, 56, -1000, -1000, -1000, -1000, 274, + -1000, 1769, -1000, 769, -1000, 74, -1000, 19, -1000, 35, + -1000, -1000, 1958, -1000, 21, 17, 280, -1000, 162, 959, + 162, 162, 277, 234, 1713, 276, -1000, 162, 162, -1000, + 289, -1000, 1958, -1000, 93, -1000, -1000, -1000, -1000, -1000, + -1000, -1000, -1000, -1000, -1000, -1000, -1000, 46, 121, 162, + 101, -1000, 272, 265, -1000, 44, 185, 1443, -1000, 10, + -1000, 32, -1000, -1000, -1000, -1000, -1000, 72, -1000, 27, + 248, 162, 148, 244, -1000, 162, 46, -1000, -9, -1000, + -1000, 1329, 80, -1000, 171, -1000, -1000, -1000, -1000, -1000, + 136, 1713, -1000, -1000, -1000, -1000, 165, 1329, 1958, 1958, + 104, 1958, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, + -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, + -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, + -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, + -1000, -1000, 46, -1000, -1000, 274, -1000, 1389, -1000, 162, + -1000, -1000, -1000, -1000, 45, 44, -1000, 163, -1000, 56, + 1389, 36, -1000, 1958, -1000, 2002, 103, -1000, -1000, -1000, + -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, + -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, + -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, + -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, + -1000, -1000, 900, -1000, -1000, -1000, 39, 128, 162, 46, + -1000, -1000, 162, -1000, -1000, -1000, -1000, 1149, 162, -1000, + -1000, 169, 14, 13, 1958, 100, -1000, 162, 71, -1000, + 162, 147, -1000, 163, -1000, 132, 11, -1000, -1000, -1000, + -1000, -1000, -1000, 162, 145, 162, 127, -1000, 162, -1000, + -1000, -1000, 1149, 242, -1000, 163, 239, 162, 144, -1000, + -1000, -1000, 126, 162, -1000, -1000, 162, -1000, 125, 162, + -1000, 162, -1000, 163, 44, -1000, 37, 124, 162, -1000, + 123, 122, 241, 162, 120, -1000, -1000, -1000, 163, 162, + 133, -1000, 119, -1000, 162, 241, -1000, -1000, -1000, -1000, + 162, -1000, 118, 162, -1000, -1000, -1000, -1000, -1000, 117, + -1000, +} + +var protoPgo = [...]int16{ + 0, 392, 391, 390, 291, 252, 389, 387, 386, 384, + 383, 7, 28, 24, 382, 381, 379, 378, 376, 61, + 59, 16, 375, 45, 41, 21, 374, 11, 9, 22, + 8, 373, 372, 14, 371, 370, 23, 5, 369, 368, + 367, 366, 365, 364, 363, 53, 58, 57, 12, 10, + 15, 362, 361, 360, 359, 358, 39, 357, 356, 18, + 355, 354, 353, 46, 352, 351, 350, 349, 55, 25, + 348, 347, 346, 345, 343, 342, 341, 340, 339, 338, + 50, 54, 337, 6, 19, 336, 335, 333, 330, 329, + 328, 29, 35, 30, 47, 327, 326, 49, 42, 325, + 324, 322, 48, 56, 321, 316, 13, 315, 44, 300, + 299, 298, 2, 297, 296, 20, 17, 0, 245, +} + +var protoR1 = [...]int8{ + 0, 1, 1, 1, 1, 1, 1, 4, 6, 6, + 5, 5, 5, 5, 5, 5, 5, 5, 118, 118, + 117, 117, 116, 116, 2, 3, 7, 7, 7, 8, + 50, 50, 56, 56, 57, 57, 47, 47, 46, 51, + 51, 52, 52, 53, 53, 54, 54, 55, 55, 58, + 58, 49, 49, 48, 10, 11, 18, 18, 19, 20, + 20, 22, 22, 21, 21, 16, 25, 25, 26, 26, + 26, 26, 30, 30, 30, 30, 31, 31, 106, 106, + 28, 28, 69, 68, 68, 67, 67, 67, 67, 67, + 67, 70, 70, 70, 17, 17, 17, 17, 24, 24, + 24, 27, 27, 27, 27, 35, 35, 29, 29, 29, + 32, 32, 32, 65, 65, 33, 33, 34, 34, 34, + 66, 66, 59, 59, 60, 60, 61, 61, 62, 62, + 63, 63, 64, 64, 45, 45, 45, 23, 23, 14, + 14, 15, 15, 13, 13, 12, 9, 9, 75, 75, + 77, 77, 77, 77, 74, 86, 86, 85, 85, 84, + 84, 84, 84, 84, 72, 72, 72, 72, 76, 76, + 76, 76, 78, 78, 78, 78, 79, 38, 38, 38, + 38, 38, 38, 38, 38, 38, 38, 38, 38, 96, + 96, 94, 94, 92, 92, 92, 95, 95, 93, 93, + 93, 36, 36, 89, 89, 90, 90, 91, 91, 87, + 87, 88, 88, 97, 100, 100, 99, 99, 98, 98, + 98, 98, 101, 101, 80, 83, 83, 82, 82, 81, + 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, + 71, 71, 71, 71, 71, 71, 71, 71, 102, 105, + 105, 104, 104, 103, 103, 103, 103, 73, 73, 73, + 73, 107, 110, 110, 109, 109, 108, 108, 108, 111, + 111, 115, 115, 114, 114, 113, 113, 112, 112, 39, + 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, + 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, + 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, + 39, 39, 40, 40, 40, 40, 40, 40, 40, 40, + 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, + 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, + 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, + 40, 40, 44, 44, 44, 44, 44, 44, 44, 44, + 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, + 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, + 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, + 44, 44, 44, 44, 41, 41, 41, 41, 41, 41, + 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, + 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, + 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, + 41, 41, 41, 42, 42, 42, 42, 42, 42, 42, + 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, + 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, + 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, + 42, 42, 42, 42, 42, 42, 43, 43, 43, 43, + 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, + 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, + 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, + 43, 43, 43, 43, 43, 43, 43, 43, 43, 37, + 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, + 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, + 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, + 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, + 37, 37, 37, +} + +var protoR2 = [...]int8{ + 0, 1, 1, 1, 2, 2, 0, 2, 2, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, + 1, 0, 1, 0, 4, 4, 3, 4, 4, 3, + 1, 3, 1, 2, 1, 2, 1, 1, 2, 1, + 3, 1, 3, 1, 3, 1, 3, 1, 2, 1, + 2, 1, 1, 2, 5, 5, 1, 1, 2, 1, + 1, 1, 2, 1, 2, 3, 1, 1, 1, 1, + 1, 1, 1, 2, 1, 2, 2, 2, 1, 2, + 3, 2, 1, 1, 2, 1, 2, 2, 2, 2, + 1, 3, 2, 3, 1, 3, 5, 3, 1, 1, + 1, 1, 1, 2, 1, 1, 1, 1, 3, 2, + 3, 2, 3, 1, 3, 1, 1, 3, 2, 3, + 1, 3, 1, 2, 1, 2, 1, 2, 1, 2, + 1, 2, 1, 2, 1, 1, 1, 3, 2, 1, + 2, 1, 2, 1, 1, 2, 3, 1, 8, 9, + 9, 10, 7, 8, 6, 0, 1, 2, 1, 1, + 1, 1, 2, 1, 5, 6, 3, 4, 7, 8, + 5, 6, 5, 6, 3, 4, 6, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 4, + 4, 1, 3, 1, 3, 3, 1, 3, 1, 3, + 3, 1, 2, 4, 1, 4, 1, 3, 3, 1, + 3, 1, 3, 6, 1, 2, 2, 1, 1, 1, + 1, 1, 4, 5, 6, 1, 2, 2, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 6, 7, 5, 6, 4, 5, 3, 4, 6, 0, + 1, 2, 1, 1, 1, 2, 1, 6, 7, 5, + 6, 6, 1, 2, 2, 1, 1, 1, 1, 6, + 9, 4, 3, 1, 2, 2, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, +} + +var protoChk = [...]int16{ + -1000, -1, -2, -3, -4, 8, 9, -117, -118, 53, + -4, -4, 52, 52, -6, -5, -7, -8, -11, -80, + -97, -102, -107, 2, 10, 13, 14, 45, 44, 46, + 47, 53, -106, 4, -106, -5, -106, 11, 12, -50, + -37, 7, 8, 9, 10, 11, 12, 13, 14, 15, + 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, + 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, + 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, + 46, 47, 48, 49, 50, -21, -20, -22, -18, -19, + -37, -16, 66, -37, -37, -59, -56, 60, -47, -57, + -37, -46, -37, 53, 4, 53, -117, -106, -106, -117, + 60, 52, -19, -20, 60, -59, 55, 55, 55, -56, + -47, -46, 60, 55, -117, -117, -37, -25, -26, -28, + -106, -30, -31, -37, 55, 6, 65, 5, 67, -83, + -117, -100, -117, -105, -104, -103, -73, -75, 2, -45, + -61, 21, 20, 19, -52, 60, -40, 7, 8, 9, + 10, 11, 12, 13, 14, 15, 16, 17, 18, 22, + 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, + 33, 34, 35, 36, 38, 39, 40, 41, 42, 43, + 44, 45, 46, 47, 48, 49, 50, -110, -117, -117, + -69, 56, -68, -67, -70, 2, -17, -37, 68, 6, + 5, 17, 18, 56, -82, -81, -71, -97, -80, -102, + -96, -77, -11, -74, -78, -89, 2, -45, -60, 40, + 38, -79, 43, -91, -51, 60, 39, -39, 7, 8, + 9, 10, 11, 12, 13, 15, 16, 17, 18, 22, + 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, + 33, 34, 35, 36, 41, 42, 47, 48, 49, 50, + 56, -99, -98, -11, -101, -90, 2, -44, 43, -91, + 7, 8, 9, 10, 11, 12, 13, 15, 16, 17, + 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, + 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, + 38, 39, 40, 41, 42, 44, 45, 46, 47, 48, + 49, 50, 56, -103, 53, 37, -63, -54, 60, -42, + 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, + 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, + 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, + 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, + 48, 49, 50, -37, 60, -50, 56, -109, -108, -11, + -111, 2, 48, 56, -68, 61, 53, 61, 53, 54, + 54, -35, -29, -34, -28, 63, 68, -56, 2, -117, + -81, 37, -63, -37, -94, -92, 5, -37, -37, -94, + -87, -88, -106, -37, 60, -50, 63, -117, -98, 52, + -95, -93, -36, 5, 65, -117, -37, -37, 60, -50, + 52, -37, -117, -108, -37, -24, -27, -29, -32, -106, + -30, 65, -37, 68, -24, -69, 62, -66, 69, 2, + -29, 69, 58, 69, -37, -37, 52, -117, -23, 68, + 53, -23, 61, 41, 55, 52, -117, -23, 53, -117, + 61, -117, 61, -37, -38, 24, 25, 26, 27, 28, + 29, 30, 31, 32, 33, 34, 35, -36, 61, 53, + 41, 5, 52, 52, -37, 5, -115, 66, -37, -65, + 69, 2, -33, -27, -29, 62, 69, 61, 69, -56, + 52, 55, -23, 52, -117, -23, 5, -117, -14, 69, + -13, -15, -9, -12, -21, -117, -117, -92, 5, 42, + -86, -85, -84, -10, -72, -76, 2, 14, -62, 37, + -53, 60, -41, 7, 8, 9, 10, 11, 12, 13, + 15, 16, 17, 18, 22, 23, 24, 25, 26, 27, + 28, 29, 30, 31, 32, 33, 34, 35, 36, 38, + 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, + 49, 50, 5, -117, -117, -106, -37, 61, -117, -23, + -93, -117, -36, 42, 5, 5, -116, -23, 53, 50, + 49, -64, -55, 60, -49, -58, -43, -48, 7, 8, + 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, + 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, + 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, + 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, + 50, 69, 61, 69, -29, 69, 5, -83, 55, 5, + -117, -117, -23, 69, -13, -12, 61, 52, 56, -84, + 53, -21, -37, -37, 60, -50, -117, -23, -59, -117, + 55, -23, -116, -23, -116, -115, -59, 67, -56, -49, + -48, 60, -33, 55, -23, 56, -83, -117, -23, -117, + -25, -117, 52, 52, -116, -23, 52, 55, -23, -37, + -117, 62, -83, 55, -116, -117, 55, 67, -83, 55, + -117, 56, -117, -25, 5, -116, 5, -83, 55, 56, + -83, -114, -117, 56, -83, -117, -116, -116, -23, 55, + -23, 56, -83, 56, 56, -113, -112, -11, 2, -117, + 56, -116, -83, 55, 56, -117, -112, -117, 56, -83, + 56, +} + +var protoDef = [...]int16{ + -2, -2, -2, -2, 3, 0, 0, 0, 20, 18, + 4, 5, 0, 0, -2, 9, 10, 11, 12, 13, + 14, 15, 16, 17, 0, 0, 0, 0, 0, 0, + 0, 19, 0, 78, 0, 8, 21, 0, 0, 21, + 30, 519, 520, 521, 522, 523, 524, 525, 526, 527, + 528, 529, 530, 531, 532, 533, 534, 535, 536, 537, + 538, 539, 540, 541, 542, 543, 544, 545, 546, 547, + 548, 549, 550, 551, 552, 553, 554, 555, 556, 557, + 558, 559, 560, 561, 562, 0, 63, 0, 59, -2, + 56, 57, 0, 0, 0, 0, 122, 0, 32, 0, + 36, -2, 0, 24, 79, 25, 26, 21, 21, 29, + 0, 0, -2, 64, 58, 0, 21, 21, -2, 123, + 33, -2, 38, 21, 27, 28, 31, 21, 66, 67, + 68, 69, 70, 71, 0, 72, 0, 74, 65, 0, + -2, 0, -2, 0, -2, 252, 253, 254, 256, 0, + 0, 134, 135, 136, 126, 0, 41, 312, 313, 314, + 315, 316, 317, 318, 319, 320, 321, 322, 323, 324, + 325, 326, 327, 328, 329, 330, 331, 332, 333, 334, + 335, 336, 337, 338, 339, 340, 341, 342, 343, 344, + 345, 346, 347, 348, 349, 350, 351, 0, -2, 55, + 0, 81, 82, -2, 85, 90, 0, 94, 0, 73, + 75, 76, 77, 21, -2, 228, 229, 230, 231, 232, + 233, 234, 235, 236, 237, 238, 239, 0, 0, 0, + 0, 0, 0, 204, 124, 0, 305, 39, 279, 280, + 281, 282, 283, 284, 285, 286, 287, 288, 289, 290, + 291, 292, 293, 294, 295, 296, 297, 298, 299, 300, + 301, 302, 303, 304, 306, 307, 308, 309, 310, 311, + 21, -2, 217, 218, 219, 220, 221, 0, 0, 206, + 352, 353, 354, 355, 356, 357, 358, 359, 360, 361, + 362, 363, 364, 365, 366, 367, 368, 369, 370, 371, + 372, 373, 374, 375, 376, 377, 378, 379, 380, 381, + 382, 383, 384, 385, 386, 387, 388, 389, 390, 391, + 392, 393, 21, 251, 255, 0, 0, 130, 0, 45, + 433, 434, 435, 436, 437, 438, 439, 440, 441, 442, + 443, 444, 445, 446, 447, 448, 449, 450, 451, 452, + 453, 454, 455, 456, 457, 458, 459, 460, 461, 462, + 463, 464, 465, 466, 467, 468, 469, 470, 471, 472, + 473, 474, 475, 0, 0, 127, 21, -2, 265, 266, + 267, 268, 0, 80, 84, 86, 87, 88, 89, 0, + 0, 92, 105, 106, 107, 0, 0, 0, 0, 224, + 227, 0, 0, 21, 0, 191, 193, 0, 21, 0, + 21, 21, 209, 211, 0, 125, 0, 213, 216, 0, + 0, 196, 198, 201, 0, 248, 0, 0, 0, 131, + 0, 42, 261, 264, 0, 93, 98, 99, 100, 101, + 102, 0, 104, 0, 91, 0, 109, 0, 118, 0, + 120, 95, 0, 97, 0, 21, 0, 246, 21, 0, + 21, 21, 0, 0, -2, 0, 174, 21, 21, 207, + 0, 208, 0, 40, 0, 177, 178, 179, 180, 181, + 182, 183, 184, 185, 186, 187, 188, 21, 0, 21, + 0, 202, 0, 0, 46, 23, 0, 0, 103, 0, + 111, 0, 113, 115, 116, 108, 117, 0, 119, 0, + 0, 21, 0, 0, 244, 21, 21, 247, 0, 138, + 139, 0, 143, -2, 147, 189, 190, 192, 194, 195, + 0, -2, 158, 159, 160, 161, 163, 0, 0, 0, + 128, 0, 43, 394, 395, 396, 397, 398, 399, 400, + 401, 402, 403, 404, 405, 406, 407, 408, 409, 410, + 411, 412, 413, 414, 415, 416, 417, 418, 419, 420, + 421, 422, 423, 424, 425, 426, 427, 428, 429, 430, + 431, 432, 21, 175, 203, 210, 212, 0, 222, 21, + 197, 205, 199, 200, 0, 23, 259, 23, 22, 0, + 0, 0, 132, 0, 47, 0, 51, -2, 476, 477, + 478, 479, 480, 481, 482, 483, 484, 485, 486, 487, + 488, 489, 490, 491, 492, 493, 494, 495, 496, 497, + 498, 499, 500, 501, 502, 503, 504, 505, 506, 507, + 508, 509, 510, 511, 512, 513, 514, 515, 516, 517, + 518, 110, 0, 112, 121, 96, 0, 0, 21, 21, + 245, 242, 21, 137, 140, -2, 145, 0, 21, 157, + 162, 0, 23, 0, 0, 129, 172, 21, 0, 223, + 21, 0, 257, 23, 260, 21, 0, 272, 133, 48, + -2, 53, 114, 21, 0, 21, 0, 240, 21, 243, + 146, 154, 0, 0, 166, 23, 0, 21, 0, 44, + 173, 176, 0, 21, 258, 269, 21, 271, 0, 21, + 152, 21, 241, 23, 23, 167, 0, 0, 21, 148, + 0, 0, -2, 21, 0, 153, 54, 164, 23, 21, + 0, 170, 0, 149, 21, -2, 276, 277, 278, 150, + 21, 165, 0, 21, 171, 270, 275, 151, 168, 0, + 169, +} + +var protoTok1 = [...]int8{ + 1, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 77, 3, 75, 74, 73, 71, 3, + 66, 67, 70, 64, 61, 65, 60, 58, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 54, 53, + 63, 52, 62, 59, 76, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 68, 57, 69, 72, 3, 79, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 55, 3, 56, 78, +} + +var protoTok2 = [...]int8{ + 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, + 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, + 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, + 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, + 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, +} + +var protoTok3 = [...]int8{ + 0, +} + +var protoErrorMessages = [...]struct { + state int + token int + msg string +}{} + +/* parser for yacc output */ + +var ( + protoDebug = 0 + protoErrorVerbose = false +) + +type protoLexer interface { + Lex(lval *protoSymType) int + Error(s string) +} + +type protoParser interface { + Parse(protoLexer) int + Lookahead() int +} + +type protoParserImpl struct { + lval protoSymType + stack [protoInitialStackSize]protoSymType + char int +} + +func (p *protoParserImpl) Lookahead() int { + return p.char +} + +func protoNewParser() protoParser { + return &protoParserImpl{} +} + +const protoFlag = -1000 + +func protoTokname(c int) string { + if c >= 1 && c-1 < len(protoToknames) { + if protoToknames[c-1] != "" { + return protoToknames[c-1] + } + } + return __yyfmt__.Sprintf("tok-%v", c) +} + +func protoStatname(s int) string { + if s >= 0 && s < len(protoStatenames) { + if protoStatenames[s] != "" { + return protoStatenames[s] + } + } + return __yyfmt__.Sprintf("state-%v", s) +} + +func protoErrorMessage(state, lookAhead int) string { + const TOKSTART = 4 + + if !protoErrorVerbose { + return "syntax error" + } + + for _, e := range protoErrorMessages { + if e.state == state && e.token == lookAhead { + return "syntax error: " + e.msg + } + } + + res := "syntax error: unexpected " + protoTokname(lookAhead) + + // To match Bison, suggest at most four expected tokens. + expected := make([]int, 0, 4) + + // Look for shiftable tokens. + base := int(protoPact[state]) + for tok := TOKSTART; tok-1 < len(protoToknames); tok++ { + if n := base + tok; n >= 0 && n < protoLast && int(protoChk[int(protoAct[n])]) == tok { + if len(expected) == cap(expected) { + return res + } + expected = append(expected, tok) + } + } + + if protoDef[state] == -2 { + i := 0 + for protoExca[i] != -1 || int(protoExca[i+1]) != state { + i += 2 + } + + // Look for tokens that we accept or reduce. + for i += 2; protoExca[i] >= 0; i += 2 { + tok := int(protoExca[i]) + if tok < TOKSTART || protoExca[i+1] == 0 { + continue + } + if len(expected) == cap(expected) { + return res + } + expected = append(expected, tok) + } + + // If the default action is to accept or reduce, give up. + if protoExca[i+1] != 0 { + return res + } + } + + for i, tok := range expected { + if i == 0 { + res += ", expecting " + } else { + res += " or " + } + res += protoTokname(tok) + } + return res +} + +func protolex1(lex protoLexer, lval *protoSymType) (char, token int) { + token = 0 + char = lex.Lex(lval) + if char <= 0 { + token = int(protoTok1[0]) + goto out + } + if char < len(protoTok1) { + token = int(protoTok1[char]) + goto out + } + if char >= protoPrivate { + if char < protoPrivate+len(protoTok2) { + token = int(protoTok2[char-protoPrivate]) + goto out + } + } + for i := 0; i < len(protoTok3); i += 2 { + token = int(protoTok3[i+0]) + if token == char { + token = int(protoTok3[i+1]) + goto out + } + } + +out: + if token == 0 { + token = int(protoTok2[1]) /* unknown char */ + } + if protoDebug >= 3 { + __yyfmt__.Printf("lex %s(%d)\n", protoTokname(token), uint(char)) + } + return char, token +} + +func protoParse(protolex protoLexer) int { + return protoNewParser().Parse(protolex) +} + +func (protorcvr *protoParserImpl) Parse(protolex protoLexer) int { + var proton int + var protoVAL protoSymType + var protoDollar []protoSymType + _ = protoDollar // silence set and not used + protoS := protorcvr.stack[:] + + Nerrs := 0 /* number of errors */ + Errflag := 0 /* error recovery flag */ + protostate := 0 + protorcvr.char = -1 + prototoken := -1 // protorcvr.char translated into internal numbering + defer func() { + // Make sure we report no lookahead when not parsing. + protostate = -1 + protorcvr.char = -1 + prototoken = -1 + }() + protop := -1 + goto protostack + +ret0: + return 0 + +ret1: + return 1 + +protostack: + /* put a state and value onto the stack */ + if protoDebug >= 4 { + __yyfmt__.Printf("char %v in %v\n", protoTokname(prototoken), protoStatname(protostate)) + } + + protop++ + if protop >= len(protoS) { + nyys := make([]protoSymType, len(protoS)*2) + copy(nyys, protoS) + protoS = nyys + } + protoS[protop] = protoVAL + protoS[protop].yys = protostate + +protonewstate: + proton = int(protoPact[protostate]) + if proton <= protoFlag { + goto protodefault /* simple state */ + } + if protorcvr.char < 0 { + protorcvr.char, prototoken = protolex1(protolex, &protorcvr.lval) + } + proton += prototoken + if proton < 0 || proton >= protoLast { + goto protodefault + } + proton = int(protoAct[proton]) + if int(protoChk[proton]) == prototoken { /* valid shift */ + protorcvr.char = -1 + prototoken = -1 + protoVAL = protorcvr.lval + protostate = proton + if Errflag > 0 { + Errflag-- + } + goto protostack + } + +protodefault: + /* default state action */ + proton = int(protoDef[protostate]) + if proton == -2 { + if protorcvr.char < 0 { + protorcvr.char, prototoken = protolex1(protolex, &protorcvr.lval) + } + + /* look through exception table */ + xi := 0 + for { + if protoExca[xi+0] == -1 && int(protoExca[xi+1]) == protostate { + break + } + xi += 2 + } + for xi += 2; ; xi += 2 { + proton = int(protoExca[xi+0]) + if proton < 0 || proton == prototoken { + break + } + } + proton = int(protoExca[xi+1]) + if proton < 0 { + goto ret0 + } + } + if proton == 0 { + /* error ... attempt to resume parsing */ + switch Errflag { + case 0: /* brand new error */ + protolex.Error(protoErrorMessage(protostate, prototoken)) + Nerrs++ + if protoDebug >= 1 { + __yyfmt__.Printf("%s", protoStatname(protostate)) + __yyfmt__.Printf(" saw %s\n", protoTokname(prototoken)) + } + fallthrough + + case 1, 2: /* incompletely recovered error ... try again */ + Errflag = 3 + + /* find a state where "error" is a legal shift action */ + for protop >= 0 { + proton = int(protoPact[protoS[protop].yys]) + protoErrCode + if proton >= 0 && proton < protoLast { + protostate = int(protoAct[proton]) /* simulate a shift of "error" */ + if int(protoChk[protostate]) == protoErrCode { + goto protostack + } + } + + /* the current p has no shift on "error", pop stack */ + if protoDebug >= 2 { + __yyfmt__.Printf("error recovery pops state %d\n", protoS[protop].yys) + } + protop-- + } + /* there is no state on the stack with an error shift ... abort */ + goto ret1 + + case 3: /* no shift yet; clobber input char */ + if protoDebug >= 2 { + __yyfmt__.Printf("error recovery discards %s\n", protoTokname(prototoken)) + } + if prototoken == protoEofCode { + goto ret1 + } + protorcvr.char = -1 + prototoken = -1 + goto protonewstate /* try again in the same state */ + } + } + + /* reduction by production proton */ + if protoDebug >= 2 { + __yyfmt__.Printf("reduce %v in:\n\t%v\n", proton, protoStatname(protostate)) + } + + protont := proton + protopt := protop + _ = protopt // guard against "declared and not used" + + protop -= int(protoR2[proton]) + // protop is now the index of $0. Perform the default action. Iff the + // reduced production is ε, $1 is possibly out of range. + if protop+1 >= len(protoS) { + nyys := make([]protoSymType, len(protoS)*2) + copy(nyys, protoS) + protoS = nyys + } + protoVAL = protoS[protop+1] + + /* consult goto table to find next state */ + proton = int(protoR1[proton]) + protog := int(protoPgo[proton]) + protoj := protog + protoS[protop].yys + 1 + + if protoj >= protoLast { + protostate = int(protoAct[protog]) + } else { + protostate = int(protoAct[protoj]) + if int(protoChk[protostate]) != -proton { + protostate = int(protoAct[protog]) + } + } + // dummy call; replaced with literal code + switch protont { + + case 1: + protoDollar = protoS[protopt-1 : protopt+1] + { + lex := protolex.(*protoLex) + protoVAL.file = ast.NewFileNode(lex.info, protoDollar[1].syn, nil, lex.eof) + lex.res = protoVAL.file + } + case 2: + protoDollar = protoS[protopt-1 : protopt+1] + { + lex := protolex.(*protoLex) + protoVAL.file = ast.NewFileNodeWithEdition(lex.info, protoDollar[1].ed, nil, lex.eof) + lex.res = protoVAL.file + } + case 3: + protoDollar = protoS[protopt-1 : protopt+1] + { + lex := protolex.(*protoLex) + protoVAL.file = ast.NewFileNode(lex.info, nil, protoDollar[1].fileElements, lex.eof) + lex.res = protoVAL.file + } + case 4: + protoDollar = protoS[protopt-2 : protopt+1] + { + lex := protolex.(*protoLex) + protoVAL.file = ast.NewFileNode(lex.info, protoDollar[1].syn, protoDollar[2].fileElements, lex.eof) + lex.res = protoVAL.file + } + case 5: + protoDollar = protoS[protopt-2 : protopt+1] + { + lex := protolex.(*protoLex) + protoVAL.file = ast.NewFileNodeWithEdition(lex.info, protoDollar[1].ed, protoDollar[2].fileElements, lex.eof) + lex.res = protoVAL.file + } + case 6: + protoDollar = protoS[protopt-0 : protopt+1] + { + lex := protolex.(*protoLex) + protoVAL.file = ast.NewFileNode(lex.info, nil, nil, lex.eof) + lex.res = protoVAL.file + } + case 7: + protoDollar = protoS[protopt-2 : protopt+1] + { + protoVAL.fileElements = prependRunes(toFileElement, protoDollar[1].bs, protoDollar[2].fileElements) + } + case 8: + protoDollar = protoS[protopt-2 : protopt+1] + { + protoVAL.fileElements = append(protoDollar[1].fileElements, protoDollar[2].fileElements...) + } + case 9: + protoDollar = protoS[protopt-1 : protopt+1] + { + protoVAL.fileElements = protoDollar[1].fileElements + } + case 10: + protoDollar = protoS[protopt-1 : protopt+1] + { + protoVAL.fileElements = toElements[ast.FileElement](toFileElement, protoDollar[1].imprt.Node, protoDollar[1].imprt.Runes) + } + case 11: + protoDollar = protoS[protopt-1 : protopt+1] + { + protoVAL.fileElements = toElements[ast.FileElement](toFileElement, protoDollar[1].pkg.Node, protoDollar[1].pkg.Runes) + } + case 12: + protoDollar = protoS[protopt-1 : protopt+1] + { + protoVAL.fileElements = toElements[ast.FileElement](toFileElement, protoDollar[1].opt.Node, protoDollar[1].opt.Runes) + } + case 13: + protoDollar = protoS[protopt-1 : protopt+1] + { + protoVAL.fileElements = toElements[ast.FileElement](toFileElement, protoDollar[1].msg.Node, protoDollar[1].msg.Runes) + } + case 14: + protoDollar = protoS[protopt-1 : protopt+1] + { + protoVAL.fileElements = toElements[ast.FileElement](toFileElement, protoDollar[1].en.Node, protoDollar[1].en.Runes) + } + case 15: + protoDollar = protoS[protopt-1 : protopt+1] + { + protoVAL.fileElements = toElements[ast.FileElement](toFileElement, protoDollar[1].extend.Node, protoDollar[1].extend.Runes) + } + case 16: + protoDollar = protoS[protopt-1 : protopt+1] + { + protoVAL.fileElements = toElements[ast.FileElement](toFileElement, protoDollar[1].svc.Node, protoDollar[1].svc.Runes) + } + case 17: + protoDollar = protoS[protopt-1 : protopt+1] + { + protoVAL.fileElements = nil + } + case 18: + protoDollar = protoS[protopt-1 : protopt+1] + { + protoVAL.bs = []*ast.RuneNode{protoDollar[1].b} + } + case 19: + protoDollar = protoS[protopt-2 : protopt+1] + { + protoVAL.bs = append(protoDollar[1].bs, protoDollar[2].b) + } + case 20: + protoDollar = protoS[protopt-1 : protopt+1] + { + protoVAL.bs = protoDollar[1].bs + } + case 21: + protoDollar = protoS[protopt-0 : protopt+1] + { + protoVAL.bs = nil + } + case 22: + protoDollar = protoS[protopt-1 : protopt+1] + { + protoVAL.b = protoDollar[1].b + } + case 23: + protoDollar = protoS[protopt-0 : protopt+1] + { + protolex.(*protoLex).Error("syntax error: expecting ';'") + protoVAL.b = nil + } + case 24: + protoDollar = protoS[protopt-4 : protopt+1] + { + protoVAL.syn = ast.NewSyntaxNode(protoDollar[1].id.ToKeyword(), protoDollar[2].b, toStringValueNode(protoDollar[3].str), protoDollar[4].b) + } + case 25: + protoDollar = protoS[protopt-4 : protopt+1] + { + protoVAL.ed = ast.NewEditionNode(protoDollar[1].id.ToKeyword(), protoDollar[2].b, toStringValueNode(protoDollar[3].str), protoDollar[4].b) + } + case 26: + protoDollar = protoS[protopt-3 : protopt+1] + { + semi, extra := protolex.(*protoLex).requireSemicolon(protoDollar[3].bs) + protoVAL.imprt = newNodeWithRunes(ast.NewImportNode(protoDollar[1].id.ToKeyword(), nil, nil, toStringValueNode(protoDollar[2].str), semi), extra...) + } + case 27: + protoDollar = protoS[protopt-4 : protopt+1] + { + semi, extra := protolex.(*protoLex).requireSemicolon(protoDollar[4].bs) + protoVAL.imprt = newNodeWithRunes(ast.NewImportNode(protoDollar[1].id.ToKeyword(), nil, protoDollar[2].id.ToKeyword(), toStringValueNode(protoDollar[3].str), semi), extra...) + } + case 28: + protoDollar = protoS[protopt-4 : protopt+1] + { + semi, extra := protolex.(*protoLex).requireSemicolon(protoDollar[4].bs) + protoVAL.imprt = newNodeWithRunes(ast.NewImportNode(protoDollar[1].id.ToKeyword(), protoDollar[2].id.ToKeyword(), nil, toStringValueNode(protoDollar[3].str), semi), extra...) + } + case 29: + protoDollar = protoS[protopt-3 : protopt+1] + { + semi, extra := protolex.(*protoLex).requireSemicolon(protoDollar[3].bs) + protoVAL.pkg = newNodeWithRunes(ast.NewPackageNode(protoDollar[1].id.ToKeyword(), protoDollar[2].cid.toIdentValueNode(nil), semi), extra...) + } + case 30: + protoDollar = protoS[protopt-1 : protopt+1] + { + protoVAL.cid = &identSlices{idents: []*ast.IdentNode{protoDollar[1].id}} + } + case 31: + protoDollar = protoS[protopt-3 : protopt+1] + { + protoDollar[1].cid.idents = append(protoDollar[1].cid.idents, protoDollar[3].id) + protoDollar[1].cid.dots = append(protoDollar[1].cid.dots, protoDollar[2].b) + protoVAL.cid = protoDollar[1].cid + } + case 32: + protoDollar = protoS[protopt-1 : protopt+1] + { + protoVAL.cid = &identSlices{idents: []*ast.IdentNode{protoDollar[1].cidPart.Node}, dots: protoDollar[1].cidPart.Runes} + } + case 33: + protoDollar = protoS[protopt-2 : protopt+1] + { + protoDollar[1].cid.idents = append(protoDollar[1].cid.idents, protoDollar[2].cidPart.Node) + protoDollar[1].cid.dots = append(protoDollar[1].cid.dots, protoDollar[2].cidPart.Runes...) + protoVAL.cid = protoDollar[1].cid + } + case 34: + protoDollar = protoS[protopt-1 : protopt+1] + { + protoVAL.cid = &identSlices{idents: []*ast.IdentNode{protoDollar[1].cidPart.Node}, dots: protoDollar[1].cidPart.Runes} + } + case 35: + protoDollar = protoS[protopt-2 : protopt+1] + { + protoDollar[1].cid.idents = append(protoDollar[1].cid.idents, protoDollar[2].cidPart.Node) + protoDollar[1].cid.dots = append(protoDollar[1].cid.dots, protoDollar[2].cidPart.Runes...) + protoVAL.cid = protoDollar[1].cid + } + case 36: + protoDollar = protoS[protopt-1 : protopt+1] + { + protoVAL.cidPart = newNodeWithRunes(protoDollar[1].id) + } + case 37: + protoDollar = protoS[protopt-1 : protopt+1] + { + protolex.(*protoLex).Error("syntax error: unexpected '.'") + protoVAL.cidPart = protoDollar[1].cidPart + } + case 38: + protoDollar = protoS[protopt-2 : protopt+1] + { + protoVAL.cidPart = newNodeWithRunes(protoDollar[1].id, protoDollar[2].b) + } + case 39: + protoDollar = protoS[protopt-1 : protopt+1] + { + protoVAL.cid = &identSlices{idents: []*ast.IdentNode{protoDollar[1].id}} + } + case 40: + protoDollar = protoS[protopt-3 : protopt+1] + { + protoDollar[1].cid.idents = append(protoDollar[1].cid.idents, protoDollar[3].id) + protoDollar[1].cid.dots = append(protoDollar[1].cid.dots, protoDollar[2].b) + protoVAL.cid = protoDollar[1].cid + } + case 41: + protoDollar = protoS[protopt-1 : protopt+1] + { + protoVAL.cid = &identSlices{idents: []*ast.IdentNode{protoDollar[1].id}} + } + case 42: + protoDollar = protoS[protopt-3 : protopt+1] + { + protoDollar[1].cid.idents = append(protoDollar[1].cid.idents, protoDollar[3].id) + protoDollar[1].cid.dots = append(protoDollar[1].cid.dots, protoDollar[2].b) + protoVAL.cid = protoDollar[1].cid + } + case 43: + protoDollar = protoS[protopt-1 : protopt+1] + { + protoVAL.cid = &identSlices{idents: []*ast.IdentNode{protoDollar[1].id}} + } + case 44: + protoDollar = protoS[protopt-3 : protopt+1] + { + protoDollar[1].cid.idents = append(protoDollar[1].cid.idents, protoDollar[3].id) + protoDollar[1].cid.dots = append(protoDollar[1].cid.dots, protoDollar[2].b) + protoVAL.cid = protoDollar[1].cid + } + case 45: + protoDollar = protoS[protopt-1 : protopt+1] + { + protoVAL.cid = &identSlices{idents: []*ast.IdentNode{protoDollar[1].id}} + } + case 46: + protoDollar = protoS[protopt-3 : protopt+1] + { + protoDollar[1].cid.idents = append(protoDollar[1].cid.idents, protoDollar[3].id) + protoDollar[1].cid.dots = append(protoDollar[1].cid.dots, protoDollar[2].b) + protoVAL.cid = protoDollar[1].cid + } + case 47: + protoDollar = protoS[protopt-1 : protopt+1] + { + protoVAL.cid = &identSlices{idents: []*ast.IdentNode{protoDollar[1].cidPart.Node}, dots: protoDollar[1].cidPart.Runes} + } + case 48: + protoDollar = protoS[protopt-2 : protopt+1] + { + protoDollar[1].cid.idents = append(protoDollar[1].cid.idents, protoDollar[2].cidPart.Node) + protoDollar[1].cid.dots = append(protoDollar[1].cid.dots, protoDollar[2].cidPart.Runes...) + protoVAL.cid = protoDollar[1].cid + } + case 49: + protoDollar = protoS[protopt-1 : protopt+1] + { + protoVAL.cid = &identSlices{idents: []*ast.IdentNode{protoDollar[1].cidPart.Node}, dots: protoDollar[1].cidPart.Runes} + } + case 50: + protoDollar = protoS[protopt-2 : protopt+1] + { + protoDollar[1].cid.idents = append(protoDollar[1].cid.idents, protoDollar[2].cidPart.Node) + protoDollar[1].cid.dots = append(protoDollar[1].cid.dots, protoDollar[2].cidPart.Runes...) + protoVAL.cid = protoDollar[1].cid + } + case 51: + protoDollar = protoS[protopt-1 : protopt+1] + { + protoVAL.cidPart = newNodeWithRunes(protoDollar[1].id) + } + case 52: + protoDollar = protoS[protopt-1 : protopt+1] + { + protolex.(*protoLex).Error("syntax error: unexpected '.'") + protoVAL.cidPart = protoDollar[1].cidPart + } + case 53: + protoDollar = protoS[protopt-2 : protopt+1] + { + protoVAL.cidPart = newNodeWithRunes(protoDollar[1].id, protoDollar[2].b) + } + case 54: + protoDollar = protoS[protopt-5 : protopt+1] + { + optName := ast.NewOptionNameNode(protoDollar[2].optNms.refs, protoDollar[2].optNms.dots) + protoVAL.optRaw = ast.NewOptionNode(protoDollar[1].id.ToKeyword(), optName, protoDollar[3].b, protoDollar[4].v, protoDollar[5].b) + } + case 55: + protoDollar = protoS[protopt-5 : protopt+1] + { + optName := ast.NewOptionNameNode(protoDollar[2].optNms.refs, protoDollar[2].optNms.dots) + semi, extra := protolex.(*protoLex).requireSemicolon(protoDollar[5].bs) + protoVAL.opt = newNodeWithRunes(ast.NewOptionNode(protoDollar[1].id.ToKeyword(), optName, protoDollar[3].b, protoDollar[4].v, semi), extra...) + } + case 56: + protoDollar = protoS[protopt-1 : protopt+1] + { + protoVAL.refRaw = ast.NewFieldReferenceNode(protoDollar[1].id) + } + case 57: + protoDollar = protoS[protopt-1 : protopt+1] + { + protoVAL.refRaw = protoDollar[1].refRaw + } + case 58: + protoDollar = protoS[protopt-2 : protopt+1] + { + protoVAL.ref = newNodeWithRunes(protoDollar[1].refRaw, protoDollar[2].b) + } + case 59: + protoDollar = protoS[protopt-1 : protopt+1] + { + protoVAL.ref = newNodeWithRunes(protoDollar[1].refRaw) + } + case 60: + protoDollar = protoS[protopt-1 : protopt+1] + { + protolex.(*protoLex).Error("syntax error: unexpected '.'") + protoVAL.ref = protoDollar[1].ref + } + case 61: + protoDollar = protoS[protopt-1 : protopt+1] + { + protoVAL.optNms = &fieldRefSlices{refs: []*ast.FieldReferenceNode{protoDollar[1].ref.Node}, dots: protoDollar[1].ref.Runes} + } + case 62: + protoDollar = protoS[protopt-2 : protopt+1] + { + protoDollar[1].optNms.refs = append(protoDollar[1].optNms.refs, protoDollar[2].ref.Node) + protoDollar[1].optNms.dots = append(protoDollar[1].optNms.dots, protoDollar[2].ref.Runes...) + protoVAL.optNms = protoDollar[1].optNms + } + case 63: + protoDollar = protoS[protopt-1 : protopt+1] + { + protoVAL.optNms = &fieldRefSlices{refs: []*ast.FieldReferenceNode{protoDollar[1].ref.Node}, dots: protoDollar[1].ref.Runes} + } + case 64: + protoDollar = protoS[protopt-2 : protopt+1] + { + protoDollar[1].optNms.refs = append(protoDollar[1].optNms.refs, protoDollar[2].ref.Node) + protoDollar[1].optNms.dots = append(protoDollar[1].optNms.dots, protoDollar[2].ref.Runes...) + protoVAL.optNms = protoDollar[1].optNms + } + case 65: + protoDollar = protoS[protopt-3 : protopt+1] + { + protoVAL.refRaw = ast.NewExtensionFieldReferenceNode(protoDollar[1].b, protoDollar[2].tid, protoDollar[3].b) + } + case 68: + protoDollar = protoS[protopt-1 : protopt+1] + { + protoVAL.v = toStringValueNode(protoDollar[1].str) + } + case 71: + protoDollar = protoS[protopt-1 : protopt+1] + { + protoVAL.v = protoDollar[1].id + } + case 72: + protoDollar = protoS[protopt-1 : protopt+1] + { + protoVAL.v = protoDollar[1].f + } + case 73: + protoDollar = protoS[protopt-2 : protopt+1] + { + protoVAL.v = ast.NewSignedFloatLiteralNode(protoDollar[1].b, protoDollar[2].f) + } + case 74: + protoDollar = protoS[protopt-1 : protopt+1] + { + protoVAL.v = protoDollar[1].i + } + case 75: + protoDollar = protoS[protopt-2 : protopt+1] + { + if protoDollar[2].i.Val > math.MaxInt64+1 { + // can't represent as int so treat as float literal + protoVAL.v = ast.NewSignedFloatLiteralNode(protoDollar[1].b, protoDollar[2].i) + } else { + protoVAL.v = ast.NewNegativeIntLiteralNode(protoDollar[1].b, protoDollar[2].i) + } + } + case 76: + protoDollar = protoS[protopt-2 : protopt+1] + { + f := ast.NewSpecialFloatLiteralNode(protoDollar[2].id.ToKeyword()) + protoVAL.v = ast.NewSignedFloatLiteralNode(protoDollar[1].b, f) + } + case 77: + protoDollar = protoS[protopt-2 : protopt+1] + { + f := ast.NewSpecialFloatLiteralNode(protoDollar[2].id.ToKeyword()) + protoVAL.v = ast.NewSignedFloatLiteralNode(protoDollar[1].b, f) + } + case 78: + protoDollar = protoS[protopt-1 : protopt+1] + { + protoVAL.str = []*ast.StringLiteralNode{protoDollar[1].s} + } + case 79: + protoDollar = protoS[protopt-2 : protopt+1] + { + protoVAL.str = append(protoDollar[1].str, protoDollar[2].s) + } + case 80: + protoDollar = protoS[protopt-3 : protopt+1] + { + if protoDollar[2].msgLitFlds == nil { + protoVAL.v = ast.NewMessageLiteralNode(protoDollar[1].b, nil, nil, protoDollar[3].b) + } else { + fields, delimiters := protoDollar[2].msgLitFlds.toNodes() + protoVAL.v = ast.NewMessageLiteralNode(protoDollar[1].b, fields, delimiters, protoDollar[3].b) + } + } + case 81: + protoDollar = protoS[protopt-2 : protopt+1] + { + protoVAL.v = ast.NewMessageLiteralNode(protoDollar[1].b, nil, nil, protoDollar[2].b) + } + case 84: + protoDollar = protoS[protopt-2 : protopt+1] + { + if protoDollar[1].msgLitFlds != nil { + protoDollar[1].msgLitFlds.next = protoDollar[2].msgLitFlds + protoVAL.msgLitFlds = protoDollar[1].msgLitFlds + } else { + protoVAL.msgLitFlds = protoDollar[2].msgLitFlds + } + } + case 85: + protoDollar = protoS[protopt-1 : protopt+1] + { + if protoDollar[1].msgLitFld != nil { + protoVAL.msgLitFlds = &messageFieldList{field: protoDollar[1].msgLitFld} + } else { + protoVAL.msgLitFlds = nil + } + } + case 86: + protoDollar = protoS[protopt-2 : protopt+1] + { + if protoDollar[1].msgLitFld != nil { + protoVAL.msgLitFlds = &messageFieldList{field: protoDollar[1].msgLitFld, delimiter: protoDollar[2].b} + } else { + protoVAL.msgLitFlds = nil + } + } + case 87: + protoDollar = protoS[protopt-2 : protopt+1] + { + if protoDollar[1].msgLitFld != nil { + protoVAL.msgLitFlds = &messageFieldList{field: protoDollar[1].msgLitFld, delimiter: protoDollar[2].b} + } else { + protoVAL.msgLitFlds = nil + } + } + case 88: + protoDollar = protoS[protopt-2 : protopt+1] + { + protoVAL.msgLitFlds = nil + } + case 89: + protoDollar = protoS[protopt-2 : protopt+1] + { + protoVAL.msgLitFlds = nil + } + case 90: + protoDollar = protoS[protopt-1 : protopt+1] + { + protoVAL.msgLitFlds = nil + } + case 91: + protoDollar = protoS[protopt-3 : protopt+1] + { + if protoDollar[1].refRaw != nil && protoDollar[2].b != nil { + protoVAL.msgLitFld = ast.NewMessageFieldNode(protoDollar[1].refRaw, protoDollar[2].b, protoDollar[3].v) + } else { + protoVAL.msgLitFld = nil + } + } + case 92: + protoDollar = protoS[protopt-2 : protopt+1] + { + if protoDollar[1].refRaw != nil && protoDollar[2].v != nil { + protoVAL.msgLitFld = ast.NewMessageFieldNode(protoDollar[1].refRaw, nil, protoDollar[2].v) + } else { + protoVAL.msgLitFld = nil + } + } + case 93: + protoDollar = protoS[protopt-3 : protopt+1] + { + protoVAL.msgLitFld = nil + } + case 94: + protoDollar = protoS[protopt-1 : protopt+1] + { + protoVAL.refRaw = ast.NewFieldReferenceNode(protoDollar[1].id) + } + case 95: + protoDollar = protoS[protopt-3 : protopt+1] + { + protoVAL.refRaw = ast.NewExtensionFieldReferenceNode(protoDollar[1].b, protoDollar[2].cid.toIdentValueNode(nil), protoDollar[3].b) + } + case 96: + protoDollar = protoS[protopt-5 : protopt+1] + { + protoVAL.refRaw = ast.NewAnyTypeReferenceNode(protoDollar[1].b, protoDollar[2].cid.toIdentValueNode(nil), protoDollar[3].b, protoDollar[4].cid.toIdentValueNode(nil), protoDollar[5].b) + } + case 97: + protoDollar = protoS[protopt-3 : protopt+1] + { + protoVAL.refRaw = nil + } + case 101: + protoDollar = protoS[protopt-1 : protopt+1] + { + protoVAL.v = toStringValueNode(protoDollar[1].str) + } + case 103: + protoDollar = protoS[protopt-2 : protopt+1] + { + kw := protoDollar[2].id.ToKeyword() + switch strings.ToLower(kw.Val) { + case "inf", "infinity", "nan": + // these are acceptable + default: + // anything else is not + protolex.(*protoLex).Error(`only identifiers "inf", "infinity", or "nan" may appear after negative sign`) + } + // we'll validate the identifier later + f := ast.NewSpecialFloatLiteralNode(kw) + protoVAL.v = ast.NewSignedFloatLiteralNode(protoDollar[1].b, f) + } + case 104: + protoDollar = protoS[protopt-1 : protopt+1] + { + protoVAL.v = protoDollar[1].id + } + case 108: + protoDollar = protoS[protopt-3 : protopt+1] + { + if protoDollar[2].msgLitFlds == nil { + protoVAL.v = ast.NewMessageLiteralNode(protoDollar[1].b, nil, nil, protoDollar[3].b) + } else { + fields, delimiters := protoDollar[2].msgLitFlds.toNodes() + protoVAL.v = ast.NewMessageLiteralNode(protoDollar[1].b, fields, delimiters, protoDollar[3].b) + } + } + case 109: + protoDollar = protoS[protopt-2 : protopt+1] + { + protoVAL.v = ast.NewMessageLiteralNode(protoDollar[1].b, nil, nil, protoDollar[2].b) + } + case 110: + protoDollar = protoS[protopt-3 : protopt+1] + { + if protoDollar[2].sl == nil { + protoVAL.v = ast.NewArrayLiteralNode(protoDollar[1].b, nil, nil, protoDollar[3].b) + } else { + protoVAL.v = ast.NewArrayLiteralNode(protoDollar[1].b, protoDollar[2].sl.vals, protoDollar[2].sl.commas, protoDollar[3].b) + } + } + case 111: + protoDollar = protoS[protopt-2 : protopt+1] + { + protoVAL.v = ast.NewArrayLiteralNode(protoDollar[1].b, nil, nil, protoDollar[2].b) + } + case 112: + protoDollar = protoS[protopt-3 : protopt+1] + { + protoVAL.v = ast.NewArrayLiteralNode(protoDollar[1].b, nil, nil, protoDollar[3].b) + } + case 113: + protoDollar = protoS[protopt-1 : protopt+1] + { + protoVAL.sl = &valueSlices{vals: []ast.ValueNode{protoDollar[1].v}} + } + case 114: + protoDollar = protoS[protopt-3 : protopt+1] + { + protoDollar[1].sl.vals = append(protoDollar[1].sl.vals, protoDollar[3].v) + protoDollar[1].sl.commas = append(protoDollar[1].sl.commas, protoDollar[2].b) + protoVAL.sl = protoDollar[1].sl + } + case 117: + protoDollar = protoS[protopt-3 : protopt+1] + { + if protoDollar[2].sl == nil { + protoVAL.v = ast.NewArrayLiteralNode(protoDollar[1].b, nil, nil, protoDollar[3].b) + } else { + protoVAL.v = ast.NewArrayLiteralNode(protoDollar[1].b, protoDollar[2].sl.vals, protoDollar[2].sl.commas, protoDollar[3].b) + } + } + case 118: + protoDollar = protoS[protopt-2 : protopt+1] + { + protoVAL.v = ast.NewArrayLiteralNode(protoDollar[1].b, nil, nil, protoDollar[2].b) + } + case 119: + protoDollar = protoS[protopt-3 : protopt+1] + { + protoVAL.v = ast.NewArrayLiteralNode(protoDollar[1].b, nil, nil, protoDollar[3].b) + } + case 120: + protoDollar = protoS[protopt-1 : protopt+1] + { + protoVAL.sl = &valueSlices{vals: []ast.ValueNode{protoDollar[1].v}} + } + case 121: + protoDollar = protoS[protopt-3 : protopt+1] + { + protoDollar[1].sl.vals = append(protoDollar[1].sl.vals, protoDollar[3].v) + protoDollar[1].sl.commas = append(protoDollar[1].sl.commas, protoDollar[2].b) + protoVAL.sl = protoDollar[1].sl + } + case 122: + protoDollar = protoS[protopt-1 : protopt+1] + { + protoVAL.tid = protoDollar[1].cid.toIdentValueNode(nil) + } + case 123: + protoDollar = protoS[protopt-2 : protopt+1] + { + protoVAL.tid = protoDollar[2].cid.toIdentValueNode(protoDollar[1].b) + } + case 124: + protoDollar = protoS[protopt-1 : protopt+1] + { + protoVAL.tid = protoDollar[1].cid.toIdentValueNode(nil) + } + case 125: + protoDollar = protoS[protopt-2 : protopt+1] + { + protoVAL.tid = protoDollar[2].cid.toIdentValueNode(protoDollar[1].b) + } + case 126: + protoDollar = protoS[protopt-1 : protopt+1] + { + protoVAL.tid = protoDollar[1].cid.toIdentValueNode(nil) + } + case 127: + protoDollar = protoS[protopt-2 : protopt+1] + { + protoVAL.tid = protoDollar[2].cid.toIdentValueNode(protoDollar[1].b) + } + case 128: + protoDollar = protoS[protopt-1 : protopt+1] + { + protoVAL.tid = protoDollar[1].cid.toIdentValueNode(nil) + } + case 129: + protoDollar = protoS[protopt-2 : protopt+1] + { + protoVAL.tid = protoDollar[2].cid.toIdentValueNode(protoDollar[1].b) + } + case 130: + protoDollar = protoS[protopt-1 : protopt+1] + { + protoVAL.tid = protoDollar[1].cid.toIdentValueNode(nil) + } + case 131: + protoDollar = protoS[protopt-2 : protopt+1] + { + protoVAL.tid = protoDollar[2].cid.toIdentValueNode(protoDollar[1].b) + } + case 132: + protoDollar = protoS[protopt-1 : protopt+1] + { + protoVAL.tid = protoDollar[1].cid.toIdentValueNode(nil) + } + case 133: + protoDollar = protoS[protopt-2 : protopt+1] + { + protoVAL.tid = protoDollar[2].cid.toIdentValueNode(protoDollar[1].b) + } + case 137: + protoDollar = protoS[protopt-3 : protopt+1] + { + protoVAL.cmpctOpts = ast.NewCompactOptionsNode(protoDollar[1].b, protoDollar[2].opts.options, protoDollar[2].opts.commas, protoDollar[3].b) + } + case 138: + protoDollar = protoS[protopt-2 : protopt+1] + { + protolex.(*protoLex).Error("compact options must have at least one option") + protoVAL.cmpctOpts = ast.NewCompactOptionsNode(protoDollar[1].b, nil, nil, protoDollar[2].b) + } + case 139: + protoDollar = protoS[protopt-1 : protopt+1] + { + protoVAL.opts = &compactOptionSlices{options: []*ast.OptionNode{protoDollar[1].opt.Node}, commas: protoDollar[1].opt.Runes} + } + case 140: + protoDollar = protoS[protopt-2 : protopt+1] + { + protoDollar[1].opts.options = append(protoDollar[1].opts.options, protoDollar[2].opt.Node) + protoDollar[1].opts.commas = append(protoDollar[1].opts.commas, protoDollar[2].opt.Runes...) + protoVAL.opts = protoDollar[1].opts + } + case 141: + protoDollar = protoS[protopt-1 : protopt+1] + { + protoVAL.opts = &compactOptionSlices{options: []*ast.OptionNode{protoDollar[1].opt.Node}, commas: protoDollar[1].opt.Runes} + } + case 142: + protoDollar = protoS[protopt-2 : protopt+1] + { + protoDollar[1].opts.options = append(protoDollar[1].opts.options, protoDollar[2].opt.Node) + protoDollar[1].opts.commas = append(protoDollar[1].opts.commas, protoDollar[2].opt.Runes...) + protoVAL.opts = protoDollar[1].opts + } + case 143: + protoDollar = protoS[protopt-1 : protopt+1] + { + protoVAL.opt = newNodeWithRunes(protoDollar[1].optRaw) + } + case 144: + protoDollar = protoS[protopt-1 : protopt+1] + { + protolex.(*protoLex).Error("syntax error: unexpected ','") + protoVAL.opt = protoDollar[1].opt + } + case 145: + protoDollar = protoS[protopt-2 : protopt+1] + { + protoVAL.opt = newNodeWithRunes(protoDollar[1].optRaw, protoDollar[2].b) + } + case 146: + protoDollar = protoS[protopt-3 : protopt+1] + { + optName := ast.NewOptionNameNode(protoDollar[1].optNms.refs, protoDollar[1].optNms.dots) + protoVAL.optRaw = ast.NewCompactOptionNode(optName, protoDollar[2].b, protoDollar[3].v) + } + case 147: + protoDollar = protoS[protopt-1 : protopt+1] + { + optName := ast.NewOptionNameNode(protoDollar[1].optNms.refs, protoDollar[1].optNms.dots) + protolex.(*protoLex).Error("compact option must have a value") + protoVAL.optRaw = ast.NewCompactOptionNode(optName, nil, nil) + } + case 148: + protoDollar = protoS[protopt-8 : protopt+1] + { + protoVAL.grp = ast.NewGroupNode(protoDollar[1].id.ToKeyword(), protoDollar[2].id.ToKeyword(), protoDollar[3].id, protoDollar[4].b, protoDollar[5].i, nil, protoDollar[6].b, protoDollar[7].msgElements, protoDollar[8].b) + } + case 149: + protoDollar = protoS[protopt-9 : protopt+1] + { + protoVAL.grp = ast.NewGroupNode(protoDollar[1].id.ToKeyword(), protoDollar[2].id.ToKeyword(), protoDollar[3].id, protoDollar[4].b, protoDollar[5].i, protoDollar[6].cmpctOpts, protoDollar[7].b, protoDollar[8].msgElements, protoDollar[9].b) + } + case 150: + protoDollar = protoS[protopt-9 : protopt+1] + { + protoVAL.msgGrp = newNodeWithRunes(ast.NewGroupNode(protoDollar[1].id.ToKeyword(), protoDollar[2].id.ToKeyword(), protoDollar[3].id, protoDollar[4].b, protoDollar[5].i, nil, protoDollar[6].b, protoDollar[7].msgElements, protoDollar[8].b), protoDollar[9].bs...) + } + case 151: + protoDollar = protoS[protopt-10 : protopt+1] + { + protoVAL.msgGrp = newNodeWithRunes(ast.NewGroupNode(protoDollar[1].id.ToKeyword(), protoDollar[2].id.ToKeyword(), protoDollar[3].id, protoDollar[4].b, protoDollar[5].i, protoDollar[6].cmpctOpts, protoDollar[7].b, protoDollar[8].msgElements, protoDollar[9].b), protoDollar[10].bs...) + } + case 152: + protoDollar = protoS[protopt-7 : protopt+1] + { + protoVAL.msgGrp = newNodeWithRunes(ast.NewGroupNode(protoDollar[1].id.ToKeyword(), protoDollar[2].id.ToKeyword(), protoDollar[3].id, nil, nil, nil, protoDollar[4].b, protoDollar[5].msgElements, protoDollar[6].b), protoDollar[7].bs...) + } + case 153: + protoDollar = protoS[protopt-8 : protopt+1] + { + protoVAL.msgGrp = newNodeWithRunes(ast.NewGroupNode(protoDollar[1].id.ToKeyword(), protoDollar[2].id.ToKeyword(), protoDollar[3].id, nil, nil, protoDollar[4].cmpctOpts, protoDollar[5].b, protoDollar[6].msgElements, protoDollar[7].b), protoDollar[8].bs...) + } + case 154: + protoDollar = protoS[protopt-6 : protopt+1] + { + protoVAL.oo = newNodeWithRunes(ast.NewOneofNode(protoDollar[1].id.ToKeyword(), protoDollar[2].id, protoDollar[3].b, protoDollar[4].ooElements, protoDollar[5].b), protoDollar[6].bs...) + } + case 155: + protoDollar = protoS[protopt-0 : protopt+1] + { + protoVAL.ooElements = nil + } + case 157: + protoDollar = protoS[protopt-2 : protopt+1] + { + if protoDollar[2].ooElement != nil { + protoVAL.ooElements = append(protoDollar[1].ooElements, protoDollar[2].ooElement) + } else { + protoVAL.ooElements = protoDollar[1].ooElements + } + } + case 158: + protoDollar = protoS[protopt-1 : protopt+1] + { + if protoDollar[1].ooElement != nil { + protoVAL.ooElements = []ast.OneofElement{protoDollar[1].ooElement} + } else { + protoVAL.ooElements = nil + } + } + case 159: + protoDollar = protoS[protopt-1 : protopt+1] + { + protoVAL.ooElement = protoDollar[1].optRaw + } + case 160: + protoDollar = protoS[protopt-1 : protopt+1] + { + protoVAL.ooElement = protoDollar[1].fld + } + case 161: + protoDollar = protoS[protopt-1 : protopt+1] + { + protoVAL.ooElement = protoDollar[1].grp + } + case 162: + protoDollar = protoS[protopt-2 : protopt+1] + { + protoVAL.ooElement = nil + } + case 163: + protoDollar = protoS[protopt-1 : protopt+1] + { + protoVAL.ooElement = nil + } + case 164: + protoDollar = protoS[protopt-5 : protopt+1] + { + protoVAL.fld = ast.NewFieldNode(nil, protoDollar[1].tid, protoDollar[2].id, protoDollar[3].b, protoDollar[4].i, nil, protoDollar[5].b) + } + case 165: + protoDollar = protoS[protopt-6 : protopt+1] + { + protoVAL.fld = ast.NewFieldNode(nil, protoDollar[1].tid, protoDollar[2].id, protoDollar[3].b, protoDollar[4].i, protoDollar[5].cmpctOpts, protoDollar[6].b) + } + case 166: + protoDollar = protoS[protopt-3 : protopt+1] + { + protoVAL.fld = ast.NewFieldNode(nil, protoDollar[1].tid, protoDollar[2].id, nil, nil, nil, protoDollar[3].b) + } + case 167: + protoDollar = protoS[protopt-4 : protopt+1] + { + protoVAL.fld = ast.NewFieldNode(nil, protoDollar[1].tid, protoDollar[2].id, nil, nil, protoDollar[3].cmpctOpts, protoDollar[4].b) + } + case 168: + protoDollar = protoS[protopt-7 : protopt+1] + { + protoVAL.grp = ast.NewGroupNode(nil, protoDollar[1].id.ToKeyword(), protoDollar[2].id, protoDollar[3].b, protoDollar[4].i, nil, protoDollar[5].b, protoDollar[6].msgElements, protoDollar[7].b) + } + case 169: + protoDollar = protoS[protopt-8 : protopt+1] + { + protoVAL.grp = ast.NewGroupNode(nil, protoDollar[1].id.ToKeyword(), protoDollar[2].id, protoDollar[3].b, protoDollar[4].i, protoDollar[5].cmpctOpts, protoDollar[6].b, protoDollar[7].msgElements, protoDollar[8].b) + } + case 170: + protoDollar = protoS[protopt-5 : protopt+1] + { + protoVAL.grp = ast.NewGroupNode(nil, protoDollar[1].id.ToKeyword(), protoDollar[2].id, nil, nil, nil, protoDollar[3].b, protoDollar[4].msgElements, protoDollar[5].b) + } + case 171: + protoDollar = protoS[protopt-6 : protopt+1] + { + protoVAL.grp = ast.NewGroupNode(nil, protoDollar[1].id.ToKeyword(), protoDollar[2].id, nil, nil, protoDollar[3].cmpctOpts, protoDollar[4].b, protoDollar[5].msgElements, protoDollar[6].b) + } + case 172: + protoDollar = protoS[protopt-5 : protopt+1] + { + semi, extra := protolex.(*protoLex).requireSemicolon(protoDollar[5].bs) + protoVAL.mapFld = newNodeWithRunes(ast.NewMapFieldNode(protoDollar[1].mapType, protoDollar[2].id, protoDollar[3].b, protoDollar[4].i, nil, semi), extra...) + } + case 173: + protoDollar = protoS[protopt-6 : protopt+1] + { + semi, extra := protolex.(*protoLex).requireSemicolon(protoDollar[6].bs) + protoVAL.mapFld = newNodeWithRunes(ast.NewMapFieldNode(protoDollar[1].mapType, protoDollar[2].id, protoDollar[3].b, protoDollar[4].i, protoDollar[5].cmpctOpts, semi), extra...) + } + case 174: + protoDollar = protoS[protopt-3 : protopt+1] + { + semi, extra := protolex.(*protoLex).requireSemicolon(protoDollar[3].bs) + protoVAL.mapFld = newNodeWithRunes(ast.NewMapFieldNode(protoDollar[1].mapType, protoDollar[2].id, nil, nil, nil, semi), extra...) + } + case 175: + protoDollar = protoS[protopt-4 : protopt+1] + { + semi, extra := protolex.(*protoLex).requireSemicolon(protoDollar[4].bs) + protoVAL.mapFld = newNodeWithRunes(ast.NewMapFieldNode(protoDollar[1].mapType, protoDollar[2].id, nil, nil, protoDollar[3].cmpctOpts, semi), extra...) + } + case 176: + protoDollar = protoS[protopt-6 : protopt+1] + { + protoVAL.mapType = ast.NewMapTypeNode(protoDollar[1].id.ToKeyword(), protoDollar[2].b, protoDollar[3].id, protoDollar[4].b, protoDollar[5].tid, protoDollar[6].b) + } + case 189: + protoDollar = protoS[protopt-4 : protopt+1] + { + // TODO: Tolerate a missing semicolon here. This currnelty creates a shift/reduce conflict + // between `extensions 1 to 10` and `extensions 1` followed by `to = 10`. + protoVAL.ext = newNodeWithRunes(ast.NewExtensionRangeNode(protoDollar[1].id.ToKeyword(), protoDollar[2].rngs.ranges, protoDollar[2].rngs.commas, nil, protoDollar[3].b), protoDollar[4].bs...) + } + case 190: + protoDollar = protoS[protopt-4 : protopt+1] + { + semi, extra := protolex.(*protoLex).requireSemicolon(protoDollar[4].bs) + protoVAL.ext = newNodeWithRunes(ast.NewExtensionRangeNode(protoDollar[1].id.ToKeyword(), protoDollar[2].rngs.ranges, protoDollar[2].rngs.commas, protoDollar[3].cmpctOpts, semi), extra...) + } + case 191: + protoDollar = protoS[protopt-1 : protopt+1] + { + protoVAL.rngs = &rangeSlices{ranges: []*ast.RangeNode{protoDollar[1].rng}} + } + case 192: + protoDollar = protoS[protopt-3 : protopt+1] + { + protoDollar[1].rngs.ranges = append(protoDollar[1].rngs.ranges, protoDollar[3].rng) + protoDollar[1].rngs.commas = append(protoDollar[1].rngs.commas, protoDollar[2].b) + protoVAL.rngs = protoDollar[1].rngs + } + case 193: + protoDollar = protoS[protopt-1 : protopt+1] + { + protoVAL.rng = ast.NewRangeNode(protoDollar[1].i, nil, nil, nil) + } + case 194: + protoDollar = protoS[protopt-3 : protopt+1] + { + protoVAL.rng = ast.NewRangeNode(protoDollar[1].i, protoDollar[2].id.ToKeyword(), protoDollar[3].i, nil) + } + case 195: + protoDollar = protoS[protopt-3 : protopt+1] + { + protoVAL.rng = ast.NewRangeNode(protoDollar[1].i, protoDollar[2].id.ToKeyword(), nil, protoDollar[3].id.ToKeyword()) + } + case 196: + protoDollar = protoS[protopt-1 : protopt+1] + { + protoVAL.rngs = &rangeSlices{ranges: []*ast.RangeNode{protoDollar[1].rng}} + } + case 197: + protoDollar = protoS[protopt-3 : protopt+1] + { + protoDollar[1].rngs.ranges = append(protoDollar[1].rngs.ranges, protoDollar[3].rng) + protoDollar[1].rngs.commas = append(protoDollar[1].rngs.commas, protoDollar[2].b) + protoVAL.rngs = protoDollar[1].rngs + } + case 198: + protoDollar = protoS[protopt-1 : protopt+1] + { + protoVAL.rng = ast.NewRangeNode(protoDollar[1].il, nil, nil, nil) + } + case 199: + protoDollar = protoS[protopt-3 : protopt+1] + { + protoVAL.rng = ast.NewRangeNode(protoDollar[1].il, protoDollar[2].id.ToKeyword(), protoDollar[3].il, nil) + } + case 200: + protoDollar = protoS[protopt-3 : protopt+1] + { + protoVAL.rng = ast.NewRangeNode(protoDollar[1].il, protoDollar[2].id.ToKeyword(), nil, protoDollar[3].id.ToKeyword()) + } + case 201: + protoDollar = protoS[protopt-1 : protopt+1] + { + protoVAL.il = protoDollar[1].i + } + case 202: + protoDollar = protoS[protopt-2 : protopt+1] + { + protoVAL.il = ast.NewNegativeIntLiteralNode(protoDollar[1].b, protoDollar[2].i) + } + case 203: + protoDollar = protoS[protopt-4 : protopt+1] + { + // TODO: Tolerate a missing semicolon here. This currnelty creates a shift/reduce conflict + // between `reserved 1 to 10` and `reserved 1` followed by `to = 10`. + protoVAL.resvd = newNodeWithRunes(ast.NewReservedRangesNode(protoDollar[1].id.ToKeyword(), protoDollar[2].rngs.ranges, protoDollar[2].rngs.commas, protoDollar[3].b), protoDollar[4].bs...) + } + case 205: + protoDollar = protoS[protopt-4 : protopt+1] + { + // TODO: Tolerate a missing semicolon here. This currnelty creates a shift/reduce conflict + // between `reserved 1 to 10` and `reserved 1` followed by `to = 10`. + protoVAL.resvd = newNodeWithRunes(ast.NewReservedRangesNode(protoDollar[1].id.ToKeyword(), protoDollar[2].rngs.ranges, protoDollar[2].rngs.commas, protoDollar[3].b), protoDollar[4].bs...) + } + case 207: + protoDollar = protoS[protopt-3 : protopt+1] + { + semi, extra := protolex.(*protoLex).requireSemicolon(protoDollar[3].bs) + protoVAL.resvd = newNodeWithRunes(ast.NewReservedNamesNode(protoDollar[1].id.ToKeyword(), protoDollar[2].names.names, protoDollar[2].names.commas, semi), extra...) + } + case 208: + protoDollar = protoS[protopt-3 : protopt+1] + { + semi, extra := protolex.(*protoLex).requireSemicolon(protoDollar[3].bs) + protoVAL.resvd = newNodeWithRunes(ast.NewReservedIdentifiersNode(protoDollar[1].id.ToKeyword(), protoDollar[2].names.idents, protoDollar[2].names.commas, semi), extra...) + } + case 209: + protoDollar = protoS[protopt-1 : protopt+1] + { + protoVAL.names = &nameSlices{names: []ast.StringValueNode{toStringValueNode(protoDollar[1].str)}} + } + case 210: + protoDollar = protoS[protopt-3 : protopt+1] + { + protoDollar[1].names.names = append(protoDollar[1].names.names, toStringValueNode(protoDollar[3].str)) + protoDollar[1].names.commas = append(protoDollar[1].names.commas, protoDollar[2].b) + protoVAL.names = protoDollar[1].names + } + case 211: + protoDollar = protoS[protopt-1 : protopt+1] + { + protoVAL.names = &nameSlices{idents: []*ast.IdentNode{protoDollar[1].id}} + } + case 212: + protoDollar = protoS[protopt-3 : protopt+1] + { + protoDollar[1].names.idents = append(protoDollar[1].names.idents, protoDollar[3].id) + protoDollar[1].names.commas = append(protoDollar[1].names.commas, protoDollar[2].b) + protoVAL.names = protoDollar[1].names + } + case 213: + protoDollar = protoS[protopt-6 : protopt+1] + { + protoVAL.en = newNodeWithRunes(ast.NewEnumNode(protoDollar[1].id.ToKeyword(), protoDollar[2].id, protoDollar[3].b, protoDollar[4].enElements, protoDollar[5].b), protoDollar[6].bs...) + } + case 214: + protoDollar = protoS[protopt-1 : protopt+1] + { + protoVAL.enElements = prependRunes(toEnumElement, protoDollar[1].bs, nil) + } + case 215: + protoDollar = protoS[protopt-2 : protopt+1] + { + protoVAL.enElements = prependRunes(toEnumElement, protoDollar[1].bs, protoDollar[2].enElements) + } + case 216: + protoDollar = protoS[protopt-2 : protopt+1] + { + protoVAL.enElements = append(protoDollar[1].enElements, protoDollar[2].enElements...) + } + case 217: + protoDollar = protoS[protopt-1 : protopt+1] + { + protoVAL.enElements = protoDollar[1].enElements + } + case 218: + protoDollar = protoS[protopt-1 : protopt+1] + { + protoVAL.enElements = toElements[ast.EnumElement](toEnumElement, protoDollar[1].opt.Node, protoDollar[1].opt.Runes) + } + case 219: + protoDollar = protoS[protopt-1 : protopt+1] + { + protoVAL.enElements = toElements[ast.EnumElement](toEnumElement, protoDollar[1].env.Node, protoDollar[1].env.Runes) + } + case 220: + protoDollar = protoS[protopt-1 : protopt+1] + { + protoVAL.enElements = toElements[ast.EnumElement](toEnumElement, protoDollar[1].resvd.Node, protoDollar[1].resvd.Runes) + } + case 221: + protoDollar = protoS[protopt-1 : protopt+1] + { + protoVAL.enElements = nil + } + case 222: + protoDollar = protoS[protopt-4 : protopt+1] + { + semi, extra := protolex.(*protoLex).requireSemicolon(protoDollar[4].bs) + protoVAL.env = newNodeWithRunes(ast.NewEnumValueNode(protoDollar[1].id, protoDollar[2].b, protoDollar[3].il, nil, semi), extra...) + } + case 223: + protoDollar = protoS[protopt-5 : protopt+1] + { + semi, extra := protolex.(*protoLex).requireSemicolon(protoDollar[5].bs) + protoVAL.env = newNodeWithRunes(ast.NewEnumValueNode(protoDollar[1].id, protoDollar[2].b, protoDollar[3].il, protoDollar[4].cmpctOpts, semi), extra...) + } + case 224: + protoDollar = protoS[protopt-6 : protopt+1] + { + protoVAL.msg = newNodeWithRunes(ast.NewMessageNode(protoDollar[1].id.ToKeyword(), protoDollar[2].id, protoDollar[3].b, protoDollar[4].msgElements, protoDollar[5].b), protoDollar[6].bs...) + } + case 225: + protoDollar = protoS[protopt-1 : protopt+1] + { + protoVAL.msgElements = prependRunes(toMessageElement, protoDollar[1].bs, nil) + } + case 226: + protoDollar = protoS[protopt-2 : protopt+1] + { + protoVAL.msgElements = prependRunes(toMessageElement, protoDollar[1].bs, protoDollar[2].msgElements) + } + case 227: + protoDollar = protoS[protopt-2 : protopt+1] + { + protoVAL.msgElements = append(protoDollar[1].msgElements, protoDollar[2].msgElements...) + } + case 228: + protoDollar = protoS[protopt-1 : protopt+1] + { + protoVAL.msgElements = protoDollar[1].msgElements + } + case 229: + protoDollar = protoS[protopt-1 : protopt+1] + { + protoVAL.msgElements = toElements[ast.MessageElement](toMessageElement, protoDollar[1].msgFld.Node, protoDollar[1].msgFld.Runes) + } + case 230: + protoDollar = protoS[protopt-1 : protopt+1] + { + protoVAL.msgElements = toElements[ast.MessageElement](toMessageElement, protoDollar[1].en.Node, protoDollar[1].en.Runes) + } + case 231: + protoDollar = protoS[protopt-1 : protopt+1] + { + protoVAL.msgElements = toElements[ast.MessageElement](toMessageElement, protoDollar[1].msg.Node, protoDollar[1].msg.Runes) + } + case 232: + protoDollar = protoS[protopt-1 : protopt+1] + { + protoVAL.msgElements = toElements[ast.MessageElement](toMessageElement, protoDollar[1].extend.Node, protoDollar[1].extend.Runes) + } + case 233: + protoDollar = protoS[protopt-1 : protopt+1] + { + protoVAL.msgElements = toElements[ast.MessageElement](toMessageElement, protoDollar[1].ext.Node, protoDollar[1].ext.Runes) + } + case 234: + protoDollar = protoS[protopt-1 : protopt+1] + { + protoVAL.msgElements = toElements[ast.MessageElement](toMessageElement, protoDollar[1].msgGrp.Node, protoDollar[1].msgGrp.Runes) + } + case 235: + protoDollar = protoS[protopt-1 : protopt+1] + { + protoVAL.msgElements = toElements[ast.MessageElement](toMessageElement, protoDollar[1].opt.Node, protoDollar[1].opt.Runes) + } + case 236: + protoDollar = protoS[protopt-1 : protopt+1] + { + protoVAL.msgElements = toElements[ast.MessageElement](toMessageElement, protoDollar[1].oo.Node, protoDollar[1].oo.Runes) + } + case 237: + protoDollar = protoS[protopt-1 : protopt+1] + { + protoVAL.msgElements = toElements[ast.MessageElement](toMessageElement, protoDollar[1].mapFld.Node, protoDollar[1].mapFld.Runes) + } + case 238: + protoDollar = protoS[protopt-1 : protopt+1] + { + protoVAL.msgElements = toElements[ast.MessageElement](toMessageElement, protoDollar[1].resvd.Node, protoDollar[1].resvd.Runes) + } + case 239: + protoDollar = protoS[protopt-1 : protopt+1] + { + protoVAL.msgElements = nil + } + case 240: + protoDollar = protoS[protopt-6 : protopt+1] + { + semis, extra := protolex.(*protoLex).requireSemicolon(protoDollar[6].bs) + protoVAL.msgFld = newNodeWithRunes(ast.NewFieldNode(protoDollar[1].id.ToKeyword(), protoDollar[2].tid, protoDollar[3].id, protoDollar[4].b, protoDollar[5].i, nil, semis), extra...) + } + case 241: + protoDollar = protoS[protopt-7 : protopt+1] + { + semis, extra := protolex.(*protoLex).requireSemicolon(protoDollar[7].bs) + protoVAL.msgFld = newNodeWithRunes(ast.NewFieldNode(protoDollar[1].id.ToKeyword(), protoDollar[2].tid, protoDollar[3].id, protoDollar[4].b, protoDollar[5].i, protoDollar[6].cmpctOpts, semis), extra...) + } + case 242: + protoDollar = protoS[protopt-5 : protopt+1] + { + semis, extra := protolex.(*protoLex).requireSemicolon(protoDollar[5].bs) + protoVAL.msgFld = newNodeWithRunes(ast.NewFieldNode(nil, protoDollar[1].tid, protoDollar[2].id, protoDollar[3].b, protoDollar[4].i, nil, semis), extra...) + } + case 243: + protoDollar = protoS[protopt-6 : protopt+1] + { + semis, extra := protolex.(*protoLex).requireSemicolon(protoDollar[6].bs) + protoVAL.msgFld = newNodeWithRunes(ast.NewFieldNode(nil, protoDollar[1].tid, protoDollar[2].id, protoDollar[3].b, protoDollar[4].i, protoDollar[5].cmpctOpts, semis), extra...) + } + case 244: + protoDollar = protoS[protopt-4 : protopt+1] + { + semis, extra := protolex.(*protoLex).requireSemicolon(protoDollar[4].bs) + protoVAL.msgFld = newNodeWithRunes(ast.NewFieldNode(protoDollar[1].id.ToKeyword(), protoDollar[2].tid, protoDollar[3].id, nil, nil, nil, semis), extra...) + } + case 245: + protoDollar = protoS[protopt-5 : protopt+1] + { + semis, extra := protolex.(*protoLex).requireSemicolon(protoDollar[5].bs) + protoVAL.msgFld = newNodeWithRunes(ast.NewFieldNode(protoDollar[1].id.ToKeyword(), protoDollar[2].tid, protoDollar[3].id, nil, nil, protoDollar[4].cmpctOpts, semis), extra...) + } + case 246: + protoDollar = protoS[protopt-3 : protopt+1] + { + semis, extra := protolex.(*protoLex).requireSemicolon(protoDollar[3].bs) + protoVAL.msgFld = newNodeWithRunes(ast.NewFieldNode(nil, protoDollar[1].tid, protoDollar[2].id, nil, nil, nil, semis), extra...) + } + case 247: + protoDollar = protoS[protopt-4 : protopt+1] + { + semis, extra := protolex.(*protoLex).requireSemicolon(protoDollar[4].bs) + protoVAL.msgFld = newNodeWithRunes(ast.NewFieldNode(nil, protoDollar[1].tid, protoDollar[2].id, nil, nil, protoDollar[3].cmpctOpts, semis), extra...) + } + case 248: + protoDollar = protoS[protopt-6 : protopt+1] + { + protoVAL.extend = newNodeWithRunes(ast.NewExtendNode(protoDollar[1].id.ToKeyword(), protoDollar[2].tid, protoDollar[3].b, protoDollar[4].extElements, protoDollar[5].b), protoDollar[6].bs...) + } + case 249: + protoDollar = protoS[protopt-0 : protopt+1] + { + protoVAL.extElements = nil + } + case 251: + protoDollar = protoS[protopt-2 : protopt+1] + { + if protoDollar[2].extElement != nil { + protoVAL.extElements = append(protoDollar[1].extElements, protoDollar[2].extElement) + } else { + protoVAL.extElements = protoDollar[1].extElements + } + } + case 252: + protoDollar = protoS[protopt-1 : protopt+1] + { + if protoDollar[1].extElement != nil { + protoVAL.extElements = []ast.ExtendElement{protoDollar[1].extElement} + } else { + protoVAL.extElements = nil + } + } + case 253: + protoDollar = protoS[protopt-1 : protopt+1] + { + protoVAL.extElement = protoDollar[1].fld + } + case 254: + protoDollar = protoS[protopt-1 : protopt+1] + { + protoVAL.extElement = protoDollar[1].grp + } + case 255: + protoDollar = protoS[protopt-2 : protopt+1] + { + protoVAL.extElement = nil + } + case 256: + protoDollar = protoS[protopt-1 : protopt+1] + { + protoVAL.extElement = nil + } + case 257: + protoDollar = protoS[protopt-6 : protopt+1] + { + protoVAL.fld = ast.NewFieldNode(protoDollar[1].id.ToKeyword(), protoDollar[2].tid, protoDollar[3].id, protoDollar[4].b, protoDollar[5].i, nil, protoDollar[6].b) + } + case 258: + protoDollar = protoS[protopt-7 : protopt+1] + { + protoVAL.fld = ast.NewFieldNode(protoDollar[1].id.ToKeyword(), protoDollar[2].tid, protoDollar[3].id, protoDollar[4].b, protoDollar[5].i, protoDollar[6].cmpctOpts, protoDollar[7].b) + } + case 259: + protoDollar = protoS[protopt-5 : protopt+1] + { + protoVAL.fld = ast.NewFieldNode(nil, protoDollar[1].tid, protoDollar[2].id, protoDollar[3].b, protoDollar[4].i, nil, protoDollar[5].b) + } + case 260: + protoDollar = protoS[protopt-6 : protopt+1] + { + protoVAL.fld = ast.NewFieldNode(nil, protoDollar[1].tid, protoDollar[2].id, protoDollar[3].b, protoDollar[4].i, protoDollar[5].cmpctOpts, protoDollar[6].b) + } + case 261: + protoDollar = protoS[protopt-6 : protopt+1] + { + protoVAL.svc = newNodeWithRunes(ast.NewServiceNode(protoDollar[1].id.ToKeyword(), protoDollar[2].id, protoDollar[3].b, protoDollar[4].svcElements, protoDollar[5].b), protoDollar[6].bs...) + } + case 262: + protoDollar = protoS[protopt-1 : protopt+1] + { + protoVAL.svcElements = prependRunes(toServiceElement, protoDollar[1].bs, nil) + } + case 263: + protoDollar = protoS[protopt-2 : protopt+1] + { + protoVAL.svcElements = prependRunes(toServiceElement, protoDollar[1].bs, protoDollar[2].svcElements) + } + case 264: + protoDollar = protoS[protopt-2 : protopt+1] + { + protoVAL.svcElements = append(protoDollar[1].svcElements, protoDollar[2].svcElements...) + } + case 265: + protoDollar = protoS[protopt-1 : protopt+1] + { + protoVAL.svcElements = protoDollar[1].svcElements + } + case 266: + protoDollar = protoS[protopt-1 : protopt+1] + { + protoVAL.svcElements = toElements[ast.ServiceElement](toServiceElement, protoDollar[1].opt.Node, protoDollar[1].opt.Runes) + } + case 267: + protoDollar = protoS[protopt-1 : protopt+1] + { + protoVAL.svcElements = toElements[ast.ServiceElement](toServiceElement, protoDollar[1].mtd.Node, protoDollar[1].mtd.Runes) + } + case 268: + protoDollar = protoS[protopt-1 : protopt+1] + { + protoVAL.svcElements = nil + } + case 269: + protoDollar = protoS[protopt-6 : protopt+1] + { + semi, extra := protolex.(*protoLex).requireSemicolon(protoDollar[6].bs) + protoVAL.mtd = newNodeWithRunes(ast.NewRPCNode(protoDollar[1].id.ToKeyword(), protoDollar[2].id, protoDollar[3].mtdMsgType, protoDollar[4].id.ToKeyword(), protoDollar[5].mtdMsgType, semi), extra...) + } + case 270: + protoDollar = protoS[protopt-9 : protopt+1] + { + protoVAL.mtd = newNodeWithRunes(ast.NewRPCNodeWithBody(protoDollar[1].id.ToKeyword(), protoDollar[2].id, protoDollar[3].mtdMsgType, protoDollar[4].id.ToKeyword(), protoDollar[5].mtdMsgType, protoDollar[6].b, protoDollar[7].mtdElements, protoDollar[8].b), protoDollar[9].bs...) + } + case 271: + protoDollar = protoS[protopt-4 : protopt+1] + { + protoVAL.mtdMsgType = ast.NewRPCTypeNode(protoDollar[1].b, protoDollar[2].id.ToKeyword(), protoDollar[3].tid, protoDollar[4].b) + } + case 272: + protoDollar = protoS[protopt-3 : protopt+1] + { + protoVAL.mtdMsgType = ast.NewRPCTypeNode(protoDollar[1].b, nil, protoDollar[2].tid, protoDollar[3].b) + } + case 273: + protoDollar = protoS[protopt-1 : protopt+1] + { + protoVAL.mtdElements = prependRunes(toMethodElement, protoDollar[1].bs, nil) + } + case 274: + protoDollar = protoS[protopt-2 : protopt+1] + { + protoVAL.mtdElements = prependRunes(toMethodElement, protoDollar[1].bs, protoDollar[2].mtdElements) + } + case 275: + protoDollar = protoS[protopt-2 : protopt+1] + { + protoVAL.mtdElements = append(protoDollar[1].mtdElements, protoDollar[2].mtdElements...) + } + case 276: + protoDollar = protoS[protopt-1 : protopt+1] + { + protoVAL.mtdElements = protoDollar[1].mtdElements + } + case 277: + protoDollar = protoS[protopt-1 : protopt+1] + { + protoVAL.mtdElements = toElements[ast.RPCElement](toMethodElement, protoDollar[1].opt.Node, protoDollar[1].opt.Runes) + } + case 278: + protoDollar = protoS[protopt-1 : protopt+1] + { + protoVAL.mtdElements = nil + } + } + goto protostack /* stack new state and value */ +} diff --git a/vendor/github.com/bufbuild/protocompile/parser/result.go b/vendor/github.com/bufbuild/protocompile/parser/result.go new file mode 100644 index 0000000..4aa83e7 --- /dev/null +++ b/vendor/github.com/bufbuild/protocompile/parser/result.go @@ -0,0 +1,1012 @@ +// Copyright 2020-2024 Buf Technologies, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package parser + +import ( + "bytes" + "fmt" + "math" + "sort" + "strings" + "unicode" + + "google.golang.org/protobuf/proto" + "google.golang.org/protobuf/reflect/protoreflect" + "google.golang.org/protobuf/types/descriptorpb" + + "github.com/bufbuild/protocompile/ast" + "github.com/bufbuild/protocompile/internal" + "github.com/bufbuild/protocompile/internal/editions" + "github.com/bufbuild/protocompile/reporter" +) + +type result struct { + file *ast.FileNode + proto *descriptorpb.FileDescriptorProto + + nodes map[proto.Message]ast.Node + ifNoAST *ast.NoSourceNode +} + +// ResultWithoutAST returns a parse result that has no AST. All methods for +// looking up AST nodes return a placeholder node that contains only the filename +// in position information. +func ResultWithoutAST(proto *descriptorpb.FileDescriptorProto) Result { + return &result{proto: proto, ifNoAST: ast.NewNoSourceNode(proto.GetName())} +} + +// ResultFromAST constructs a descriptor proto from the given AST. The returned +// result includes the descriptor proto and also contains an index that can be +// used to lookup AST node information for elements in the descriptor proto +// hierarchy. +// +// If validate is true, some basic validation is performed, to make sure the +// resulting descriptor proto is valid per protobuf rules and semantics. Only +// some language elements can be validated since some rules and semantics can +// only be checked after all symbols are all resolved, which happens in the +// linking step. +// +// The given handler is used to report any errors or warnings encountered. If any +// errors are reported, this function returns a non-nil error. +func ResultFromAST(file *ast.FileNode, validate bool, handler *reporter.Handler) (Result, error) { + filename := file.Name() + r := &result{file: file, nodes: map[proto.Message]ast.Node{}} + r.createFileDescriptor(filename, file, handler) + if validate { + validateBasic(r, handler) + } + // Now that we're done validating, we can set any missing labels to optional + // (we leave them absent in first pass if label was missing in source, so we + // can do validation on presence of label, but final descriptors are expected + // to always have them present). + fillInMissingLabels(r.proto) + return r, handler.Error() +} + +func (r *result) AST() *ast.FileNode { + return r.file +} + +func (r *result) FileDescriptorProto() *descriptorpb.FileDescriptorProto { + return r.proto +} + +func (r *result) createFileDescriptor(filename string, file *ast.FileNode, handler *reporter.Handler) { + fd := &descriptorpb.FileDescriptorProto{Name: proto.String(filename)} + r.proto = fd + + r.putFileNode(fd, file) + + var syntax protoreflect.Syntax + switch { + case file.Syntax != nil: + switch file.Syntax.Syntax.AsString() { + case "proto3": + syntax = protoreflect.Proto3 + case "proto2": + syntax = protoreflect.Proto2 + default: + nodeInfo := file.NodeInfo(file.Syntax.Syntax) + if handler.HandleErrorf(nodeInfo, `syntax value must be "proto2" or "proto3"`) != nil { + return + } + } + + // proto2 is the default, so no need to set for that value + if syntax != protoreflect.Proto2 { + fd.Syntax = proto.String(file.Syntax.Syntax.AsString()) + } + case file.Edition != nil: + edition := file.Edition.Edition.AsString() + syntax = protoreflect.Editions + + fd.Syntax = proto.String("editions") + editionEnum, ok := editions.SupportedEditions[edition] + if !ok { + nodeInfo := file.NodeInfo(file.Edition.Edition) + editionStrs := make([]string, 0, len(editions.SupportedEditions)) + for supportedEdition := range editions.SupportedEditions { + editionStrs = append(editionStrs, fmt.Sprintf("%q", supportedEdition)) + } + sort.Strings(editionStrs) + if handler.HandleErrorf(nodeInfo, `edition value %q not recognized; should be one of [%s]`, edition, strings.Join(editionStrs, ",")) != nil { + return + } + } + fd.Edition = editionEnum.Enum() + default: + syntax = protoreflect.Proto2 + nodeInfo := file.NodeInfo(file) + handler.HandleWarningWithPos(nodeInfo, ErrNoSyntax) + } + + for _, decl := range file.Decls { + if handler.ReporterError() != nil { + return + } + switch decl := decl.(type) { + case *ast.EnumNode: + fd.EnumType = append(fd.EnumType, r.asEnumDescriptor(decl, syntax, handler)) + case *ast.ExtendNode: + r.addExtensions(decl, &fd.Extension, &fd.MessageType, syntax, handler, 0) + case *ast.ImportNode: + index := len(fd.Dependency) + fd.Dependency = append(fd.Dependency, decl.Name.AsString()) + if decl.Public != nil { + fd.PublicDependency = append(fd.PublicDependency, int32(index)) + } else if decl.Weak != nil { + fd.WeakDependency = append(fd.WeakDependency, int32(index)) + } + case *ast.MessageNode: + fd.MessageType = append(fd.MessageType, r.asMessageDescriptor(decl, syntax, handler, 1)) + case *ast.OptionNode: + if fd.Options == nil { + fd.Options = &descriptorpb.FileOptions{} + } + fd.Options.UninterpretedOption = append(fd.Options.UninterpretedOption, r.asUninterpretedOption(decl)) + case *ast.ServiceNode: + fd.Service = append(fd.Service, r.asServiceDescriptor(decl)) + case *ast.PackageNode: + if fd.Package != nil { + nodeInfo := file.NodeInfo(decl) + if handler.HandleErrorf(nodeInfo, "files should have only one package declaration") != nil { + return + } + } + pkgName := string(decl.Name.AsIdentifier()) + if len(pkgName) >= 512 { + nodeInfo := file.NodeInfo(decl.Name) + if handler.HandleErrorf(nodeInfo, "package name (with whitespace removed) must be less than 512 characters long") != nil { + return + } + } + if strings.Count(pkgName, ".") > 100 { + nodeInfo := file.NodeInfo(decl.Name) + if handler.HandleErrorf(nodeInfo, "package name may not contain more than 100 periods") != nil { + return + } + } + fd.Package = proto.String(string(decl.Name.AsIdentifier())) + } + } +} + +func (r *result) asUninterpretedOptions(nodes []*ast.OptionNode) []*descriptorpb.UninterpretedOption { + if len(nodes) == 0 { + return nil + } + opts := make([]*descriptorpb.UninterpretedOption, len(nodes)) + for i, n := range nodes { + opts[i] = r.asUninterpretedOption(n) + } + return opts +} + +func (r *result) asUninterpretedOption(node *ast.OptionNode) *descriptorpb.UninterpretedOption { + opt := &descriptorpb.UninterpretedOption{Name: r.asUninterpretedOptionName(node.Name.Parts)} + r.putOptionNode(opt, node) + + switch val := node.Val.Value().(type) { + case bool: + if val { + opt.IdentifierValue = proto.String("true") + } else { + opt.IdentifierValue = proto.String("false") + } + case int64: + opt.NegativeIntValue = proto.Int64(val) + case uint64: + opt.PositiveIntValue = proto.Uint64(val) + case float64: + opt.DoubleValue = proto.Float64(val) + case string: + opt.StringValue = []byte(val) + case ast.Identifier: + opt.IdentifierValue = proto.String(string(val)) + default: + // the grammar does not allow arrays here, so the only possible case + // left should be []*ast.MessageFieldNode, which corresponds to an + // *ast.MessageLiteralNode + if n, ok := node.Val.(*ast.MessageLiteralNode); ok { + var buf bytes.Buffer + for i, el := range n.Elements { + flattenNode(r.file, el, &buf) + if len(n.Seps) > i && n.Seps[i] != nil { + buf.WriteRune(' ') + buf.WriteRune(n.Seps[i].Rune) + } + } + aggStr := buf.String() + opt.AggregateValue = proto.String(aggStr) + } + // TODO: else that reports an error or panics?? + } + return opt +} + +func flattenNode(f *ast.FileNode, n ast.Node, buf *bytes.Buffer) { + if cn, ok := n.(ast.CompositeNode); ok { + for _, ch := range cn.Children() { + flattenNode(f, ch, buf) + } + return + } + + if buf.Len() > 0 { + buf.WriteRune(' ') + } + buf.WriteString(f.NodeInfo(n).RawText()) +} + +func (r *result) asUninterpretedOptionName(parts []*ast.FieldReferenceNode) []*descriptorpb.UninterpretedOption_NamePart { + ret := make([]*descriptorpb.UninterpretedOption_NamePart, len(parts)) + for i, part := range parts { + np := &descriptorpb.UninterpretedOption_NamePart{ + NamePart: proto.String(string(part.Name.AsIdentifier())), + IsExtension: proto.Bool(part.IsExtension()), + } + r.putOptionNamePartNode(np, part) + ret[i] = np + } + return ret +} + +func (r *result) addExtensions(ext *ast.ExtendNode, flds *[]*descriptorpb.FieldDescriptorProto, msgs *[]*descriptorpb.DescriptorProto, syntax protoreflect.Syntax, handler *reporter.Handler, depth int) { + extendee := string(ext.Extendee.AsIdentifier()) + count := 0 + for _, decl := range ext.Decls { + switch decl := decl.(type) { + case *ast.FieldNode: + count++ + // use higher limit since we don't know yet whether extendee is messageset wire format + fd := r.asFieldDescriptor(decl, internal.MaxTag, syntax, handler) + fd.Extendee = proto.String(extendee) + *flds = append(*flds, fd) + case *ast.GroupNode: + count++ + // ditto: use higher limit right now + fd, md := r.asGroupDescriptors(decl, syntax, internal.MaxTag, handler, depth+1) + fd.Extendee = proto.String(extendee) + *flds = append(*flds, fd) + *msgs = append(*msgs, md) + } + } + if count == 0 { + nodeInfo := r.file.NodeInfo(ext) + _ = handler.HandleErrorf(nodeInfo, "extend sections must define at least one extension") + } +} + +func asLabel(lbl *ast.FieldLabel) *descriptorpb.FieldDescriptorProto_Label { + if !lbl.IsPresent() { + return nil + } + switch { + case lbl.Repeated: + return descriptorpb.FieldDescriptorProto_LABEL_REPEATED.Enum() + case lbl.Required: + return descriptorpb.FieldDescriptorProto_LABEL_REQUIRED.Enum() + default: + return descriptorpb.FieldDescriptorProto_LABEL_OPTIONAL.Enum() + } +} + +func (r *result) asFieldDescriptor(node *ast.FieldNode, maxTag int32, syntax protoreflect.Syntax, handler *reporter.Handler) *descriptorpb.FieldDescriptorProto { + var tag *int32 + if node.Tag != nil { + if err := r.checkTag(node.Tag, node.Tag.Val, maxTag); err != nil { + _ = handler.HandleError(err) + } + tag = proto.Int32(int32(node.Tag.Val)) + } + fd := newFieldDescriptor(node.Name.Val, string(node.FldType.AsIdentifier()), tag, asLabel(&node.Label)) + r.putFieldNode(fd, node) + if opts := node.Options.GetElements(); len(opts) > 0 { + fd.Options = &descriptorpb.FieldOptions{UninterpretedOption: r.asUninterpretedOptions(opts)} + } + if syntax == protoreflect.Proto3 && fd.Label != nil && fd.GetLabel() == descriptorpb.FieldDescriptorProto_LABEL_OPTIONAL { + fd.Proto3Optional = proto.Bool(true) + } + return fd +} + +var fieldTypes = map[string]descriptorpb.FieldDescriptorProto_Type{ + "double": descriptorpb.FieldDescriptorProto_TYPE_DOUBLE, + "float": descriptorpb.FieldDescriptorProto_TYPE_FLOAT, + "int32": descriptorpb.FieldDescriptorProto_TYPE_INT32, + "int64": descriptorpb.FieldDescriptorProto_TYPE_INT64, + "uint32": descriptorpb.FieldDescriptorProto_TYPE_UINT32, + "uint64": descriptorpb.FieldDescriptorProto_TYPE_UINT64, + "sint32": descriptorpb.FieldDescriptorProto_TYPE_SINT32, + "sint64": descriptorpb.FieldDescriptorProto_TYPE_SINT64, + "fixed32": descriptorpb.FieldDescriptorProto_TYPE_FIXED32, + "fixed64": descriptorpb.FieldDescriptorProto_TYPE_FIXED64, + "sfixed32": descriptorpb.FieldDescriptorProto_TYPE_SFIXED32, + "sfixed64": descriptorpb.FieldDescriptorProto_TYPE_SFIXED64, + "bool": descriptorpb.FieldDescriptorProto_TYPE_BOOL, + "string": descriptorpb.FieldDescriptorProto_TYPE_STRING, + "bytes": descriptorpb.FieldDescriptorProto_TYPE_BYTES, +} + +func newFieldDescriptor(name string, fieldType string, tag *int32, lbl *descriptorpb.FieldDescriptorProto_Label) *descriptorpb.FieldDescriptorProto { + fd := &descriptorpb.FieldDescriptorProto{ + Name: proto.String(name), + JsonName: proto.String(internal.JSONName(name)), + Number: tag, + Label: lbl, + } + t, ok := fieldTypes[fieldType] + if ok { + fd.Type = t.Enum() + } else { + // NB: we don't have enough info to determine whether this is an enum + // or a message type, so we'll leave Type nil and set it later + // (during linking) + fd.TypeName = proto.String(fieldType) + } + return fd +} + +func (r *result) asGroupDescriptors(group *ast.GroupNode, syntax protoreflect.Syntax, maxTag int32, handler *reporter.Handler, depth int) (*descriptorpb.FieldDescriptorProto, *descriptorpb.DescriptorProto) { + var tag *int32 + if group.Tag != nil { + if err := r.checkTag(group.Tag, group.Tag.Val, maxTag); err != nil { + _ = handler.HandleError(err) + } + tag = proto.Int32(int32(group.Tag.Val)) + } + if !unicode.IsUpper(rune(group.Name.Val[0])) { + nameNodeInfo := r.file.NodeInfo(group.Name) + _ = handler.HandleErrorf(nameNodeInfo, "group %s should have a name that starts with a capital letter", group.Name.Val) + } + fieldName := strings.ToLower(group.Name.Val) + fd := &descriptorpb.FieldDescriptorProto{ + Name: proto.String(fieldName), + JsonName: proto.String(internal.JSONName(fieldName)), + Number: tag, + Label: asLabel(&group.Label), + Type: descriptorpb.FieldDescriptorProto_TYPE_GROUP.Enum(), + TypeName: proto.String(group.Name.Val), + } + r.putFieldNode(fd, group) + if opts := group.Options.GetElements(); len(opts) > 0 { + fd.Options = &descriptorpb.FieldOptions{UninterpretedOption: r.asUninterpretedOptions(opts)} + } + md := &descriptorpb.DescriptorProto{Name: proto.String(group.Name.Val)} + groupMsg := group.AsMessage() + r.putMessageNode(md, groupMsg) + // don't bother processing body if we've exceeded depth + if r.checkDepth(depth, groupMsg, handler) { + r.addMessageBody(md, &group.MessageBody, syntax, handler, depth) + } + return fd, md +} + +func (r *result) asMapDescriptors(mapField *ast.MapFieldNode, syntax protoreflect.Syntax, maxTag int32, handler *reporter.Handler, depth int) (*descriptorpb.FieldDescriptorProto, *descriptorpb.DescriptorProto) { + var tag *int32 + if mapField.Tag != nil { + if err := r.checkTag(mapField.Tag, mapField.Tag.Val, maxTag); err != nil { + _ = handler.HandleError(err) + } + tag = proto.Int32(int32(mapField.Tag.Val)) + } + mapEntry := mapField.AsMessage() + r.checkDepth(depth, mapEntry, handler) + var lbl *descriptorpb.FieldDescriptorProto_Label + if syntax == protoreflect.Proto2 { + lbl = descriptorpb.FieldDescriptorProto_LABEL_OPTIONAL.Enum() + } + keyFd := newFieldDescriptor("key", mapField.MapType.KeyType.Val, proto.Int32(1), lbl) + r.putFieldNode(keyFd, mapField.KeyField()) + valFd := newFieldDescriptor("value", string(mapField.MapType.ValueType.AsIdentifier()), proto.Int32(2), lbl) + r.putFieldNode(valFd, mapField.ValueField()) + entryName := internal.InitCap(internal.JSONName(mapField.Name.Val)) + "Entry" + fd := newFieldDescriptor(mapField.Name.Val, entryName, tag, descriptorpb.FieldDescriptorProto_LABEL_REPEATED.Enum()) + if opts := mapField.Options.GetElements(); len(opts) > 0 { + fd.Options = &descriptorpb.FieldOptions{UninterpretedOption: r.asUninterpretedOptions(opts)} + } + r.putFieldNode(fd, mapField) + md := &descriptorpb.DescriptorProto{ + Name: proto.String(entryName), + Options: &descriptorpb.MessageOptions{MapEntry: proto.Bool(true)}, + Field: []*descriptorpb.FieldDescriptorProto{keyFd, valFd}, + } + r.putMessageNode(md, mapEntry) + return fd, md +} + +func (r *result) asExtensionRanges(node *ast.ExtensionRangeNode, maxTag int32, handler *reporter.Handler) []*descriptorpb.DescriptorProto_ExtensionRange { + opts := r.asUninterpretedOptions(node.Options.GetElements()) + ers := make([]*descriptorpb.DescriptorProto_ExtensionRange, len(node.Ranges)) + for i, rng := range node.Ranges { + start, end := r.getRangeBounds(rng, 1, maxTag, handler) + er := &descriptorpb.DescriptorProto_ExtensionRange{ + Start: proto.Int32(start), + End: proto.Int32(end + 1), + } + if len(opts) > 0 { + er.Options = &descriptorpb.ExtensionRangeOptions{UninterpretedOption: opts} + } + r.putExtensionRangeNode(er, node, rng) + ers[i] = er + } + return ers +} + +func (r *result) asEnumValue(ev *ast.EnumValueNode, handler *reporter.Handler) *descriptorpb.EnumValueDescriptorProto { + num, ok := ast.AsInt32(ev.Number, math.MinInt32, math.MaxInt32) + if !ok { + numberNodeInfo := r.file.NodeInfo(ev.Number) + _ = handler.HandleErrorf(numberNodeInfo, "value %d is out of range: should be between %d and %d", ev.Number.Value(), math.MinInt32, math.MaxInt32) + } + evd := &descriptorpb.EnumValueDescriptorProto{Name: proto.String(ev.Name.Val), Number: proto.Int32(num)} + r.putEnumValueNode(evd, ev) + if opts := ev.Options.GetElements(); len(opts) > 0 { + evd.Options = &descriptorpb.EnumValueOptions{UninterpretedOption: r.asUninterpretedOptions(opts)} + } + return evd +} + +func (r *result) asMethodDescriptor(node *ast.RPCNode) *descriptorpb.MethodDescriptorProto { + md := &descriptorpb.MethodDescriptorProto{ + Name: proto.String(node.Name.Val), + InputType: proto.String(string(node.Input.MessageType.AsIdentifier())), + OutputType: proto.String(string(node.Output.MessageType.AsIdentifier())), + } + r.putMethodNode(md, node) + if node.Input.Stream != nil { + md.ClientStreaming = proto.Bool(true) + } + if node.Output.Stream != nil { + md.ServerStreaming = proto.Bool(true) + } + // protoc always adds a MethodOptions if there are brackets + // We do the same to match protoc as closely as possible + // https://github.com/protocolbuffers/protobuf/blob/0c3f43a6190b77f1f68b7425d1b7e1a8257a8d0c/src/google/protobuf/compiler/parser.cc#L2152 + if node.OpenBrace != nil { + md.Options = &descriptorpb.MethodOptions{} + for _, decl := range node.Decls { + if option, ok := decl.(*ast.OptionNode); ok { + md.Options.UninterpretedOption = append(md.Options.UninterpretedOption, r.asUninterpretedOption(option)) + } + } + } + return md +} + +func (r *result) asEnumDescriptor(en *ast.EnumNode, syntax protoreflect.Syntax, handler *reporter.Handler) *descriptorpb.EnumDescriptorProto { + ed := &descriptorpb.EnumDescriptorProto{Name: proto.String(en.Name.Val)} + r.putEnumNode(ed, en) + rsvdNames := map[string]ast.SourcePos{} + for _, decl := range en.Decls { + switch decl := decl.(type) { + case *ast.OptionNode: + if ed.Options == nil { + ed.Options = &descriptorpb.EnumOptions{} + } + ed.Options.UninterpretedOption = append(ed.Options.UninterpretedOption, r.asUninterpretedOption(decl)) + case *ast.EnumValueNode: + ed.Value = append(ed.Value, r.asEnumValue(decl, handler)) + case *ast.ReservedNode: + r.addReservedNames(&ed.ReservedName, decl, syntax, handler, rsvdNames) + for _, rng := range decl.Ranges { + ed.ReservedRange = append(ed.ReservedRange, r.asEnumReservedRange(rng, handler)) + } + } + } + return ed +} + +func (r *result) asEnumReservedRange(rng *ast.RangeNode, handler *reporter.Handler) *descriptorpb.EnumDescriptorProto_EnumReservedRange { + start, end := r.getRangeBounds(rng, math.MinInt32, math.MaxInt32, handler) + rr := &descriptorpb.EnumDescriptorProto_EnumReservedRange{ + Start: proto.Int32(start), + End: proto.Int32(end), + } + r.putEnumReservedRangeNode(rr, rng) + return rr +} + +func (r *result) asMessageDescriptor(node *ast.MessageNode, syntax protoreflect.Syntax, handler *reporter.Handler, depth int) *descriptorpb.DescriptorProto { + msgd := &descriptorpb.DescriptorProto{Name: proto.String(node.Name.Val)} + r.putMessageNode(msgd, node) + // don't bother processing body if we've exceeded depth + if r.checkDepth(depth, node, handler) { + r.addMessageBody(msgd, &node.MessageBody, syntax, handler, depth) + } + return msgd +} + +func (r *result) addReservedNames(names *[]string, node *ast.ReservedNode, syntax protoreflect.Syntax, handler *reporter.Handler, alreadyReserved map[string]ast.SourcePos) { + if syntax == protoreflect.Editions { + if len(node.Names) > 0 { + nameNodeInfo := r.file.NodeInfo(node.Names[0]) + _ = handler.HandleErrorf(nameNodeInfo, `must use identifiers, not string literals, to reserved names with editions`) + } + for _, n := range node.Identifiers { + name := string(n.AsIdentifier()) + nameNodeInfo := r.file.NodeInfo(n) + if existing, ok := alreadyReserved[name]; ok { + _ = handler.HandleErrorf(nameNodeInfo, "name %q is already reserved at %s", name, existing) + continue + } + alreadyReserved[name] = nameNodeInfo.Start() + *names = append(*names, name) + } + return + } + + if len(node.Identifiers) > 0 { + nameNodeInfo := r.file.NodeInfo(node.Identifiers[0]) + _ = handler.HandleErrorf(nameNodeInfo, `must use string literals, not identifiers, to reserved names with proto2 and proto3`) + } + for _, n := range node.Names { + name := n.AsString() + nameNodeInfo := r.file.NodeInfo(n) + if existing, ok := alreadyReserved[name]; ok { + _ = handler.HandleErrorf(nameNodeInfo, "name %q is already reserved at %s", name, existing) + continue + } + alreadyReserved[name] = nameNodeInfo.Start() + *names = append(*names, name) + } +} + +func (r *result) checkDepth(depth int, node ast.MessageDeclNode, handler *reporter.Handler) bool { + if depth < 32 { + return true + } + n := ast.Node(node) + if grp, ok := n.(*ast.SyntheticGroupMessageNode); ok { + // pinpoint the group keyword if the source is a group + n = grp.Keyword + } + _ = handler.HandleErrorf(r.file.NodeInfo(n), "message nesting depth must be less than 32") + return false +} + +func (r *result) addMessageBody(msgd *descriptorpb.DescriptorProto, body *ast.MessageBody, syntax protoreflect.Syntax, handler *reporter.Handler, depth int) { + // first process any options + for _, decl := range body.Decls { + if opt, ok := decl.(*ast.OptionNode); ok { + if msgd.Options == nil { + msgd.Options = &descriptorpb.MessageOptions{} + } + msgd.Options.UninterpretedOption = append(msgd.Options.UninterpretedOption, r.asUninterpretedOption(opt)) + } + } + + // now that we have options, we can see if this uses messageset wire format, which + // impacts how we validate tag numbers in any fields in the message + maxTag := int32(internal.MaxNormalTag) + messageSetOpt, err := r.isMessageSetWireFormat("message "+msgd.GetName(), msgd, handler) + if err != nil { + return + } else if messageSetOpt != nil { + if syntax == protoreflect.Proto3 { + node := r.OptionNode(messageSetOpt) + nodeInfo := r.file.NodeInfo(node) + _ = handler.HandleErrorf(nodeInfo, "messages with message-set wire format are not allowed with proto3 syntax") + } + maxTag = internal.MaxTag // higher limit for messageset wire format + } + + rsvdNames := map[string]ast.SourcePos{} + + // now we can process the rest + for _, decl := range body.Decls { + switch decl := decl.(type) { + case *ast.EnumNode: + msgd.EnumType = append(msgd.EnumType, r.asEnumDescriptor(decl, syntax, handler)) + case *ast.ExtendNode: + r.addExtensions(decl, &msgd.Extension, &msgd.NestedType, syntax, handler, depth) + case *ast.ExtensionRangeNode: + msgd.ExtensionRange = append(msgd.ExtensionRange, r.asExtensionRanges(decl, maxTag, handler)...) + case *ast.FieldNode: + fd := r.asFieldDescriptor(decl, maxTag, syntax, handler) + msgd.Field = append(msgd.Field, fd) + case *ast.MapFieldNode: + fd, md := r.asMapDescriptors(decl, syntax, maxTag, handler, depth+1) + msgd.Field = append(msgd.Field, fd) + msgd.NestedType = append(msgd.NestedType, md) + case *ast.GroupNode: + fd, md := r.asGroupDescriptors(decl, syntax, maxTag, handler, depth+1) + msgd.Field = append(msgd.Field, fd) + msgd.NestedType = append(msgd.NestedType, md) + case *ast.OneofNode: + oodIndex := len(msgd.OneofDecl) + ood := &descriptorpb.OneofDescriptorProto{Name: proto.String(decl.Name.Val)} + r.putOneofNode(ood, decl) + msgd.OneofDecl = append(msgd.OneofDecl, ood) + ooFields := 0 + for _, oodecl := range decl.Decls { + switch oodecl := oodecl.(type) { + case *ast.OptionNode: + if ood.Options == nil { + ood.Options = &descriptorpb.OneofOptions{} + } + ood.Options.UninterpretedOption = append(ood.Options.UninterpretedOption, r.asUninterpretedOption(oodecl)) + case *ast.FieldNode: + fd := r.asFieldDescriptor(oodecl, maxTag, syntax, handler) + fd.OneofIndex = proto.Int32(int32(oodIndex)) + msgd.Field = append(msgd.Field, fd) + ooFields++ + case *ast.GroupNode: + fd, md := r.asGroupDescriptors(oodecl, syntax, maxTag, handler, depth+1) + fd.OneofIndex = proto.Int32(int32(oodIndex)) + msgd.Field = append(msgd.Field, fd) + msgd.NestedType = append(msgd.NestedType, md) + ooFields++ + } + } + if ooFields == 0 { + declNodeInfo := r.file.NodeInfo(decl) + _ = handler.HandleErrorf(declNodeInfo, "oneof must contain at least one field") + } + case *ast.MessageNode: + msgd.NestedType = append(msgd.NestedType, r.asMessageDescriptor(decl, syntax, handler, depth+1)) + case *ast.ReservedNode: + r.addReservedNames(&msgd.ReservedName, decl, syntax, handler, rsvdNames) + for _, rng := range decl.Ranges { + msgd.ReservedRange = append(msgd.ReservedRange, r.asMessageReservedRange(rng, maxTag, handler)) + } + } + } + + if messageSetOpt != nil { + if len(msgd.Field) > 0 { + node := r.FieldNode(msgd.Field[0]) + nodeInfo := r.file.NodeInfo(node) + _ = handler.HandleErrorf(nodeInfo, "messages with message-set wire format cannot contain non-extension fields") + } + if len(msgd.ExtensionRange) == 0 { + node := r.OptionNode(messageSetOpt) + nodeInfo := r.file.NodeInfo(node) + _ = handler.HandleErrorf(nodeInfo, "messages with message-set wire format must contain at least one extension range") + } + } + + // process any proto3_optional fields + if syntax == protoreflect.Proto3 { + r.processProto3OptionalFields(msgd) + } +} + +func (r *result) isMessageSetWireFormat(scope string, md *descriptorpb.DescriptorProto, handler *reporter.Handler) (*descriptorpb.UninterpretedOption, error) { + uo := md.GetOptions().GetUninterpretedOption() + index, err := internal.FindOption(r, handler.HandleErrorf, scope, uo, "message_set_wire_format") + if err != nil { + return nil, err + } + if index == -1 { + // no such option + return nil, nil + } + + opt := uo[index] + + switch opt.GetIdentifierValue() { + case "true": + return opt, nil + case "false": + return nil, nil + default: + optNode := r.OptionNode(opt) + optNodeInfo := r.file.NodeInfo(optNode.GetValue()) + return nil, handler.HandleErrorf(optNodeInfo, "%s: expecting bool value for message_set_wire_format option", scope) + } +} + +func (r *result) asMessageReservedRange(rng *ast.RangeNode, maxTag int32, handler *reporter.Handler) *descriptorpb.DescriptorProto_ReservedRange { + start, end := r.getRangeBounds(rng, 1, maxTag, handler) + rr := &descriptorpb.DescriptorProto_ReservedRange{ + Start: proto.Int32(start), + End: proto.Int32(end + 1), + } + r.putMessageReservedRangeNode(rr, rng) + return rr +} + +func (r *result) getRangeBounds(rng *ast.RangeNode, minVal, maxVal int32, handler *reporter.Handler) (int32, int32) { + checkOrder := true + start, ok := rng.StartValueAsInt32(minVal, maxVal) + if !ok { + checkOrder = false + startValNodeInfo := r.file.NodeInfo(rng.StartVal) + _ = handler.HandleErrorf(startValNodeInfo, "range start %d is out of range: should be between %d and %d", rng.StartValue(), minVal, maxVal) + } + + end, ok := rng.EndValueAsInt32(minVal, maxVal) + if !ok { + checkOrder = false + if rng.EndVal != nil { + endValNodeInfo := r.file.NodeInfo(rng.EndVal) + _ = handler.HandleErrorf(endValNodeInfo, "range end %d is out of range: should be between %d and %d", rng.EndValue(), minVal, maxVal) + } + } + + if checkOrder && start > end { + rangeStartNodeInfo := r.file.NodeInfo(rng.RangeStart()) + _ = handler.HandleErrorf(rangeStartNodeInfo, "range, %d to %d, is invalid: start must be <= end", start, end) + } + + return start, end +} + +func (r *result) asServiceDescriptor(svc *ast.ServiceNode) *descriptorpb.ServiceDescriptorProto { + sd := &descriptorpb.ServiceDescriptorProto{Name: proto.String(svc.Name.Val)} + r.putServiceNode(sd, svc) + for _, decl := range svc.Decls { + switch decl := decl.(type) { + case *ast.OptionNode: + if sd.Options == nil { + sd.Options = &descriptorpb.ServiceOptions{} + } + sd.Options.UninterpretedOption = append(sd.Options.UninterpretedOption, r.asUninterpretedOption(decl)) + case *ast.RPCNode: + sd.Method = append(sd.Method, r.asMethodDescriptor(decl)) + } + } + return sd +} + +func (r *result) checkTag(n ast.Node, v uint64, maxTag int32) error { + switch { + case v < 1: + return reporter.Errorf(r.file.NodeInfo(n), "tag number %d must be greater than zero", v) + case v > uint64(maxTag): + return reporter.Errorf(r.file.NodeInfo(n), "tag number %d is higher than max allowed tag number (%d)", v, maxTag) + case v >= internal.SpecialReservedStart && v <= internal.SpecialReservedEnd: + return reporter.Errorf(r.file.NodeInfo(n), "tag number %d is in disallowed reserved range %d-%d", v, internal.SpecialReservedStart, internal.SpecialReservedEnd) + default: + return nil + } +} + +// processProto3OptionalFields adds synthetic oneofs to the given message descriptor +// for each proto3 optional field. It also updates the fields to have the correct +// oneof index reference. +func (r *result) processProto3OptionalFields(msgd *descriptorpb.DescriptorProto) { + // add synthetic oneofs to the given message descriptor for each proto3 + // optional field, and update each field to have correct oneof index + var allNames map[string]struct{} + for _, fd := range msgd.Field { + if fd.GetProto3Optional() { + // lazy init the set of all names + if allNames == nil { + allNames = map[string]struct{}{} + for _, fd := range msgd.Field { + allNames[fd.GetName()] = struct{}{} + } + for _, od := range msgd.OneofDecl { + allNames[od.GetName()] = struct{}{} + } + // NB: protoc only considers names of other fields and oneofs + // when computing the synthetic oneof name. But that feels like + // a bug, since it means it could generate a name that conflicts + // with some other symbol defined in the message. If it's decided + // that's NOT a bug and is desirable, then we should remove the + // following four loops to mimic protoc's behavior. + for _, fd := range msgd.Extension { + allNames[fd.GetName()] = struct{}{} + } + for _, ed := range msgd.EnumType { + allNames[ed.GetName()] = struct{}{} + for _, evd := range ed.Value { + allNames[evd.GetName()] = struct{}{} + } + } + for _, fd := range msgd.NestedType { + allNames[fd.GetName()] = struct{}{} + } + } + + // Compute a name for the synthetic oneof. This uses the same + // algorithm as used in protoc: + // https://github.com/protocolbuffers/protobuf/blob/74ad62759e0a9b5a21094f3fb9bb4ebfaa0d1ab8/src/google/protobuf/compiler/parser.cc#L785-L803 + ooName := fd.GetName() + if !strings.HasPrefix(ooName, "_") { + ooName = "_" + ooName + } + for { + _, ok := allNames[ooName] + if !ok { + // found a unique name + allNames[ooName] = struct{}{} + break + } + ooName = "X" + ooName + } + + fd.OneofIndex = proto.Int32(int32(len(msgd.OneofDecl))) + ood := &descriptorpb.OneofDescriptorProto{Name: proto.String(ooName)} + msgd.OneofDecl = append(msgd.OneofDecl, ood) + ooident := r.FieldNode(fd).(*ast.FieldNode) //nolint:errcheck + r.putOneofNode(ood, ast.NewSyntheticOneof(ooident)) + } + } +} + +func (r *result) Node(m proto.Message) ast.Node { + if r.nodes == nil { + return r.ifNoAST + } + return r.nodes[m] +} + +func (r *result) FileNode() ast.FileDeclNode { + if r.nodes == nil { + return r.ifNoAST + } + return r.nodes[r.proto].(ast.FileDeclNode) +} + +func (r *result) OptionNode(o *descriptorpb.UninterpretedOption) ast.OptionDeclNode { + if r.nodes == nil { + return r.ifNoAST + } + return r.nodes[o].(ast.OptionDeclNode) +} + +func (r *result) OptionNamePartNode(o *descriptorpb.UninterpretedOption_NamePart) ast.Node { + if r.nodes == nil { + return r.ifNoAST + } + return r.nodes[o] +} + +func (r *result) MessageNode(m *descriptorpb.DescriptorProto) ast.MessageDeclNode { + if r.nodes == nil { + return r.ifNoAST + } + return r.nodes[m].(ast.MessageDeclNode) +} + +func (r *result) FieldNode(f *descriptorpb.FieldDescriptorProto) ast.FieldDeclNode { + if r.nodes == nil { + return r.ifNoAST + } + return r.nodes[f].(ast.FieldDeclNode) +} + +func (r *result) OneofNode(o *descriptorpb.OneofDescriptorProto) ast.OneofDeclNode { + if r.nodes == nil { + return r.ifNoAST + } + return r.nodes[o].(ast.OneofDeclNode) +} + +func (r *result) ExtensionsNode(e *descriptorpb.DescriptorProto_ExtensionRange) ast.NodeWithOptions { + if r.nodes == nil { + return r.ifNoAST + } + return r.nodes[asExtsNode(e)].(ast.NodeWithOptions) +} + +func (r *result) ExtensionRangeNode(e *descriptorpb.DescriptorProto_ExtensionRange) ast.RangeDeclNode { + if r.nodes == nil { + return r.ifNoAST + } + return r.nodes[e].(ast.RangeDeclNode) +} + +func (r *result) MessageReservedRangeNode(rr *descriptorpb.DescriptorProto_ReservedRange) ast.RangeDeclNode { + if r.nodes == nil { + return r.ifNoAST + } + return r.nodes[rr].(ast.RangeDeclNode) +} + +func (r *result) EnumNode(e *descriptorpb.EnumDescriptorProto) ast.NodeWithOptions { + if r.nodes == nil { + return r.ifNoAST + } + return r.nodes[e].(ast.NodeWithOptions) +} + +func (r *result) EnumValueNode(e *descriptorpb.EnumValueDescriptorProto) ast.EnumValueDeclNode { + if r.nodes == nil { + return r.ifNoAST + } + return r.nodes[e].(ast.EnumValueDeclNode) +} + +func (r *result) EnumReservedRangeNode(rr *descriptorpb.EnumDescriptorProto_EnumReservedRange) ast.RangeDeclNode { + if r.nodes == nil { + return r.ifNoAST + } + return r.nodes[rr].(ast.RangeDeclNode) +} + +func (r *result) ServiceNode(s *descriptorpb.ServiceDescriptorProto) ast.NodeWithOptions { + if r.nodes == nil { + return r.ifNoAST + } + return r.nodes[s].(ast.NodeWithOptions) +} + +func (r *result) MethodNode(m *descriptorpb.MethodDescriptorProto) ast.RPCDeclNode { + if r.nodes == nil { + return r.ifNoAST + } + return r.nodes[m].(ast.RPCDeclNode) +} + +func (r *result) putFileNode(f *descriptorpb.FileDescriptorProto, n *ast.FileNode) { + r.nodes[f] = n +} + +func (r *result) putOptionNode(o *descriptorpb.UninterpretedOption, n *ast.OptionNode) { + r.nodes[o] = n +} + +func (r *result) putOptionNamePartNode(o *descriptorpb.UninterpretedOption_NamePart, n *ast.FieldReferenceNode) { + r.nodes[o] = n +} + +func (r *result) putMessageNode(m *descriptorpb.DescriptorProto, n ast.MessageDeclNode) { + r.nodes[m] = n +} + +func (r *result) putFieldNode(f *descriptorpb.FieldDescriptorProto, n ast.FieldDeclNode) { + r.nodes[f] = n +} + +func (r *result) putOneofNode(o *descriptorpb.OneofDescriptorProto, n ast.OneofDeclNode) { + r.nodes[o] = n +} + +func (r *result) putExtensionRangeNode(e *descriptorpb.DescriptorProto_ExtensionRange, er *ast.ExtensionRangeNode, n *ast.RangeNode) { + r.nodes[asExtsNode(e)] = er + r.nodes[e] = n +} + +func (r *result) putMessageReservedRangeNode(rr *descriptorpb.DescriptorProto_ReservedRange, n *ast.RangeNode) { + r.nodes[rr] = n +} + +func (r *result) putEnumNode(e *descriptorpb.EnumDescriptorProto, n *ast.EnumNode) { + r.nodes[e] = n +} + +func (r *result) putEnumValueNode(e *descriptorpb.EnumValueDescriptorProto, n *ast.EnumValueNode) { + r.nodes[e] = n +} + +func (r *result) putEnumReservedRangeNode(rr *descriptorpb.EnumDescriptorProto_EnumReservedRange, n *ast.RangeNode) { + r.nodes[rr] = n +} + +func (r *result) putServiceNode(s *descriptorpb.ServiceDescriptorProto, n *ast.ServiceNode) { + r.nodes[s] = n +} + +func (r *result) putMethodNode(m *descriptorpb.MethodDescriptorProto, n *ast.RPCNode) { + r.nodes[m] = n +} + +// NB: If we ever add other put*Node methods, to index other kinds of elements in the descriptor +// proto hierarchy, we need to update the index recreation logic in clone.go, too. + +func asExtsNode(er *descriptorpb.DescriptorProto_ExtensionRange) proto.Message { + return extsParent{er} +} + +// a simple marker type that allows us to have two distinct keys in a map for +// the same ExtensionRange proto -- one for the range itself and another to +// associate with the enclosing/parent AST node. +type extsParent struct { + *descriptorpb.DescriptorProto_ExtensionRange +} diff --git a/vendor/github.com/bufbuild/protocompile/parser/validate.go b/vendor/github.com/bufbuild/protocompile/parser/validate.go new file mode 100644 index 0000000..64ebdaa --- /dev/null +++ b/vendor/github.com/bufbuild/protocompile/parser/validate.go @@ -0,0 +1,568 @@ +// Copyright 2020-2024 Buf Technologies, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package parser + +import ( + "fmt" + "sort" + + "google.golang.org/protobuf/proto" + "google.golang.org/protobuf/reflect/protoreflect" + "google.golang.org/protobuf/types/descriptorpb" + + "github.com/bufbuild/protocompile/ast" + "github.com/bufbuild/protocompile/internal" + "github.com/bufbuild/protocompile/reporter" + "github.com/bufbuild/protocompile/walk" +) + +func validateBasic(res *result, handler *reporter.Handler) { + fd := res.proto + var syntax protoreflect.Syntax + switch fd.GetSyntax() { + case "", "proto2": + syntax = protoreflect.Proto2 + case "proto3": + syntax = protoreflect.Proto3 + case "editions": + syntax = protoreflect.Editions + // TODO: default: error? + } + + if err := validateImports(res, handler); err != nil { + return + } + + if err := validateNoFeatures(res, syntax, "file options", fd.Options.GetUninterpretedOption(), handler); err != nil { + return + } + + _ = walk.DescriptorProtos(fd, + func(name protoreflect.FullName, d proto.Message) error { + switch d := d.(type) { + case *descriptorpb.DescriptorProto: + if err := validateMessage(res, syntax, name, d, handler); err != nil { + // exit func is not called when enter returns error + return err + } + case *descriptorpb.FieldDescriptorProto: + if err := validateField(res, syntax, name, d, handler); err != nil { + return err + } + case *descriptorpb.OneofDescriptorProto: + if err := validateNoFeatures(res, syntax, fmt.Sprintf("oneof %s", name), d.Options.GetUninterpretedOption(), handler); err != nil { + return err + } + case *descriptorpb.EnumDescriptorProto: + if err := validateEnum(res, syntax, name, d, handler); err != nil { + return err + } + case *descriptorpb.EnumValueDescriptorProto: + if err := validateNoFeatures(res, syntax, fmt.Sprintf("enum value %s", name), d.Options.GetUninterpretedOption(), handler); err != nil { + return err + } + case *descriptorpb.ServiceDescriptorProto: + if err := validateNoFeatures(res, syntax, fmt.Sprintf("service %s", name), d.Options.GetUninterpretedOption(), handler); err != nil { + return err + } + case *descriptorpb.MethodDescriptorProto: + if err := validateNoFeatures(res, syntax, fmt.Sprintf("method %s", name), d.Options.GetUninterpretedOption(), handler); err != nil { + return err + } + } + return nil + }) +} + +func validateImports(res *result, handler *reporter.Handler) error { + fileNode := res.file + if fileNode == nil { + return nil + } + imports := make(map[string]ast.SourcePos) + for _, decl := range fileNode.Decls { + imp, ok := decl.(*ast.ImportNode) + if !ok { + continue + } + info := fileNode.NodeInfo(decl) + name := imp.Name.AsString() + if prev, ok := imports[name]; ok { + return handler.HandleErrorf(info, "%q was already imported at %v", name, prev) + } + imports[name] = info.Start() + } + return nil +} + +func validateNoFeatures(res *result, syntax protoreflect.Syntax, scope string, opts []*descriptorpb.UninterpretedOption, handler *reporter.Handler) error { + if syntax == protoreflect.Editions { + // Editions is allowed to use features + return nil + } + if index, err := internal.FindFirstOption(res, handler.HandleErrorf, scope, opts, "features"); err != nil { + return err + } else if index >= 0 { + optNode := res.OptionNode(opts[index]) + optNameNodeInfo := res.file.NodeInfo(optNode.GetName()) + if err := handler.HandleErrorf(optNameNodeInfo, "%s: option 'features' may only be used with editions but file uses %s syntax", scope, syntax); err != nil { + return err + } + } + return nil +} + +func validateMessage(res *result, syntax protoreflect.Syntax, name protoreflect.FullName, md *descriptorpb.DescriptorProto, handler *reporter.Handler) error { + scope := fmt.Sprintf("message %s", name) + + if syntax == protoreflect.Proto3 && len(md.ExtensionRange) > 0 { + n := res.ExtensionRangeNode(md.ExtensionRange[0]) + nInfo := res.file.NodeInfo(n) + if err := handler.HandleErrorf(nInfo, "%s: extension ranges are not allowed in proto3", scope); err != nil { + return err + } + } + + if index, err := internal.FindOption(res, handler.HandleErrorf, scope, md.Options.GetUninterpretedOption(), "map_entry"); err != nil { + return err + } else if index >= 0 { + optNode := res.OptionNode(md.Options.GetUninterpretedOption()[index]) + optNameNodeInfo := res.file.NodeInfo(optNode.GetName()) + if err := handler.HandleErrorf(optNameNodeInfo, "%s: map_entry option should not be set explicitly; use map type instead", scope); err != nil { + return err + } + } + + if err := validateNoFeatures(res, syntax, scope, md.Options.GetUninterpretedOption(), handler); err != nil { + return err + } + + // reserved ranges should not overlap + rsvd := make(tagRanges, len(md.ReservedRange)) + for i, r := range md.ReservedRange { + n := res.MessageReservedRangeNode(r) + rsvd[i] = tagRange{start: r.GetStart(), end: r.GetEnd(), node: n} + } + sort.Sort(rsvd) + for i := 1; i < len(rsvd); i++ { + if rsvd[i].start < rsvd[i-1].end { + rangeNodeInfo := res.file.NodeInfo(rsvd[i].node) + if err := handler.HandleErrorf(rangeNodeInfo, "%s: reserved ranges overlap: %d to %d and %d to %d", scope, rsvd[i-1].start, rsvd[i-1].end-1, rsvd[i].start, rsvd[i].end-1); err != nil { + return err + } + } + } + + // extensions ranges should not overlap + exts := make(tagRanges, len(md.ExtensionRange)) + for i, r := range md.ExtensionRange { + if err := validateNoFeatures(res, syntax, scope, r.Options.GetUninterpretedOption(), handler); err != nil { + return err + } + n := res.ExtensionRangeNode(r) + exts[i] = tagRange{start: r.GetStart(), end: r.GetEnd(), node: n} + } + sort.Sort(exts) + for i := 1; i < len(exts); i++ { + if exts[i].start < exts[i-1].end { + rangeNodeInfo := res.file.NodeInfo(exts[i].node) + if err := handler.HandleErrorf(rangeNodeInfo, "%s: extension ranges overlap: %d to %d and %d to %d", scope, exts[i-1].start, exts[i-1].end-1, exts[i].start, exts[i].end-1); err != nil { + return err + } + } + } + + // see if any extension range overlaps any reserved range + var i, j int // i indexes rsvd; j indexes exts + for i < len(rsvd) && j < len(exts) { + if rsvd[i].start >= exts[j].start && rsvd[i].start < exts[j].end || + exts[j].start >= rsvd[i].start && exts[j].start < rsvd[i].end { + var span ast.SourceSpan + if rsvd[i].start >= exts[j].start && rsvd[i].start < exts[j].end { + rangeNodeInfo := res.file.NodeInfo(rsvd[i].node) + span = rangeNodeInfo + } else { + rangeNodeInfo := res.file.NodeInfo(exts[j].node) + span = rangeNodeInfo + } + // ranges overlap + if err := handler.HandleErrorf(span, "%s: extension range %d to %d overlaps reserved range %d to %d", scope, exts[j].start, exts[j].end-1, rsvd[i].start, rsvd[i].end-1); err != nil { + return err + } + } + if rsvd[i].start < exts[j].start { + i++ + } else { + j++ + } + } + + // now, check that fields don't re-use tags and don't try to use extension + // or reserved ranges or reserved names + rsvdNames := map[string]struct{}{} + for _, n := range md.ReservedName { + // validate reserved name while we're here + if !isIdentifier(n) { + node := findMessageReservedNameNode(res.MessageNode(md), n) + nodeInfo := res.file.NodeInfo(node) + if err := handler.HandleErrorf(nodeInfo, "%s: reserved name %q is not a valid identifier", scope, n); err != nil { + return err + } + } + rsvdNames[n] = struct{}{} + } + fieldTags := map[int32]string{} + for _, fld := range md.Field { + fn := res.FieldNode(fld) + if _, ok := rsvdNames[fld.GetName()]; ok { + fieldNameNodeInfo := res.file.NodeInfo(fn.FieldName()) + if err := handler.HandleErrorf(fieldNameNodeInfo, "%s: field %s is using a reserved name", scope, fld.GetName()); err != nil { + return err + } + } + if existing := fieldTags[fld.GetNumber()]; existing != "" { + fieldTagNodeInfo := res.file.NodeInfo(fn.FieldTag()) + if err := handler.HandleErrorf(fieldTagNodeInfo, "%s: fields %s and %s both have the same tag %d", scope, existing, fld.GetName(), fld.GetNumber()); err != nil { + return err + } + } + fieldTags[fld.GetNumber()] = fld.GetName() + // check reserved ranges + r := sort.Search(len(rsvd), func(index int) bool { return rsvd[index].end > fld.GetNumber() }) + if r < len(rsvd) && rsvd[r].start <= fld.GetNumber() { + fieldTagNodeInfo := res.file.NodeInfo(fn.FieldTag()) + if err := handler.HandleErrorf(fieldTagNodeInfo, "%s: field %s is using tag %d which is in reserved range %d to %d", scope, fld.GetName(), fld.GetNumber(), rsvd[r].start, rsvd[r].end-1); err != nil { + return err + } + } + // and check extension ranges + e := sort.Search(len(exts), func(index int) bool { return exts[index].end > fld.GetNumber() }) + if e < len(exts) && exts[e].start <= fld.GetNumber() { + fieldTagNodeInfo := res.file.NodeInfo(fn.FieldTag()) + if err := handler.HandleErrorf(fieldTagNodeInfo, "%s: field %s is using tag %d which is in extension range %d to %d", scope, fld.GetName(), fld.GetNumber(), exts[e].start, exts[e].end-1); err != nil { + return err + } + } + } + + return nil +} + +func isIdentifier(s string) bool { + if len(s) == 0 { + return false + } + for i, r := range s { + if i == 0 && r >= '0' && r <= '9' { + // can't start with number + return false + } + // alphanumeric and underscore ok; everything else bad + switch { + case r >= '0' && r <= '9': + case r >= 'a' && r <= 'z': + case r >= 'A' && r <= 'Z': + case r == '_': + default: + return false + } + } + return true +} + +func findMessageReservedNameNode(msgNode ast.MessageDeclNode, name string) ast.Node { + var decls []ast.MessageElement + switch msgNode := msgNode.(type) { + case *ast.MessageNode: + decls = msgNode.Decls + case *ast.SyntheticGroupMessageNode: + decls = msgNode.Decls + default: + // leave decls empty + } + return findReservedNameNode(msgNode, decls, name) +} + +func findReservedNameNode[T ast.Node](parent ast.Node, decls []T, name string) ast.Node { + for _, decl := range decls { + // NB: We have to convert to empty interface first, before we can do a type + // assertion because type assertions on type parameters aren't allowed. (The + // compiler cannot yet know whether T is an interface type or not.) + rsvd, ok := any(decl).(*ast.ReservedNode) + if !ok { + continue + } + for _, rsvdName := range rsvd.Names { + if rsvdName.AsString() == name { + return rsvdName + } + } + } + // couldn't find it? Instead of puking, report position of the parent. + return parent +} + +func validateEnum(res *result, syntax protoreflect.Syntax, name protoreflect.FullName, ed *descriptorpb.EnumDescriptorProto, handler *reporter.Handler) error { + scope := fmt.Sprintf("enum %s", name) + + if len(ed.Value) == 0 { + enNode := res.EnumNode(ed) + enNodeInfo := res.file.NodeInfo(enNode) + if err := handler.HandleErrorf(enNodeInfo, "%s: enums must define at least one value", scope); err != nil { + return err + } + } + + if err := validateNoFeatures(res, syntax, scope, ed.Options.GetUninterpretedOption(), handler); err != nil { + return err + } + + allowAlias := false + var allowAliasOpt *descriptorpb.UninterpretedOption + if index, err := internal.FindOption(res, handler.HandleErrorf, scope, ed.Options.GetUninterpretedOption(), "allow_alias"); err != nil { + return err + } else if index >= 0 { + allowAliasOpt = ed.Options.UninterpretedOption[index] + valid := false + if allowAliasOpt.IdentifierValue != nil { + if allowAliasOpt.GetIdentifierValue() == "true" { + allowAlias = true + valid = true + } else if allowAliasOpt.GetIdentifierValue() == "false" { + valid = true + } + } + if !valid { + optNode := res.OptionNode(allowAliasOpt) + optNodeInfo := res.file.NodeInfo(optNode.GetValue()) + if err := handler.HandleErrorf(optNodeInfo, "%s: expecting bool value for allow_alias option", scope); err != nil { + return err + } + } + } + + if syntax == protoreflect.Proto3 && len(ed.Value) > 0 && ed.Value[0].GetNumber() != 0 { + evNode := res.EnumValueNode(ed.Value[0]) + evNodeInfo := res.file.NodeInfo(evNode.GetNumber()) + if err := handler.HandleErrorf(evNodeInfo, "%s: proto3 requires that first value of enum have numeric value zero", scope); err != nil { + return err + } + } + + // check for aliases + vals := map[int32]string{} + hasAlias := false + for _, evd := range ed.Value { + existing := vals[evd.GetNumber()] + if existing != "" { + if allowAlias { + hasAlias = true + } else { + evNode := res.EnumValueNode(evd) + evNodeInfo := res.file.NodeInfo(evNode.GetNumber()) + if err := handler.HandleErrorf(evNodeInfo, "%s: values %s and %s both have the same numeric value %d; use allow_alias option if intentional", scope, existing, evd.GetName(), evd.GetNumber()); err != nil { + return err + } + } + } + vals[evd.GetNumber()] = evd.GetName() + } + if allowAlias && !hasAlias { + optNode := res.OptionNode(allowAliasOpt) + optNodeInfo := res.file.NodeInfo(optNode.GetValue()) + if err := handler.HandleErrorf(optNodeInfo, "%s: allow_alias is true but no values are aliases", scope); err != nil { + return err + } + } + + // reserved ranges should not overlap + rsvd := make(tagRanges, len(ed.ReservedRange)) + for i, r := range ed.ReservedRange { + n := res.EnumReservedRangeNode(r) + rsvd[i] = tagRange{start: r.GetStart(), end: r.GetEnd(), node: n} + } + sort.Sort(rsvd) + for i := 1; i < len(rsvd); i++ { + if rsvd[i].start <= rsvd[i-1].end { + rangeNodeInfo := res.file.NodeInfo(rsvd[i].node) + if err := handler.HandleErrorf(rangeNodeInfo, "%s: reserved ranges overlap: %d to %d and %d to %d", scope, rsvd[i-1].start, rsvd[i-1].end, rsvd[i].start, rsvd[i].end); err != nil { + return err + } + } + } + + // now, check that fields don't re-use tags and don't try to use extension + // or reserved ranges or reserved names + rsvdNames := map[string]struct{}{} + for _, n := range ed.ReservedName { + // validate reserved name while we're here + if !isIdentifier(n) { + node := findEnumReservedNameNode(res.EnumNode(ed), n) + nodeInfo := res.file.NodeInfo(node) + if err := handler.HandleErrorf(nodeInfo, "%s: reserved name %q is not a valid identifier", scope, n); err != nil { + return err + } + } + rsvdNames[n] = struct{}{} + } + for _, ev := range ed.Value { + evn := res.EnumValueNode(ev) + if _, ok := rsvdNames[ev.GetName()]; ok { + enumValNodeInfo := res.file.NodeInfo(evn.GetName()) + if err := handler.HandleErrorf(enumValNodeInfo, "%s: value %s is using a reserved name", scope, ev.GetName()); err != nil { + return err + } + } + // check reserved ranges + r := sort.Search(len(rsvd), func(index int) bool { return rsvd[index].end >= ev.GetNumber() }) + if r < len(rsvd) && rsvd[r].start <= ev.GetNumber() { + enumValNodeInfo := res.file.NodeInfo(evn.GetNumber()) + if err := handler.HandleErrorf(enumValNodeInfo, "%s: value %s is using number %d which is in reserved range %d to %d", scope, ev.GetName(), ev.GetNumber(), rsvd[r].start, rsvd[r].end); err != nil { + return err + } + } + } + + return nil +} + +func findEnumReservedNameNode(enumNode ast.Node, name string) ast.Node { + var decls []ast.EnumElement + if enumNode, ok := enumNode.(*ast.EnumNode); ok { + decls = enumNode.Decls + // if not the right type, we leave decls empty + } + return findReservedNameNode(enumNode, decls, name) +} + +func validateField(res *result, syntax protoreflect.Syntax, name protoreflect.FullName, fld *descriptorpb.FieldDescriptorProto, handler *reporter.Handler) error { + var scope string + if fld.Extendee != nil { + scope = fmt.Sprintf("extension %s", name) + } else { + scope = fmt.Sprintf("field %s", name) + } + + node := res.FieldNode(fld) + if fld.Number == nil { + fieldTagNodeInfo := res.file.NodeInfo(node) + if err := handler.HandleErrorf(fieldTagNodeInfo, "%s: missing field tag number", scope); err != nil { + return err + } + } + if syntax != protoreflect.Proto2 { + if fld.GetType() == descriptorpb.FieldDescriptorProto_TYPE_GROUP { + groupNodeInfo := res.file.NodeInfo(node.GetGroupKeyword()) + if err := handler.HandleErrorf(groupNodeInfo, "%s: groups are not allowed in proto3 or editions", scope); err != nil { + return err + } + } else if fld.Label != nil && fld.GetLabel() == descriptorpb.FieldDescriptorProto_LABEL_REQUIRED { + fieldLabelNodeInfo := res.file.NodeInfo(node.FieldLabel()) + if err := handler.HandleErrorf(fieldLabelNodeInfo, "%s: label 'required' is not allowed in proto3 or editions", scope); err != nil { + return err + } + } + if syntax == protoreflect.Editions { + if fld.Label != nil && fld.GetLabel() == descriptorpb.FieldDescriptorProto_LABEL_OPTIONAL { + fieldLabelNodeInfo := res.file.NodeInfo(node.FieldLabel()) + if err := handler.HandleErrorf(fieldLabelNodeInfo, "%s: label 'optional' is not allowed in editions; use option features.field_presence instead", scope); err != nil { + return err + } + } + if index, err := internal.FindOption(res, handler.HandleErrorf, scope, fld.Options.GetUninterpretedOption(), "packed"); err != nil { + return err + } else if index >= 0 { + optNode := res.OptionNode(fld.Options.GetUninterpretedOption()[index]) + optNameNodeInfo := res.file.NodeInfo(optNode.GetName()) + if err := handler.HandleErrorf(optNameNodeInfo, "%s: packed option is not allowed in editions; use option features.repeated_field_encoding instead", scope); err != nil { + return err + } + } + } else if syntax == protoreflect.Proto3 { + if index, err := internal.FindOption(res, handler.HandleErrorf, scope, fld.Options.GetUninterpretedOption(), "default"); err != nil { + return err + } else if index >= 0 { + optNode := res.OptionNode(fld.Options.GetUninterpretedOption()[index]) + optNameNodeInfo := res.file.NodeInfo(optNode.GetName()) + if err := handler.HandleErrorf(optNameNodeInfo, "%s: default values are not allowed in proto3", scope); err != nil { + return err + } + } + } + } else { + if fld.Label == nil && fld.OneofIndex == nil { + fieldNameNodeInfo := res.file.NodeInfo(node.FieldName()) + if err := handler.HandleErrorf(fieldNameNodeInfo, "%s: field has no label; proto2 requires explicit 'optional' label", scope); err != nil { + return err + } + } + if fld.GetExtendee() != "" && fld.Label != nil && fld.GetLabel() == descriptorpb.FieldDescriptorProto_LABEL_REQUIRED { + fieldLabelNodeInfo := res.file.NodeInfo(node.FieldLabel()) + if err := handler.HandleErrorf(fieldLabelNodeInfo, "%s: extension fields cannot be 'required'", scope); err != nil { + return err + } + } + } + + return validateNoFeatures(res, syntax, scope, fld.Options.GetUninterpretedOption(), handler) +} + +type tagRange struct { + start int32 + end int32 + node ast.RangeDeclNode +} + +type tagRanges []tagRange + +func (r tagRanges) Len() int { + return len(r) +} + +func (r tagRanges) Less(i, j int) bool { + return r[i].start < r[j].start || + (r[i].start == r[j].start && r[i].end < r[j].end) +} + +func (r tagRanges) Swap(i, j int) { + r[i], r[j] = r[j], r[i] +} + +func fillInMissingLabels(fd *descriptorpb.FileDescriptorProto) { + for _, md := range fd.MessageType { + fillInMissingLabelsInMsg(md) + } + for _, extd := range fd.Extension { + fillInMissingLabel(extd) + } +} + +func fillInMissingLabelsInMsg(md *descriptorpb.DescriptorProto) { + for _, fld := range md.Field { + fillInMissingLabel(fld) + } + for _, nmd := range md.NestedType { + fillInMissingLabelsInMsg(nmd) + } + for _, extd := range md.Extension { + fillInMissingLabel(extd) + } +} + +func fillInMissingLabel(fld *descriptorpb.FieldDescriptorProto) { + if fld.Label == nil { + fld.Label = descriptorpb.FieldDescriptorProto_LABEL_OPTIONAL.Enum() + } +} diff --git a/vendor/github.com/bufbuild/protocompile/protoutil/editions.go b/vendor/github.com/bufbuild/protocompile/protoutil/editions.go new file mode 100644 index 0000000..fb21dff --- /dev/null +++ b/vendor/github.com/bufbuild/protocompile/protoutil/editions.go @@ -0,0 +1,140 @@ +// Copyright 2020-2024 Buf Technologies, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package protoutil + +import ( + "fmt" + + "google.golang.org/protobuf/reflect/protoreflect" + "google.golang.org/protobuf/types/descriptorpb" + "google.golang.org/protobuf/types/dynamicpb" + + "github.com/bufbuild/protocompile/internal/editions" +) + +// GetFeatureDefault gets the default value for the given feature and the given +// edition. The given feature must represent a field of the google.protobuf.FeatureSet +// message and must not be an extension. +// +// If the given field is from a dynamically built descriptor (i.e. it's containing +// message descriptor is different from the linked-in descriptor for +// [*descriptorpb.FeatureSet]), the returned value may be a dynamic value. In such +// cases, the value may not be directly usable using [protoreflect.Message.Set] with +// an instance of [*descriptorpb.FeatureSet] and must instead be used with a +// [*dynamicpb.Message]. +// +// To get the default value of a custom feature, use [GetCustomFeatureDefault] +// instead. +func GetFeatureDefault(edition descriptorpb.Edition, feature protoreflect.FieldDescriptor) (protoreflect.Value, error) { + if feature.ContainingMessage().FullName() != editions.FeatureSetDescriptor.FullName() { + return protoreflect.Value{}, fmt.Errorf("feature %s is a field of %s but should be a field of %s", + feature.Name(), feature.ContainingMessage().FullName(), editions.FeatureSetDescriptor.FullName()) + } + var msgType protoreflect.MessageType + if feature.ContainingMessage() == editions.FeatureSetDescriptor { + msgType = editions.FeatureSetType + } else { + msgType = dynamicpb.NewMessageType(feature.ContainingMessage()) + } + return editions.GetFeatureDefault(edition, msgType, feature) +} + +// GetCustomFeatureDefault gets the default value for the given custom feature +// and given edition. A custom feature is a field whose containing message is the +// type of an extension field of google.protobuf.FeatureSet. The given extension +// describes that extension field and message type. The given feature must be a +// field of that extension's message type. +func GetCustomFeatureDefault(edition descriptorpb.Edition, extension protoreflect.ExtensionType, feature protoreflect.FieldDescriptor) (protoreflect.Value, error) { + extDesc := extension.TypeDescriptor() + if extDesc.ContainingMessage().FullName() != editions.FeatureSetDescriptor.FullName() { + return protoreflect.Value{}, fmt.Errorf("extension %s does not extend %s", extDesc.FullName(), editions.FeatureSetDescriptor.FullName()) + } + if extDesc.Message() == nil { + return protoreflect.Value{}, fmt.Errorf("extensions of %s should be messages; %s is instead %s", + editions.FeatureSetDescriptor.FullName(), extDesc.FullName(), extDesc.Kind().String()) + } + if feature.IsExtension() { + return protoreflect.Value{}, fmt.Errorf("feature %s is an extension, but feature extension %s may not itself have extensions", + feature.FullName(), extDesc.FullName()) + } + if feature.ContainingMessage().FullName() != extDesc.Message().FullName() { + return protoreflect.Value{}, fmt.Errorf("feature %s is a field of %s but should be a field of %s", + feature.Name(), feature.ContainingMessage().FullName(), extDesc.Message().FullName()) + } + if feature.ContainingMessage() != extDesc.Message() { + return protoreflect.Value{}, fmt.Errorf("feature %s has a different message descriptor from the given extension type for %s", + feature.Name(), extDesc.Message().FullName()) + } + return editions.GetFeatureDefault(edition, extension.Zero().Message().Type(), feature) +} + +// ResolveFeature resolves a feature for the given descriptor. +// +// If the given element is in a proto2 or proto3 syntax file, this skips +// resolution and just returns the relevant default (since such files are not +// allowed to override features). If neither the given element nor any of its +// ancestors override the given feature, the relevant default is returned. +// +// This has the same caveat as GetFeatureDefault if the given feature is from a +// dynamically built descriptor. +func ResolveFeature(element protoreflect.Descriptor, feature protoreflect.FieldDescriptor) (protoreflect.Value, error) { + edition := editions.GetEdition(element) + defaultVal, err := GetFeatureDefault(edition, feature) + if err != nil { + return protoreflect.Value{}, err + } + return resolveFeature(edition, defaultVal, element, feature) +} + +// ResolveCustomFeature resolves a custom feature for the given extension and +// field descriptor. +// +// The given extension must be an extension of google.protobuf.FeatureSet that +// represents a non-repeated message value. The given feature is a field in +// that extension's message type. +// +// If the given element is in a proto2 or proto3 syntax file, this skips +// resolution and just returns the relevant default (since such files are not +// allowed to override features). If neither the given element nor any of its +// ancestors override the given feature, the relevant default is returned. +func ResolveCustomFeature(element protoreflect.Descriptor, extension protoreflect.ExtensionType, feature protoreflect.FieldDescriptor) (protoreflect.Value, error) { + edition := editions.GetEdition(element) + defaultVal, err := GetCustomFeatureDefault(edition, extension, feature) + if err != nil { + return protoreflect.Value{}, err + } + return resolveFeature(edition, defaultVal, element, extension.TypeDescriptor(), feature) +} + +func resolveFeature( + edition descriptorpb.Edition, + defaultVal protoreflect.Value, + element protoreflect.Descriptor, + fields ...protoreflect.FieldDescriptor, +) (protoreflect.Value, error) { + if edition == descriptorpb.Edition_EDITION_PROTO2 || edition == descriptorpb.Edition_EDITION_PROTO3 { + // these syntax levels can't specify features, so we can short-circuit the search + // through the descriptor hierarchy for feature overrides + return defaultVal, nil + } + val, err := editions.ResolveFeature(element, fields...) + if err != nil { + return protoreflect.Value{}, err + } + if val.IsValid() { + return val, nil + } + return defaultVal, nil +} diff --git a/vendor/github.com/bufbuild/protocompile/protoutil/protos.go b/vendor/github.com/bufbuild/protocompile/protoutil/protos.go new file mode 100644 index 0000000..9c55999 --- /dev/null +++ b/vendor/github.com/bufbuild/protocompile/protoutil/protos.go @@ -0,0 +1,262 @@ +// Copyright 2020-2024 Buf Technologies, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Package protoutil contains useful functions for interacting with descriptors. +// For now these include only functions for efficiently converting descriptors +// produced by the compiler to descriptor protos and functions for resolving +// "features" (a core concept of Protobuf Editions). +// +// Despite the fact that descriptor protos are mutable, calling code should NOT +// mutate any of the protos returned from this package. For efficiency, some +// values returned from this package may reference internal state of a compiler +// result, and mutating the proto could corrupt or invalidate parts of that +// result. +package protoutil + +import ( + "google.golang.org/protobuf/proto" + "google.golang.org/protobuf/reflect/protodesc" + "google.golang.org/protobuf/reflect/protoreflect" + "google.golang.org/protobuf/types/descriptorpb" +) + +// DescriptorProtoWrapper is a protoreflect.Descriptor that wraps an +// underlying descriptor proto. It provides the same interface as +// Descriptor but with one extra operation, to efficiently query for +// the underlying descriptor proto. +// +// Descriptors that implement this will also implement another method +// whose specified return type is the concrete type returned by the +// AsProto method. The name of this method varies by the type of this +// descriptor: +// +// Descriptor Type Other Method Name +// ---------------------+------------------------------------ +// FileDescriptor | FileDescriptorProto() +// MessageDescriptor | MessageDescriptorProto() +// FieldDescriptor | FieldDescriptorProto() +// OneofDescriptor | OneofDescriptorProto() +// EnumDescriptor | EnumDescriptorProto() +// EnumValueDescriptor | EnumValueDescriptorProto() +// ServiceDescriptor | ServiceDescriptorProto() +// MethodDescriptor | MethodDescriptorProto() +// +// For example, a DescriptorProtoWrapper that implements FileDescriptor +// returns a *descriptorpb.FileDescriptorProto value from its AsProto +// method and also provides a method with the following signature: +// +// FileDescriptorProto() *descriptorpb.FileDescriptorProto +type DescriptorProtoWrapper interface { + protoreflect.Descriptor + // AsProto returns the underlying descriptor proto. The concrete + // type of the proto message depends on the type of this + // descriptor: + // Descriptor Type Proto Message Type + // ---------------------+------------------------------------ + // FileDescriptor | *descriptorpb.FileDescriptorProto + // MessageDescriptor | *descriptorpb.DescriptorProto + // FieldDescriptor | *descriptorpb.FieldDescriptorProto + // OneofDescriptor | *descriptorpb.OneofDescriptorProto + // EnumDescriptor | *descriptorpb.EnumDescriptorProto + // EnumValueDescriptor | *descriptorpb.EnumValueDescriptorProto + // ServiceDescriptor | *descriptorpb.ServiceDescriptorProto + // MethodDescriptor | *descriptorpb.MethodDescriptorProto + AsProto() proto.Message +} + +// ProtoFromDescriptor extracts a descriptor proto from the given "rich" +// descriptor. For descriptors generated by the compiler, this is an +// inexpensive and non-lossy operation. Descriptors from other sources +// however may be expensive (to re-create a proto) and even lossy. +func ProtoFromDescriptor(d protoreflect.Descriptor) proto.Message { + switch d := d.(type) { + case protoreflect.FileDescriptor: + return ProtoFromFileDescriptor(d) + case protoreflect.MessageDescriptor: + return ProtoFromMessageDescriptor(d) + case protoreflect.FieldDescriptor: + return ProtoFromFieldDescriptor(d) + case protoreflect.OneofDescriptor: + return ProtoFromOneofDescriptor(d) + case protoreflect.EnumDescriptor: + return ProtoFromEnumDescriptor(d) + case protoreflect.EnumValueDescriptor: + return ProtoFromEnumValueDescriptor(d) + case protoreflect.ServiceDescriptor: + return ProtoFromServiceDescriptor(d) + case protoreflect.MethodDescriptor: + return ProtoFromMethodDescriptor(d) + default: + // WTF?? + if res, ok := d.(DescriptorProtoWrapper); ok { + return res.AsProto() + } + return nil + } +} + +// ProtoFromFileDescriptor extracts a descriptor proto from the given "rich" +// descriptor. For file descriptors generated by the compiler, this is an +// inexpensive and non-lossy operation. File descriptors from other sources +// however may be expensive (to re-create a proto) and even lossy. +func ProtoFromFileDescriptor(d protoreflect.FileDescriptor) *descriptorpb.FileDescriptorProto { + if imp, ok := d.(protoreflect.FileImport); ok { + d = imp.FileDescriptor + } + type canProto interface { + FileDescriptorProto() *descriptorpb.FileDescriptorProto + } + if res, ok := d.(canProto); ok { + return res.FileDescriptorProto() + } + if res, ok := d.(DescriptorProtoWrapper); ok { + if fd, ok := res.AsProto().(*descriptorpb.FileDescriptorProto); ok { + return fd + } + } + return protodesc.ToFileDescriptorProto(d) +} + +// ProtoFromMessageDescriptor extracts a descriptor proto from the given "rich" +// descriptor. For message descriptors generated by the compiler, this is an +// inexpensive and non-lossy operation. Message descriptors from other sources +// however may be expensive (to re-create a proto) and even lossy. +func ProtoFromMessageDescriptor(d protoreflect.MessageDescriptor) *descriptorpb.DescriptorProto { + type canProto interface { + MessageDescriptorProto() *descriptorpb.DescriptorProto + } + if res, ok := d.(canProto); ok { + return res.MessageDescriptorProto() + } + if res, ok := d.(DescriptorProtoWrapper); ok { + if md, ok := res.AsProto().(*descriptorpb.DescriptorProto); ok { + return md + } + } + return protodesc.ToDescriptorProto(d) +} + +// ProtoFromFieldDescriptor extracts a descriptor proto from the given "rich" +// descriptor. For field descriptors generated by the compiler, this is an +// inexpensive and non-lossy operation. Field descriptors from other sources +// however may be expensive (to re-create a proto) and even lossy. +func ProtoFromFieldDescriptor(d protoreflect.FieldDescriptor) *descriptorpb.FieldDescriptorProto { + type canProto interface { + FieldDescriptorProto() *descriptorpb.FieldDescriptorProto + } + if res, ok := d.(canProto); ok { + return res.FieldDescriptorProto() + } + if res, ok := d.(DescriptorProtoWrapper); ok { + if fd, ok := res.AsProto().(*descriptorpb.FieldDescriptorProto); ok { + return fd + } + } + return protodesc.ToFieldDescriptorProto(d) +} + +// ProtoFromOneofDescriptor extracts a descriptor proto from the given "rich" +// descriptor. For oneof descriptors generated by the compiler, this is an +// inexpensive and non-lossy operation. Oneof descriptors from other sources +// however may be expensive (to re-create a proto) and even lossy. +func ProtoFromOneofDescriptor(d protoreflect.OneofDescriptor) *descriptorpb.OneofDescriptorProto { + type canProto interface { + OneofDescriptorProto() *descriptorpb.OneofDescriptorProto + } + if res, ok := d.(canProto); ok { + return res.OneofDescriptorProto() + } + if res, ok := d.(DescriptorProtoWrapper); ok { + if ood, ok := res.AsProto().(*descriptorpb.OneofDescriptorProto); ok { + return ood + } + } + return protodesc.ToOneofDescriptorProto(d) +} + +// ProtoFromEnumDescriptor extracts a descriptor proto from the given "rich" +// descriptor. For enum descriptors generated by the compiler, this is an +// inexpensive and non-lossy operation. Enum descriptors from other sources +// however may be expensive (to re-create a proto) and even lossy. +func ProtoFromEnumDescriptor(d protoreflect.EnumDescriptor) *descriptorpb.EnumDescriptorProto { + type canProto interface { + EnumDescriptorProto() *descriptorpb.EnumDescriptorProto + } + if res, ok := d.(canProto); ok { + return res.EnumDescriptorProto() + } + if res, ok := d.(DescriptorProtoWrapper); ok { + if ed, ok := res.AsProto().(*descriptorpb.EnumDescriptorProto); ok { + return ed + } + } + return protodesc.ToEnumDescriptorProto(d) +} + +// ProtoFromEnumValueDescriptor extracts a descriptor proto from the given "rich" +// descriptor. For enum value descriptors generated by the compiler, this is an +// inexpensive and non-lossy operation. Enum value descriptors from other sources +// however may be expensive (to re-create a proto) and even lossy. +func ProtoFromEnumValueDescriptor(d protoreflect.EnumValueDescriptor) *descriptorpb.EnumValueDescriptorProto { + type canProto interface { + EnumValueDescriptorProto() *descriptorpb.EnumValueDescriptorProto + } + if res, ok := d.(canProto); ok { + return res.EnumValueDescriptorProto() + } + if res, ok := d.(DescriptorProtoWrapper); ok { + if ed, ok := res.AsProto().(*descriptorpb.EnumValueDescriptorProto); ok { + return ed + } + } + return protodesc.ToEnumValueDescriptorProto(d) +} + +// ProtoFromServiceDescriptor extracts a descriptor proto from the given "rich" +// descriptor. For service descriptors generated by the compiler, this is an +// inexpensive and non-lossy operation. Service descriptors from other sources +// however may be expensive (to re-create a proto) and even lossy. +func ProtoFromServiceDescriptor(d protoreflect.ServiceDescriptor) *descriptorpb.ServiceDescriptorProto { + type canProto interface { + ServiceDescriptorProto() *descriptorpb.ServiceDescriptorProto + } + if res, ok := d.(canProto); ok { + return res.ServiceDescriptorProto() + } + if res, ok := d.(DescriptorProtoWrapper); ok { + if sd, ok := res.AsProto().(*descriptorpb.ServiceDescriptorProto); ok { + return sd + } + } + return protodesc.ToServiceDescriptorProto(d) +} + +// ProtoFromMethodDescriptor extracts a descriptor proto from the given "rich" +// descriptor. For method descriptors generated by the compiler, this is an +// inexpensive and non-lossy operation. Method descriptors from other sources +// however may be expensive (to re-create a proto) and even lossy. +func ProtoFromMethodDescriptor(d protoreflect.MethodDescriptor) *descriptorpb.MethodDescriptorProto { + type canProto interface { + MethodDescriptorProto() *descriptorpb.MethodDescriptorProto + } + if res, ok := d.(canProto); ok { + return res.MethodDescriptorProto() + } + if res, ok := d.(DescriptorProtoWrapper); ok { + if md, ok := res.AsProto().(*descriptorpb.MethodDescriptorProto); ok { + return md + } + } + return protodesc.ToMethodDescriptorProto(d) +} diff --git a/vendor/github.com/bufbuild/protocompile/reporter/errors.go b/vendor/github.com/bufbuild/protocompile/reporter/errors.go new file mode 100644 index 0000000..3a70a43 --- /dev/null +++ b/vendor/github.com/bufbuild/protocompile/reporter/errors.go @@ -0,0 +1,74 @@ +// Copyright 2020-2024 Buf Technologies, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package reporter + +import ( + "errors" + "fmt" + + "github.com/bufbuild/protocompile/ast" +) + +// ErrInvalidSource is a sentinel error that is returned by compilation and +// stand-alone compilation steps (such as parsing, linking) when one or more +// errors is reported but the configured ErrorReporter always returns nil. +var ErrInvalidSource = errors.New("parse failed: invalid proto source") + +// ErrorWithPos is an error about a proto source file that adds information +// about the location in the file that caused the error. +type ErrorWithPos interface { + error + ast.SourceSpan + // GetPosition returns the start source position that caused the underlying error. + GetPosition() ast.SourcePos + // Unwrap returns the underlying error. + Unwrap() error +} + +// Error creates a new ErrorWithPos from the given error and source position. +func Error(span ast.SourceSpan, err error) ErrorWithPos { + var ewp ErrorWithPos + if errors.As(err, &ewp) { + // replace existing position with given one + return &errorWithSpan{SourceSpan: span, underlying: ewp.Unwrap()} + } + return &errorWithSpan{SourceSpan: span, underlying: err} +} + +// Errorf creates a new ErrorWithPos whose underlying error is created using the +// given message format and arguments (via fmt.Errorf). +func Errorf(span ast.SourceSpan, format string, args ...interface{}) ErrorWithPos { + return Error(span, fmt.Errorf(format, args...)) +} + +type errorWithSpan struct { + ast.SourceSpan + underlying error +} + +func (e *errorWithSpan) Error() string { + sourcePos := e.GetPosition() + return fmt.Sprintf("%s: %v", sourcePos, e.underlying) +} + +func (e *errorWithSpan) GetPosition() ast.SourcePos { + return e.Start() +} + +func (e *errorWithSpan) Unwrap() error { + return e.underlying +} + +var _ ErrorWithPos = (*errorWithSpan)(nil) diff --git a/vendor/github.com/bufbuild/protocompile/reporter/reporter.go b/vendor/github.com/bufbuild/protocompile/reporter/reporter.go new file mode 100644 index 0000000..8e90640 --- /dev/null +++ b/vendor/github.com/bufbuild/protocompile/reporter/reporter.go @@ -0,0 +1,219 @@ +// Copyright 2020-2024 Buf Technologies, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Package reporter contains the types used for reporting errors from +// protocompile operations. It contains error types as well as interfaces +// for reporting and handling errors and warnings. +package reporter + +import ( + "sync" + + "github.com/bufbuild/protocompile/ast" +) + +// ErrorReporter is responsible for reporting the given error. If the reporter +// returns a non-nil error, parsing/linking will abort with that error. If the +// reporter returns nil, parsing will continue, allowing the parser to try to +// report as many syntax and/or link errors as it can find. +type ErrorReporter func(err ErrorWithPos) error + +// WarningReporter is responsible for reporting the given warning. This is used +// for indicating non-error messages to the calling program for things that do +// not cause the parse to fail but are considered bad practice. Though they are +// just warnings, the details are supplied to the reporter via an error type. +type WarningReporter func(ErrorWithPos) + +// Reporter is a type that handles reporting both errors and warnings. +// A reporter does not need to be thread-safe. Safe concurrent access is +// managed by a Handler. +type Reporter interface { + // Error is called when the given error is encountered and needs to be + // reported to the calling program. This signature matches ErrorReporter + // because it has the same semantics. If this function returns non-nil + // then the operation will abort immediately with the given error. But + // if it returns nil, the operation will continue, reporting more errors + // as they are encountered. If the reporter never returns non-nil then + // the operation will eventually fail with ErrInvalidSource. + Error(ErrorWithPos) error + // Warning is called when the given warnings is encountered and needs to be + // reported to the calling program. Despite the argument being an error + // type, a warning will never cause the operation to abort or fail (unless + // the reporter's implementation of this method panics). + Warning(ErrorWithPos) +} + +// NewReporter creates a new reporter that invokes the given functions on error +// or warning. +func NewReporter(errs ErrorReporter, warnings WarningReporter) Reporter { + return reporterFuncs{errs: errs, warnings: warnings} +} + +type reporterFuncs struct { + errs ErrorReporter + warnings WarningReporter +} + +func (r reporterFuncs) Error(err ErrorWithPos) error { + if r.errs == nil { + return err + } + return r.errs(err) +} + +func (r reporterFuncs) Warning(err ErrorWithPos) { + if r.warnings != nil { + r.warnings(err) + } +} + +// Handler is used by protocompile operations for handling errors and warnings. +// This type is thread-safe. It uses a mutex to serialize calls to its reporter +// so that reporter instances do not have to be thread-safe (unless re-used +// across multiple handlers). +type Handler struct { + parent *Handler + mu sync.Mutex + reporter Reporter + errsReported bool + err error +} + +// NewHandler creates a new Handler that reports errors and warnings using the +// given reporter. +func NewHandler(rep Reporter) *Handler { + if rep == nil { + rep = NewReporter(nil, nil) + } + return &Handler{reporter: rep} +} + +// SubHandler returns a "child" of h. Use of a child handler is the same as use +// of the parent, except that the Error() and ReporterError() functions only +// report non-nil for errors that were reported using the child handler. So +// errors reported directly to the parent or to a different child handler won't +// be returned. This is useful for making concurrent access to the handler more +// deterministic: if a child handler is only used from one goroutine, its view +// of reported errors is consistent and unimpacted by concurrent operations. +func (h *Handler) SubHandler() *Handler { + return &Handler{parent: h} +} + +// HandleError handles the given error. If the given err is an ErrorWithPos, it +// is reported, and this function returns the error returned by the reporter. If +// the given err is NOT an ErrorWithPos, the current operation will abort +// immediately. +// +// If the handler has already aborted (by returning a non-nil error from a prior +// call to HandleError or HandleErrorf), that same error is returned and the +// given error is not reported. +func (h *Handler) HandleError(err error) error { + if h.parent != nil { + _, isErrWithPos := err.(ErrorWithPos) + err = h.parent.HandleError(err) + + // update child state + h.mu.Lock() + defer h.mu.Unlock() + if isErrWithPos { + h.errsReported = true + } + h.err = err + return err + } + + h.mu.Lock() + defer h.mu.Unlock() + + if h.err != nil { + return h.err + } + if ewp, ok := err.(ErrorWithPos); ok { + h.errsReported = true + err = h.reporter.Error(ewp) + } + h.err = err + return err +} + +// HandleErrorWithPos handles an error with the given source position. +// +// If the handler has already aborted (by returning a non-nil error from a prior +// call to HandleError or HandleErrorf), that same error is returned and the +// given error is not reported. +func (h *Handler) HandleErrorWithPos(span ast.SourceSpan, err error) error { + return h.HandleError(Error(span, err)) +} + +// HandleErrorf handles an error with the given source position, creating the +// error using the given message format and arguments. +// +// If the handler has already aborted (by returning a non-nil error from a call +// to HandleError or HandleErrorf), that same error is returned and the given +// error is not reported. +func (h *Handler) HandleErrorf(span ast.SourceSpan, format string, args ...interface{}) error { + return h.HandleError(Errorf(span, format, args...)) +} + +// HandleWarning handles the given warning. This will delegate to the handler's +// configured reporter. +func (h *Handler) HandleWarning(err ErrorWithPos) { + if h.parent != nil { + h.parent.HandleWarning(err) + return + } + + // even though we aren't touching mutable fields, we acquire lock anyway so + // that underlying reporter does not have to be thread-safe + h.mu.Lock() + defer h.mu.Unlock() + + h.reporter.Warning(err) +} + +// HandleWarningWithPos handles a warning with the given source position. This will +// delegate to the handler's configured reporter. +func (h *Handler) HandleWarningWithPos(span ast.SourceSpan, err error) { + h.HandleWarning(Error(span, err)) +} + +// HandleWarningf handles a warning with the given source position, creating the +// actual error value using the given message format and arguments. +func (h *Handler) HandleWarningf(span ast.SourceSpan, format string, args ...interface{}) { + h.HandleWarning(Errorf(span, format, args...)) +} + +// Error returns the handler result. If any errors have been reported then this +// returns a non-nil error. If the reporter never returned a non-nil error then +// ErrInvalidSource is returned. Otherwise, this returns the error returned by +// the handler's reporter (the same value returned by ReporterError). +func (h *Handler) Error() error { + h.mu.Lock() + defer h.mu.Unlock() + + if h.errsReported && h.err == nil { + return ErrInvalidSource + } + return h.err +} + +// ReporterError returns the error returned by the handler's reporter. If +// the reporter has either not been invoked (no errors handled) or has not +// returned any non-nil value, then this returns nil. +func (h *Handler) ReporterError() error { + h.mu.Lock() + defer h.mu.Unlock() + + return h.err +} diff --git a/vendor/github.com/bufbuild/protocompile/resolver.go b/vendor/github.com/bufbuild/protocompile/resolver.go new file mode 100644 index 0000000..400d554 --- /dev/null +++ b/vendor/github.com/bufbuild/protocompile/resolver.go @@ -0,0 +1,215 @@ +// Copyright 2020-2024 Buf Technologies, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package protocompile + +import ( + "errors" + "io" + "io/fs" + "os" + "path/filepath" + "strings" + + "google.golang.org/protobuf/reflect/protoreflect" + "google.golang.org/protobuf/reflect/protoregistry" + "google.golang.org/protobuf/types/descriptorpb" + + "github.com/bufbuild/protocompile/ast" + "github.com/bufbuild/protocompile/parser" +) + +// Resolver is used by the compiler to resolve a proto source file name +// into some unit that is usable by the compiler. The result could be source +// for a proto file or it could be an already-parsed AST or descriptor. +// +// Resolver implementations must be thread-safe as a single compilation +// operation could invoke FindFileByPath from multiple goroutines. +type Resolver interface { + // FindFileByPath searches for information for the given file path. If no + // result is available, it should return a non-nil error, such as + // protoregistry.NotFound. + FindFileByPath(path string) (SearchResult, error) +} + +// SearchResult represents information about a proto source file. Only one of +// the various fields must be set, based on what is available for a file. If +// multiple fields are set, the compiler prefers them in opposite order listed: +// so it uses a descriptor if present and only falls back to source if nothing +// else is available. +type SearchResult struct { + // Represents source code for the file. This should be nil if source code + // is not available. If no field below is set, then the compiler will parse + // the source code into an AST. + Source io.Reader + // Represents the abstract syntax tree for the file. If no field below is + // set, then the compiler will convert the AST into a descriptor proto. + AST *ast.FileNode + // A descriptor proto that represents the file. If the field below is not + // set, then the compiler will link this proto with its dependencies to + // produce a linked descriptor. + Proto *descriptorpb.FileDescriptorProto + // A parse result for the file. This packages both an AST and a descriptor + // proto in one. When a parser result is available, it is more efficient + // than using an AST search result, since the descriptor proto need not be + // re-created. And it provides better error messages than a descriptor proto + // search result, since the AST has greater fidelity with regard to source + // positions (even if the descriptor proto includes source code info). + ParseResult parser.Result + // A fully linked descriptor that represents the file. If this field is set, + // then the compiler has little or no additional work to do for this file as + // it is already compiled. If this value implements linker.File, there is no + // additional work. Otherwise, the additional work is to compute an index of + // symbols in the file, for efficient lookup. + Desc protoreflect.FileDescriptor +} + +// ResolverFunc is a simple function type that implements Resolver. +type ResolverFunc func(string) (SearchResult, error) + +var _ Resolver = ResolverFunc(nil) + +func (f ResolverFunc) FindFileByPath(path string) (SearchResult, error) { + return f(path) +} + +// CompositeResolver is a slice of resolvers, which are consulted in order +// until one can supply a result. If none of the constituent resolvers can +// supply a result, the error returned by the first resolver is returned. If +// the slice of resolvers is empty, all operations return +// protoregistry.NotFound. +type CompositeResolver []Resolver + +var _ Resolver = CompositeResolver(nil) + +func (f CompositeResolver) FindFileByPath(path string) (SearchResult, error) { + if len(f) == 0 { + return SearchResult{}, protoregistry.NotFound + } + var firstErr error + for _, res := range f { + r, err := res.FindFileByPath(path) + if err == nil { + return r, nil + } + if firstErr == nil { + firstErr = err + } + } + return SearchResult{}, firstErr +} + +// SourceResolver can resolve file names by returning source code. It uses +// an optional list of import paths to search. By default, it searches the +// file system. +type SourceResolver struct { + // Optional list of import paths. If present and not empty, then all + // file paths to find are assumed to be relative to one of these paths. + // If nil or empty, all file paths to find are assumed to be relative to + // the current working directory. + ImportPaths []string + // Optional function for returning a file's contents. If nil, then + // os.Open is used to open files on the file system. + // + // This function must be thread-safe as a single compilation operation + // could result in concurrent invocations of this function from + // multiple goroutines. + Accessor func(path string) (io.ReadCloser, error) +} + +var _ Resolver = (*SourceResolver)(nil) + +func (r *SourceResolver) FindFileByPath(path string) (SearchResult, error) { + if len(r.ImportPaths) == 0 { + reader, err := r.accessFile(path) + if err != nil { + return SearchResult{}, err + } + return SearchResult{Source: reader}, nil + } + + var e error + for _, importPath := range r.ImportPaths { + reader, err := r.accessFile(filepath.Join(importPath, path)) + if err != nil { + if errors.Is(err, fs.ErrNotExist) { + e = err + continue + } + return SearchResult{}, err + } + return SearchResult{Source: reader}, nil + } + return SearchResult{}, e +} + +func (r *SourceResolver) accessFile(path string) (io.ReadCloser, error) { + if r.Accessor != nil { + return r.Accessor(path) + } + return os.Open(path) +} + +// SourceAccessorFromMap returns a function that can be used as the Accessor +// field of a SourceResolver that uses the given map to load source. The map +// keys are file names and the values are the corresponding file contents. +// +// The given map is used directly and not copied. Since accessor functions +// must be thread-safe, this means that the provided map must not be mutated +// once this accessor is provided to a compile operation. +func SourceAccessorFromMap(srcs map[string]string) func(string) (io.ReadCloser, error) { + return func(path string) (io.ReadCloser, error) { + src, ok := srcs[path] + if !ok { + return nil, os.ErrNotExist + } + return io.NopCloser(strings.NewReader(src)), nil + } +} + +// WithStandardImports returns a new resolver that knows about the same standard +// imports that are included with protoc. +// +// Note that this uses the descriptors embedded in generated code in the packages +// of the Protobuf Go module, except for "google/protobuf/cpp_features.proto" and +// "google/protobuf/java_features.proto". For those two files, compiled descriptors +// are embedded in this module because there is no package in the Protobuf Go module +// that contains generated code for those files. This resolver also provides results +// for the "google/protobuf/go_features.proto", which is technically not a standard +// file (it is not included with protoc) but is included in generated code in the +// Protobuf Go module. +// +// As of v0.14.0 of this module (and v1.34.2 of the Protobuf Go module and v27.0 of +// Protobuf), the contents of the standard import "google/protobuf/descriptor.proto" +// contain extension declarations which are *absent* from the descriptors that this +// resolver returns. That is because extension declarations are only retained in +// source, not at runtime, which means they are not available in the embedded +// descriptors in generated code. +// +// To use versions of the standard imports that *do* include these extension +// declarations, see wellknownimports.WithStandardImports instead. As of this +// writing, the declarations are only needed to prevent source files from +// illegally re-defining the custom features for C++, Java, and Go. +func WithStandardImports(r Resolver) Resolver { + return ResolverFunc(func(name string) (SearchResult, error) { + res, err := r.FindFileByPath(name) + if err != nil { + // error from given resolver? see if it's a known standard file + if d, ok := standardImports[name]; ok { + return SearchResult{Desc: d}, nil + } + } + return res, err + }) +} diff --git a/vendor/github.com/bufbuild/protocompile/sourceinfo/source_code_info.go b/vendor/github.com/bufbuild/protocompile/sourceinfo/source_code_info.go new file mode 100644 index 0000000..3b0ae65 --- /dev/null +++ b/vendor/github.com/bufbuild/protocompile/sourceinfo/source_code_info.go @@ -0,0 +1,962 @@ +// Copyright 2020-2024 Buf Technologies, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Package sourceinfo contains the logic for computing source code info for a +// file descriptor. +// +// The inputs to the computation are an AST for a file as well as the index of +// interpreted options for that file. +package sourceinfo + +import ( + "bytes" + "fmt" + "strings" + + "google.golang.org/protobuf/proto" + "google.golang.org/protobuf/types/descriptorpb" + + "github.com/bufbuild/protocompile/ast" + "github.com/bufbuild/protocompile/internal" +) + +// OptionIndex is a mapping of AST nodes that define options to corresponding +// paths into the containing file descriptor. The path is a sequence of field +// tags and indexes that define a traversal path from the root (the file +// descriptor) to the resolved option field. The info also includes similar +// information about child elements, for options whose values are composite +// (like a list or message literal). +type OptionIndex map[*ast.OptionNode]*OptionSourceInfo + +// OptionSourceInfo describes the source info path for an option value and +// contains information about the value's descendants in the AST. +type OptionSourceInfo struct { + // The source info path to this element. If this element represents a + // declaration with an array-literal value, the last element of the + // path is the index of the first item in the array. + // + // This path is relative to the options message. So the first element + // is a field number of the options message. + // + // If the first element is negative, it indicates the number of path + // components to remove from the path to the relevant options. This is + // used for field pseudo-options, so that the path indicates a field on + // the descriptor, which is a parent of the options message (since that + // is how the pseudo-options are actually stored). + Path []int32 + // Children can be an *ArrayLiteralSourceInfo, a *MessageLiteralSourceInfo, + // or nil, depending on whether the option's value is an + // [*ast.ArrayLiteralNode], an [*ast.MessageLiteralNode], or neither. + // For [*ast.ArrayLiteralNode] values, this is only populated if the + // value is a non-empty array of messages. (Empty arrays and arrays + // of scalar values do not need any additional info.) + Children OptionChildrenSourceInfo +} + +// OptionChildrenSourceInfo represents source info paths for child elements of +// an option value. +type OptionChildrenSourceInfo interface { + isChildSourceInfo() +} + +// ArrayLiteralSourceInfo represents source info paths for the child +// elements of an [*ast.ArrayLiteralNode]. This value is only useful for +// non-empty array literals that contain messages. +type ArrayLiteralSourceInfo struct { + Elements []OptionSourceInfo +} + +func (*ArrayLiteralSourceInfo) isChildSourceInfo() {} + +// MessageLiteralSourceInfo represents source info paths for the child +// elements of an [*ast.MessageLiteralNode]. +type MessageLiteralSourceInfo struct { + Fields map[*ast.MessageFieldNode]*OptionSourceInfo +} + +func (*MessageLiteralSourceInfo) isChildSourceInfo() {} + +// GenerateSourceInfo generates source code info for the given AST. If the given +// opts is present, it can generate source code info for interpreted options. +// Otherwise, any options in the AST will get source code info as uninterpreted +// options. +func GenerateSourceInfo(file *ast.FileNode, opts OptionIndex, genOpts ...GenerateOption) *descriptorpb.SourceCodeInfo { + if file == nil { + return nil + } + sci := sourceCodeInfo{file: file, commentsUsed: map[ast.SourcePos]struct{}{}} + for _, sourceInfoOpt := range genOpts { + sourceInfoOpt.apply(&sci) + } + generateSourceInfoForFile(opts, &sci, file) + return &descriptorpb.SourceCodeInfo{Location: sci.locs} +} + +// GenerateOption represents an option for how source code info is generated. +type GenerateOption interface { + apply(*sourceCodeInfo) +} + +// WithExtraComments will result in source code info that contains extra comments. +// By default, comments are only generated for full declarations. Inline comments +// around elements of a declaration are not included in source code info. This option +// changes that behavior so that as many comments as possible are described in the +// source code info. +func WithExtraComments() GenerateOption { + return extraCommentsOption{} +} + +// WithExtraOptionLocations will result in source code info that contains extra +// locations to describe elements inside of a message literal. By default, option +// values are treated as opaque, so the only locations included are for the entire +// option value. But with this option, paths to the various fields set inside a +// message literal will also have locations. This makes it possible for usages of +// the source code info to report precise locations for specific fields inside the +// value. +func WithExtraOptionLocations() GenerateOption { + return extraOptionLocationsOption{} +} + +type extraCommentsOption struct{} + +func (e extraCommentsOption) apply(info *sourceCodeInfo) { + info.extraComments = true +} + +type extraOptionLocationsOption struct{} + +func (e extraOptionLocationsOption) apply(info *sourceCodeInfo) { + info.extraOptionLocs = true +} + +func generateSourceInfoForFile(opts OptionIndex, sci *sourceCodeInfo, file *ast.FileNode) { + path := make([]int32, 0, 16) + + sci.newLocWithoutComments(file, nil) + + if file.Syntax != nil { + sci.newLocWithComments(file.Syntax, append(path, internal.FileSyntaxTag)) + } + if file.Edition != nil { + sci.newLocWithComments(file.Edition, append(path, internal.FileEditionTag)) + } + + var depIndex, pubDepIndex, weakDepIndex, optIndex, msgIndex, enumIndex, extendIndex, svcIndex int32 + + for _, child := range file.Decls { + switch child := child.(type) { + case *ast.ImportNode: + sci.newLocWithComments(child, append(path, internal.FileDependencyTag, depIndex)) + depIndex++ + if child.Public != nil { + sci.newLoc(child.Public, append(path, internal.FilePublicDependencyTag, pubDepIndex)) + pubDepIndex++ + } else if child.Weak != nil { + sci.newLoc(child.Weak, append(path, internal.FileWeakDependencyTag, weakDepIndex)) + weakDepIndex++ + } + case *ast.PackageNode: + sci.newLocWithComments(child, append(path, internal.FilePackageTag)) + case *ast.OptionNode: + generateSourceCodeInfoForOption(opts, sci, child, false, &optIndex, append(path, internal.FileOptionsTag)) + case *ast.MessageNode: + generateSourceCodeInfoForMessage(opts, sci, child, nil, append(path, internal.FileMessagesTag, msgIndex)) + msgIndex++ + case *ast.EnumNode: + generateSourceCodeInfoForEnum(opts, sci, child, append(path, internal.FileEnumsTag, enumIndex)) + enumIndex++ + case *ast.ExtendNode: + extsPath := append(path, internal.FileExtensionsTag) //nolint:gocritic // intentionally creating new slice var + // we clone the path here so that append can't mutate extsPath, since they may share storage + msgsPath := append(internal.ClonePath(path), internal.FileMessagesTag) + generateSourceCodeInfoForExtensions(opts, sci, child, &extendIndex, &msgIndex, extsPath, msgsPath) + case *ast.ServiceNode: + generateSourceCodeInfoForService(opts, sci, child, append(path, internal.FileServicesTag, svcIndex)) + svcIndex++ + } + } +} + +func generateSourceCodeInfoForOption(opts OptionIndex, sci *sourceCodeInfo, n *ast.OptionNode, compact bool, uninterpIndex *int32, path []int32) { + if !compact { + sci.newLocWithoutComments(n, path) + } + optInfo := opts[n] + if optInfo != nil { + fullPath := combinePathsForOption(path, optInfo.Path) + if compact { + sci.newLoc(n, fullPath) + } else { + sci.newLocWithComments(n, fullPath) + } + if sci.extraOptionLocs { + generateSourceInfoForOptionChildren(sci, n.Val, path, fullPath, optInfo.Children) + } + return + } + + // it's an uninterpreted option + optPath := path + optPath = append(optPath, internal.UninterpretedOptionsTag, *uninterpIndex) + *uninterpIndex++ + sci.newLoc(n, optPath) + var valTag int32 + switch n.Val.(type) { + case ast.IdentValueNode: + valTag = internal.UninterpretedIdentTag + case *ast.NegativeIntLiteralNode: + valTag = internal.UninterpretedNegIntTag + case ast.IntValueNode: + valTag = internal.UninterpretedPosIntTag + case ast.FloatValueNode: + valTag = internal.UninterpretedDoubleTag + case ast.StringValueNode: + valTag = internal.UninterpretedStringTag + case *ast.MessageLiteralNode: + valTag = internal.UninterpretedAggregateTag + } + if valTag != 0 { + sci.newLoc(n.Val, append(optPath, valTag)) + } + for j, nn := range n.Name.Parts { + optNmPath := optPath + optNmPath = append(optNmPath, internal.UninterpretedNameTag, int32(j)) + sci.newLoc(nn, optNmPath) + sci.newLoc(nn.Name, append(optNmPath, internal.UninterpretedNameNameTag)) + } +} + +func combinePathsForOption(prefix, optionPath []int32) []int32 { + fullPath := make([]int32, len(prefix), len(prefix)+len(optionPath)) + copy(fullPath, prefix) + if optionPath[0] == -1 { + // used by "default" and "json_name" field pseudo-options + // to attribute path to parent element (since those are + // stored directly on the descriptor, not its options) + optionPath = optionPath[1:] + fullPath = fullPath[:len(prefix)-1] + } + return append(fullPath, optionPath...) +} + +func generateSourceInfoForOptionChildren(sci *sourceCodeInfo, n ast.ValueNode, pathPrefix, path []int32, childInfo OptionChildrenSourceInfo) { + switch childInfo := childInfo.(type) { + case *ArrayLiteralSourceInfo: + if arrayLiteral, ok := n.(*ast.ArrayLiteralNode); ok { + for i, val := range arrayLiteral.Elements { + elementInfo := childInfo.Elements[i] + fullPath := combinePathsForOption(pathPrefix, elementInfo.Path) + sci.newLoc(val, fullPath) + generateSourceInfoForOptionChildren(sci, val, pathPrefix, fullPath, elementInfo.Children) + } + } + case *MessageLiteralSourceInfo: + if msgLiteral, ok := n.(*ast.MessageLiteralNode); ok { + for _, fieldNode := range msgLiteral.Elements { + fieldInfo, ok := childInfo.Fields[fieldNode] + if !ok { + continue + } + fullPath := combinePathsForOption(pathPrefix, fieldInfo.Path) + locationNode := ast.Node(fieldNode) + if fieldNode.Name.IsAnyTypeReference() && fullPath[len(fullPath)-1] == internal.AnyValueTag { + // This is a special expanded Any. So also insert a location + // for the type URL field. + typeURLPath := make([]int32, len(fullPath)) + copy(typeURLPath, fullPath) + typeURLPath[len(typeURLPath)-1] = internal.AnyTypeURLTag + sci.newLoc(fieldNode.Name, fullPath) + // And create the next location so it's just the value, + // not the full field definition. + locationNode = fieldNode.Val + } + _, isArrayLiteral := fieldNode.Val.(*ast.ArrayLiteralNode) + if !isArrayLiteral { + // We don't include this with an array literal since the path + // is to the first element of the array. If we added it here, + // it would be redundant with the child info we add next, and + // it wouldn't be entirely correct since it only indicates the + // index of the first element in the array (and not the others). + sci.newLoc(locationNode, fullPath) + } + generateSourceInfoForOptionChildren(sci, fieldNode.Val, pathPrefix, fullPath, fieldInfo.Children) + } + } + case nil: + if arrayLiteral, ok := n.(*ast.ArrayLiteralNode); ok { + // an array literal without child source info is an array of scalars + for i, val := range arrayLiteral.Elements { + // last element of path is starting index for array literal + elementPath := append(([]int32)(nil), path...) + elementPath[len(elementPath)-1] += int32(i) + sci.newLoc(val, elementPath) + } + } + } +} + +func generateSourceCodeInfoForMessage(opts OptionIndex, sci *sourceCodeInfo, n ast.MessageDeclNode, fieldPath []int32, path []int32) { + var openBrace ast.Node + + var decls []ast.MessageElement + switch n := n.(type) { + case *ast.MessageNode: + openBrace = n.OpenBrace + decls = n.Decls + case *ast.SyntheticGroupMessageNode: + openBrace = n.OpenBrace + decls = n.Decls + case *ast.SyntheticMapEntryNode: + sci.newLoc(n, path) + // map entry so nothing else to do + return + } + sci.newBlockLocWithComments(n, openBrace, path) + + sci.newLoc(n.MessageName(), append(path, internal.MessageNameTag)) + // matching protoc, which emits the corresponding field type name (for group fields) + // right after the source location for the group message name + if fieldPath != nil { + sci.newLoc(n.MessageName(), append(fieldPath, internal.FieldTypeNameTag)) + } + + var optIndex, fieldIndex, oneofIndex, extendIndex, nestedMsgIndex int32 + var nestedEnumIndex, extRangeIndex, reservedRangeIndex, reservedNameIndex int32 + for _, child := range decls { + switch child := child.(type) { + case *ast.OptionNode: + generateSourceCodeInfoForOption(opts, sci, child, false, &optIndex, append(path, internal.MessageOptionsTag)) + case *ast.FieldNode: + generateSourceCodeInfoForField(opts, sci, child, append(path, internal.MessageFieldsTag, fieldIndex)) + fieldIndex++ + case *ast.GroupNode: + fldPath := append(path, internal.MessageFieldsTag, fieldIndex) //nolint:gocritic // intentionally creating new slice var + generateSourceCodeInfoForField(opts, sci, child, fldPath) + fieldIndex++ + // we clone the path here so that append can't mutate fldPath, since they may share storage + msgPath := append(internal.ClonePath(path), internal.MessageNestedMessagesTag, nestedMsgIndex) + generateSourceCodeInfoForMessage(opts, sci, child.AsMessage(), fldPath, msgPath) + nestedMsgIndex++ + case *ast.MapFieldNode: + generateSourceCodeInfoForField(opts, sci, child, append(path, internal.MessageFieldsTag, fieldIndex)) + fieldIndex++ + nestedMsgIndex++ + case *ast.OneofNode: + fldsPath := append(path, internal.MessageFieldsTag) //nolint:gocritic // intentionally creating new slice var + // we clone the path here and below so that append ops can't mutate + // fldPath or msgsPath, since they may otherwise share storage + msgsPath := append(internal.ClonePath(path), internal.MessageNestedMessagesTag) + ooPath := append(internal.ClonePath(path), internal.MessageOneofsTag, oneofIndex) + generateSourceCodeInfoForOneof(opts, sci, child, &fieldIndex, &nestedMsgIndex, fldsPath, msgsPath, ooPath) + oneofIndex++ + case *ast.MessageNode: + generateSourceCodeInfoForMessage(opts, sci, child, nil, append(path, internal.MessageNestedMessagesTag, nestedMsgIndex)) + nestedMsgIndex++ + case *ast.EnumNode: + generateSourceCodeInfoForEnum(opts, sci, child, append(path, internal.MessageEnumsTag, nestedEnumIndex)) + nestedEnumIndex++ + case *ast.ExtendNode: + extsPath := append(path, internal.MessageExtensionsTag) //nolint:gocritic // intentionally creating new slice var + // we clone the path here so that append can't mutate extsPath, since they may share storage + msgsPath := append(internal.ClonePath(path), internal.MessageNestedMessagesTag) + generateSourceCodeInfoForExtensions(opts, sci, child, &extendIndex, &nestedMsgIndex, extsPath, msgsPath) + case *ast.ExtensionRangeNode: + generateSourceCodeInfoForExtensionRanges(opts, sci, child, &extRangeIndex, append(path, internal.MessageExtensionRangesTag)) + case *ast.ReservedNode: + if len(child.Names) > 0 { + resPath := path + resPath = append(resPath, internal.MessageReservedNamesTag) + sci.newLocWithComments(child, resPath) + for _, rn := range child.Names { + sci.newLoc(rn, append(resPath, reservedNameIndex)) + reservedNameIndex++ + } + } + if len(child.Ranges) > 0 { + resPath := path + resPath = append(resPath, internal.MessageReservedRangesTag) + sci.newLocWithComments(child, resPath) + for _, rr := range child.Ranges { + generateSourceCodeInfoForReservedRange(sci, rr, append(resPath, reservedRangeIndex)) + reservedRangeIndex++ + } + } + } + } +} + +func generateSourceCodeInfoForEnum(opts OptionIndex, sci *sourceCodeInfo, n *ast.EnumNode, path []int32) { + sci.newBlockLocWithComments(n, n.OpenBrace, path) + sci.newLoc(n.Name, append(path, internal.EnumNameTag)) + + var optIndex, valIndex, reservedNameIndex, reservedRangeIndex int32 + for _, child := range n.Decls { + switch child := child.(type) { + case *ast.OptionNode: + generateSourceCodeInfoForOption(opts, sci, child, false, &optIndex, append(path, internal.EnumOptionsTag)) + case *ast.EnumValueNode: + generateSourceCodeInfoForEnumValue(opts, sci, child, append(path, internal.EnumValuesTag, valIndex)) + valIndex++ + case *ast.ReservedNode: + if len(child.Names) > 0 { + resPath := path + resPath = append(resPath, internal.EnumReservedNamesTag) + sci.newLocWithComments(child, resPath) + for _, rn := range child.Names { + sci.newLoc(rn, append(resPath, reservedNameIndex)) + reservedNameIndex++ + } + } + if len(child.Ranges) > 0 { + resPath := path + resPath = append(resPath, internal.EnumReservedRangesTag) + sci.newLocWithComments(child, resPath) + for _, rr := range child.Ranges { + generateSourceCodeInfoForReservedRange(sci, rr, append(resPath, reservedRangeIndex)) + reservedRangeIndex++ + } + } + } + } +} + +func generateSourceCodeInfoForEnumValue(opts OptionIndex, sci *sourceCodeInfo, n *ast.EnumValueNode, path []int32) { + sci.newLocWithComments(n, path) + sci.newLoc(n.Name, append(path, internal.EnumValNameTag)) + sci.newLoc(n.Number, append(path, internal.EnumValNumberTag)) + + // enum value options + if n.Options != nil { + optsPath := path + optsPath = append(optsPath, internal.EnumValOptionsTag) + sci.newLoc(n.Options, optsPath) + var optIndex int32 + for _, opt := range n.Options.GetElements() { + generateSourceCodeInfoForOption(opts, sci, opt, true, &optIndex, optsPath) + } + } +} + +func generateSourceCodeInfoForReservedRange(sci *sourceCodeInfo, n *ast.RangeNode, path []int32) { + sci.newLoc(n, path) + sci.newLoc(n.StartVal, append(path, internal.ReservedRangeStartTag)) + switch { + case n.EndVal != nil: + sci.newLoc(n.EndVal, append(path, internal.ReservedRangeEndTag)) + case n.Max != nil: + sci.newLoc(n.Max, append(path, internal.ReservedRangeEndTag)) + default: + sci.newLoc(n.StartVal, append(path, internal.ReservedRangeEndTag)) + } +} + +func generateSourceCodeInfoForExtensions(opts OptionIndex, sci *sourceCodeInfo, n *ast.ExtendNode, extendIndex, msgIndex *int32, extendPath, msgPath []int32) { + sci.newBlockLocWithComments(n, n.OpenBrace, extendPath) + for _, decl := range n.Decls { + switch decl := decl.(type) { + case *ast.FieldNode: + generateSourceCodeInfoForField(opts, sci, decl, append(extendPath, *extendIndex)) + *extendIndex++ + case *ast.GroupNode: + fldPath := extendPath + fldPath = append(fldPath, *extendIndex) + generateSourceCodeInfoForField(opts, sci, decl, fldPath) + *extendIndex++ + generateSourceCodeInfoForMessage(opts, sci, decl.AsMessage(), fldPath, append(msgPath, *msgIndex)) + *msgIndex++ + } + } +} + +func generateSourceCodeInfoForOneof(opts OptionIndex, sci *sourceCodeInfo, n *ast.OneofNode, fieldIndex, nestedMsgIndex *int32, fieldPath, nestedMsgPath, oneofPath []int32) { + sci.newBlockLocWithComments(n, n.OpenBrace, oneofPath) + sci.newLoc(n.Name, append(oneofPath, internal.OneofNameTag)) + + var optIndex int32 + for _, child := range n.Decls { + switch child := child.(type) { + case *ast.OptionNode: + generateSourceCodeInfoForOption(opts, sci, child, false, &optIndex, append(oneofPath, internal.OneofOptionsTag)) + case *ast.FieldNode: + generateSourceCodeInfoForField(opts, sci, child, append(fieldPath, *fieldIndex)) + *fieldIndex++ + case *ast.GroupNode: + fldPath := fieldPath + fldPath = append(fldPath, *fieldIndex) + generateSourceCodeInfoForField(opts, sci, child, fldPath) + *fieldIndex++ + generateSourceCodeInfoForMessage(opts, sci, child.AsMessage(), fldPath, append(nestedMsgPath, *nestedMsgIndex)) + *nestedMsgIndex++ + } + } +} + +func generateSourceCodeInfoForField(opts OptionIndex, sci *sourceCodeInfo, n ast.FieldDeclNode, path []int32) { + var fieldType string + if f, ok := n.(*ast.FieldNode); ok { + fieldType = string(f.FldType.AsIdentifier()) + } + + if n.GetGroupKeyword() != nil { + // comments will appear on group message + sci.newLocWithoutComments(n, path) + if n.FieldExtendee() != nil { + sci.newLoc(n.FieldExtendee(), append(path, internal.FieldExtendeeTag)) + } + if n.FieldLabel() != nil { + // no comments here either (label is first token for group, so we want + // to leave the comments to be associated with the group message instead) + sci.newLocWithoutComments(n.FieldLabel(), append(path, internal.FieldLabelTag)) + } + sci.newLoc(n.FieldType(), append(path, internal.FieldTypeTag)) + // let the name comments be attributed to the group name + sci.newLocWithoutComments(n.FieldName(), append(path, internal.FieldNameTag)) + } else { + sci.newLocWithComments(n, path) + if n.FieldExtendee() != nil { + sci.newLoc(n.FieldExtendee(), append(path, internal.FieldExtendeeTag)) + } + if n.FieldLabel() != nil { + sci.newLoc(n.FieldLabel(), append(path, internal.FieldLabelTag)) + } + var tag int32 + if _, isScalar := internal.FieldTypes[fieldType]; isScalar { + tag = internal.FieldTypeTag + } else { + // this is a message or an enum, so attribute type location + // to the type name field + tag = internal.FieldTypeNameTag + } + sci.newLoc(n.FieldType(), append(path, tag)) + sci.newLoc(n.FieldName(), append(path, internal.FieldNameTag)) + } + sci.newLoc(n.FieldTag(), append(path, internal.FieldNumberTag)) + + if n.GetOptions() != nil { + optsPath := path + optsPath = append(optsPath, internal.FieldOptionsTag) + sci.newLoc(n.GetOptions(), optsPath) + var optIndex int32 + for _, opt := range n.GetOptions().GetElements() { + generateSourceCodeInfoForOption(opts, sci, opt, true, &optIndex, optsPath) + } + } +} + +func generateSourceCodeInfoForExtensionRanges(opts OptionIndex, sci *sourceCodeInfo, n *ast.ExtensionRangeNode, extRangeIndex *int32, path []int32) { + sci.newLocWithComments(n, path) + startExtRangeIndex := *extRangeIndex + for _, child := range n.Ranges { + path := append(path, *extRangeIndex) + *extRangeIndex++ + sci.newLoc(child, path) + sci.newLoc(child.StartVal, append(path, internal.ExtensionRangeStartTag)) + switch { + case child.EndVal != nil: + sci.newLoc(child.EndVal, append(path, internal.ExtensionRangeEndTag)) + case child.Max != nil: + sci.newLoc(child.Max, append(path, internal.ExtensionRangeEndTag)) + default: + sci.newLoc(child.StartVal, append(path, internal.ExtensionRangeEndTag)) + } + } + // options for all ranges go after the start+end values + for range n.Ranges { + path := append(path, startExtRangeIndex) + startExtRangeIndex++ + if n.Options != nil { + optsPath := path + optsPath = append(optsPath, internal.ExtensionRangeOptionsTag) + sci.newLoc(n.Options, optsPath) + var optIndex int32 + for _, opt := range n.Options.GetElements() { + generateSourceCodeInfoForOption(opts, sci, opt, true, &optIndex, optsPath) + } + } + } +} + +func generateSourceCodeInfoForService(opts OptionIndex, sci *sourceCodeInfo, n *ast.ServiceNode, path []int32) { + sci.newBlockLocWithComments(n, n.OpenBrace, path) + sci.newLoc(n.Name, append(path, internal.ServiceNameTag)) + var optIndex, rpcIndex int32 + for _, child := range n.Decls { + switch child := child.(type) { + case *ast.OptionNode: + generateSourceCodeInfoForOption(opts, sci, child, false, &optIndex, append(path, internal.ServiceOptionsTag)) + case *ast.RPCNode: + generateSourceCodeInfoForMethod(opts, sci, child, append(path, internal.ServiceMethodsTag, rpcIndex)) + rpcIndex++ + } + } +} + +func generateSourceCodeInfoForMethod(opts OptionIndex, sci *sourceCodeInfo, n *ast.RPCNode, path []int32) { + if n.OpenBrace != nil { + sci.newBlockLocWithComments(n, n.OpenBrace, path) + } else { + sci.newLocWithComments(n, path) + } + sci.newLoc(n.Name, append(path, internal.MethodNameTag)) + if n.Input.Stream != nil { + sci.newLoc(n.Input.Stream, append(path, internal.MethodInputStreamTag)) + } + sci.newLoc(n.Input.MessageType, append(path, internal.MethodInputTag)) + if n.Output.Stream != nil { + sci.newLoc(n.Output.Stream, append(path, internal.MethodOutputStreamTag)) + } + sci.newLoc(n.Output.MessageType, append(path, internal.MethodOutputTag)) + + optsPath := path + optsPath = append(optsPath, internal.MethodOptionsTag) + var optIndex int32 + for _, decl := range n.Decls { + if opt, ok := decl.(*ast.OptionNode); ok { + generateSourceCodeInfoForOption(opts, sci, opt, false, &optIndex, optsPath) + } + } +} + +type sourceCodeInfo struct { + file *ast.FileNode + extraComments bool + extraOptionLocs bool + locs []*descriptorpb.SourceCodeInfo_Location + commentsUsed map[ast.SourcePos]struct{} +} + +func (sci *sourceCodeInfo) newLocWithoutComments(n ast.Node, path []int32) { + var start, end ast.SourcePos + if n == sci.file { + // For files, we don't want to consider trailing EOF token + // as part of the span. We want the span to only include + // actual lexical elements in the file (which also excludes + // whitespace and comments). + children := sci.file.Children() + if len(children) > 0 && isEOF(children[len(children)-1]) { + children = children[:len(children)-1] + } + if len(children) == 0 { + start = ast.SourcePos{Filename: sci.file.Name(), Line: 1, Col: 1} + end = start + } else { + start = sci.file.TokenInfo(n.Start()).Start() + end = sci.file.TokenInfo(children[len(children)-1].End()).End() + } + } else { + info := sci.file.NodeInfo(n) + start, end = info.Start(), info.End() + } + sci.locs = append(sci.locs, &descriptorpb.SourceCodeInfo_Location{ + Path: internal.ClonePath(path), + Span: makeSpan(start, end), + }) +} + +func (sci *sourceCodeInfo) newLoc(n ast.Node, path []int32) { + info := sci.file.NodeInfo(n) + if !sci.extraComments { + start, end := info.Start(), info.End() + sci.locs = append(sci.locs, &descriptorpb.SourceCodeInfo_Location{ + Path: internal.ClonePath(path), + Span: makeSpan(start, end), + }) + } else { + detachedComments, leadingComments := sci.getLeadingComments(n) + trailingComments := sci.getTrailingComments(n) + sci.newLocWithGivenComments(info, detachedComments, leadingComments, trailingComments, path) + } +} + +func isEOF(n ast.Node) bool { + r, ok := n.(*ast.RuneNode) + return ok && r.Rune == 0 +} + +func (sci *sourceCodeInfo) newBlockLocWithComments(n, openBrace ast.Node, path []int32) { + // Block definitions use trailing comments after the open brace "{" as the + // element's trailing comments. For example: + // + // message Foo { // this is a trailing comment for a message + // + // } // not this + // + nodeInfo := sci.file.NodeInfo(n) + detachedComments, leadingComments := sci.getLeadingComments(n) + trailingComments := sci.getTrailingComments(openBrace) + sci.newLocWithGivenComments(nodeInfo, detachedComments, leadingComments, trailingComments, path) +} + +func (sci *sourceCodeInfo) newLocWithComments(n ast.Node, path []int32) { + nodeInfo := sci.file.NodeInfo(n) + detachedComments, leadingComments := sci.getLeadingComments(n) + trailingComments := sci.getTrailingComments(n) + sci.newLocWithGivenComments(nodeInfo, detachedComments, leadingComments, trailingComments, path) +} + +func (sci *sourceCodeInfo) newLocWithGivenComments(nodeInfo ast.NodeInfo, detachedComments []comments, leadingComments comments, trailingComments comments, path []int32) { + if (len(detachedComments) > 0 && sci.commentUsed(detachedComments[0])) || + (len(detachedComments) == 0 && sci.commentUsed(leadingComments)) { + detachedComments = nil + leadingComments = ast.EmptyComments + } + if sci.commentUsed(trailingComments) { + trailingComments = ast.EmptyComments + } + + var trail *string + if trailingComments.Len() > 0 { + trail = proto.String(sci.combineComments(trailingComments)) + } + + var lead *string + if leadingComments.Len() > 0 { + lead = proto.String(sci.combineComments(leadingComments)) + } + + detached := make([]string, len(detachedComments)) + for i, cmts := range detachedComments { + detached[i] = sci.combineComments(cmts) + } + + sci.locs = append(sci.locs, &descriptorpb.SourceCodeInfo_Location{ + LeadingDetachedComments: detached, + LeadingComments: lead, + TrailingComments: trail, + Path: internal.ClonePath(path), + Span: makeSpan(nodeInfo.Start(), nodeInfo.End()), + }) +} + +type comments interface { + Len() int + Index(int) ast.Comment +} + +type subComments struct { + offs, n int + c ast.Comments +} + +func (s subComments) Len() int { + return s.n +} + +func (s subComments) Index(i int) ast.Comment { + if i < 0 || i >= s.n { + panic(fmt.Errorf("runtime error: index out of range [%d] with length %d", i, s.n)) + } + return s.c.Index(i + s.offs) +} + +func (sci *sourceCodeInfo) getLeadingComments(n ast.Node) ([]comments, comments) { + s := n.Start() + info := sci.file.TokenInfo(s) + var prevInfo ast.NodeInfo + if prev, ok := sci.file.Tokens().Previous(s); ok { + prevInfo = sci.file.TokenInfo(prev) + } + _, d, l := sci.attributeComments(prevInfo, info) + return d, l +} + +func (sci *sourceCodeInfo) getTrailingComments(n ast.Node) comments { + e := n.End() + next, ok := sci.file.Tokens().Next(e) + if !ok { + return ast.EmptyComments + } + info := sci.file.TokenInfo(e) + nextInfo := sci.file.TokenInfo(next) + t, _, _ := sci.attributeComments(info, nextInfo) + return t +} + +func (sci *sourceCodeInfo) attributeComments(prevInfo, info ast.NodeInfo) (t comments, d []comments, l comments) { + detached := groupComments(info.LeadingComments()) + var trail comments + if prevInfo.IsValid() { + trail = comments(prevInfo.TrailingComments()) + if trail.Len() == 0 { + trail, detached = sci.maybeDonate(prevInfo, info, detached) + } + } else { + trail = ast.EmptyComments + } + detached, lead := sci.maybeAttach(prevInfo, info, trail.Len() > 0, detached) + return trail, detached, lead +} + +func (sci *sourceCodeInfo) maybeDonate(prevInfo ast.NodeInfo, info ast.NodeInfo, lead []comments) (t comments, l []comments) { + if len(lead) == 0 { + // nothing to donate + return ast.EmptyComments, nil + } + firstCommentPos := lead[0].Index(0) + if firstCommentPos.Start().Line > prevInfo.End().Line+1 { + // first comment is detached from previous token, so can't be a trailing comment + return ast.EmptyComments, lead + } + if len(lead) > 1 { + // multiple groups? then donate first comment to previous token + return lead[0], lead[1:] + } + // there is only one element in lead + comment := lead[0] + lastCommentPos := comment.Index(comment.Len() - 1) + if lastCommentPos.End().Line < info.Start().Line-1 { + // there is a blank line between the comments and subsequent token, so + // we can donate the comment to previous token + return comment, nil + } + if txt := info.RawText(); txt == "" || (len(txt) == 1 && strings.ContainsAny(txt, "}]),;")) { + // token is a symbol for the end of a scope or EOF, which doesn't need a leading comment + if !sci.extraComments && txt != "" && + firstCommentPos.Start().Line == prevInfo.End().Line && + lastCommentPos.End().Line == info.Start().Line { + // protoc does not donate if prev and next token are on the same line since it's + // ambiguous which one should get the comment; so we mirror that here + return ast.EmptyComments, lead + } + // But with extra comments, we always donate in this situation in order to capture + // more comments. Because otherwise, these comments are lost since these symbols + // don't map to a location in source code info. + return comment, nil + } + // cannot donate + return ast.EmptyComments, lead +} + +func (sci *sourceCodeInfo) maybeAttach(prevInfo ast.NodeInfo, info ast.NodeInfo, hasTrail bool, lead []comments) (d []comments, l comments) { + if len(lead) == 0 { + return nil, ast.EmptyComments + } + + if len(lead) == 1 && !hasTrail && prevInfo.IsValid() { + // If the one comment appears attached to both previous and next tokens, + // don't attach to either. + comment := lead[0] + attachedToPrevious := comment.Index(0).Start().Line == prevInfo.End().Line + attachedToNext := comment.Index(comment.Len()-1).End().Line == info.Start().Line + if attachedToPrevious && attachedToNext { + // Since attachment is ambiguous, leave it detached. + return lead, ast.EmptyComments + } + } + + lastComment := lead[len(lead)-1] + if lastComment.Index(lastComment.Len()-1).End().Line >= info.Start().Line-1 { + return lead[:len(lead)-1], lastComment + } + + return lead, ast.EmptyComments +} + +func makeSpan(start, end ast.SourcePos) []int32 { + if start.Line == end.Line { + return []int32{int32(start.Line) - 1, int32(start.Col) - 1, int32(end.Col) - 1} + } + return []int32{int32(start.Line) - 1, int32(start.Col) - 1, int32(end.Line) - 1, int32(end.Col) - 1} +} + +func (sci *sourceCodeInfo) commentUsed(c comments) bool { + if c.Len() == 0 { + return false + } + pos := c.Index(0).Start() + if _, ok := sci.commentsUsed[pos]; ok { + return true + } + + sci.commentsUsed[pos] = struct{}{} + return false +} + +func groupComments(cmts ast.Comments) []comments { + if cmts.Len() == 0 { + return nil + } + var groups []comments + singleLineStyle := cmts.Index(0).RawText()[:2] == "//" + line := cmts.Index(0).End().Line + start := 0 + for i := 1; i < cmts.Len(); i++ { + c := cmts.Index(i) + prevSingleLine := singleLineStyle + singleLineStyle = strings.HasPrefix(c.RawText(), "//") + if !singleLineStyle || prevSingleLine != singleLineStyle || c.Start().Line > line+1 { + // new group! + groups = append(groups, subComments{offs: start, n: i - start, c: cmts}) + start = i + } + line = c.End().Line + } + // don't forget last group + groups = append(groups, subComments{offs: start, n: cmts.Len() - start, c: cmts}) + return groups +} + +func (sci *sourceCodeInfo) combineComments(comments comments) string { + if comments.Len() == 0 { + return "" + } + var buf bytes.Buffer + for i, l := 0, comments.Len(); i < l; i++ { + c := comments.Index(i) + txt := c.RawText() + if txt[:2] == "//" { + buf.WriteString(txt[2:]) + // protoc includes trailing newline for line comments, + // but it's not present in the AST comment. So we need + // to add it if present. + if i, ok := sci.file.Items().Next(c.AsItem()); ok { + info := sci.file.ItemInfo(i) + if strings.HasPrefix(info.LeadingWhitespace(), "\n") { + buf.WriteRune('\n') + } + } + } else { + lines := strings.Split(txt[2:len(txt)-2], "\n") + first := true + for _, l := range lines { + if first { + first = false + buf.WriteString(l) + continue + } + buf.WriteByte('\n') + + // strip a prefix of whitespace followed by '*' + j := 0 + for j < len(l) { + if l[j] != ' ' && l[j] != '\t' { + break + } + j++ + } + switch { + case j == len(l): + l = "" + case l[j] == '*': + l = l[j+1:] + case j > 0: + l = l[j:] + } + + buf.WriteString(l) + } + } + } + return buf.String() +} diff --git a/vendor/github.com/bufbuild/protocompile/std_imports.go b/vendor/github.com/bufbuild/protocompile/std_imports.go new file mode 100644 index 0000000..a31232a --- /dev/null +++ b/vendor/github.com/bufbuild/protocompile/std_imports.go @@ -0,0 +1,96 @@ +// Copyright 2020-2024 Buf Technologies, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package protocompile + +import ( + "google.golang.org/protobuf/reflect/protoreflect" + "google.golang.org/protobuf/reflect/protoregistry" + _ "google.golang.org/protobuf/types/gofeaturespb" // link in packages that include the standard protos included with protoc. + _ "google.golang.org/protobuf/types/known/anypb" + _ "google.golang.org/protobuf/types/known/apipb" + _ "google.golang.org/protobuf/types/known/durationpb" + _ "google.golang.org/protobuf/types/known/emptypb" + _ "google.golang.org/protobuf/types/known/fieldmaskpb" + _ "google.golang.org/protobuf/types/known/sourcecontextpb" + _ "google.golang.org/protobuf/types/known/structpb" + _ "google.golang.org/protobuf/types/known/timestamppb" + _ "google.golang.org/protobuf/types/known/typepb" + _ "google.golang.org/protobuf/types/known/wrapperspb" + _ "google.golang.org/protobuf/types/pluginpb" + + "github.com/bufbuild/protocompile/internal/featuresext" +) + +// All files that are included with protoc are also included with this package +// so that clients do not need to explicitly supply a copy of these protos (just +// like callers of protoc do not need to supply them). +var standardImports map[string]protoreflect.FileDescriptor + +func init() { + standardFilenames := []string{ + "google/protobuf/any.proto", + "google/protobuf/api.proto", + "google/protobuf/compiler/plugin.proto", + "google/protobuf/descriptor.proto", + "google/protobuf/duration.proto", + "google/protobuf/empty.proto", + "google/protobuf/field_mask.proto", + "google/protobuf/go_features.proto", + "google/protobuf/source_context.proto", + "google/protobuf/struct.proto", + "google/protobuf/timestamp.proto", + "google/protobuf/type.proto", + "google/protobuf/wrappers.proto", + } + + standardImports = map[string]protoreflect.FileDescriptor{} + for _, fn := range standardFilenames { + fd, err := protoregistry.GlobalFiles.FindFileByPath(fn) + if err != nil { + panic(err.Error()) + } + standardImports[fn] = fd + } + + otherFeatures := []struct { + Name string + GetDescriptor func() (protoreflect.FileDescriptor, error) + }{ + { + Name: "google/protobuf/cpp_features.proto", + GetDescriptor: featuresext.CppFeaturesDescriptor, + }, + { + Name: "google/protobuf/java_features.proto", + GetDescriptor: featuresext.JavaFeaturesDescriptor, + }, + } + for _, feature := range otherFeatures { + // First see if the program has generated Go code for this + // file linked in: + fd, err := protoregistry.GlobalFiles.FindFileByPath(feature.Name) + if err == nil { + standardImports[feature.Name] = fd + continue + } + fd, err = feature.GetDescriptor() + if err != nil { + // For these extensions to FeatureSet, we are lenient. If + // we can't load them, just ignore them. + continue + } + standardImports[feature.Name] = fd + } +} diff --git a/vendor/github.com/bufbuild/protocompile/supported_editions.go b/vendor/github.com/bufbuild/protocompile/supported_editions.go new file mode 100644 index 0000000..72bd51f --- /dev/null +++ b/vendor/github.com/bufbuild/protocompile/supported_editions.go @@ -0,0 +1,30 @@ +// Copyright 2020-2024 Buf Technologies, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package protocompile + +import ( + "google.golang.org/protobuf/types/descriptorpb" + + "github.com/bufbuild/protocompile/internal/editions" +) + +// IsEditionSupported returns true if this module can compile sources for +// the given edition. This returns true for the special EDITION_PROTO2 and +// EDITION_PROTO3 as well as all actual editions supported. +func IsEditionSupported(edition descriptorpb.Edition) bool { + return edition == descriptorpb.Edition_EDITION_PROTO2 || + edition == descriptorpb.Edition_EDITION_PROTO3 || + (edition >= editions.MinSupportedEdition && edition <= editions.MaxSupportedEdition) +} diff --git a/vendor/github.com/bufbuild/protocompile/walk/walk.go b/vendor/github.com/bufbuild/protocompile/walk/walk.go new file mode 100644 index 0000000..244fa72 --- /dev/null +++ b/vendor/github.com/bufbuild/protocompile/walk/walk.go @@ -0,0 +1,446 @@ +// Copyright 2020-2024 Buf Technologies, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Package walk provides helper functions for traversing all elements in a +// protobuf file descriptor. There are versions both for traversing "rich" +// descriptors (protoreflect.Descriptor) and for traversing the underlying +// "raw" descriptor protos. +// +// # Enter And Exit +// +// This package includes variants of the functions that accept two callback +// functions. These variants have names ending with "EnterAndExit". One function +// is called as each element is visited ("enter") and the other is called after +// the element and all of its descendants have been visited ("exit"). This +// can be useful when you need to track state that is scoped to the visitation +// of a single element. +// +// # Source Path +// +// When traversing raw descriptor protos, this package include variants whose +// callback accepts a protoreflect.SourcePath. These variants have names that +// include "WithPath". This path can be used to locate corresponding data in the +// file's source code info (if present). +package walk + +import ( + "google.golang.org/protobuf/proto" + "google.golang.org/protobuf/reflect/protoreflect" + "google.golang.org/protobuf/types/descriptorpb" + + "github.com/bufbuild/protocompile/internal" +) + +// Descriptors walks all descriptors in the given file using a depth-first +// traversal, calling the given function for each descriptor in the hierarchy. +// The walk ends when traversal is complete or when the function returns an +// error. If the function returns an error, that is returned as the result of the +// walk operation. +// +// Descriptors are visited using a pre-order traversal, where the function is +// called for a descriptor before it is called for any of its descendants. +func Descriptors(file protoreflect.FileDescriptor, fn func(protoreflect.Descriptor) error) error { + return DescriptorsEnterAndExit(file, fn, nil) +} + +// DescriptorsEnterAndExit walks all descriptors in the given file using a +// depth-first traversal, calling the given functions on entry and on exit +// for each descriptor in the hierarchy. The walk ends when traversal is +// complete or when a function returns an error. If a function returns an error, +// that is returned as the result of the walk operation. +// +// The enter function is called using a pre-order traversal, where the function +// is called for a descriptor before it is called for any of its descendants. +// The exit function is called using a post-order traversal, where the function +// is called for a descriptor only after it is called for any descendants. +func DescriptorsEnterAndExit(file protoreflect.FileDescriptor, enter, exit func(protoreflect.Descriptor) error) error { + if err := walkContainer(file, enter, exit); err != nil { + return err + } + services := file.Services() + for i, length := 0, services.Len(); i < length; i++ { + svc := services.Get(i) + if err := enter(svc); err != nil { + return err + } + methods := svc.Methods() + for i, length := 0, methods.Len(); i < length; i++ { + mtd := methods.Get(i) + if err := enter(mtd); err != nil { + return err + } + if exit != nil { + if err := exit(mtd); err != nil { + return err + } + } + } + if exit != nil { + if err := exit(svc); err != nil { + return err + } + } + } + return nil +} + +type container interface { + Messages() protoreflect.MessageDescriptors + Enums() protoreflect.EnumDescriptors + Extensions() protoreflect.ExtensionDescriptors +} + +func walkContainer(container container, enter, exit func(protoreflect.Descriptor) error) error { + messages := container.Messages() + for i, length := 0, messages.Len(); i < length; i++ { + msg := messages.Get(i) + if err := messageDescriptor(msg, enter, exit); err != nil { + return err + } + } + enums := container.Enums() + for i, length := 0, enums.Len(); i < length; i++ { + en := enums.Get(i) + if err := enumDescriptor(en, enter, exit); err != nil { + return err + } + } + exts := container.Extensions() + for i, length := 0, exts.Len(); i < length; i++ { + ext := exts.Get(i) + if err := enter(ext); err != nil { + return err + } + if exit != nil { + if err := exit(ext); err != nil { + return err + } + } + } + return nil +} + +func messageDescriptor(msg protoreflect.MessageDescriptor, enter, exit func(protoreflect.Descriptor) error) error { + if err := enter(msg); err != nil { + return err + } + fields := msg.Fields() + for i, length := 0, fields.Len(); i < length; i++ { + fld := fields.Get(i) + if err := enter(fld); err != nil { + return err + } + if exit != nil { + if err := exit(fld); err != nil { + return err + } + } + } + oneofs := msg.Oneofs() + for i, length := 0, oneofs.Len(); i < length; i++ { + oo := oneofs.Get(i) + if err := enter(oo); err != nil { + return err + } + if exit != nil { + if err := exit(oo); err != nil { + return err + } + } + } + if err := walkContainer(msg, enter, exit); err != nil { + return err + } + if exit != nil { + if err := exit(msg); err != nil { + return err + } + } + return nil +} + +func enumDescriptor(en protoreflect.EnumDescriptor, enter, exit func(protoreflect.Descriptor) error) error { + if err := enter(en); err != nil { + return err + } + vals := en.Values() + for i, length := 0, vals.Len(); i < length; i++ { + enVal := vals.Get(i) + if err := enter(enVal); err != nil { + return err + } + if exit != nil { + if err := exit(enVal); err != nil { + return err + } + } + } + if exit != nil { + if err := exit(en); err != nil { + return err + } + } + return nil +} + +// DescriptorProtosWithPath walks all descriptor protos in the given file using +// a depth-first traversal. This is the same as DescriptorProtos except that the +// callback function, fn, receives a protoreflect.SourcePath, that indicates the +// path for the element in the file's source code info. +func DescriptorProtosWithPath(file *descriptorpb.FileDescriptorProto, fn func(protoreflect.FullName, protoreflect.SourcePath, proto.Message) error) error { + return DescriptorProtosWithPathEnterAndExit(file, fn, nil) +} + +// DescriptorProtosWithPathEnterAndExit walks all descriptor protos in the given +// file using a depth-first traversal. This is the same as +// DescriptorProtosEnterAndExit except that the callback function, fn, receives +// a protoreflect.SourcePath, that indicates the path for the element in the +// file's source code info. +func DescriptorProtosWithPathEnterAndExit(file *descriptorpb.FileDescriptorProto, enter, exit func(protoreflect.FullName, protoreflect.SourcePath, proto.Message) error) error { + w := &protoWalker{usePath: true, enter: enter, exit: exit} + return w.walkDescriptorProtos(file) +} + +// DescriptorProtos walks all descriptor protos in the given file using a +// depth-first traversal, calling the given function for each descriptor proto +// in the hierarchy. The walk ends when traversal is complete or when the +// function returns an error. If the function returns an error, that is +// returned as the result of the walk operation. +// +// Descriptor protos are visited using a pre-order traversal, where the function +// is called for a descriptor before it is called for any of its descendants. +func DescriptorProtos(file *descriptorpb.FileDescriptorProto, fn func(protoreflect.FullName, proto.Message) error) error { + return DescriptorProtosEnterAndExit(file, fn, nil) +} + +// DescriptorProtosEnterAndExit walks all descriptor protos in the given file +// using a depth-first traversal, calling the given functions on entry and on +// exit for each descriptor in the hierarchy. The walk ends when traversal is +// complete or when a function returns an error. If a function returns an error, +// that is returned as the result of the walk operation. +// +// The enter function is called using a pre-order traversal, where the function +// is called for a descriptor proto before it is called for any of its +// descendants. The exit function is called using a post-order traversal, where +// the function is called for a descriptor proto only after it is called for any +// descendants. +func DescriptorProtosEnterAndExit(file *descriptorpb.FileDescriptorProto, enter, exit func(protoreflect.FullName, proto.Message) error) error { + enterWithPath := func(n protoreflect.FullName, _ protoreflect.SourcePath, m proto.Message) error { + return enter(n, m) + } + var exitWithPath func(protoreflect.FullName, protoreflect.SourcePath, proto.Message) error + if exit != nil { + exitWithPath = func(n protoreflect.FullName, _ protoreflect.SourcePath, m proto.Message) error { + return exit(n, m) + } + } + w := &protoWalker{ + enter: enterWithPath, + exit: exitWithPath, + } + return w.walkDescriptorProtos(file) +} + +type protoWalker struct { + usePath bool + enter, exit func(protoreflect.FullName, protoreflect.SourcePath, proto.Message) error +} + +func (w *protoWalker) walkDescriptorProtos(file *descriptorpb.FileDescriptorProto) error { + prefix := file.GetPackage() + if prefix != "" { + prefix += "." + } + var path protoreflect.SourcePath + for i, msg := range file.MessageType { + var p protoreflect.SourcePath + if w.usePath { + p = path + p = append(p, internal.FileMessagesTag, int32(i)) + } + if err := w.walkDescriptorProto(prefix, p, msg); err != nil { + return err + } + } + for i, en := range file.EnumType { + var p protoreflect.SourcePath + if w.usePath { + p = path + p = append(p, internal.FileEnumsTag, int32(i)) + } + if err := w.walkEnumDescriptorProto(prefix, p, en); err != nil { + return err + } + } + for i, ext := range file.Extension { + var p protoreflect.SourcePath + if w.usePath { + p = path + p = append(p, internal.FileExtensionsTag, int32(i)) + } + fqn := prefix + ext.GetName() + if err := w.enter(protoreflect.FullName(fqn), p, ext); err != nil { + return err + } + if w.exit != nil { + if err := w.exit(protoreflect.FullName(fqn), p, ext); err != nil { + return err + } + } + } + for i, svc := range file.Service { + var p protoreflect.SourcePath + if w.usePath { + p = path + p = append(p, internal.FileServicesTag, int32(i)) + } + fqn := prefix + svc.GetName() + if err := w.enter(protoreflect.FullName(fqn), p, svc); err != nil { + return err + } + for j, mtd := range svc.Method { + var mp protoreflect.SourcePath + if w.usePath { + mp = p + mp = append(mp, internal.ServiceMethodsTag, int32(j)) + } + mtdFqn := fqn + "." + mtd.GetName() + if err := w.enter(protoreflect.FullName(mtdFqn), mp, mtd); err != nil { + return err + } + if w.exit != nil { + if err := w.exit(protoreflect.FullName(mtdFqn), mp, mtd); err != nil { + return err + } + } + } + if w.exit != nil { + if err := w.exit(protoreflect.FullName(fqn), p, svc); err != nil { + return err + } + } + } + return nil +} + +func (w *protoWalker) walkDescriptorProto(prefix string, path protoreflect.SourcePath, msg *descriptorpb.DescriptorProto) error { + fqn := prefix + msg.GetName() + if err := w.enter(protoreflect.FullName(fqn), path, msg); err != nil { + return err + } + prefix = fqn + "." + for i, fld := range msg.Field { + var p protoreflect.SourcePath + if w.usePath { + p = path + p = append(p, internal.MessageFieldsTag, int32(i)) + } + fqn := prefix + fld.GetName() + if err := w.enter(protoreflect.FullName(fqn), p, fld); err != nil { + return err + } + if w.exit != nil { + if err := w.exit(protoreflect.FullName(fqn), p, fld); err != nil { + return err + } + } + } + for i, oo := range msg.OneofDecl { + var p protoreflect.SourcePath + if w.usePath { + p = path + p = append(p, internal.MessageOneofsTag, int32(i)) + } + fqn := prefix + oo.GetName() + if err := w.enter(protoreflect.FullName(fqn), p, oo); err != nil { + return err + } + if w.exit != nil { + if err := w.exit(protoreflect.FullName(fqn), p, oo); err != nil { + return err + } + } + } + for i, nested := range msg.NestedType { + var p protoreflect.SourcePath + if w.usePath { + p = path + p = append(p, internal.MessageNestedMessagesTag, int32(i)) + } + if err := w.walkDescriptorProto(prefix, p, nested); err != nil { + return err + } + } + for i, en := range msg.EnumType { + var p protoreflect.SourcePath + if w.usePath { + p = path + p = append(p, internal.MessageEnumsTag, int32(i)) + } + if err := w.walkEnumDescriptorProto(prefix, p, en); err != nil { + return err + } + } + for i, ext := range msg.Extension { + var p protoreflect.SourcePath + if w.usePath { + p = path + p = append(p, internal.MessageExtensionsTag, int32(i)) + } + fqn := prefix + ext.GetName() + if err := w.enter(protoreflect.FullName(fqn), p, ext); err != nil { + return err + } + if w.exit != nil { + if err := w.exit(protoreflect.FullName(fqn), p, ext); err != nil { + return err + } + } + } + if w.exit != nil { + if err := w.exit(protoreflect.FullName(fqn), path, msg); err != nil { + return err + } + } + return nil +} + +func (w *protoWalker) walkEnumDescriptorProto(prefix string, path protoreflect.SourcePath, en *descriptorpb.EnumDescriptorProto) error { + fqn := prefix + en.GetName() + if err := w.enter(protoreflect.FullName(fqn), path, en); err != nil { + return err + } + for i, val := range en.Value { + var p protoreflect.SourcePath + if w.usePath { + p = path + p = append(p, internal.EnumValuesTag, int32(i)) + } + fqn := prefix + val.GetName() + if err := w.enter(protoreflect.FullName(fqn), p, val); err != nil { + return err + } + if w.exit != nil { + if err := w.exit(protoreflect.FullName(fqn), p, val); err != nil { + return err + } + } + } + if w.exit != nil { + if err := w.exit(protoreflect.FullName(fqn), path, en); err != nil { + return err + } + } + return nil +} |
