Declaration of a union type
ID |
objectivec.maintainability.avoid_union |
Severity |
info |
Remediation Complexity |
hard |
Remediation Risk |
medium |
Remediation Effort |
medium |
Resource |
Type Safety |
Language |
Objective-C |
Description
Reports the declaration of a union - a named union Payload { … }, an anonymous typedef union { … } Name, and a union nested inside another type. Declaring a variable of an already-declared union type is not reported, since the declaration is where the design decision is made, and neither are occurrences inside a comment or a string literal. A struct declaration is not reported.
Rationale
A union has no member that says which member is currently live, so the knowledge of what the bytes mean lives outside the type - in a sibling tag field, in a comment, or in the head of whoever wrote it. Reading a member other than the one last written is unspecified behaviour: padding, endianness and the trap representations of the member’s type all become observable, and the result differs between architectures, which is exactly the reinterpretation a union is usually reached for. Nothing in the language or the compiler checks the pairing, so a member added later, or a write on a path the reader did not consider, breaks every read with no diagnostic.
The following code illustrates the pattern detected by this rule:
// FLAGGED: Declaration of a union type
union Payload {
int32_t asInteger;
float asFloat;
};
Remediation
Where the union models alternatives, replace it with a tagged representation - a struct holding an enum discriminant alongside the payload, or in Objective-C a small class hierarchy or an NSValue/id field - so the live member is part of the value and can be checked. Where the union is being used to reinterpret bytes, copy them explicitly with memcpy into a variable of the target type, which is well defined and states the intent.