Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 28 additions & 5 deletions graphify/extract.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
_resolve_cross_file_csharp_imports,
_resolve_csharp_type_references,
)
from graphify.extractors.csharp_extract import _CSHARP_CONFIG as _SPLIT_CSHARP_CONFIG # noqa: F401
from graphify.extractors.dart import extract_dart # noqa: F401
from graphify.extractors.dm import extract_dm, extract_dmf, extract_dmi, extract_dmm # noqa: F401
from graphify.extractors.elixir import extract_elixir # noqa: F401
Expand Down Expand Up @@ -132,6 +133,12 @@
)

from graphify.extractors.engine import REFERENCE_CONTEXTS, _CSHARP_TYPE_PARAMETER_SCOPE_DECLARATIONS, _C_PRIMITIVE_TYPE_NODES, _JAVA_BUILTIN_TYPES, _JAVA_TYPE_PARAMETER_SCOPE_DECLARATIONS, _JS_FUNCTION_VALUE_TYPES, _JS_SCOPE_BOUNDARY, _PYTHON_ANNOTATION_NOISE, _PYTHON_TYPE_CONTAINERS, _RUBY_CLASS_FACTORIES, _c_collect_type_refs, _cpp_collect_type_refs, _cpp_declarator_name, _cpp_local_var_types, _csharp_attribute_names, _csharp_classify_base, _csharp_collect_type_refs, _csharp_extra_walk, _csharp_member_type_table, _csharp_namespace_id, _csharp_namespace_name, _csharp_pre_scan_interfaces, _csharp_type_parameters_in_scope, _dynamic_import_js, _extract_generic, _find_body, _find_require_call, _get_cpp_func_name, _java_annotation_names, _java_collect_type_refs, _java_extra_walk, _java_type_parameters_in_scope, _js_collect_pattern_idents, _js_dispatch_value_idents, _js_extra_walk, _js_local_bound_names, _js_member_assignment_target, _js_module_bound_names, _kotlin_collect_type_refs, _kotlin_function_return_type_node, _kotlin_property_type_node, _kotlin_user_type_name, _php_collect_type_refs, _php_method_return_type_node, _php_name_text, _python_collect_assignment_targets, _python_collect_param_refs, _python_collect_type_refs, _python_local_bound_names, _python_module_bound_names, _python_param_names, _read_csharp_type_name, _require_imports_js, _ruby_const_last_name, _ruby_extra_walk, _ruby_local_class_bindings, _ruby_new_class_name, _scala_collect_type_refs, _semantic_reference_edge, _source_location, _swift_classify_base, _swift_collect_type_refs, _swift_constructor_type, _swift_declaration_keyword, _swift_extra_walk, _swift_local_var_types, _swift_pre_scan, _swift_property_name, _swift_property_type_node, _swift_receiver_name, _swift_user_type_name, _ts_decorator_name, _ts_descendant_decorators, _ts_emit_decorator_edges, _ts_extra_walk, _ts_method_name, _ts_receiver_type_table # noqa: E402,F401
from graphify.extractors.csharp_extract import ( # noqa: E402,F401
_build_csharp_shadow_names,
_build_csharp_type_table,
_csharp_designator_names,
_csharp_names_from_variable_declaration,
)

from graphify.extractors.pascal import _PAS_BEGIN_END_TOKEN_RE, _PAS_CALL_RE, _PAS_END_SEMI_RE, _PAS_IMPL_HEADER_RE, _PAS_KEYWORDS, _PAS_METHOD_DECL_RE, _PAS_MODULE_RE, _PAS_TOKEN_RE, _PAS_TYPE_HEADER_RE, _PAS_USES_RE, _extract_pascal_regex, _pascal_find_body, _pascal_split_bases, _pascal_split_sections, _pascal_split_uses, _pascal_strip_comments, extract_pascal # noqa: E402,F401

Expand Down Expand Up @@ -758,6 +765,7 @@ def _get_c_func_name(node, source: bytes) -> str | None:
function_boundary_types=frozenset({"method_declaration"}),
import_handler=_import_csharp,
)
_CSHARP_CONFIG = _SPLIT_CSHARP_CONFIG

