fix: Add GDScript-specific dependency extraction to eliminate syntax errors
PROBLEM:
- 265+ "Syntax error in *.gd" warnings during analysis
- GDScript files were routed to Python AST parser (_extract_python_imports)
- Python AST failed because GDScript syntax differs (extends, signal, @export)
SOLUTION:
- Created dedicated _extract_gdscript_imports() method using regex
- Parses GDScript-specific patterns:
* const/var = preload("res://path")
* const/var = load("res://path")
* extends "res://path/to/base.gd"
* extends MyBaseClass (with built-in Godot class filtering)
- Converts res:// paths to relative paths
- Routes GDScript files to new extractor instead of Python AST
CHANGES:
- dependency_analyzer.py (line 114-116): Route GDScript to new extractor
- dependency_analyzer.py (line 201-318): Add _extract_gdscript_imports()
- Updated module docstring: 9 → 10 languages + Godot ecosystem
- Updated analyze_file() docstring with GDScript support
IMPACT:
- Eliminates all 265+ syntax error warnings
- Correctly extracts GDScript dependencies (preload/load/extends)
- Completes C3.10 Signal Flow Analysis integration
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -3,7 +3,7 @@
|
||||
Dependency Graph Analyzer (C2.6)
|
||||
|
||||
Analyzes import/require/include/use statements to build dependency graphs.
|
||||
Supports 9 programming languages with language-specific extraction.
|
||||
Supports 10 programming languages + Godot ecosystem with language-specific extraction.
|
||||
|
||||
Features:
|
||||
- Multi-language import extraction (Python AST, others regex-based)
|
||||
@@ -14,6 +14,8 @@ Features:
|
||||
|
||||
Supported Languages:
|
||||
- Python: import, from...import, relative imports (AST-based)
|
||||
- GDScript: preload(), load(), extends (regex-based, Godot game engine)
|
||||
- Godot Files: .tscn, .tres, .gdshader ext_resource parsing
|
||||
- JavaScript/TypeScript: ES6 import, CommonJS require (regex-based)
|
||||
- C/C++: #include directives (regex-based)
|
||||
- C#: using statements (regex, based on MS C# spec)
|
||||
@@ -101,7 +103,8 @@ class DependencyAnalyzer:
|
||||
Args:
|
||||
file_path: Path to source file
|
||||
content: File content
|
||||
language: Programming language (Python, JavaScript, TypeScript, C, C++, C#, Go, Rust, Java, Ruby, PHP)
|
||||
language: Programming language (Python, GDScript, GodotScene, GodotResource, GodotShader,
|
||||
JavaScript, TypeScript, C, C++, C#, Go, Rust, Java, Ruby, PHP)
|
||||
|
||||
Returns:
|
||||
List of DependencyInfo objects
|
||||
@@ -109,8 +112,8 @@ class DependencyAnalyzer:
|
||||
if language == "Python":
|
||||
deps = self._extract_python_imports(content, file_path)
|
||||
elif language == "GDScript":
|
||||
# GDScript is Python-like, uses similar import syntax
|
||||
deps = self._extract_python_imports(content, file_path)
|
||||
# GDScript uses preload/load, not Python imports
|
||||
deps = self._extract_gdscript_imports(content, file_path)
|
||||
elif language in ("GodotScene", "GodotResource", "GodotShader"):
|
||||
# Godot resource files use ext_resource references
|
||||
deps = self._extract_godot_resources(content, file_path)
|
||||
@@ -195,6 +198,125 @@ class DependencyAnalyzer:
|
||||
|
||||
return deps
|
||||
|
||||
def _extract_gdscript_imports(self, content: str, file_path: str) -> list[DependencyInfo]:
|
||||
"""
|
||||
Extract GDScript import/preload/load statements.
|
||||
|
||||
Handles:
|
||||
- const MyClass = preload("res://path/to/file.gd")
|
||||
- var scene = load("res://path/to/scene.tscn")
|
||||
- extends "res://path/to/base.gd"
|
||||
- extends MyBaseClass (implicit dependency)
|
||||
|
||||
Note: GDScript uses res:// paths which are converted to relative paths.
|
||||
"""
|
||||
deps = []
|
||||
|
||||
# Extract preload() calls: const/var NAME = preload("path")
|
||||
preload_pattern = r'(?:const|var)\s+\w+\s*=\s*preload\("(.+?)"\)'
|
||||
for match in re.finditer(preload_pattern, content):
|
||||
resource_path = match.group(1)
|
||||
line_num = content[: match.start()].count("\n") + 1
|
||||
|
||||
# Convert res:// paths to relative
|
||||
if resource_path.startswith("res://"):
|
||||
resource_path = resource_path[6:]
|
||||
|
||||
deps.append(
|
||||
DependencyInfo(
|
||||
source_file=file_path,
|
||||
imported_module=resource_path,
|
||||
import_type="preload",
|
||||
is_relative=True,
|
||||
line_number=line_num,
|
||||
)
|
||||
)
|
||||
|
||||
# Extract load() calls: var/const NAME = load("path")
|
||||
load_pattern = r'(?:const|var)\s+\w+\s*=\s*load\("(.+?)"\)'
|
||||
for match in re.finditer(load_pattern, content):
|
||||
resource_path = match.group(1)
|
||||
line_num = content[: match.start()].count("\n") + 1
|
||||
|
||||
if resource_path.startswith("res://"):
|
||||
resource_path = resource_path[6:]
|
||||
|
||||
deps.append(
|
||||
DependencyInfo(
|
||||
source_file=file_path,
|
||||
imported_module=resource_path,
|
||||
import_type="load",
|
||||
is_relative=True,
|
||||
line_number=line_num,
|
||||
)
|
||||
)
|
||||
|
||||
# Extract extends with string path: extends "res://path/to/base.gd"
|
||||
extends_path_pattern = r'extends\s+"(.+?)"'
|
||||
for match in re.finditer(extends_path_pattern, content):
|
||||
resource_path = match.group(1)
|
||||
line_num = content[: match.start()].count("\n") + 1
|
||||
|
||||
if resource_path.startswith("res://"):
|
||||
resource_path = resource_path[6:]
|
||||
|
||||
deps.append(
|
||||
DependencyInfo(
|
||||
source_file=file_path,
|
||||
imported_module=resource_path,
|
||||
import_type="extends",
|
||||
is_relative=True,
|
||||
line_number=line_num,
|
||||
)
|
||||
)
|
||||
|
||||
# Extract extends with class name: extends MyBaseClass
|
||||
# Note: This creates a symbolic dependency that may not resolve to a file
|
||||
extends_class_pattern = r'extends\s+([A-Z]\w+)'
|
||||
for match in re.finditer(extends_class_pattern, content):
|
||||
class_name = match.group(1)
|
||||
line_num = content[: match.start()].count("\n") + 1
|
||||
|
||||
# Skip built-in Godot classes (Node, Resource, etc.)
|
||||
if class_name not in (
|
||||
"Node",
|
||||
"Node2D",
|
||||
"Node3D",
|
||||
"Resource",
|
||||
"RefCounted",
|
||||
"Object",
|
||||
"Control",
|
||||
"Area2D",
|
||||
"Area3D",
|
||||
"CharacterBody2D",
|
||||
"CharacterBody3D",
|
||||
"RigidBody2D",
|
||||
"RigidBody3D",
|
||||
"StaticBody2D",
|
||||
"StaticBody3D",
|
||||
"Camera2D",
|
||||
"Camera3D",
|
||||
"Sprite2D",
|
||||
"Sprite3D",
|
||||
"Label",
|
||||
"Button",
|
||||
"Panel",
|
||||
"Container",
|
||||
"VBoxContainer",
|
||||
"HBoxContainer",
|
||||
):
|
||||
deps.append(
|
||||
DependencyInfo(
|
||||
source_file=file_path,
|
||||
imported_module=class_name,
|
||||
import_type="extends",
|
||||
is_relative=False,
|
||||
line_number=line_num,
|
||||
)
|
||||
)
|
||||
|
||||
return deps
|
||||
|
||||
def _extract_js_imports(self, content: str, file_path: str) -> list[DependencyInfo]:
|
||||
"""
|
||||
Extract JavaScript/TypeScript import statements.
|
||||
|
||||
Reference in New Issue
Block a user