Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion experimental/ir/builtins.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ type builtins struct {
MessageOptions Member
FieldOptions Member
OneofOptions Member
RangeOptions Member
RangeOptions Member `builtin:"optional"`
EnumOptions Member
EnumValueOptions Member
ServiceOptions Member
Expand Down
92 changes: 74 additions & 18 deletions experimental/ir/lower_options.go
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,7 @@ func resolveEarlyOptions(file *File) {
// resolveOptions resolves all of the options in a file.
func resolveOptions(file *File, r *report.Report) {
builtins := file.builtins()
ids := &file.session.builtins
bodyOptions := func(decls seq.Inserter[ast.DeclAny]) iter.Seq[ast.Option] {
return iterx.FilterMap(seq.Values(decls), func(d ast.DeclAny) (ast.Option, bool) {
def := d.AsDef()
Expand All @@ -126,8 +127,9 @@ func resolveOptions(file *File, r *report.Report) {
scope: file.Package(),
def: def,

field: builtins.FileOptions,
raw: &file.options,
field: builtins.FileOptions,
fieldFQN: ids.FileOptions,
raw: &file.options,
}.resolve()
}

Expand All @@ -141,8 +143,10 @@ func resolveOptions(file *File, r *report.Report) {

for def := range bodyOptions(ty.AST().Body().Decls()) {
options := builtins.MessageOptions
optionsFQN := ids.MessageOptions
if ty.IsEnum() {
options = builtins.EnumOptions
optionsFQN = ids.EnumOptions
}
optionRef{
File: file,
Expand All @@ -151,16 +155,19 @@ func resolveOptions(file *File, r *report.Report) {
scope: ty.Scope(),
def: def,

field: options,
raw: &ty.Raw().options,
field: options,
fieldFQN: optionsFQN,
raw: &ty.Raw().options,
}.resolve()
}

for field := range seq.Values(ty.Members()) {
for def := range seq.Values(field.AST().Options().Entries()) {
options := builtins.FieldOptions
optionsFQN := ids.FieldOptions
if ty.IsEnum() {
options = builtins.EnumValueOptions
optionsFQN = ids.EnumValueOptions
}
optionRef{
File: file,
Expand All @@ -169,9 +176,10 @@ func resolveOptions(file *File, r *report.Report) {
scope: field.Scope(),
def: def,

field: options,
raw: &field.Raw().options,
target: field,
field: options,
fieldFQN: optionsFQN,
raw: &field.Raw().options,
target: field,
}.resolve()
}
}
Expand All @@ -184,8 +192,9 @@ func resolveOptions(file *File, r *report.Report) {
scope: ty.Scope(),
def: def,

field: builtins.OneofOptions,
raw: &oneof.Raw().options,
field: builtins.OneofOptions,
fieldFQN: ids.OneofOptions,
raw: &oneof.Raw().options,
}.resolve()
}
}
Expand All @@ -206,8 +215,9 @@ func resolveOptions(file *File, r *report.Report) {
scope: ty.Scope(),
def: def,

field: builtins.RangeOptions,
raw: &extns.Raw().options,
field: builtins.RangeOptions,
fieldFQN: ids.RangeOptions,
raw: &extns.Raw().options,
}.resolve()
}

Expand All @@ -223,9 +233,10 @@ func resolveOptions(file *File, r *report.Report) {
scope: field.Scope(),
def: def,

field: builtins.FieldOptions,
raw: &field.Raw().options,
target: field,
field: builtins.FieldOptions,
fieldFQN: ids.FieldOptions,
raw: &field.Raw().options,
target: field,
}.resolve()
}
}
Expand All @@ -238,8 +249,9 @@ func resolveOptions(file *File, r *report.Report) {
scope: service.FullName(),
def: def,

field: builtins.ServiceOptions,
raw: &service.Raw().options,
field: builtins.ServiceOptions,
fieldFQN: ids.ServiceOptions,
raw: &service.Raw().options,
}.resolve()
}

Expand All @@ -252,8 +264,9 @@ func resolveOptions(file *File, r *report.Report) {
scope: service.FullName(),
def: def,

field: builtins.MethodOptions,
raw: &method.Raw().options,
field: builtins.MethodOptions,
fieldFQN: ids.MethodOptions,
raw: &method.Raw().options,
}.resolve()
}
}
Expand Down Expand Up @@ -398,12 +411,55 @@ type optionRef struct {
field Member
raw *id.ID[Value]

// fieldFQN is the interned FQN of the *Options builtin field that field
// resolves to. It is set even when field is zero (because the vendored
// descriptor.proto does not declare it), so the diagnostic emitted on the
// zero-field path can name the missing symbol.
fieldFQN intern.ID

// A member being annotated. This is used for pseudo-option resolution.
target Member
}

