godotengine / godot-proposals

Godot Improvement Proposals (GIPs)
MIT License
1.15k stars 97 forks source link

Improve GDScript conditional type deduction and autocompletion #3691

Open mieldepoche opened 2 years ago

mieldepoche commented 2 years ago

Describe the project you are working on

Not relevant

Describe the problem or limitation you are having in your project

GDScript currently (3.x, 4.x) provides autocompletion in conditional type checks: simple if

However this feature is limited to:

meaning conditions containing otherwise unambiguous type information do not lead to such autocompletion / type validation:

no autocompletion, in 3.x a typing error that shouldn't happen, in 4.1.1
composition image

Describe the feature / enhancement and how it helps to overcome the problem or limitation

Godot should be smarter about type inference. It should allow:

if thing is Actor and thing.is_hostile(): # no error, `thing` *has* an `is_hostile` method!
    thing.take_damage() # in this scope, we know `thing` is an `Actor`. So, we get autocompletion!

instead of having to do

if thing is Actor:
    # with autocompletion!
    if thing.is_hostile():
        thing.take_damage()

In cases the type cannot be deduced from the condition (such as in if x is Vector2 or x is Vector3) I see no sensible changes to be made.

Describe how your proposal will work, with code, pseudo-code, mock-ups, and/or diagrams

I don't know how the internals of GDScript parsing but the high-level idea would be to have a type-deduction algorithm rather than a naive pattern check.

If this enhancement will not be used often, can it be worked around with a few lines of script?

That's used very often. The equivalent can be achieved at a 1 line and 1 indentation level cost but it often feels less natural to chain ifs that can be merged.

Is there a reason why this should be core and not an add-on in the asset library?

It can't be an addon.

MBoqui commented 10 months ago

A few other cases that I think fit in this proposal:

A)

if not thing is Fish:
    pass
else:
    thing.swim() # should this have auto complete?

B)

if not thing is Fish: return

thing.swim() # what about this?

C)

for thing in get_children():
    thing.something_for_all_children()

    if not thing is Fish: continue

    thing.swim() # and this?

In my opinion, all of them should have auto completion. For C we could do for thing : Fish in get_children(): and check for null in 4.2, but if you still want to do something for the other types I usually resort to type casting to get auto completion.

But this bring the question of what about these: D)

if not thing is Fish:
    pass
elif not thing is Bird:
    pass
else:
    thing.swim() # should this show up in the auto complete since we are assuming thing is Fish?
    thing.fly() # what about this? thing is Bird, right?

E)

if not thing is Fish: return
if not thing is Bird: return

thing.swim() # same as above
thing.fly() # same as above

F)

for thing in get_children():
    thing.something_for_all_children()

    if not thing is Fish: continue
    if not thing is Bird: continue

    thing.swim() # same as above
    thing.fly() # same as above

Currently (as far as I know) if you do:

if thing is Fish:
    if thing is Bird:
        thing.swim() # this shows up in auto complete
        thing.fly() # this doesn't. even if Bird extends Fish

So should we just use the first class we encounter in the cases D, E and F? This seems consistent with current behavior.

Also worth mentioning I have no idea how this is currently implemented or the changes needed.