{"record":{"id":"b65432335f05b930","repo":"astral-sh/ruff","slug":"binding-definition-should-have-retained-declaratio","errorCode":null,"errorMessage":"binding definition should have retained declarations","messagePattern":"binding definition should have retained declarations","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/ty_python_core/src/use_def.rs","lineNumber":1165,"sourceCode":"        nested_laziness: ScopeLaziness,\n    ) -> EnclosingSnapshotResult<'_, 'db> {\n        let boundness_analysis = if nested_laziness.is_eager() {\n            BoundnessAnalysis::BasedOnUnboundVisibility\n        } else {\n            // TODO: We haven't implemented proper boundness analysis for nonlocal symbols, so we assume the boundness is bound for now.\n            BoundnessAnalysis::AssumeBound\n        };\n\n        let Some(extra) = self.extra.as_deref() else {\n            return EnclosingSnapshotResult::NotFound;\n        };\n\n        match extra.enclosing_snapshots.get(snapshot_id) {\n            Some(InternedEnclosingSnapshotId::Constraint(constraint)) => {\n                EnclosingSnapshotResult::FoundConstraint(*constraint)\n            }\n            Some(InternedEnclosingSnapshotId::Bindings(bindings_id)) => {\n                EnclosingSnapshotResult::FoundBindings(\n                    self.bindings_iterator(\n                        &self.interned_bindings[*bindings_id],\n                        boundness_analysis,\n                    ),\n                )\n            }\n            None => EnclosingSnapshotResult::NotFound,\n        }\n    }\n\n    pub fn bindings_at_definition(\n        &self,\n        definition: Definition<'db>,\n    ) -> BindingWithConstraintsIterator<'_, 'db> {\n        let bindings = self.definitions_by_definition.get(&definition).map_or_else(\n            || ALWAYS_UNBOUND_BINDINGS.as_slice(),\n            |definitions| &self.interned_bindings[definitions.bindings],\n        );","sourceCodeStart":1147,"sourceCodeEnd":1183,"githubUrl":"https://github.com/astral-sh/ruff/blob/15f3fe6b15a5f00172f34b0f542f8ea277f5a586/crates/ty_python_core/src/use_def.rs#L1147-L1183","documentation":"The use-def map stores two flavors of entry per definition: record_binding retains the declarations visible at the binding (declarations: Some, use_def.rs:1977) while record_declaration stores None for declaration-only definitions such as the declaring phase of an annotated assignment (use_def.rs:2374). declarations_at_binding unwraps the Some case because it is only meant to be called with definitions that bind a value; the expect fires when a declaration-only definition is passed instead.","triggerScenarios":"Calling declarations_at_binding with a Definition that was classified as a declaration rather than a binding, e.g., the declare-only half of an annotated assignment like `x: int` with no right-hand side, or any newly added definition kind routed through record_declaration but later queried through the binding API.","commonSituations":"Contributor changes that add a new definition kind or alter the declare-vs-bind split of annotated assignments without updating the retention path; semantic query code that iterates all definitions instead of only binding definitions.","solutions":["Verify the caller passes a binding Definition obtained from the bindings API (bindings_iterator and friends), not an arbitrary definition from the index","If a new definition kind was added, route it through record_binding (or the combined declare-and-bind path) so declarations are retained for it","Otherwise minimize the module and file a ty issue: the definition classification for that syntax node is wrong"],"exampleFix":null,"handlingStrategy":"validation","validationCode":"// Only query declarations at definitions that actually bind:\nfor binding in use_def_map.all_bindings(db, file) {\n    let decls = use_def_map.declarations_at_binding(binding);\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Pass only binding Definitions (from the bindings iteration APIs) to declarations_at_binding, never raw definitions from the index","When adding a definition kind, decide explicitly whether it binds, declares, or both, and route it through the matching record_* path","Cover declaration-only syntax (`x: int` with no value) in tests that query declarations"],"tags":["rust","ty","use-def","panic","definition-classification","internal-invariant"],"backgroundTag":"missing-optional-data-access","analyzedSha":"15f3fe6b15a5f00172f34b0f542f8ea277f5a586","analyzedAt":"2026-08-20T16:33:49.445Z","contentChangedAt":"2026-08-20T16:33:49.445Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}