Skip to content
Merged
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
13 changes: 10 additions & 3 deletions internal/cbm/extract_defs.c
Original file line number Diff line number Diff line change
Expand Up @@ -745,9 +745,14 @@ TSNode cbm_resolve_func_name(TSNode node, CBMLanguage lang) {
}

/* Swift and newer tree-sitter-kotlin: function_declaration has no `name`
* field; the function name is a `simple_identifier` child. */
* field; the function name is a `simple_identifier` child. A Swift
* protocol requirement (a bodyless `func` inside a protocol) is a
* separate node type with the same shape; it is named here as well as in
* resolve_method_name because swift_func_types now admits it, so it can
* reach the free-function path too and would otherwise land unnamed. */
if ((lang == CBM_LANG_SWIFT || lang == CBM_LANG_KOTLIN) &&
strcmp(kind, "function_declaration") == 0) {
(strcmp(kind, "function_declaration") == 0 ||
strcmp(kind, "protocol_function_declaration") == 0)) {
TSNode si = cbm_find_child_by_kind(node, "simple_identifier");
if (!ts_node_is_null(si)) {
return si;
Expand Down Expand Up @@ -4579,6 +4584,7 @@ static TSNode find_class_body(TSNode class_node, CBMLanguage lang) {
static const char *body_types[] = {"class_body",
"interface_body",
"enum_body",
"protocol_body",
"template_body",
"interface_type",
"struct_type",
Expand Down Expand Up @@ -4699,7 +4705,8 @@ static TSNode resolve_method_name(TSNode child, CBMLanguage lang) {
}

if ((lang == CBM_LANG_SWIFT || lang == CBM_LANG_KOTLIN) &&
strcmp(ck, "function_declaration") == 0) {
(strcmp(ck, "function_declaration") == 0 ||
strcmp(ck, "protocol_function_declaration") == 0)) {
return cbm_find_child_by_kind(child, "simple_identifier");
}

Expand Down
11 changes: 10 additions & 1 deletion internal/cbm/lang_specs.c
Original file line number Diff line number Diff line change
Expand Up @@ -547,7 +547,16 @@ static const char *objc_var_types[] = {"declaration", NULL};
static const char *objc_assign_types[] = {"assignment_expression", NULL};

// ==================== SWIFT ====================
static const char *swift_func_types[] = {"function_declaration", "macro_declaration", NULL};
// protocol_function_declaration: a protocol's method requirements. Needed here as
// well as in the name resolvers — extract_class_methods gates on this set, so
// without the entry the requirement is walked and then discarded.
static const char *swift_func_types[] = {"function_declaration", "protocol_function_declaration",
"macro_declaration", NULL};
// KNOWN GAP: struct_declaration and enum_declaration are not node types in the
// vendored tree-sitter-swift grammar — it models both as class_declaration — so
// these two entries never match anything, and a bare Swift `enum` is labeled
// Class rather than Enum. Left in place deliberately: they are the only marker
// of that modelling gap, and deleting them would hide it. Tracked separately.
static const char *swift_class_types[] = {"class_declaration", "protocol_declaration",
"struct_declaration", "enum_declaration", NULL};
static const char *swift_field_types[] = {"property_declaration", NULL};
Expand Down
16 changes: 16 additions & 0 deletions tests/test_extraction.c
Original file line number Diff line number Diff line change
Expand Up @@ -655,6 +655,21 @@ TEST(swift_class) {
PASS();
}

TEST(swift_protocol) {
/* A protocol requirement is a bodyless func inside a protocol body. Swift
* codebases are heavily protocol-driven, so the requirement is very often
* the declaration a reader is actually looking for — before this it was
* absent from the graph entirely. */
CBMFileResult *r = extract("protocol StudyRunning {\n func generate() -> String\n}\n",
CBM_LANG_SWIFT, "t", "StudyRunning.swift");
ASSERT_NOT_NULL(r);
ASSERT_FALSE(r->has_error);
ASSERT(has_def(r, "Interface", "StudyRunning"));
ASSERT(has_def(r, "Method", "generate"));
cbm_free_result(r);
PASS();
}

/* --- Kotlin --- */
TEST(kotlin_function) {
CBMFileResult *r = extract("fun greet(name: String): String = \"Hello $name\"\nfun main() { "
Expand Down Expand Up @@ -5451,6 +5466,7 @@ SUITE(extraction) {
RUN_TEST(csharp_class);
RUN_TEST(csharp_interface);
RUN_TEST(swift_class);
RUN_TEST(swift_protocol);
RUN_TEST(kotlin_function);
RUN_TEST(kotlin_class);
RUN_TEST(scala_function);
Expand Down
Loading