// resolve performs symbol resolution.
func (r optionRef) resolve() {
// If the *Options builtin we are resolving into is unresolved, we cannot
// resolve the option ref. This is the case when an invalid descriptor.proto
// has been vendored (missing a required builtin) or when the user wrote an
// option targeting an optional descriptor.proto field that the vendored
// copy predates. In both cases we bail out before the resolver attempts to
// dereference the zero builtin, which would otherwise panic in
// [Member.toRef] and [newMessage].
//
// Note that `protoc` does not respect vendored descriptor.protos for option
// resolution; it always uses its own internally-bundled copy. This compiler
// and the legacy compiler both honor the vendored descriptor.proto so that
// users can depend on their own descriptor.proto files, and so downstream
// tooling built on the compiler can provide descriptor.proto (and the
// well-known types as a whole).
//
// We emit a diagnostic at the user's option site so they have a pointer
// from their source to the underlying descriptor.proto problem.
// [resolveBuiltins] also emits a "missing required symbol" diagnostic on
// descriptor.proto for required builtins that is complementary to this error.
if r.field.IsZero() {
fqn := r.session.intern.Value(r.fieldFQN)
d := r.Errorf("cannot resolve %s", taxa.Option).Apply(
report.Snippet(r.def),
)
if dpFile := r.imports.DescriptorProto(); dpFile != nil {
d.Apply(report.Snippetf(dpFile.AST().Syntax(),
"resolved against this descriptor.proto"))
}
if _, optional := r.session.optionalBuiltins[r.fieldFQN]; optional {
d.Apply(report.Helpf(
"`%s` is not declared by this descriptor.proto; "+
"use a newer descriptor.proto or remove this option",
fqn))
}
return
}

ids := &r.session.builtins
root := r.field.Element()

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,11 @@
# like FileOptions, MessageOptions, FieldOptions) is diagnosed by the
# compiler with one error per missing required symbol, attached to
# descriptor.proto itself rather than to the user's proto file.
#
# The user file additionally sets `option go_package = ...` so we exercise
# the optionRef.resolve guard against a zero `field` builtin: with FileOptions
# unresolved, lowering would otherwise panic when constructing an options
# message rooted at the unresolved builtin.
exclude_wkt_sources: true
descriptor: true
symtab: true
Expand Down Expand Up @@ -43,6 +48,8 @@ files:
syntax = "proto3";
package test;

option go_package = "example.com";

message Simple {
string name = 1;
}
Original file line number Diff line number Diff line change
@@ -1,17 +1,3 @@
error: `google/protobuf/descriptor.proto` is missing required symbol
`google.protobuf.DescriptorProto.ExtensionRange.options`
--> google/protobuf/descriptor.proto:1:1
|
1 | syntax = "proto2";
| ^
2 | / package google.protobuf;
... |
14 | | }
| \_^
= help: the descriptor.proto supplied to the compiler does not declare this
message field; it may be vendored from a version that predates this
symbol, or may be genuinely corrupt

error: `google/protobuf/descriptor.proto` is missing required symbol
`google.protobuf.DescriptorProto.options`
--> google/protobuf/descriptor.proto:1:1
Expand Down Expand Up @@ -376,4 +362,14 @@ error: `google/protobuf/descriptor.proto` is missing required symbol
message field; it may be vendored from a version that predates this
symbol, or may be genuinely corrupt

error: cannot resolve option setting
--> test.proto:4:8
|
4 | option go_package = "example.com";
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
::: google/protobuf/descriptor.proto:1:1
|
1 | syntax = "proto2";
| ------------------ resolved against this descriptor.proto

encountered 27 errors
Loading
Loading