fix(swift): extract method requirements declared in a protocol body - #3673
rajatnagda45 wants to merge 1 commit into
Conversation
tree-sitter-swift gives a protocol's body-less method requirement its own
node type, protocol_function_declaration, instead of reusing the
function_declaration used inside a class or struct. The Swift config's
function_types only listed function_declaration, so a protocol like
protocol Drawable {
func draw()
func area() -> Double
}
became an empty node - the method contract every conformer must implement
never entered the graph.
Add protocol_function_declaration to function_types and
function_boundary_types. Requirements now surface as .method() nodes hung
off the protocol, their signature type references (return/param types) are
captured, and they stay distinct from a conformer's implementation of the
same name. Stored property requirements are left alone, matching how
stored properties on a class are already handled.
Adds regression coverage for all three.
There was a problem hiding this comment.
Graphify reviewed this change.
Looks safe to merge — no coupling regressions and no blocking issues, checked against the code graph (not a self-assessment).
Graphify review — findings
Adds protocol_function_declaration to the Swift config's function and function-boundary types so body-less method requirements inside a protocol { ... } are extracted as method nodes with their signature references, instead of being dropped and leaving the protocol an empty node. Conformer methods stay distinct nodes from the protocol requirements, and the implements heritage edge is unaffected. Covered by a new regression test.
No blocking issues surfaced.
Analysis details — impact, health, verification
Impact & health
Graphify review
Impact — 2044 functions depend on the 256 functions this change touches.
Health — this change adds coupling hotspots:
- new:
extract()— 649 callers, 45 callees - new:
_rebuild_code()— 137 callers, 54 callees - new:
extract_js()— 85 callers, 4 callees - new:
extract_xaml()— 19 callers, 17 callees - new:
dispatch_command()— 2 callers, 125 callees - new:
_get_extractor()— 26 callers, 6 callees - new:
run_pipeline()— 8 callers, 13 callees - new:
collect_files()— 17 callers, 6 callees - …and 30 more — each is listed as a finding
Verification — 2044 functions in the blast radius were not formally verified this run (proofs are advisory here).
Gate & verification
graphify gate
PASS — objectively clean (no health regressions, tests not run — proofs not run this pass (advisory)). Grounded, not self-assessed.
Advisory (not blocking):
- verification_scope: 1869 function(s) in the blast radius were not formally verified this run
Test selection
Test selection
118 of 287 test file(s) selected (41%) via static blast radius.
tests/test_astro_extraction.py— impacttests/test_astro_import_ids.py— impacttests/test_build.py— impacttests/test_builtin_global_type_refs.py— impacttests/test_case_sensitive_resolution.py— impacttests/test_cjs_module_extension.py— impacttests/test_cpp_nested_and_cli.py— impacttests/test_cpp_objc_cross_file_calls.py— impacttests/test_cross_extension_reexport_self_cycle.py— impacttests/test_cross_language_call_resolution.py— impacttests/test_cross_repo_external_call_guards.py— impacttests/test_cross_repo_member_calls.py— impacttests/test_csharp_call_site_generic_args.py— impacttests/test_csharp_enum_members.py— impacttests/test_csharp_field_generic_args.py— impacttests/test_csharp_generic_callsites.py— impacttests/test_csharp_interface_dispatch.py— impacttests/test_csharp_member_calls.py— impacttests/test_csharp_member_nodes.py— impacttests/test_csharp_object_creation.py— impacttests/test_csharp_partial_classes.py— impacttests/test_csharp_type_resolution.py— impacttests/test_definition_file_portability.py— impacttests/test_detect.py— impacttests/test_dotnet.py— impacttests/test_duplicate_annotation_edges.py— impacttests/test_elixir_import_resolution.py— impacttests/test_extract.py— impacttests/test_extract_cache_location.py— impacttests/test_file_label_disambiguation.py— impacttests/test_file_node_id_spec.py— impacttests/test_forwarding_review_findings.py— impacttests/test_go_builtin_call_targets.py— impacttests/test_go_qualified_resolution.py— impacttests/test_import_extension_resolution.py— impacttests/test_import_self_loops.py— impacttests/test_imported_export_forwarding.py— impacttests/test_incremental.py— impacttests/test_indirect_call_arrow_single_param_shadow.py— impacttests/test_indirect_call_catch_binding_shadow.py— impacttests/test_indirect_call_external_import_shadow.py— impacttests/test_indirect_call_for_of_binding_shadow.py— impacttests/test_indirect_call_function_expression_shadow.py— impacttests/test_indirect_call_nested_closure_shadow.py— impacttests/test_indirect_dispatch.py— impacttests/test_indirect_dispatch_assign_return.py— impacttests/test_indirect_dispatch_getattr.py— impacttests/test_inferred_confidence_rubric.py— impacttests/test_inherited_field_receivers.py— impacttests/test_issue_3405_python_resolution.py— impact- … and 68 more
Selection is safe under the controlled-regression assumption; always-run tests + a periodic full run are the backstops. Advisory — it never changes the check verdict.
· 38 more finding(s) on lines outside this diff (see the check run).
|
Same empty-node symptom I hit on the Go side (#3672), but for Swift protocols — turns out |
Problem
tree-sitter-swift gives a protocol's body-less method requirement its own node type —
protocol_function_declaration— rather than reusing thefunction_declarationused inside a class or struct. The Swift config'sfunction_typesonly listedfunction_declaration, so those requirements were never recognised.The result: a protocol like
became an empty node. The method contract — the whole point of a protocol, and the API surface every conforming type must implement — never entered the graph. Protocol-oriented Swift codebases lost their most important abstraction layer.
Fix
Add
protocol_function_declarationto bothfunction_typesandfunction_boundary_typesin_SWIFT_CONFIG. The generic engine already knows how to turn a function node inside a class body into a.method()node hung off the owner and to walk its signature for type references; it just needed to be told this node type counts.Now:
.method()nodes under the protocol;drawand a conformer'sdrawstay distinct nodes, and theimplementsconformance edge is untouched.Stored-property requirements are deliberately left alone — that matches how stored properties on a class are already handled (they emit a type reference, not a member node), so behaviour stays consistent across declaration kinds. This mirrors the sibling receiver-method / interface-member handling in the other extractors.
Test
New
tests/test_swift_protocol_requirements.py— three cases, all failing before and passing after:.method()nodes under the protocol;referencesedge;All 67 Swift language tests pass.