diff --git a/experimental/ir/lower_features.go b/experimental/ir/lower_features.go index 8fd7aed2..d527bb6a 100644 --- a/experimental/ir/lower_features.go +++ b/experimental/ir/lower_features.go @@ -278,25 +278,33 @@ func validateAllFeatures(file *File, r *report.Report) { parent: parent, })) + // Oneof features must be resolved before member features, since a field + // declared inside a oneof inherits from the oneof. + for oneof := range seq.Values(ty.Oneofs()) { + features := oneof.Options().Field(builtins.OneofFeatures) + validateFeatures(features.AsMessage(), r) + oneof.Raw().features = id.ID[FeatureSet](file.arenas.features.NewCompressed(rawFeatureSet{ + options: features.ID(), + parent: ty.Raw().features, + })) + } for member := range seq.Values(ty.Members()) { option := builtins.FieldFeatures if member.IsEnumValue() { option = builtins.EnumFeatures } + // A field belonging to a oneof inherits features from the oneof. + parent := ty.Raw().features + if oneof := member.Oneof(); !oneof.IsZero() { + parent = oneof.Raw().features + } + features := member.Options().Field(option) validateFeatures(features.AsMessage(), r) member.Raw().features = id.ID[FeatureSet](file.arenas.features.NewCompressed(rawFeatureSet{ options: features.ID(), - parent: ty.Raw().features, - })) - } - for oneof := range seq.Values(ty.Oneofs()) { - features := oneof.Options().Field(builtins.OneofFeatures) - validateFeatures(features.AsMessage(), r) - oneof.Raw().features = id.ID[FeatureSet](file.arenas.features.NewCompressed(rawFeatureSet{ - options: features.ID(), - parent: ty.Raw().features, + parent: parent, })) } for extns := range seq.Values(ty.ExtensionRanges()) { diff --git a/experimental/ir/testdata/editions/naming_style_2024_bad.proto b/experimental/ir/testdata/editions/naming_style_2024_bad.proto index 5c7962f8..049add12 100644 --- a/experimental/ir/testdata/editions/naming_style_2024_bad.proto +++ b/experimental/ir/testdata/editions/naming_style_2024_bad.proto @@ -13,6 +13,7 @@ message my_message { string field__name = 6; // Consecutive underscores oneof TestChoice { // PascalCase oneof string choice_a = 7; + string badChoice = 8; // camelCase field name } } diff --git a/experimental/ir/testdata/editions/naming_style_2024_bad.proto.fds.yaml b/experimental/ir/testdata/editions/naming_style_2024_bad.proto.fds.yaml index 42cf1a8a..6f1f96a9 100644 --- a/experimental/ir/testdata/editions/naming_style_2024_bad.proto.fds.yaml +++ b/experimental/ir/testdata/editions/naming_style_2024_bad.proto.fds.yaml @@ -40,6 +40,12 @@ file: type: TYPE_STRING oneof_index: 0 json_name: "choiceA" + - name: "badChoice" + number: 8 + label: LABEL_OPTIONAL + type: TYPE_STRING + oneof_index: 0 + json_name: "badChoice" oneof_decl: [{ name: "TestChoice" }] - name: "LegacyStyleMessage" field: diff --git a/experimental/ir/testdata/editions/naming_style_2024_bad.proto.stderr.txt b/experimental/ir/testdata/editions/naming_style_2024_bad.proto.stderr.txt index 11db7faa..1baa5286 100644 --- a/experimental/ir/testdata/editions/naming_style_2024_bad.proto.stderr.txt +++ b/experimental/ir/testdata/editions/naming_style_2024_bad.proto.stderr.txt @@ -71,106 +71,114 @@ error: oneof name should be snake_case | = help: STYLE2024 requires oneof names to be snake_case (e.g., my_choice) +error: field name should be snake_case + --> testdata/editions/naming_style_2024_bad.proto:16:12 + | +16 | string badChoice = 8; // camelCase field name + | ^^^^^^^^^ this name violates STYLE2024 + | + = help: STYLE2024 requires field names to be snake_case (e.g., my_field) + error: enum type name should be PascalCase - --> testdata/editions/naming_style_2024_bad.proto:19:6 + --> testdata/editions/naming_style_2024_bad.proto:20:6 | -19 | enum my_enum { +20 | enum my_enum { | ^^^^^^^ this name violates STYLE2024 | = help: STYLE2024 requires enum names to be PascalCase (e.g., MyEnum) error: enum value name should be SCREAMING_SNAKE_CASE - --> testdata/editions/naming_style_2024_bad.proto:20:3 + --> testdata/editions/naming_style_2024_bad.proto:21:3 | -20 | unspecified = 0; // lowercase enum value +21 | unspecified = 0; // lowercase enum value | ^^^^^^^^^^^ this name violates STYLE2024 | = help: STYLE2024 requires enum value names to be SCREAMING_SNAKE_CASE (e.g., MY_VALUE) error: enum value name should be SCREAMING_SNAKE_CASE - --> testdata/editions/naming_style_2024_bad.proto:21:3 + --> testdata/editions/naming_style_2024_bad.proto:22:3 | -21 | valueOne = 1; // camelCase enum value +22 | valueOne = 1; // camelCase enum value | ^^^^^^^^ this name violates STYLE2024 | = help: STYLE2024 requires enum value names to be SCREAMING_SNAKE_CASE (e.g., MY_VALUE) error: enum value name should be SCREAMING_SNAKE_CASE - --> testdata/editions/naming_style_2024_bad.proto:22:3 + --> testdata/editions/naming_style_2024_bad.proto:23:3 | -22 | _VALUE = 2; // Leading underscore +23 | _VALUE = 2; // Leading underscore | ^^^^^^ this name violates STYLE2024 | = help: STYLE2024 requires enum value names to be SCREAMING_SNAKE_CASE (e.g., MY_VALUE) error: enum value name should be SCREAMING_SNAKE_CASE - --> testdata/editions/naming_style_2024_bad.proto:23:3 + --> testdata/editions/naming_style_2024_bad.proto:24:3 | -23 | VALUE_ = 3; // Trailing underscore +24 | VALUE_ = 3; // Trailing underscore | ^^^^^^ this name violates STYLE2024 | = help: STYLE2024 requires enum value names to be SCREAMING_SNAKE_CASE (e.g., MY_VALUE) error: enum values have the same name with the `my_enum` prefix removed - --> testdata/editions/naming_style_2024_bad.proto:23:3 + --> testdata/editions/naming_style_2024_bad.proto:24:3 | -22 | _VALUE = 2; // Leading underscore +23 | _VALUE = 2; // Leading underscore | ------ this implies canonical name `Value` -23 | VALUE_ = 3; // Trailing underscore +24 | VALUE_ = 3; // Trailing underscore | ^^^^^^ this also implies that name error: enum value name should be SCREAMING_SNAKE_CASE - --> testdata/editions/naming_style_2024_bad.proto:24:3 + --> testdata/editions/naming_style_2024_bad.proto:25:3 | -24 | VALUE_1 = 4; // Underscore followed by number +25 | VALUE_1 = 4; // Underscore followed by number | ^^^^^^^ this name violates STYLE2024 | = help: STYLE2024 requires enum value names to be SCREAMING_SNAKE_CASE (e.g., MY_VALUE) error: service name should be PascalCase - --> testdata/editions/naming_style_2024_bad.proto:27:9 + --> testdata/editions/naming_style_2024_bad.proto:28:9 | -27 | service My_Service { // Snake_Case service name +28 | service My_Service { // Snake_Case service name | ^^^^^^^^^^ this name violates STYLE2024 | = help: STYLE2024 requires service names to be PascalCase (e.g., MyService) error: RPC method name should be PascalCase - --> testdata/editions/naming_style_2024_bad.proto:28:7 + --> testdata/editions/naming_style_2024_bad.proto:29:7 | -28 | rpc test_method(my_message) returns (my_message); // snake_case RPC method names +29 | rpc test_method(my_message) returns (my_message); // snake_case RPC method names | ^^^^^^^^^^^ this name violates STYLE2024 | = help: STYLE2024 requires RPC method names to be PascalCase (e.g., GetMessage) error: field name should be snake_case - --> testdata/editions/naming_style_2024_bad.proto:36:10 + --> testdata/editions/naming_style_2024_bad.proto:37:10 | -36 | string BadField = 2 [features.enforce_naming_style = STYLE2024]; +37 | string BadField = 2 [features.enforce_naming_style = STYLE2024]; | ^^^^^^^^ this name violates STYLE2024 | = help: STYLE2024 requires field names to be snake_case (e.g., my_field) error: message type name should be PascalCase - --> testdata/editions/naming_style_2024_bad.proto:37:11 + --> testdata/editions/naming_style_2024_bad.proto:38:11 | -37 | message bad_nested { +38 | message bad_nested { | ^^^^^^^^^^ this name violates STYLE2024 | = help: STYLE2024 requires message names to be PascalCase (e.g., MyMessage) error: field name should be snake_case - --> testdata/editions/naming_style_2024_bad.proto:40:12 + --> testdata/editions/naming_style_2024_bad.proto:41:12 | -40 | string _bad_field = 1; +41 | string _bad_field = 1; | ^^^^^^^^^^ this name violates STYLE2024 | = help: STYLE2024 requires field names to be snake_case (e.g., my_field) -encountered 21 errors +encountered 22 errors diff --git a/experimental/ir/testdata/editions/naming_style_2024_bad.proto.symtab.yaml b/experimental/ir/testdata/editions/naming_style_2024_bad.proto.symtab.yaml index 6e92658d..5b30cbf1 100644 --- a/experimental/ir/testdata/editions/naming_style_2024_bad.proto.symtab.yaml +++ b/experimental/ir/testdata/editions/naming_style_2024_bad.proto.symtab.yaml @@ -66,54 +66,59 @@ tables: file: "testdata/editions/naming_style_2024_bad.proto" index: 7 visible: true + - fqn: "Buf.Test.my_message.badChoice" + kind: KIND_FIELD + file: "testdata/editions/naming_style_2024_bad.proto" + index: 8 + visible: true - fqn: "Buf.Test.LegacyStyleMessage._allowed" kind: KIND_FIELD file: "testdata/editions/naming_style_2024_bad.proto" - index: 13 + index: 14 visible: true - fqn: "Buf.Test.LegacyStyleMessage.BadField" kind: KIND_FIELD file: "testdata/editions/naming_style_2024_bad.proto" - index: 14 + index: 15 visible: true options.message.fields: "features": { message.fields: { "enforce_naming_style": { i32: 1 } } } - fqn: "Buf.Test.LegacyStyleMessage.bad_nested._bad_field" kind: KIND_FIELD file: "testdata/editions/naming_style_2024_bad.proto" - index: 15 + index: 16 visible: true - fqn: "Buf.Test.LegacyStyleMessage.bad_nested._another_field" kind: KIND_FIELD file: "testdata/editions/naming_style_2024_bad.proto" - index: 16 + index: 17 visible: true options.message.fields: "features": { message.fields: { "enforce_naming_style": { i32: 2 } } } - fqn: "Buf.Test.unspecified" kind: KIND_ENUM_VALUE file: "testdata/editions/naming_style_2024_bad.proto" - index: 8 + index: 9 visible: true - fqn: "Buf.Test.valueOne" kind: KIND_ENUM_VALUE file: "testdata/editions/naming_style_2024_bad.proto" - index: 9 + index: 10 visible: true - fqn: "Buf.Test._VALUE" kind: KIND_ENUM_VALUE file: "testdata/editions/naming_style_2024_bad.proto" - index: 10 + index: 11 visible: true - fqn: "Buf.Test.VALUE_" kind: KIND_ENUM_VALUE file: "testdata/editions/naming_style_2024_bad.proto" - index: 11 + index: 12 visible: true - fqn: "Buf.Test.VALUE_1" kind: KIND_ENUM_VALUE file: "testdata/editions/naming_style_2024_bad.proto" - index: 12 + index: 13 visible: true - fqn: "Buf.Test.my_message.TestChoice" kind: KIND_ONEOF diff --git a/experimental/ir/testdata/editions/naming_style_2024_ok.proto b/experimental/ir/testdata/editions/naming_style_2024_ok.proto index 89fb81be..640b385c 100644 --- a/experimental/ir/testdata/editions/naming_style_2024_ok.proto +++ b/experimental/ir/testdata/editions/naming_style_2024_ok.proto @@ -41,6 +41,8 @@ message bad_message_but_ok { oneof BadChoice { string opt_a = 4; + int32 BadName = 5; // PascalCase, allowed inherited STYLE_LEGACY. + int32 anotherBad = 6; // camelCase, allowed inherited STYLE_LEGACY. } // Nested message inherits STYLE_LEGACY from parent @@ -51,6 +53,13 @@ message bad_message_but_ok { message GoodNested { option features.enforce_naming_style = STYLE2024; string good_field = 1; // Must follow STYLE2024 rules + + oneof BadChoice { + option features.enforce_naming_style = STYLE_LEGACY; + + int32 oneof_e_1 = 2; // Underscore-before-digit, allowed under STYLE_LEGACY. + int32 oneofE2 = 3; // camelCase, allowed under STYLE_LEGACY. + } } } diff --git a/experimental/ir/testdata/editions/naming_style_2024_ok.proto.fds.yaml b/experimental/ir/testdata/editions/naming_style_2024_ok.proto.fds.yaml index 2b7ac5e8..7a5fef89 100644 --- a/experimental/ir/testdata/editions/naming_style_2024_ok.proto.fds.yaml +++ b/experimental/ir/testdata/editions/naming_style_2024_ok.proto.fds.yaml @@ -60,6 +60,18 @@ file: type: TYPE_STRING oneof_index: 0 json_name: "optA" + - name: "BadName" + number: 5 + label: LABEL_OPTIONAL + type: TYPE_INT32 + oneof_index: 0 + json_name: "BadName" + - name: "anotherBad" + number: 6 + label: LABEL_OPTIONAL + type: TYPE_INT32 + oneof_index: 0 + json_name: "anotherBad" nested_type: - name: "nested_message" field: @@ -75,6 +87,21 @@ file: label: LABEL_OPTIONAL type: TYPE_STRING json_name: "goodField" + - name: "oneof_e_1" + number: 2 + label: LABEL_OPTIONAL + type: TYPE_INT32 + oneof_index: 0 + json_name: "oneofE1" + - name: "oneofE2" + number: 3 + label: LABEL_OPTIONAL + type: TYPE_INT32 + oneof_index: 0 + json_name: "oneofE2" + oneof_decl: + - name: "BadChoice" + options.features.enforce_naming_style: STYLE_LEGACY options.features.enforce_naming_style: STYLE2024 oneof_decl: [{ name: "BadChoice" }] options.features.enforce_naming_style: STYLE_LEGACY diff --git a/experimental/ir/testdata/editions/naming_style_2024_ok.proto.symtab.yaml b/experimental/ir/testdata/editions/naming_style_2024_ok.proto.symtab.yaml index 61f2aa76..88c03104 100644 --- a/experimental/ir/testdata/editions/naming_style_2024_ok.proto.symtab.yaml +++ b/experimental/ir/testdata/editions/naming_style_2024_ok.proto.symtab.yaml @@ -93,16 +93,36 @@ tables: file: "testdata/editions/naming_style_2024_ok.proto" index: 14 visible: true - - fqn: "buf.test.bad_message_but_ok.nested_message.AnyName" + - fqn: "buf.test.bad_message_but_ok.BadName" kind: KIND_FIELD file: "testdata/editions/naming_style_2024_ok.proto" index: 15 visible: true - - fqn: "buf.test.bad_message_but_ok.GoodNested.good_field" + - fqn: "buf.test.bad_message_but_ok.anotherBad" kind: KIND_FIELD file: "testdata/editions/naming_style_2024_ok.proto" index: 16 visible: true + - fqn: "buf.test.bad_message_but_ok.nested_message.AnyName" + kind: KIND_FIELD + file: "testdata/editions/naming_style_2024_ok.proto" + index: 17 + visible: true + - fqn: "buf.test.bad_message_but_ok.GoodNested.good_field" + kind: KIND_FIELD + file: "testdata/editions/naming_style_2024_ok.proto" + index: 18 + visible: true + - fqn: "buf.test.bad_message_but_ok.GoodNested.oneof_e_1" + kind: KIND_FIELD + file: "testdata/editions/naming_style_2024_ok.proto" + index: 19 + visible: true + - fqn: "buf.test.bad_message_but_ok.GoodNested.oneofE2" + kind: KIND_FIELD + file: "testdata/editions/naming_style_2024_ok.proto" + index: 20 + visible: true - fqn: "buf.test.MY_ENUM_UNSPECIFIED" kind: KIND_ENUM_VALUE file: "testdata/editions/naming_style_2024_ok.proto" @@ -126,17 +146,17 @@ tables: - fqn: "buf.test.unspecified" kind: KIND_ENUM_VALUE file: "testdata/editions/naming_style_2024_ok.proto" - index: 17 + index: 21 visible: true - fqn: "buf.test.valueOne" kind: KIND_ENUM_VALUE file: "testdata/editions/naming_style_2024_ok.proto" - index: 18 + index: 22 visible: true - fqn: "buf.test._VALUE" kind: KIND_ENUM_VALUE file: "testdata/editions/naming_style_2024_ok.proto" - index: 19 + index: 23 visible: true - fqn: "buf.test.MyMessage.my_choice" kind: KIND_ONEOF @@ -148,6 +168,13 @@ tables: file: "testdata/editions/naming_style_2024_ok.proto" index: 2 visible: true + - fqn: "buf.test.bad_message_but_ok.GoodNested.BadChoice" + kind: KIND_ONEOF + file: "testdata/editions/naming_style_2024_ok.proto" + index: 3 + visible: true + options.message.fields: + "features": { message.fields: { "enforce_naming_style": { i32: 2 } } } - fqn: "buf.test.MyService" kind: 9 file: "testdata/editions/naming_style_2024_ok.proto"