{"record":{"id":"0120627f34eb8b63","repo":"astral-sh/ruff","slug":"calling-find-name-in-mro-on-dynamic-type-should","errorCode":null,"errorMessage":"Calling `find_name_in_mro` on dynamic type should return `Some`","messagePattern":"Calling `find_name_in_mro` on dynamic type should return `Some`","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/ty_python_semantic/src/types/bound_super.rs","lineNumber":955,"sourceCode":"            descriptor_error.map(MemberLookupErrorKind::DescriptorGet),\n            instance\n                .and_then(|_| attribute.place.ignore_possibly_undefined())\n                .and_then(|ty| ty.property_deprecations(db))\n                // `super` delegates reads to the owner's descriptors, but not writes or deletions.\n                .map(|properties| properties.getters_only(db)),\n        ))\n    }\n\n    /// Similar to `Type::find_name_in_mro_with_policy`, but performs lookup starting *after* the\n    /// pivot class in the MRO, based on the `owner` type instead of the `super` type.\n    pub(super) fn find_name_in_mro_after_pivot(\n        self,\n        db: &'db dyn Db,\n        env: &ProgramEnvironment<'db>,\n        name: &str,\n        policy: MemberLookupPolicy,\n    ) -> PlaceAndQualifiers<'db> {\n        let owner = self.owner(db);\n        let class = match &owner {\n            SuperOwnerKind::Dynamic(dynamic) => {\n                return Type::Dynamic(*dynamic)\n                    .find_name_in_mro_with_policy(db, env, name, policy)\n                    .expect(\"Calling `find_name_in_mro` on dynamic type should return `Some`\");\n            }\n            SuperOwnerKind::Divergent(_) => {\n                return Type::unknown()\n                    .find_name_in_mro_with_policy(db, env, name, policy)\n                    .expect(\"Calling `find_name_in_mro` on Unknown should return `Some`\");\n            }\n            SuperOwnerKind::Resolved(resolved_owner) => resolved_owner.lookup_anchor,\n        };\n\n        let mut mro_after_pivot = self.skip_until_after_pivot(db, env, owner.iter_mro(db, env));\n        let class_literal = class.class_literal(db);\n        let result =\n            class_literal.class_member_from_mro(db, env, name, policy, mro_after_pivot.clone());","sourceCodeStart":937,"sourceCodeEnd":973,"githubUrl":"https://github.com/astral-sh/ruff/blob/15f3fe6b15a5f00172f34b0f542f8ea277f5a586/crates/ty_python_semantic/src/types/bound_super.rs#L937-L973","documentation":"In `super()` member lookup (find_name_in_mro_after_pivot), when the super owner is dynamic (Any/Unknown), lookup delegates to Type::Dynamic::find_name_in_mro_with_policy, which is guaranteed to return Some because dynamic types fall back to object-level members with an undefined place. The expect enforces that guarantee.","triggerScenarios":"`super().attr` where the enclosing class resolves to a dynamic type, and the dynamic MRO lookup returns None - an internal regression in the dynamic lookup path (early returns or name filtering) rather than a property of the user's Python input.","commonSituations":"Refactors of find_name_in_mro or MemberLookupPolicy that introduce early None returns (e.g., filtered member names) without preserving the dynamic fallback; usually surfaced by fuzzing or large codebases with untyped classes.","solutions":["File a ty issue with the `super()` expression, the member name, and the backtrace","As a contributor: audit find_name_in_mro_with_policy early-return paths; they must still yield Some with an undefined place for dynamic types","Add a regression test covering `super().<member>` in a class with an untyped/dynamic base"],"exampleFix":null,"handlingStrategy":"fallback","validationCode":null,"typeGuard":null,"tryCatchPattern":"let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| check_file(&db, file)));\nmatch result {\n    Ok(diags) => diags,\n    Err(payload) => { log::warn!(\"ty panicked in super() lookup on {}: {:?}\", file, payload); vec![] }\n}","preventionTips":["Report `super().<member>` panics with the exact member name - it identifies the filtered-lookup path","When changing MemberLookupPolicy or find_name_in_mro early returns, preserve the Some(undefined-place) contract for dynamic types","Keep a regression test for super() in untyped classes"],"tags":["rust","ty","super","mro","member-lookup","panic"],"backgroundTag":"mro-lookup-returned-none","analyzedSha":"15f3fe6b15a5f00172f34b0f542f8ea277f5a586","analyzedAt":"2026-08-20T16:33:49.445Z","contentChangedAt":"2026-08-20T16:33:49.445Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}