Repository: godotengine/godot (language: cpp)

Issue:
"The default value is using "$" which won't return nodes..." warning despite using Unique Name ("%")
### Tested versions

- Reproducible in v4.4.beta.custom_build [eee39f004]

### System information

Godot v4.4.beta (eee39f004) - macOS Sequoia (15.3.0) - Multi-window, 2 monitors - OpenGL 3 (Compatibility) - AMD Radeon Pro 5500 XT OpenGL Engine - Intel(R) Core(TM) i7-10700K CPU @ 3.80GHz (16 threads)

### Issue description

The error message for attempting to access a unique-named node in a class body without the `@onready` annotation is slightly incorrect - it claims the user is using "$", when in reality the user is using "%".

![Image](https://github.com/user-attachments/assets/9ba9b6f5-1bf6-40f2-a5a8-1ab799010175)

 

### Steps to reproduce

In a new GDScript file, on the class level, type the following:
```gdscript
var something = %Hello
```

You will get an error like
```
The default value is using "$" which won't return nodes in the scene tree before "_ready()" is called. Use the "@onready" annotation to solve this. (Warning treated as error.)gdscript(-1)
```

### Minimal reproduction project (MRP)

N/A


Discussion / hints:

Fix patch:
diff --git a/modules/gdscript/gdscript_analyzer.cpp b/modules/gdscript/gdscript_analyzer.cpp
index 60109efc4d5b..99fab724e8da 100644
--- a/modules/gdscript/gdscript_analyzer.cpp
+++ b/modules/gdscript/gdscript_analyzer.cpp
@@ -1093,7 +1093,12 @@ void GDScriptAnalyzer::resolve_class_member(GDScriptParser::ClassNode *p_class,
 							}
 						}
 						if (is_get_node) {
-							parser->push_warning(member.variable, GDScriptWarning::GET_NODE_DEFAULT_WITHOUT_ONREADY, is_using_shorthand ? "$" : "get_node()");
+							String offending_syntax = "get_node()";
+							if (is_using_shorthand) {
+								GDScriptParser::GetNodeNode *get_node_node = static_cast<GDScriptParser::GetNodeNode *>(expr);
+								offending_syntax = get_node_node->use_dollar ? "$" : "%";
+							}
+							parser->push_warning(member.variable, GDScriptWarning::GET_NODE_DEFAULT_WITHOUT_ONREADY, offending_syntax);
 						}
 					}
 				}


Test patch:
diff --git a/modules/gdscript/tests/scripts/analyzer/warnings/get_node_without_onready.gd b/modules/gdscript/tests/scripts/analyzer/warnings/get_node_without_onready.gd
index c1776fe1b4d0..5feb1ad4e765 100644
--- a/modules/gdscript/tests/scripts/analyzer/warnings/get_node_without_onready.gd
+++ b/modules/gdscript/tests/scripts/analyzer/warnings/get_node_without_onready.gd
@@ -1,6 +1,6 @@
 extends Node
 
-var add_node = do_add_node() # Hack to have one node on init and not fail at runtime.
+var add_node = do_add_nodes() # Hack to have nodes on init and not fail at runtime.
 
 var shorthand = $Node
 var with_self = self.get_node(^"Node")
@@ -8,11 +8,23 @@ var without_self = get_node(^"Node")
 var with_cast = get_node(^"Node") as Node
 var shorthand_with_cast = $Node as Node
 
+var shorthand_unique = %UniqueNode
+var shorthand_in_dollar_unique = $"%UniqueNode"
+var without_self_unique = get_node(^"%UniqueNode")
+var shorthand_with_cast_unique = %UniqueNode as Node
+
 func test():
 	print("warn")
 
-func do_add_node():
+func do_add_nodes():
 	var node = Node.new()
 	node.name = "Node"
 	@warning_ignore("unsafe_call_argument")
 	add_child(node)
+
+	var unique_node = Node.new()
+	unique_node.name = "UniqueNode"
+	@warning_ignore("unsafe_call_argument")
+	add_child(unique_node)
+	unique_node.owner = self
+	unique_node.unique_name_in_owner = true
diff --git a/modules/gdscript/tests/scripts/analyzer/warnings/get_node_without_onready.out b/modules/gdscript/tests/scripts/analyzer/warnings/get_node_without_onready.out
index ad92088a1273..fcfa7b3c45cf 100644
--- a/modules/gdscript/tests/scripts/analyzer/warnings/get_node_without_onready.out
+++ b/modules/gdscript/tests/scripts/analyzer/warnings/get_node_without_onready.out
@@ -4,4 +4,8 @@ GDTEST_OK
 ~~ WARNING at line 7: (GET_NODE_DEFAULT_WITHOUT_ONREADY) The default value is using "get_node()" which won't return nodes in the scene tree before "_ready()" is called. Use the "@onready" annotation to solve this.
 ~~ WARNING at line 8: (GET_NODE_DEFAULT_WITHOUT_ONREADY) The default value is using "get_node()" which won't return nodes in the scene tree before "_ready()" is called. Use the "@onready" annotation to solve this.
 ~~ WARNING at line 9: (GET_NODE_DEFAULT_WITHOUT_ONREADY) The default value is using "$" which won't return nodes in the scene tree before "_ready()" is called. Use the "@onready" annotation to solve this.
+~~ WARNING at line 11: (GET_NODE_DEFAULT_WITHOUT_ONREADY) The default value is using "%" which won't return nodes in the scene tree before "_ready()" is called. Use the "@onready" annotation to solve this.
+~~ WARNING at line 12: (GET_NODE_DEFAULT_WITHOUT_ONREADY) The default value is using "$" which won't return nodes in the scene tree before "_ready()" is called. Use the "@onready" annotation to solve this.
+~~ WARNING at line 13: (GET_NODE_DEFAULT_WITHOUT_ONREADY) The default value is using "get_node()" which won't return nodes in the scene tree before "_ready()" is called. Use the "@onready" annotation to solve this.
+~~ WARNING at line 14: (GET_NODE_DEFAULT_WITHOUT_ONREADY) The default value is using "%" which won't return nodes in the scene tree before "_ready()" is called. Use the "@onready" annotation to solve this.
 warn