godot icon indicating copy to clipboard operation
godot copied to clipboard

Autocompletion breaks until the editor is restarted when a cyclic reference occurs

Open Tipyx opened this issue 1 year ago • 13 comments

Godot version

4.0.3 stable

System information

Windows 10

Issue description

Something is breaking the autocompletion when I used 2nd degree or more member of a variable.

Here's an exemple:

class_name MyClassA extends Node2D

func get_num():
	return 1

var classB : MyClassB

func _ready() -> void:
	Global.classA = self
	
	pass

And

class_name MyClassB extends Node2D

var classA : MyClassA

# Called when the node enters the scene tree for the first time.
func _ready() -> void:
	classA.classB.classA.get_num()
	pass

If you rename get_num to get_numb for exemple, the autocompletion in MyClassB will continue to show get_num (and CTRL+Click to it will refer to get.num), and I need to reload all the project because all future autocompletion will be outdated (like it stopped to update)

By the way, maybe it's related to https://github.com/godotengine/godot/issues/78003

I tested with the 4.1Beta, it still present

(First issue, I hope it's complete enough 😄)

Steps to reproduce

  • Download the sample project
  • Rename get_num to get_numb (or something else) in MyClassA
  • Try to autocomplete in MyClassB script after classA.classB.classA.

Minimal reproduction project

Issue.zip

Tipyx avatar Jun 10 '23 05:06 Tipyx

Could you test the latest builds of Godot 4.1 (beta 3 at the time of writing) and compare?

YuriSizov avatar Jun 22 '23 17:06 YuriSizov

Just tried with the Godot 4.1 RC 1, and it's still there

Tipyx avatar Jun 28 '23 08:06 Tipyx

I confirm that the issue is still there in Godot 4.1.1 stable.

Godot v4.1.1.stable - Windows 10.0.22621 - Vulkan (Forward+) - dedicated NVIDIA GeForce RTX 3060 (NVIDIA; 31.0.15.3713) - AMD Ryzen 5 5500 (12 Threads))

class_name Foo extends Node

func bar() -> int:
    return 5
class_name Mob extends Node

@onready var foo: Foo = get_node("Foo")

func _ready():
    foo.bar() // autocomplete succeeds
extends Node

@onready var mob: Mob = get_node("Mob")

func _ready():
    mob.foo.bar() // autocomplete fails

ericmorand avatar Aug 30 '23 10:08 ericmorand

Did further testing, it looks like autocompletion works for members of the base class (e.g. Node2D), just not for custom members of the child class.

image

image

Similarly, Ctrl+click won't work at all.

I tried VS Code + GDScript plugin as a workaround, but it doesn't work either - I suspect because the language server asks Godot for autocompletion, but Godot has the bug so it propagates to VS Code.

hsandt avatar Sep 05 '23 15:09 hsandt

Having the same problem. Did anyone find a solution?

eddieataberk avatar Oct 03 '23 04:10 eddieataberk

Looking at this I think it is not quite clear which issue we are talking about. The OP looks like a reload problem with Cyclic references.

I confirm that the issue is still there in Godot 4.1.1 stable.

Godot v4.1.1.stable - Windows 10.0.22621 - Vulkan (Forward+) - dedicated NVIDIA GeForce RTX 3060 (NVIDIA; 31.0.15.3713) - AMD Ryzen 5 5500 (12 Threads))

class_name Foo extends Node

func bar() -> int:
    return 5
class_name Mob extends Node

@onready var foo: Foo = get_node("Foo")

func _ready():
    foo.bar() // autocomplete succeeds
extends Node

@onready var mob: Mob = get_node("Mob")

func _ready():
    mob.foo.bar() // autocomplete fails

This is a different setup without cyclic references. Please try substituting @onready var mob: Mob = get_node("Mob") with @onready var mob: Mob = get_node("Mob") as Mob. (Same for Foo). If this solves the problem you are actually looking at #73638. If this does not solve your problem please provide a MRP with your setup.

HolonProduction avatar Oct 30 '23 22:10 HolonProduction

I think I'm running into this quite a bit, I'm running on a modified version of 4.3dev3 and was wondering if something I did in my modifications was causing the behavior but after looking at this and other similar issues it seems to be a known issue. I find myself restarting the editor a lot when coding. If I'm mostly in the same file it's fine, but the moment I need to edit a file that's referenced by a lot of others, I ran into the issue and have to restart.

Since the restart seems to fix it I guess a possible solution would be some sort of "clean and rebuild autocomplete cache" or something? Perhaps adding a button to the GDScript "File menu" similar to the existing "Soft Reload Tool Script" but a "Rebuild Autocompletion Cache" button? (When I was trying to find solutions that was one of the first things/places I looked)

DanielSnd avatar Mar 27 '24 23:03 DanielSnd

