Use `#[serde(deny_unknown_fields)]` for IR.
https://serde.rs/container-attrs.html#deny_unknown_fields
Unknown fields are always a bug, since we control both serialization and deserialization and they both come from the same version of the codebase. (Unlike traditional deserialization code, we don't need to deal with data that was generated a long time ago.)
This is a common, and potentially confusing, source of bugs. As an example, during recent feature work I accidentally forgot to rename a field in `ir.rs` when renaming it in `ir.h`, and the result was that everything compiled and ran but did a subtly incorrect thing, which took time to debug. `deny_unknown_fields` would have prevented this.
(We don't need to stop using `default`, because a mismatch in name will show up as both an unknown field and a defaulted field -- it's never a major or confusing bug to just have a defaulted field with no corresponding unknown field.)
Note that the attribute isn't necessary for enums. [Fieldless enums](https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=aeedbefac83cb6e73eb459927dcc3682) fail as one might expect even without the attribute, but so do [fieldful enums](https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=b752164835f2b9d853f2ed1e669d1a36).
PiperOrigin-RevId: 515738803
diff --git a/rs_bindings_from_cc/ir.rs b/rs_bindings_from_cc/ir.rs
index e7fd18e..d2cf11b 100644
--- a/rs_bindings_from_cc/ir.rs
+++ b/rs_bindings_from_cc/ir.rs
@@ -129,21 +129,25 @@
}
#[derive(Debug, PartialEq, Eq, Hash, Clone, Deserialize)]
+#[serde(deny_unknown_fields)]
pub struct HeaderName {
pub name: Rc<str>,
}
#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy, Deserialize)]
+#[serde(deny_unknown_fields)]
#[serde(transparent)]
pub struct LifetimeId(pub i32);
#[derive(Debug, PartialEq, Eq, Hash, Clone, Deserialize)]
+#[serde(deny_unknown_fields)]
pub struct LifetimeName {
pub name: Rc<str>,
pub id: LifetimeId,
}
#[derive(Debug, PartialEq, Eq, Hash, Clone, Deserialize)]
+#[serde(deny_unknown_fields)]
pub struct RsType {
pub name: Option<Rc<str>>,
pub lifetime_args: Rc<[LifetimeId]>,
@@ -158,6 +162,7 @@
}
#[derive(Debug, PartialEq, Eq, Hash, Clone, Deserialize)]
+#[serde(deny_unknown_fields)]
pub struct CcType {
pub name: Option<Rc<str>>,
pub is_const: bool,
@@ -182,12 +187,14 @@
}
#[derive(Debug, PartialEq, Eq, Hash, Clone, Deserialize)]
+#[serde(deny_unknown_fields)]
pub struct MappedType {
pub rs_type: RsType,
pub cc_type: CcType,
}
#[derive(PartialEq, Eq, Hash, Clone, Deserialize)]
+#[serde(deny_unknown_fields)]
pub struct Identifier {
pub identifier: Rc<str>,
}
@@ -199,12 +206,14 @@
}
#[derive(Debug, PartialEq, Eq, Hash, Copy, Clone, Deserialize)]
+#[serde(deny_unknown_fields)]
pub struct IntegerConstant {
pub is_negative: bool,
pub wrapped_value: u64,
}
#[derive(PartialEq, Eq, Hash, Clone, Deserialize)]
+#[serde(deny_unknown_fields)]
pub struct Operator {
pub name: Rc<str>,
}
@@ -296,6 +305,7 @@
}
#[derive(Debug, PartialEq, Eq, Hash, Clone, Deserialize)]
+#[serde(deny_unknown_fields)]
pub struct InstanceMethodMetadata {
pub reference: ReferenceQualification,
pub is_const: bool,
@@ -303,12 +313,14 @@
}
#[derive(Debug, PartialEq, Eq, Hash, Clone, Deserialize)]
+#[serde(deny_unknown_fields)]
pub struct MemberFuncMetadata {
pub record_id: ItemId,
pub instance_method_metadata: Option<InstanceMethodMetadata>,
}
#[derive(Debug, PartialEq, Eq, Hash, Clone, Deserialize)]
+#[serde(deny_unknown_fields)]
pub struct FuncParam {
#[serde(rename(deserialize = "type"))]
pub type_: MappedType,
@@ -316,6 +328,7 @@
}
#[derive(Debug, PartialEq, Eq, Hash, Clone, Deserialize)]
+#[serde(deny_unknown_fields)]
pub struct Func {
pub name: UnqualifiedIdentifier,
pub owning_target: BazelLabel,
@@ -356,6 +369,7 @@
}
#[derive(Debug, PartialEq, Eq, Hash, Clone, Deserialize)]
+#[serde(deny_unknown_fields)]
pub struct Field {
pub identifier: Option<Identifier>,
pub doc_comment: Option<Rc<str>>,
@@ -380,12 +394,14 @@
}
#[derive(Debug, PartialEq, Eq, Hash, Clone, Deserialize)]
+#[serde(deny_unknown_fields)]
pub struct BaseClass {
pub base_record_id: ItemId,
pub offset: Option<i64>,
}
#[derive(Debug, PartialEq, Eq, Hash, Clone, Deserialize)]
+#[serde(deny_unknown_fields)]
pub struct IncompleteRecord {
pub cc_name: Rc<str>,
pub rs_name: Rc<str>,
@@ -414,6 +430,7 @@
}
#[derive(Debug, PartialEq, Eq, Hash, Clone, Deserialize)]
+#[serde(deny_unknown_fields)]
pub struct Record {
pub rs_name: Rc<str>,
pub cc_name: Rc<str>,
@@ -483,6 +500,7 @@
}
#[derive(Debug, PartialEq, Eq, Hash, Clone, Deserialize)]
+#[serde(deny_unknown_fields)]
pub struct Enum {
pub identifier: Identifier,
pub id: ItemId,
@@ -494,12 +512,14 @@
}
#[derive(Debug, PartialEq, Eq, Hash, Clone, Deserialize)]
+#[serde(deny_unknown_fields)]
pub struct Enumerator {
pub identifier: Identifier,
pub value: IntegerConstant,
}
#[derive(Debug, PartialEq, Eq, Hash, Clone, Deserialize)]
+#[serde(deny_unknown_fields)]
pub struct TypeAlias {
pub identifier: Identifier,
pub id: ItemId,
@@ -535,6 +555,7 @@
}
#[derive(Debug, PartialEq, Eq, Hash, Clone, Deserialize)]
+#[serde(deny_unknown_fields)]
pub struct UnsupportedItem {
pub name: Rc<str>,
message: Rc<str>,
@@ -573,12 +594,14 @@
}
#[derive(Debug, PartialEq, Eq, Hash, Clone, Deserialize)]
+#[serde(deny_unknown_fields)]
pub struct Comment {
pub text: Rc<str>,
pub id: ItemId,
}
#[derive(Debug, PartialEq, Eq, Hash, Clone, Deserialize)]
+#[serde(deny_unknown_fields)]
pub struct Namespace {
pub name: Identifier,
pub id: ItemId,
@@ -591,6 +614,7 @@
}
#[derive(Debug, PartialEq, Eq, Hash, Clone, Deserialize)]
+#[serde(deny_unknown_fields)]
pub struct UseMod {
pub path: Rc<str>,
pub mod_name: Identifier,
@@ -756,7 +780,7 @@
}
#[derive(Debug, PartialEq, Eq, Clone, Deserialize)]
-#[serde(rename(deserialize = "IR"))]
+#[serde(deny_unknown_fields, rename(deserialize = "IR"))]
struct FlatIR {
#[serde(default)]
public_headers: Vec<HeaderName>,