You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
feat: reject hand-set deprecated in the field_metadata annotation
The `deprecated` attribute is generator-managed — mirrored from the field's
standard `[deprecated = true]` option. A hand-set value inside
(meshtastic.field_metadata) previously produced silently divergent output:
the Go and Kotlin/Wire generators emitted it while the Swift plugin ignored
it (breaking byte-identity), and the schema carried two sources of truth for
the same fact.
Make it a hard error at generation time in all three generators, with the
same message naming the field and the fix. Verified: both protoc plugins
reject a hand-set fixture identically; parity over the real schema remains
byte-identical; Go tests cover true/false/redundant hand-set cases.
Also document the Go plugin's `target=swift` assumption that files set
`option swift_prefix = ""` (all meshtastic protos do) — without it,
swift-protobuf prefixes generated type names and the emitted extensions
would reference nonexistent types; the parity CI catches that divergence.
The bundled field_metadata.pb.swift regen picks up the proto comment change
(comments only, no functional difference).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
// A field earns an entry if it carries the custom annotation OR
78
78
// the standard `deprecated` option (which we mirror below).
79
79
forfieldin message.fields where field.options.hasFieldMetadata || field.options.deprecated {
80
+
// `deprecated` is generator-managed (mirrored from the standard
81
+
// option); a hand-set value in the annotation is a hard error,
82
+
// matching the Go plugin, so the generators can't disagree.
83
+
if field.options.fieldMetadata.hasDeprecated {
84
+
throwGenError.message(
85
+
"\(message.fullName).\(field.name): the \"deprecated\" attribute is generator-managed and cannot be set in (meshtastic.field_metadata); mark the field `[deprecated = true]` instead and it is mirrored automatically"
// Mirror the standard `deprecated` field option into the registry.
249
-
// The custom annotation isn't expected to set it (see
250
-
// field_metadata.proto); the standard option is authoritative, so
251
-
// upsert to a single deprecated=true attribute either way.
254
+
// `deprecated` is generator-managed: it mirrors the field's standard
255
+
// option, and a hand-set value in the annotation is rejected rather
256
+
// than merged — the generators would otherwise disagree about it (the
257
+
// Swift plugin reads only the standard option), and the schema would
258
+
// carry two sources of truth for the same fact.
259
+
for_, a:=rangemf {
260
+
ifa.Name==deprecatedAttr {
261
+
returnfmt.Errorf(
262
+
"%s.%s: the %q attribute is generator-managed and cannot be set in (meshtastic.field_metadata); mark the field `[deprecated = true]` instead and it is mirrored automatically",
0 commit comments