IsDescendantOf(nil) returns true, but should throw error

Instance:IsDescendantOf(nil) returns true. This doesn’t make any sense, and was the cause a very hard-to-find bug. FWIW, Instance:IsAncestorOf(nil) returns false.

The justification behind this (according to the documentation) is that the parent of the DataModel is nil (game.Parent == nil)

However, passing a non-instance to IsDescendantOf or IsAncestorOf should probably throw an error instead. There’s no reason why nil should be passed to these two methods and expect something usable.

21 Likes

This isn’t backwards compatible. It would remove the implicit guarantee that IsDescendantOf and IsAncestorOf will never throw an error, and would break cases where the behavior is known and assumed.

if A:IsDescendantOf(B:FindFirstChild(name)) then
    -- Assumed true if FindFirstChild returns nil
end
if A:IsAncestorOf(B:FindFirstChild(name)) then
    -- Assumed false if FindFirstChild returns nil
end
7 Likes

Perhaps in this circumstance, the function should log a warning and give a stack trace so there isn’t confusion in the future. I agree these functions should not start throwing an error, but this behavior seems to be an unintended side effect of how IsDescendantOf is implemented:

local function isDescendantOf(object, ancestor)
    while object ~= nil do
        object = object.Parent
        
        if object == ancestor then
            -- if object becomes nil and provided 
            -- ancestor is nil, this collapses to true.
            return true
        end
    end

    return false
end

IsAncestorOf shares the same behavior, as it’s just an inverse redirect to IsDescendantOf

local function isAncestorOf(object, descendant)
    return isDescendantOf(descendant, object)
end
4 Likes