{"record":{"id":"bfd1e24ea5ac77ef","repo":"astral-sh/ruff","slug":"last-target-should-have-a-corresponding-entry","errorCode":null,"errorMessage":"last_target should have a corresponding entry","messagePattern":"last_target should have a corresponding entry","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/ruff_linter/src/rules/flake8_simplify/rules/ast_bool_op.rs","lineNumber":363,"sourceCode":"    };\n\n    // Locate duplicate `isinstance` calls, represented as a vector of vectors\n    // of indices of the relevant `Expr` instances in `values`.\n    let mut duplicates: Vec<Vec<usize>> = Vec::new();\n    let mut last_target_option: Option<ComparableExpr> = None;\n    for (index, call) in values.iter().enumerate() {\n        let Some(target) = isinstance_target(call, checker.semantic()) else {\n            last_target_option = None;\n            continue;\n        };\n\n        if last_target_option\n            .as_ref()\n            .is_some_and(|last_target| *last_target == ComparableExpr::from(target))\n        {\n            duplicates\n                .last_mut()\n                .expect(\"last_target should have a corresponding entry\")\n                .push(index);\n        } else {\n            last_target_option = Some(target.into());\n            duplicates.push(vec![index]);\n        }\n    }\n\n    // Generate a `Diagnostic` for each duplicate.\n    for indices in duplicates {\n        if indices.len() > 1 {\n            // Grab the target used in each duplicate `isinstance` call (e.g., `obj` in\n            // `isinstance(obj, int)`).\n            let target = if let Expr::Call(ast::ExprCall {\n                arguments: Arguments { args, .. },\n                ..\n            }) = &values[indices[0]]\n            {\n                args.first()","sourceCodeStart":345,"sourceCodeEnd":381,"githubUrl":"https://github.com/astral-sh/ruff/blob/15f3fe6b15a5f00172f34b0f542f8ea277f5a586/crates/ruff_linter/src/rules/flake8_simplify/rules/ast_bool_op.rs#L345-L381","documentation":"In the TC (flake8_simplify) `duplicate_isinstance_call` rule, `duplicates` maps each distinct isinstance target to the list of call indices sharing it. The code pushes onto `duplicates.last_mut()` immediately after confirming the last target equals the current one; the expect asserts that correspondence. It is an internal invariant: last_target_option and duplicates are updated in lockstep.","triggerScenarios":"Not reachable from user input. A panic would mean the lockstep invariant was broken — e.g. code between iterations mutating `duplicates` (clearing/popping) without resetting `last_target_option`, so the Option says a duplicate exists while `duplicates` is empty.","commonSituations":"Hit only by contributors refactoring the iteration loop in ast_bool_op.rs, e.g. reordering the else branch or filtering duplicates after construction.","solutions":["Ensure `last_target_option` is always set in the same branch that pushes a new entry into `duplicates` (never update one without the other)","Restructure to a HashMap<ComparableExpr, Vec<usize>> so the correspondence is enforced by construction instead of by `.last_mut()`","Add a debug_assert that duplicates.len() equals the number of distinct targets seen"],"exampleFix":"// before\nduplicates.last_mut().expect(\"last_target should have a corresponding entry\").push(index);\n// after\nif let Some(entry) = duplicates.last_mut() {\n    entry.push(index);\n} else {\n    // fall back to creating the entry, keeping the invariant explicit\n    duplicates.push(vec![index]);\n}","handlingStrategy":"validation","validationCode":"debug_assert_eq!(last_target_option.is_some(), !duplicates.is_empty());","typeGuard":"if duplicates.last_mut().is_some() { /* safe to push */ }","tryCatchPattern":null,"preventionTips":["Update correlated structures in the same branch/atomic step","Prefer HashMap keyed by target over parallel last-element invariants","Add debug_assertions for paired-structure invariants"],"tags":["rust","panic","invariant","flake8-simplify"],"backgroundTag":"invariant-violation-panic","analyzedSha":"15f3fe6b15a5f00172f34b0f542f8ea277f5a586","analyzedAt":"2026-09-05T10:32:37.492Z","contentChangedAt":"2026-09-05T10:32:37.492Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}