{"record":{"id":"b14ccd7d6204b1b1","repo":"astral-sh/ruff","slug":"definition-value-should-be-a-call-expression","errorCode":null,"errorMessage":"Definition value should be a call expression","messagePattern":"Definition value should be a call expression","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/ty_python_semantic/src/types/class/dynamic_literal.rs","lineNumber":216,"sourceCode":"        /// 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(),\n            // For assigned calls, use deferred inference.\n            DynamicClassAnchor::Definition(definition) => deferred_explicit_bases(db, *definition),\n        }","sourceCodeStart":198,"sourceCodeEnd":234,"githubUrl":"https://github.com/astral-sh/ruff/blob/15f3fe6b15a5f00172f34b0f542f8ea277f5a586/crates/ty_python_semantic/src/types/class/dynamic_literal.rs#L198-L234","documentation":"After asserting the definition is an assignment (error 145), `deferred_explicit_bases` further asserts its value is a call expression, since dynamic classes are created by calls like `make_class('C', ...)` or `Enum('Color', 'RED GREEN')`. The `as_call_expr().expect(...)` fires when the stored value node is not an `ast::ExprCall`.","triggerScenarios":"Resolving explicit bases for a Definition-anchored dynamic class whose definition value is not a call expression - e.g. the anchor was built before confirming the RHS is a recognized dynamic-class call, or the definition was rebound (`C = C_or_something_else`) between anchoring and resolution.","commonSituations":"Contributions broadening dynamic-class detection beyond call RHS patterns; changes to how assigned names are re-bound (multiple assignment targets, `C = D = make_class(...)`); stale anchors after reparse.","solutions":["Only build the Definition anchor after matching the RHS as a call expression with the recognized callee.","When the value is not a call, treat the class as having no explicit bases rather than anchoring it.","Cover the new construct with an mdtest; run `cargo nextest run -p ty_python_semantic`.","Report upstream with a minimal file if reachable on an unmodified build."],"exampleFix":"// before\nlet call_expr = value.as_call_expr().expect(\"Definition value should be a call expression\");\n\n// after: match instead of expect\nlet Some(call_expr) = value.as_call_expr() else {\n    return Box::default();\n};","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()]));","preventionTips":["When reporting, note whether the assigned RHS is actually a call expression in the repro.","Avoid rebinding a dynamic-class name (`C = something_else`) in reproducers unless that is the bug being reported.","Re-run mdtests after any change to definition anchoring."],"tags":["rust","panic","expect","ty","dynamic-class","call-expression","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"}