Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 8 additions & 2 deletions crates/emmylua_code_analysis/src/semantic_model/flow.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5517,8 +5517,14 @@ fn filter_type_by_member_truthiness(
}
// Missing member is equivalent to nil: `obj.missing` is false.
let member_ty = model.member_type(&component, key).unwrap_or(LuaType::Nil);
let truthy = type_is_truthy(&member_ty);
if truthy == keep_matching {
// The true branch keeps a component whose member can be truthy, the false branch one
// whose member can be falsy (a `boolean` member can be either).
let possible = if keep_matching {
type_is_truthy(&member_ty)
} else {
!member_ty.is_always_truthy()
};
if possible {
kept.push(component);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4943,4 +4943,65 @@ _ = a[2]
let ty = ws.expr_ty("B");
assert_eq!(ws.humanize_type(ty), "string");
}

/// The false branch of a member test keeps an owner whose member can be falsy, and drops one
/// whose member is always truthy.
#[test]
fn test_member_false_branch_keeps_owner_whose_member_can_be_falsy() {
let mut ws = VirtualWorkspace::new();
ws.def(
r#"
---@class Res
---@field ok boolean
---@field maybe boolean?
---@field name string

---@param res Res?
local function and_guard(res)
if res and res.ok then return end
AND_GUARD = res
end

---@param res Res?
local function nested(res)
if res then
if res.ok then return end
end
NESTED = res
end

---@param res Res?
local function negated(res)
if not (res and res.ok) then
NEGATED = res
end
end

---@param res Res?
local function optional_member(res)
if res and res.maybe then return end
OPTIONAL_MEMBER = res
end

---@param res Res
local function plain_owner(res)
if res.ok then return end
PLAIN_OWNER = res
end

---@param res Res?
local function always_truthy_member(res)
if res and res.name then return end
ALWAYS_TRUTHY_MEMBER = res
end
"#,
);

assert_eq!(ws.expr_ty("AND_GUARD"), ws.ty("Res?"));
assert_eq!(ws.expr_ty("NESTED"), ws.ty("Res?"));
assert_eq!(ws.expr_ty("NEGATED"), ws.ty("Res?"));
assert_eq!(ws.expr_ty("OPTIONAL_MEMBER"), ws.ty("Res?"));
assert_eq!(ws.expr_ty("PLAIN_OWNER"), ws.ty("Res"));
assert_eq!(ws.expr_ty("ALWAYS_TRUTHY_MEMBER"), ws.ty("nil"));
}
}
Loading