astral-sh/ruff · error
Definition value should be a call expression
Error message
Definition value should be a call expression
What it means
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`.
Source
Thrown at crates/ty_python_semantic/src/types/class/dynamic_literal.rs:216
/// Inner cached function for deferred inference of bases.
/// Only called for assigned calls where inference was deferred.
#[salsa::tracked(returns(deref), cycle_initial=|_, _, _| Box::default(), heap_size=ruff_memory_usage::heap_size)]
fn deferred_explicit_bases<'db>(
db: &'db dyn Db,
definition: Definition<'db>,
) -> Box<[Type<'db>]> {
let program_file = definition.program_file(db);
let python_file = program_file.python_file(db);
let env = ProgramEnvironment::from_file(program_file);
let module = parsed_module(db, python_file).load(db);
let value = definition
.kind(db)
.value(&module)
.expect("DynamicClassAnchor::Definition should only be used for assignments");
let call_expr = value
.as_call_expr()
.expect("Definition value should be a call expression");
let Some(bases_arg) = dynamic_class_bases_argument(&call_expr.arguments) else {
return Box::default();
};
// Use `definition_expression_type` for deferred inference support.
extract_fixed_length_iterable_element_types(db, &env, bases_arg, |expr| {
definition_expression_type(db, definition, expr)
})
.unwrap_or_else(|| Box::from([Type::unknown()]))
}
match self.anchor(db) {
// For dangling calls, bases are stored directly on the anchor.
DynamicClassAnchor::ScopeOffset { explicit_bases, .. } => explicit_bases.as_ref(),
// For assigned calls, use deferred inference.
DynamicClassAnchor::Definition(definition) => deferred_explicit_bases(db, *definition),
}View on GitHub (pinned to 15f3fe6b15)
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.
Example fix
// before
let call_expr = value.as_call_expr().expect("Definition value should be a call expression");
// after: match instead of expect
let Some(call_expr) = value.as_call_expr() else {
return Box::default();
}; Defensive patterns
Strategy: try-catch
Try / catch
let bases = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
dynamic_class.explicit_bases(db)
}))
.unwrap_or_else(|_| Box::from([Type::unknown()])); Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- Expected `NamedTuple` definition r.h.s. to be a call express
- dynamic class definitions should only be used for assignment
- anchor should not be NodeIndex::NONE
- scope offset should point to ExprCall
- DynamicClassAnchor::Definition should only be used for assig
AI-assisted analysis of astral-sh/ruff@15f3fe6b15 (2026-08-20).
Data as JSON: /api/errors/b14ccd7d6204b1b1.
Report an issue: GitHub.