From 58dd7df4ec8d04f74be26f935d756f2891d0c778 Mon Sep 17 00:00:00 2001 From: Sarah Wesker Date: Thu, 26 Mar 2026 09:05:10 -0400 Subject: [PATCH 1/2] fix: handle Def types in object constraint duck typing `check_object_type_compact` only matched `LuaType::Ref` when checking if a class satisfies a structural object constraint (e.g. `{ x: number, y: number, z: number }`). When the type was inferred as `LuaType::Def` (e.g. from `return self` without an explicit `@return`), it fell through to the wildcard arm and incorrectly reported a generic-constraint-mismatch. Add `LuaType::Def` alongside `LuaType::Ref` in the match arm so both representations are checked structurally against the object constraint. --- .../test/generic_constraint_mismatch_test.rs | 41 +++++++++++++++++++ .../complex_type/object_type_check.rs | 2 +- 2 files changed, 42 insertions(+), 1 deletion(-) diff --git a/crates/emmylua_code_analysis/src/diagnostic/test/generic_constraint_mismatch_test.rs b/crates/emmylua_code_analysis/src/diagnostic/test/generic_constraint_mismatch_test.rs index 055a48e4e..0c6c47230 100644 --- a/crates/emmylua_code_analysis/src/diagnostic/test/generic_constraint_mismatch_test.rs +++ b/crates/emmylua_code_analysis/src/diagnostic/test/generic_constraint_mismatch_test.rs @@ -300,6 +300,47 @@ mod test { )); } + #[test] + fn test_object_constraint_with_class_ref() { + let mut ws = VirtualWorkspace::new(); + // A @class whose inferred Def type (e.g. from `return self`) should satisfy + // an object constraint via structural duck typing, same as Ref types do. + assert!(ws.check_code_for( + DiagnosticCode::GenericConstraintMismatch, + r#" + ---@class MyPos + ---@field x number + ---@field y number + ---@field z number + ---@overload fun(x: number, y: number, z: number): MyPos + MyPos = {} + MyPos.__index = MyPos + + setmetatable(MyPos, { + __call = function(x, y, z) + return setmetatable({ x = x, y = y, z = z }, MyPos) + end + }) + + function MyPos:next() + self.x = self.x + 1 + self.y = self.y + 1 + return self + end + + ---@generic T: { x: number, y: number, z: number } + ---@param pos T + local function getTile(pos) end + + local p = MyPos(0, 0, 0):next() + getTile(p) + + local p2 = MyPos(1, 1, 1) + getTile(p2:next()) + "# + )); + } + #[test] fn test_generic_keyof_param_scope() { let mut ws = VirtualWorkspace::new(); diff --git a/crates/emmylua_code_analysis/src/semantic/type_check/complex_type/object_type_check.rs b/crates/emmylua_code_analysis/src/semantic/type_check/complex_type/object_type_check.rs index 371c017b5..d1184b52b 100644 --- a/crates/emmylua_code_analysis/src/semantic/type_check/complex_type/object_type_check.rs +++ b/crates/emmylua_code_analysis/src/semantic/type_check/complex_type/object_type_check.rs @@ -35,7 +35,7 @@ pub fn check_object_type_compact( check_guard.next_level()?, ); } - LuaType::Ref(type_id) => { + LuaType::Ref(type_id) | LuaType::Def(type_id) => { return check_object_type_compact_type_ref( context, source_object, From 2fc17d991e5bce170a2a331062480b17842ef27a Mon Sep 17 00:00:00 2001 From: Sarah Wesker Date: Thu, 26 Mar 2026 09:25:04 -0400 Subject: [PATCH 2/2] rename --- .../src/diagnostic/test/generic_constraint_mismatch_test.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/emmylua_code_analysis/src/diagnostic/test/generic_constraint_mismatch_test.rs b/crates/emmylua_code_analysis/src/diagnostic/test/generic_constraint_mismatch_test.rs index 0c6c47230..1e85409b1 100644 --- a/crates/emmylua_code_analysis/src/diagnostic/test/generic_constraint_mismatch_test.rs +++ b/crates/emmylua_code_analysis/src/diagnostic/test/generic_constraint_mismatch_test.rs @@ -301,7 +301,7 @@ mod test { } #[test] - fn test_object_constraint_with_class_ref() { + fn test_object_constraint_with_class_duck_typing() { let mut ws = VirtualWorkspace::new(); // A @class whose inferred Def type (e.g. from `return self`) should satisfy // an object constraint via structural duck typing, same as Ref types do.