_KOTLIN_CONFIG = LanguageConfig(
ts_module="tree_sitter_kotlin",
Expand Down Expand Up @@ -1632,11 +1640,24 @@ def extract_ruby(path: Path) -> dict:
return _extract_generic(path, _RUBY_CONFIG)


def extract_csharp(path: Path) -> dict:
"""Extract C# type declarations, methods, namespaces, and usings from a .cs file."""
def _extract_csharp_file(path: Path) -> dict:
"""Extract raw C# type declarations, methods, namespaces, and usings from a .cs file."""
return _extract_generic(path, _CSHARP_CONFIG)


def extract_csharp(path: Path) -> dict:
"""Extract C# type declarations, methods, namespaces, usings, and member calls from a .cs file."""
result = _extract_csharp_file(path)
if not isinstance(result, dict) or result.get("error"):
return result
nodes = result.get("nodes")
edges = result.get("edges")
if not isinstance(nodes, list) or not isinstance(edges, list):
return result
run_language_resolvers([path], [result], nodes, edges)
return result


def extract_kotlin(path: Path) -> dict:
"""Extract classes, objects, functions, and imports from a .kt/.kts file."""
return _extract_generic(path, _KOTLIN_CONFIG)
Expand Down Expand Up @@ -2727,8 +2748,10 @@ def _key(label: str) -> str:
_resolve_objc_member_calls,
)
)
# C# receiver-typed member-call resolution (#1609): `field/param/local.Method()`
# bound to the receiver's declared type instead of a bare same-named match.
from graphify.extractors.csharp_resolve import _resolve_csharp_member_calls # noqa: E402,F401

# C# receiver-typed member-call resolution: use the split resolver with scoped
# receiver/type facts from graphify.extractors.csharp_extract.
register_language_resolver(
LanguageResolver("csharp_member_calls", frozenset({".cs"}), _resolve_csharp_member_calls)
)
Expand Down Expand Up @@ -3762,7 +3785,7 @@ def add_existing_edge(edge: dict) -> None:
".cuh": extract_cpp,
".metal": extract_cpp,
".rb": extract_ruby,
".cs": extract_csharp,
".cs": _extract_csharp_file,
".kt": extract_kotlin,
".kts": extract_kotlin,
".scala": extract_scala,
Expand Down
9 changes: 7 additions & 2 deletions graphify/extractors/MIGRATION.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ written so an AI agent can execute it in a single session.
| zig | yes |
| elixir | yes |
| razor | yes |
| csharp | partial — helpers + cross-file/member-call resolvers live in `extractors/csharp*.py`; config-driven entry point still uses shared `_extract_generic` |
| dart | yes |
| rust | yes |
| go | yes |
Expand All @@ -25,14 +26,18 @@ written so an AI agent can execute it in a single session.
| sln | yes |
| pascal_forms (dfm + lfm) | yes |
| json_config | yes |
| (config-driven core: python, js, java, c, cpp, csharp, kotlin, scala, php, lua, swift, groovy, vue, svelte, astro, xaml, groovy) | no — shared _extract_generic core, move as one batch |
| (config-driven core: python, js, java, c, cpp, kotlin, scala, php, lua, swift, groovy, vue, svelte, astro, xaml, groovy) | no — shared _extract_generic core, move as one batch |
| (other bespoke: julia, verilog, markdown, objc, csproj, slnx, lazarus_package, pascal) | no |

Note: config-driven extractors (python, js, java, c, cpp, ruby, csharp,
Note: config-driven extractors (python, js, java, c, cpp, ruby,
kotlin, scala, php, lua, swift, groovy) depend on the shared
`_extract_generic` core (~1,300 lines). Do NOT port them one-by-one; the core
must move first as its own coordinated batch. Pick a bespoke extractor.

C# is a deliberate middle path: its config-driven entry point still uses the
shared engine, while C#-specific helpers, scoped facts, and cross-file/member-call
resolvers live in dedicated modules.

## Invariants (non-negotiable)

1. **Verbatim moves only.** No renames, no docstring edits, no reformatting,
Expand Down
1 change: 1 addition & 0 deletions graphify/extractors/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
from pathlib import Path

from graphify.ids import make_id
from graphify.extractors.models import LanguageConfig # re-export for migrated helpers

# Language built-in globals that AST may classify as call targets when used as
# constructors or coercion functions (e.g. String(x), Number(x), Boolean(x)).
Expand Down
Loading
Loading