Bit-field of width one declared with a signed type

ID

objectivec.correctness.signed_bit_field

Severity

low

Remediation Complexity

trivial

Remediation Risk

low

Remediation Effort

low

Resource

Type Safety

Language

Objective-C

Description

Reports a bit-field of width one whose type is signed - signed int, signed char, or a plain int, char, short, long, int8_t or NSInteger, none of which is unsigned. A bit-field with an unsigned type, one wider than a single bit, and a BOOL field - whose underlying type differs between targets - are not reported. Wider bit-fields and unsigned ones are reported by the avoid-bit-fields rule instead, so the two rules never report the same member. A case label, a keyword argument in a message send, a ternary, and occurrences inside a block comment are not reported.

Rationale

A signed bit-field spends its only bit on the sign, so the field holds exactly two values - 0 and -1. Storing 1 into it therefore reads back as -1, and a test written as if (flags.direction == 1) is never true, while if (flags.direction) still works, which is why the defect survives so long: the flag appears to function until something compares it against a value. Whether a plain int bit-field is signed at all is implementation-defined, so the same declaration can behave one way on one target and the other way on the next.

The following code illustrates the pattern detected by this rule:

struct DeviceFlags {
    // FLAGGED: Bit-field of width one declared with a signed type
    signed int direction : 1;
    // FLAGGED: Bit-field of width one declared with a signed type
    int polarity : 1;
    // FLAGGED: Bit-field of width one declared with a signed type
    signed char toggle : 1;
    // FLAGGED: Bit-field of width one declared with a signed type
    int8_t marker : 1;
    // FLAGGED: Bit-field of width one declared with a signed type
    NSInteger mark : 1;
    unsigned int enabled : 1;
    uint8_t retries : 1;
    NSUInteger counter : 1;
    BOOL ready : 1;
    signed int span : 3;
    int wide : 4;
    int plainMember;
};

Remediation

Declare the field unsigned - unsigned int direction : 1 - so the single bit holds 0 and 1 as written. Where the field genuinely needs to represent a signed value, give it at least two bits, and prefer a separate BOOL member or a named mask over a bit-field where the packing is not actually required.

Configuration

This detector does not need any configuration.

© 2026 Xygeni. All rights reserved