From 45b48bd15e94e2890f7db1d5d231ff6593a5a73f Mon Sep 17 00:00:00 2001 From: nicodes Date: Sun, 6 Sep 2026 11:28:10 -0600 Subject: [PATCH] fix: preserve Godot virtual callbacks during unused-code checks --- src/core/callback_test.go | 24 ++++++++++++++++++++++++ src/models/entity.go | 19 +++++-------------- src/models/entity_test.go | 16 ++++++++++++++++ 3 files changed, 45 insertions(+), 14 deletions(-) create mode 100644 src/core/callback_test.go create mode 100644 src/models/entity_test.go diff --git a/src/core/callback_test.go b/src/core/callback_test.go new file mode 100644 index 0000000..646603e --- /dev/null +++ b/src/core/callback_test.go @@ -0,0 +1,24 @@ +package core + +import ( + "github.com/aviorstudio/gdlint/src/models" + "os" + "path/filepath" + "testing" +) + +func TestUnusedAnalysisKeepsEngineCallbacks(t *testing.T) { + root := t.TempDir() + script := "@tool\nextends Control\n\nfunc _get_configuration_warnings() -> PackedStringArray:\n\treturn PackedStringArray()\n\nfunc _get_property_list() -> Array:\n\treturn []\n" + if err := os.WriteFile(filepath.Join(root, "example.gd"), []byte(script), 0600); err != nil { + t.Fatal(err) + } + config := &models.LintConfig{Errors: models.ErrorSection{UnusedFunctions: true}} + results, err := NewAnalyzer(root, config).Analyze() + if err != nil { + t.Fatal(err) + } + if len(results.UnusedFunctions) != 0 { + t.Fatalf("engine callbacks reported unused: %+v", results.UnusedFunctions) + } +} diff --git a/src/models/entity.go b/src/models/entity.go index 2d030d2..ac4c0b4 100644 --- a/src/models/entity.go +++ b/src/models/entity.go @@ -1,6 +1,9 @@ package models -import "fmt" +import ( + "fmt" + "github.com/aviorstudio/gdlint/src/patterns" +) type EntityType string @@ -49,19 +52,7 @@ func (e *Entity) IsProtected() bool { return false } - protectedPrefixes := []string{ - "_ready", "_init", "_enter_tree", "_exit_tree", - "_process", "_physics_process", "_input", "_unhandled_input", - "_draw", "_gui_input", "_notification", - } - - for _, prefix := range protectedPrefixes { - if e.Name == prefix { - return true - } - } - - if len(e.Name) >= 4 && e.Name[:4] == "_on_" { + if patterns.IsProtectedFunction(e.Name) { return true } diff --git a/src/models/entity_test.go b/src/models/entity_test.go new file mode 100644 index 0000000..491e9af --- /dev/null +++ b/src/models/entity_test.go @@ -0,0 +1,16 @@ +package models + +import "testing" + +func TestGodotVirtualCallbacksAreProtectedWithoutCalls(t *testing.T) { + for _, name := range []string{"_get_configuration_warnings", "_get_property_list", "_property_can_revert", "_property_get_revert", "_get", "_set", "_to_string", "_ready", "_on_pressed"} { + if !(&Entity{Type: EntityFunction, Name: name}).IsProtected() { + t.Errorf("engine callback %s must survive unused-code cleanup", name) + } + } + for _, entity := range []*Entity{{Type: EntityFunction, Name: "_get_configuration_warnings_extra"}, {Type: EntityFunction, Name: "_helper"}, {Type: EntitySignal, Name: "_get_configuration_warnings"}} { + if entity.IsProtected() { + t.Errorf("ordinary entity must still require a use: %+v", entity) + } + } +}