[ty] Detect illegal non-enum attribute accesses in Literal annotation (#19477)

## Summary

Detect illegal attribute accesses in `Literal[X.Y]` annotations if `X`
is not an enum class.

## Test Plan

New Markdown test
This commit is contained in:
David Peter 2025-07-22 11:42:03 +02:00 committed by GitHub
parent 5e29278aa2
commit 215a1c55d4
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 34 additions and 8 deletions

View file

@ -240,3 +240,10 @@ pub(crate) fn enum_member_literals<'a, 'db: 'a>(
pub(crate) fn is_single_member_enum<'db>(db: &'db dyn Db, class: ClassLiteral<'db>) -> bool {
enum_metadata(db, class).is_some_and(|metadata| metadata.members.len() == 1)
}
pub(crate) fn is_enum_class<'db>(db: &'db dyn Db, ty: Type<'db>) -> bool {
match ty {
Type::ClassLiteral(class_literal) => enum_metadata(db, class_literal).is_some(),
_ => false,
}
}

View file

@ -104,6 +104,7 @@ use crate::types::diagnostic::{
report_invalid_generator_function_return_type, report_invalid_return_type,
report_possibly_unbound_attribute,
};
use crate::types::enums::is_enum_class;
use crate::types::function::{
FunctionDecorators, FunctionLiteral, FunctionType, KnownFunction, OverloadLiteral,
};
@ -10414,14 +10415,23 @@ impl<'db> TypeInferenceBuilder<'db, '_> {
// For enum values
ast::Expr::Attribute(ast::ExprAttribute { value, attr, .. }) => {
let value_ty = self.infer_expression(value);
// TODO: Check that value type is enum otherwise return None
let ty = value_ty
.member(self.db(), &attr.id)
.place
.ignore_possibly_unbound()
.unwrap_or(Type::unknown());
self.store_expression_type(parameters, ty);
ty
if is_enum_class(self.db(), value_ty) {
let ty = value_ty
.member(self.db(), &attr.id)
.place
.ignore_possibly_unbound()
.unwrap_or(Type::unknown());
self.store_expression_type(parameters, ty);
ty
} else {
self.store_expression_type(parameters, Type::unknown());
if value_ty.is_todo() {
value_ty
} else {
return Err(vec![parameters]);
}
}
}
// for negative and positive numbers
ast::Expr::UnaryOp(u)