{"record":{"id":"9f5eacd324755566","repo":"astral-sh/ruff","slug":"i-is-bounded-by-inline-max-segments","errorCode":null,"errorMessage":"i is bounded by INLINE_MAX_SEGMENTS","messagePattern":"i is bounded by INLINE_MAX_SEGMENTS","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/ty_python_core/src/member.rs","lineNumber":878,"sourceCode":"        }\n\n        // Pack into inline representation\n        // Store count minus 1 (since segments are never empty, range 0-6 represents 1-7 segments)\n        let mut packed = (segments.len() - 1) as u64;\n        let mut prev_offset = TextSize::new(0);\n\n        for (i, segment) in segments.iter().enumerate() {\n            // Compute relative offset on-the-fly\n            let relative_offset = segment.offset() - prev_offset;\n            if relative_offset > TextSize::from(INLINE_MAX_RELATIVE_OFFSET) {\n                return None;\n            }\n\n            let kind = segment.kind() as u64;\n            let relative_offset_val = u64::from(relative_offset.to_u32());\n            let segment_data = (relative_offset_val << INLINE_KIND_BITS) | kind;\n            let shift = INLINE_COUNT_BITS\n                + (u32::try_from(i).expect(\"i is bounded by INLINE_MAX_SEGMENTS\")\n                    * INLINE_SEGMENT_BITS);\n            packed |= segment_data << shift;\n\n            prev_offset = segment.offset();\n        }\n\n        Some(Self(packed))\n    }\n\n    #[expect(\n        clippy::cast_possible_truncation,\n        reason = \"INLINE_COUNT_MASK ensures value is at most 7\"\n    )]\n    const fn len(self) -> usize {\n        // Add 1 because we store count minus 1\n        ((self.0 & INLINE_COUNT_MASK) + 1) as usize\n    }\n","sourceCodeStart":860,"sourceCodeEnd":896,"githubUrl":"https://github.com/astral-sh/ruff/blob/fca5c7cf2cd2f8338f18b9485f936b8e9f07bd0b/crates/ty_python_core/src/member.rs#L860-L896","documentation":"SmallSegments packs up to 7 member-access segments into a single u64. try_from_slice returns None up front when segments.len() > INLINE_MAX_SEGMENTS (7), so inside the loop u32::try_from(i) with i < 7 cannot fail; .expect(\"i is bounded by INLINE_MAX_SEGMENTS\") documents that invariant, not a user-facing condition.","triggerScenarios":"Only reachable if the early-return guard at the top of try_from_slice is removed or changed inconsistently with the bit layout, or a SmallSegments value is constructed bypassing try_from_slice - an internal regression, not an input-driven failure.","commonSituations":"Contributors editing the packing constants (INLINE_COUNT_BITS, INLINE_SEGMENT_BITS, INLINE_MAX_SEGMENTS) without keeping the guard in sync; fuzzers mutating the packed representation.","solutions":["Keep the segments.len() > INLINE_MAX_SEGMENTS early-return consistent with the bit layout whenever the constants change","If observed at runtime, capture a repro and file a ty issue - the value was produced through an unsupported path","Keep unit tests covering the 6/7/8-segment boundary in pack/iterate round trips"],"exampleFix":null,"handlingStrategy":"validation","validationCode":"assert!(segments.len() <= INLINE_MAX_SEGMENTS); // keep paired with the packing layout","typeGuard":null,"tryCatchPattern":"let packed = std::panic::catch_unwind(|| SmallSegments::try_from_slice(&segs)); // fuzz harness isolation only","preventionTips":["When changing INLINE_* bit constants, update the length guard and layout doc together","Construct SmallSegments only via try_from_slice","Round-trip test pack/iterate at the 7-segment boundary"],"tags":["internal","invariant","panic","bit-packing","member-access"],"backgroundTag":"internal-invariant-violation","analyzedSha":"fca5c7cf2cd2f8338f18b9485f936b8e9f07bd0b","analyzedAt":"2026-08-20T16:33:49.445Z","contentChangedAt":"2026-08-20T16:33:49.445Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}