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
8 changes: 7 additions & 1 deletion internal/cbm/lsp/py_lsp.c
Original file line number Diff line number Diff line change
Expand Up @@ -4854,9 +4854,15 @@ static void py_process_class_in(PyLSPContext *ctx, TSNode class_node, const char
strcmp(ts_node_type(def), "function_definition") == 0 &&
!py_is_init_method(ctx, def)) {
py_process_function(ctx, def, ctx->enclosing_class_qn);
} else if (!ts_node_is_null(def) &&
strcmp(ts_node_type(def), "class_definition") == 0) {
py_process_class_in(ctx, def, ctx->enclosing_class_qn);
}
} else if (strcmp(ck, "class_definition") == 0) {
py_process_class(ctx, c);
/* A class nested in a class is qualified by its outer class
* (proj.m.Outer.Inner), the QN extraction gives it; under the
* module its methods' calls never joined their callers. */
py_process_class_in(ctx, c, ctx->enclosing_class_qn);
}
}
}
Expand Down
89 changes: 89 additions & 0 deletions tests/test_pipeline.c
Original file line number Diff line number Diff line change
Expand Up @@ -15599,6 +15599,93 @@ TEST(pipeline_python_decorated_nested_class_extracted_parallel) {
return pyn_run_decorated_nested(52);
}

/* A class nested in a class is walked by the Python LSP under its outer
* class (m.Outer.Inner): a typed-parameter call, a self call and a
* constructor call in its methods resolve, and so does a decorated nested
* class. Walked under the module (m.Inner) none of the LSP rows joined the
* method nodes, so the calls fell to name guesses or got no edge. */
static const char pyn_nested_class_py[] = "class Helper:\n"
" def go(self):\n"
" return 1\n"
"\n"
"def deco(cls):\n"
" return cls\n"
"\n"
"class Outer:\n"
" class Inner:\n"
" def run(self, h: Helper):\n"
" return h.go()\n"
"\n"
" def again(self):\n"
" return self.run(Helper())\n"
"\n"
" @deco\n"
" class Decorated:\n"
" def run(self, h: Helper):\n"
" return h.go()\n";

static int pyn_run_nested_class(int fillers) {
char tmp[256];
snprintf(tmp, sizeof(tmp), "/tmp/cbm_py_nestcls_XXXXXX");
if (!cbm_mkdtemp(tmp)) {
FAIL("tmpdir");
}
write_temp_file(tmp, "sn.py", pyn_nested_class_py);
for (int i = 0; i < fillers; i++) {
char name[64];
char body[128];
snprintf(name, sizeof(name), "filler%d.py", i);
snprintf(body, sizeof(body), "def filler%d():\n return %d\n", i, i);
write_temp_file(tmp, name, body);
}
char *old_workers = getenv("CBM_WORKERS");
char *saved = old_workers ? strdup(old_workers) : NULL;
if (fillers > 0) {
cbm_setenv("CBM_WORKERS", "4", 1);
}
char db_path[512];
snprintf(db_path, sizeof(db_path), "%s/nestcls.db", tmp);
cbm_pipeline_t *p = cbm_pipeline_new(tmp, db_path, CBM_MODE_FULL);
int rc = p ? cbm_pipeline_run(p) : -1;
cbm_store_t *s = rc == 0 ? cbm_store_open_path(db_path) : NULL;
const char *project = p ? cbm_pipeline_project_name(p) : NULL;
bool typed = s && pyn_edge_with(s, project, "sn.Outer.Inner.run", "sn.Helper.go", "CALLS",
"\"strategy\":\"lsp_");
bool self_call = s && pyn_edge_with(s, project, "sn.Outer.Inner.again", "sn.Outer.Inner.run",
"CALLS", "\"strategy\":\"lsp_");
bool ctor = s && pyn_edge_with(s, project, "sn.Outer.Inner.again", "sn.Helper", "CALLS",
"\"strategy\":\"lsp_");
bool decorated = s && pyn_edge_with(s, project, "sn.Outer.Decorated.run", "sn.Helper.go",
"CALLS", "\"strategy\":\"lsp_");
if (s) {
cbm_store_close(s);
}
if (p) {
cbm_pipeline_free(p);
}
if (saved) {
cbm_setenv("CBM_WORKERS", saved, 1);
free(saved);
} else {
cbm_unsetenv("CBM_WORKERS");
}
th_rmtree(tmp);
ASSERT_EQ(rc, 0);
ASSERT_TRUE(typed);
ASSERT_TRUE(self_call);
ASSERT_TRUE(ctor);
ASSERT_TRUE(decorated);
PASS();
}

TEST(pipeline_python_nested_class_methods_resolve) {
return pyn_run_nested_class(0);
}

TEST(pipeline_python_nested_class_methods_resolve_parallel) {
return pyn_run_nested_class(52);
}

TEST(pipeline_python_project) {
/* Port of TestPipelinePythonProject */
const char *files[] = {"main.py", "utils.py"};
Expand Down Expand Up @@ -25118,6 +25205,8 @@ SUITE(pipeline) {
RUN_TEST(pipeline_python_nested_helper_keeps_tests_parallel_issue1548);
RUN_TEST(pipeline_python_decorated_nested_class_extracted);
RUN_TEST(pipeline_python_decorated_nested_class_extracted_parallel);
RUN_TEST(pipeline_python_nested_class_methods_resolve);
RUN_TEST(pipeline_python_nested_class_methods_resolve_parallel);
RUN_TEST(pipeline_header_include_target_is_independent_of_registration_order);
RUN_TEST(pipeline_imports_multi_symbol_edges);
RUN_TEST(pipeline_go_cross_package_call);
Expand Down
Loading