{"record":{"id":"330b09255fca4cac","repo":"astral-sh/ruff","slug":"dynamicclassanchor-definition-should-only-be-used","errorCode":null,"errorMessage":"DynamicClassAnchor::Definition should only be used for assignments","messagePattern":"DynamicClassAnchor::Definition should only be used for assignments","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/ty_python_semantic/src/types/class/dynamic_literal.rs","lineNumber":213,"sourceCode":"    ///\n    /// Returns `[Unknown]` if the bases iterable is variable-length.\n    pub(crate) fn explicit_bases(self, db: &'db dyn Db) -> &'db [Type<'db>] {\n        /// Inner cached function for deferred inference of bases.\n        /// Only called for assigned calls where inference was deferred.\n        #[salsa::tracked(returns(deref), cycle_initial=|_, _, _| Box::default(), heap_size=ruff_memory_usage::heap_size)]\n        fn deferred_explicit_bases<'db>(\n            db: &'db dyn Db,\n            definition: Definition<'db>,\n        ) -> Box<[Type<'db>]> {\n            let program_file = definition.program_file(db);\n            let python_file = program_file.python_file(db);\n            let env = ProgramEnvironment::from_file(program_file);\n            let module = parsed_module(db, python_file).load(db);\n\n            let value = definition\n                .kind(db)\n                .value(&module)\n                .expect(\"DynamicClassAnchor::Definition should only be used for assignments\");\n            let call_expr = value\n                .as_call_expr()\n                .expect(\"Definition value should be a call expression\");\n\n            let Some(bases_arg) = dynamic_class_bases_argument(&call_expr.arguments) else {\n                return Box::default();\n            };\n\n            // Use `definition_expression_type` for deferred inference support.\n            extract_fixed_length_iterable_element_types(db, &env, bases_arg, |expr| {\n                definition_expression_type(db, definition, expr)\n            })\n            .unwrap_or_else(|| Box::from([Type::unknown()]))\n        }\n\n        match self.anchor(db) {\n            // For dangling calls, bases are stored directly on the anchor.\n            DynamicClassAnchor::ScopeOffset { explicit_bases, .. } => explicit_bases.as_ref(),","sourceCodeStart":195,"sourceCodeEnd":231,"githubUrl":"https://github.com/astral-sh/ruff/blob/15f3fe6b15a5f00172f34b0f542f8ea277f5a586/crates/ty_python_semantic/src/types/class/dynamic_literal.rs#L195-L231","documentation":"For an assigned dynamic-class call (`C = make_class('C', bases)`), ty defers reading the explicit base classes until needed. `deferred_explicit_bases` re-reads the definition's value node from the parsed module; the expect asserts the definition is an assignment with a value. It fires when a `DynamicClassAnchor::Definition` was created for a definition whose kind yields no value expression.","triggerScenarios":"Calling `explicit_bases`/base resolution on a dynamic class anchored via `DynamicClassAnchor::Definition` (dynamic_literal.rs:233) where `definition.kind(db).value(&module)` is None - a non-assignment definition, or a definition resolved against a different module parse.","commonSituations":"Extending dynamic-class support to new constructs (module-level `type(...)` statements, conditional assignments, aliased targets) and anchoring definitions that are not plain assignments; refactors of definition kinds; anchors resolved after the file was reparsed without invalidation.","solutions":["At the site that builds `DynamicClassAnchor::Definition`, only anchor definitions for which `.kind(db).value(&module)` is Some.","Return empty bases (as the `dynamic_class_bases_argument` None path already does) instead of anchoring unmatched definitions.","Add an mdtest for the offending assignment shape and run the ty_python_semantic suite.","File an issue with the reproducer if it happens on stock ty."],"exampleFix":"// before\nDynamicClassAnchor::Definition(definition)\n\n// after: guard at construction, degrade to no explicit bases\nlet value = definition.kind(db).value(&module);\nlet anchor = value.is_some().then(|| DynamicClassAnchor::Definition(definition));","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"let bases = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {\n    dynamic_class.explicit_bases(db)\n}))\n.unwrap_or_else(|_| Box::from([Type::unknown()])); // degrade to unknown bases","preventionTips":["Contributors: after touching dynamic-class detection, run the full ty_python_semantic mdtest suite.","Keep reproducers as named assignments (`C = make_class('C', ...)`) when filing issues.","Embedders: catch_unwind around semantic queries and degrade to Type::unknown() so one anchor bug does not kill the run."],"tags":["rust","panic","expect","ty","dynamic-class","definition","invariant"],"backgroundTag":"rust-expect-panic","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"}