-
Notifications
You must be signed in to change notification settings - Fork 71
feat(infer): resolve nested table literals as Instance types #987
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from 1 commit
51ad350
e98efd5
6d73899
c3cc7c6
0339b6a
0f50901
646bd23
0bf0265
d1682cc
69032e9
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -97,10 +97,28 @@ mod test { | |
| let e_ty = ws.expr_ty("e"); | ||
| let f_ty = ws.expr_ty("f"); | ||
|
|
||
| assert_eq!(a_ty, LuaType::Integer); | ||
| assert_eq!(d_ty, LuaType::Integer); | ||
| assert_eq!(e_ty, LuaType::Integer); | ||
| assert_eq!(f_ty, LuaType::Integer); | ||
| // a and d: direct field access resolves via the table literal (IntegerConst) | ||
| // or via the class declaration (Integer); both are valid integer types | ||
| assert!( | ||
| matches!(a_ty, LuaType::Integer | LuaType::IntegerConst(_)), | ||
| "expected integer type for a, got {:?}", | ||
| a_ty | ||
| ); | ||
| assert!( | ||
| matches!(d_ty, LuaType::Integer | LuaType::IntegerConst(_)), | ||
| "expected integer type for d, got {:?}", | ||
| d_ty | ||
| ); | ||
| assert!( | ||
| matches!(e_ty, LuaType::Integer | LuaType::IntegerConst(_)), | ||
| "expected integer type for e, got {:?}", | ||
| e_ty | ||
| ); | ||
| assert!( | ||
| matches!(f_ty, LuaType::Integer | LuaType::IntegerConst(_)), | ||
| "expected integer type for f, got {:?}", | ||
| f_ty | ||
| ); | ||
| } | ||
|
|
||
| #[test] | ||
|
|
@@ -322,4 +340,90 @@ mod test { | |
| value_ty | ||
| ); | ||
| } | ||
|
|
||
| #[test] | ||
| fn test_optional_field_narrowed_by_table_literal() { | ||
| let mut ws = VirtualWorkspace::new(); | ||
| ws.def( | ||
| r#" | ||
| ---@class NarrowFieldTest | ||
| ---@field a? integer | ||
| ---@field b? integer | ||
|
|
||
| ---@type NarrowFieldTest | ||
| local test = { a = 1 } | ||
| c = test.a | ||
| d = test.b | ||
| "#, | ||
| ); | ||
|
|
||
| let c_ty = ws.expr_ty("c"); | ||
| assert!( | ||
| matches!(c_ty, LuaType::Integer | LuaType::IntegerConst(_)), | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. why keep two type match?
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hi, the two variants are there because the current inference intersects the declared type with the literal value (e.g. { a = 1 } → IntegerConst(1) instead of Integer).
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The returned type must be one of these; the | here indicates you're not sure which one it is, and that shouldn't happen in tests. |
||
| "expected integer type for provided field, got {:?}", | ||
| c_ty | ||
| ); | ||
|
|
||
| // b is not provided in the literal, should remain integer? (nullable) | ||
| let d_ty = ws.expr_ty("d"); | ||
| assert!( | ||
| matches!(d_ty, LuaType::Union(_) | LuaType::Nil), | ||
| "expected nullable type for unprovided field, got {:?}", | ||
|
NeOzay marked this conversation as resolved.
Outdated
|
||
| d_ty | ||
| ); | ||
| } | ||
|
|
||
| #[test] | ||
| fn test_recursive_instance_member_resolution() { | ||
| let mut ws = VirtualWorkspace::new(); | ||
| ws.def( | ||
| r#" | ||
| ---@class DeepInner | ||
| ---@field c integer | ||
|
|
||
| ---@class DeepMiddle | ||
| ---@field b? DeepInner | ||
|
|
||
| ---@class DeepOuter | ||
| ---@field a? DeepMiddle | ||
|
|
||
| ---@type DeepOuter | ||
| local test = { a = { b = { c = 1 } } } | ||
| x = test.a.b.c | ||
| "#, | ||
| ); | ||
|
|
||
| let x_ty = ws.expr_ty("x"); | ||
| assert!( | ||
| matches!(x_ty, LuaType::Integer | LuaType::IntegerConst(_)), | ||
| "expected integer type for deeply nested field, got {:?}", | ||
| x_ty | ||
| ); | ||
| } | ||
|
|
||
| #[test] | ||
| fn test_optional_class_field_narrowed_by_table_literal() { | ||
| let mut ws = VirtualWorkspace::new(); | ||
|
|
||
| ws.def( | ||
| r#" | ||
| ---@class InnerClass | ||
| ---@field b integer | ||
|
|
||
| ---@class OuterClass | ||
| ---@field a? InnerClass | ||
|
|
||
| ---@type OuterClass | ||
| local test = { a = { b = 1 } } | ||
| c = test.a | ||
| "#, | ||
| ); | ||
|
|
||
| let c_ty = ws.expr_ty("c"); | ||
| assert!( | ||
| matches!(c_ty, LuaType::Ref(_) | LuaType::Instance(_)), | ||
| "expected InnerClass (non-nullable, possibly Instance) for provided optional field, got {:?}", | ||
| c_ty | ||
| ); | ||
| } | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why match LuaType::Integer | LuaType::IntegerConst?