I'm not 100% certain if cyclic reference per se causes the autocomplete break. I've had cyclic references in my v4.2.stable.official [46dc27791] version and the autocomplete was working fine until I copy-pasted some code into the project.

The code causes an error, I immediately commented the whole thing but the autocomplete is irrevocably broken after that. It's really frustrating. This bug has been around since 4.0 iirc. (Probably unrelated: enums doesn't autocomplete if it's in a match block or if invoked from an instance instead of the class name.)

LO-0116 avatar Apr 16 '24 23:04 LO-0116

I think I'm running into this quite a bit, I'm running on a modified version of 4.3dev3 and was wondering if something I did in my modifications was causing the behavior but after looking at this and other similar issues it seems to be a known issue. I find myself restarting the editor a lot when coding. If I'm mostly in the same file it's fine, but the moment I need to edit a file that's referenced by a lot of others, I ran into the issue and have to restart.

Since the restart seems to fix it I guess a possible solution would be some sort of "clean and rebuild autocomplete cache" or something? Perhaps adding a button to the GDScript "File menu" similar to the existing "Soft Reload Tool Script" but a "Rebuild Autocompletion Cache" button? (When I was trying to find solutions that was one of the first things/places I looked)

I'm not sure if rebuilding the cache matters, I tried deleting and regenerating the .godot folder which contains all cache files but autocompletion remains broken

LO-0116 avatar Apr 17 '24 23:04 LO-0116

Can also confirm this. It is highly reproducible with specific scripts and not others. And, like it was noted above, it does not seem solely related to the presence of cyclic references (there appears to be at least one other trigger required, perhaps a threshold number of references?), and deleting the .godot folder does not fix the problem.

It currently appears that there are three possible outcomes that may be different stages of same problem. As the project grows and the cross-referencing complexity increases, the scripts that are affected by (1) later become affected by (2) and then (3). Not being familiar with the core code, I wonder if perhaps these reflect different levels of recursion timeouts or other limitations leading to different fail states?

Edit: To clarify, the errors occur when adding a new member to the affected script, then attempting to reference that member from another script.

(1) Autocomplete fails, but no errors are reported in the editor or running the game. (2) Autocomplete fails, and errors are reported in the script editor, but the game runs without error. After waiting some time, the errors in the script editor disappear. (3) Autocomplete fails, errors are reported in the script editor, and the game fails to run due to errors. No amount of waiting resolves it. It requires a project reload.

This progression has been consistent for me across many projects. The first script affected is always the file that contains my global constants. It does not reference any other script, but many scripts reference it, and some of them contain cyclic references. The next scripts to become affected are usually superclasses with multiple subclasses and users (like a generic "Entity" script).

I start to see the process starting around the time the projects grow from about a dozen scripts to a few dozen scripts -- so they are still relatively small.

dandeliondino avatar Apr 22 '24 08:04 dandeliondino

CC @godotengine/gdscript Given the last description, I think this might relate to other issues we've had where cached scripts seem to be out of sync with the files on disk and lead to weird errors.

akien-mga avatar Apr 22 '24 10:04 akien-mga

Thanks for looking into it! I want to clarify that I'm not 100% sure that it's the same problem as the original post, but it matches #80674, which was closed as being the same cause as this issue. And @LO-0116's observations seemed consistent with my experience that it has not been just cyclic references, but seems to require a second trigger.

dandeliondino avatar Apr 22 '24 13:04 dandeliondino

Another thing I've noticed: static functions does not seem affected by broken auto-completes. That is to say, autocompletes that wouldn't work in the body of func will work in the body of static func and newly added static func will appear in other scripts whereas newly added func wouldn't

LO-0116 avatar Apr 29 '24 01:04 LO-0116

I tried to reproduce on master using the scripts that have no cyclic reference, and wasn't able to. Using the mrp from the original issue which has cyclic references, I was able to.

When auto completion shows old names, outdated class types are set by the analyzer. This happens because the new parser created by code_completion is not part of the GDScriptCache, so when the analyzer encounters the same type which is currently completed on, it will use an old parser ref from the script, which might still have some old class types, depending on the idle parse time (I could only reproduce when being fast enough, and also needed some tries before it happened). Invalidating the gdscript cache for the current script when autocompleting could probably solve this issue.

On a different note, with #84266 I wasn't able to reproduce even with the cyclic references, I suppose it forces an update on the cache. This isn't the correct fix for this issue though.

Also the bug I found here, does not really fit all users who reported on having this problem in this thread. There have been some fixes with GDScript caching so I could imagine, that some more grave issues were already fixed by them. Did anyone with the problem give it a try with the last dev build?

HolonProduction avatar Apr 29 '24 11:04 HolonProduction

@HolonProduction So far, the issue I described appears not to be reproducible in 4.3-dev6! When I try to access newly-added variables from the previously affected scripts, autocomplete is populating immediately and there are no errors. Going back and forth with the same project in 4.3-dev5 still reproduces the errors I described.

dandeliondino avatar May 02 '24 23:05 dandeliondino

Another thing I've noticed: static functions does not seem affected by broken auto-completes. That is to say, autocompletes that wouldn't work in the body of func will work in the body of static func and newly added static func will appear in other scripts whereas newly added func wouldn't

Update: this also breaks after adding enough things to the project. I get the feeling now that the autocomplete breaking is not a matter of quality but a matter of quantity. i.e.: when there are too many things to autocomplete, the autocompletion deteriorates, starting with "vulnerable" things like classes with cyclic references then eventually affecting even less vulnerable things like static functions

LO-0116 avatar May 28 '24 21:05 LO-0116

I feel like now I have a new version of this issue, which now spawns a new error.

In general, I feel like the issue has been mostly solved, the autocompletion seems to be way more resilient to code changes and in places where they would often break before it stays working just fine in 4.3beta1.

But every once in a while I noticed I would get errors like this spammed in the output:

USER ERROR: Parser bug: Mismatched external parser.

The error message doesn't tell me much, so I went digging around. The error was coming from this method in gdscript_analyzer.cpp:

void GDScriptAnalyzer::resolve_class_member(GDScriptParser::ClassNode *p_class, int p_index, const GDScriptParser::Node *p_source) {
		ERR_FAIL_COND_MSG(!parser_ref->get_parser()->has_class(p_class), R"(Parser bug: Mismatched external parser.)");

So I added these before it to catch some more information:

if (!parser_ref->get_parser()->has_class(p_class)) {
			if (p_class == nullptr) {
				push_error(vformat(R"(Parser did not have class NULLPTR script "%s" member "%s")",script_path,member.get_name()),p_source);
			} else {
				push_error(vformat(R"(Parser did not have class %s script "%s" member "%s")",p_class->get_global_name(), script_path,member.get_name()),p_source);
			}
		}

I think it would be good in general to include some more information in that error.

It took a while to happen in the editor again, but in the meantime it popped up in a build for me.

USER ERROR: Parser bug: Mismatched external parser.
   at: GDScriptAnalyzer::resolve_class_member (modules\gdscript\gdscript_analyzer.cpp:916)
USER SCRIPT ERROR: Parse Error: Parser did not have class Skill script "res://resources/skill.gd" member "found_button"
   at: GDScript::reload (res://resources/skill.gd:100)
USER SCRIPT ERROR: Parse Error: Parser did not have class Skill script "res://resources/skill.gd" member "relevant_button_ui"
   at: GDScript::reload (res://resources/skill.gd:103)

The code in question was this, which was in the Skill class, referencing a member in the Skill class itself, but iterating over an Array[Skill] that was in another class:

	for skill in p.current_skills:
		if skill.found_button:
				continue
		skill.button_desired = "skill_"+str(non_ult_non_mov_skill_count+1)
		skill.relevant_button_ui.texture = App.skill_glyphs[non_ult_non_mov_skill_count]
			non_ult_non_mov_skill_count+= 1
		skill.found_button=true

That error was present every time I built, that one was pretty reproducible. I tried building an MRP trying to do a similar thing but I couldn't get an MRP to reproduce the same error.

I noticed I had not typecasted skill in the for loop. Typecasting it instantly made the error go away on builds.

I'm not sure why the error on builds happened, but this script used to be fine when I was using a previous 4.3dev so I'm assuming some sort of regression.

Now back to the editor, took a while but the same USER ERROR: Parser bug: Mismatched external parser. came back.

image

In this case the script (the one in game_syncer) was valid, then I edited it became invalid, I fixed whatever error made it invalid but the fix is not taken into account in other scripts that reference it maybe? Restarting the editor makes the error go away and the parser recognizes the member just fine. This made me think that it's related to this issue with cyclic reference.

Should I start a new issue or is this still related to the same issue? So far it seems to happen way more rarely than the original issue, and I can't seem to make an MRP for it as it happens semi-randomly while working. I'm also running a custom build with custom modules so I can't 100% say it isn't related to my own custom code.

DanielSnd avatar Jun 07 '24 14:06 DanielSnd

@godotengine/bugsquad Can we please close this issue?

As per the comment of @dandeliondino the original issue seems to be not a problem anymore. The other problems/issues mentioned in this thread require different conditions than the original report and should be their own issue with an MRP. The way it currently is, it's just not possible to reproduce, work on or keep track of any of them.

@DanielSnd please create a new issue including an MRP if this issue is still present with 4.3.beta3. Same to all other users who encountered loosely related problems. (This issue is about cyclic dependencies and outdated members. Anything else belongs in an own report.)

HolonProduction avatar Jul 12 '24 15:07 HolonProduction

Closing the issue as the MRP behaves as it should on v4.3.beta.custom_build [26d1577f3] (latest master).

adamscott avatar Jul 12 '24 16:07 adamscott