#
tokens: 27147/50000 2/367 files (page 14/18)
lines: on (toggle) GitHub
raw markdown copy reset
This is page 14 of 18. Use http://codebase.md/shashankss1205/codegraphcontext?lines=true&page={x} to view the full context.

# Directory Structure

```
├── .github
│   └── workflows
│       ├── e2e-tests.yml
│       ├── post_discord_invite.yml
│       ├── test.yml
│       └── update-contributors.yml
├── .gitignore
├── CONTRIBUTING.md
├── contributors.md
├── docs
│   ├── docs
│   │   ├── architecture.md
│   │   ├── cli.md
│   │   ├── contributing_languages.md
│   │   ├── contributing.md
│   │   ├── cookbook.md
│   │   ├── core.md
│   │   ├── future_work.md
│   │   ├── images
│   │   │   ├── 1.png
│   │   │   ├── 11.png
│   │   │   ├── 12.png
│   │   │   ├── 13.png
│   │   │   ├── 14.png
│   │   │   ├── 16.png
│   │   │   ├── 19.png
│   │   │   ├── 2.png
│   │   │   ├── 20.png
│   │   │   ├── 21.png
│   │   │   ├── 22.png
│   │   │   ├── 23.png
│   │   │   ├── 24.png
│   │   │   ├── 26.png
│   │   │   ├── 28.png
│   │   │   ├── 29.png
│   │   │   ├── 3.png
│   │   │   ├── 30.png
│   │   │   ├── 31.png
│   │   │   ├── 32.png
│   │   │   ├── 33.png
│   │   │   ├── 34.png
│   │   │   ├── 35.png
│   │   │   ├── 36.png
│   │   │   ├── 38.png
│   │   │   ├── 39.png
│   │   │   ├── 4.png
│   │   │   ├── 40.png
│   │   │   ├── 41.png
│   │   │   ├── 42.png
│   │   │   ├── 43.png
│   │   │   ├── 44.png
│   │   │   ├── 5.png
│   │   │   ├── 6.png
│   │   │   ├── 7.png
│   │   │   ├── 8.png
│   │   │   ├── 9.png
│   │   │   ├── Indexing.gif
│   │   │   ├── tool_images
│   │   │   │   ├── 1.png
│   │   │   │   ├── 2.png
│   │   │   │   └── 3.png
│   │   │   └── Usecase.gif
│   │   ├── index.md
│   │   ├── installation.md
│   │   ├── license.md
│   │   ├── server.md
│   │   ├── tools.md
│   │   ├── troubleshooting.md
│   │   └── use_cases.md
│   ├── mkdocs.yml
│   └── site
│       ├── 404.html
│       ├── architecture
│       │   └── index.html
│       ├── assets
│       │   ├── images
│       │   │   └── favicon.png
│       │   ├── javascripts
│       │   │   ├── bundle.f55a23d4.min.js
│       │   │   ├── bundle.f55a23d4.min.js.map
│       │   │   ├── lunr
│       │   │   │   ├── min
│       │   │   │   │   ├── lunr.ar.min.js
│       │   │   │   │   ├── lunr.da.min.js
│       │   │   │   │   ├── lunr.de.min.js
│       │   │   │   │   ├── lunr.du.min.js
│       │   │   │   │   ├── lunr.el.min.js
│       │   │   │   │   ├── lunr.es.min.js
│       │   │   │   │   ├── lunr.fi.min.js
│       │   │   │   │   ├── lunr.fr.min.js
│       │   │   │   │   ├── lunr.he.min.js
│       │   │   │   │   ├── lunr.hi.min.js
│       │   │   │   │   ├── lunr.hu.min.js
│       │   │   │   │   ├── lunr.hy.min.js
│       │   │   │   │   ├── lunr.it.min.js
│       │   │   │   │   ├── lunr.ja.min.js
│       │   │   │   │   ├── lunr.jp.min.js
│       │   │   │   │   ├── lunr.kn.min.js
│       │   │   │   │   ├── lunr.ko.min.js
│       │   │   │   │   ├── lunr.multi.min.js
│       │   │   │   │   ├── lunr.nl.min.js
│       │   │   │   │   ├── lunr.no.min.js
│       │   │   │   │   ├── lunr.pt.min.js
│       │   │   │   │   ├── lunr.ro.min.js
│       │   │   │   │   ├── lunr.ru.min.js
│       │   │   │   │   ├── lunr.sa.min.js
│       │   │   │   │   ├── lunr.stemmer.support.min.js
│       │   │   │   │   ├── lunr.sv.min.js
│       │   │   │   │   ├── lunr.ta.min.js
│       │   │   │   │   ├── lunr.te.min.js
│       │   │   │   │   ├── lunr.th.min.js
│       │   │   │   │   ├── lunr.tr.min.js
│       │   │   │   │   ├── lunr.vi.min.js
│       │   │   │   │   └── lunr.zh.min.js
│       │   │   │   ├── tinyseg.js
│       │   │   │   └── wordcut.js
│       │   │   └── workers
│       │   │       ├── search.973d3a69.min.js
│       │   │       └── search.973d3a69.min.js.map
│       │   └── stylesheets
│       │       ├── main.2a3383ac.min.css
│       │       ├── main.2a3383ac.min.css.map
│       │       ├── palette.06af60db.min.css
│       │       └── palette.06af60db.min.css.map
│       ├── cli
│       │   └── index.html
│       ├── contributing
│       │   └── index.html
│       ├── contributing_languages
│       │   └── index.html
│       ├── cookbook
│       │   └── index.html
│       ├── core
│       │   └── index.html
│       ├── future_work
│       │   └── index.html
│       ├── images
│       │   ├── 1.png
│       │   ├── 11.png
│       │   ├── 12.png
│       │   ├── 13.png
│       │   ├── 14.png
│       │   ├── 16.png
│       │   ├── 19.png
│       │   ├── 2.png
│       │   ├── 20.png
│       │   ├── 21.png
│       │   ├── 22.png
│       │   ├── 23.png
│       │   ├── 24.png
│       │   ├── 26.png
│       │   ├── 28.png
│       │   ├── 29.png
│       │   ├── 3.png
│       │   ├── 30.png
│       │   ├── 31.png
│       │   ├── 32.png
│       │   ├── 33.png
│       │   ├── 34.png
│       │   ├── 35.png
│       │   ├── 36.png
│       │   ├── 38.png
│       │   ├── 39.png
│       │   ├── 4.png
│       │   ├── 40.png
│       │   ├── 41.png
│       │   ├── 42.png
│       │   ├── 43.png
│       │   ├── 44.png
│       │   ├── 5.png
│       │   ├── 6.png
│       │   ├── 7.png
│       │   ├── 8.png
│       │   ├── 9.png
│       │   ├── Indexing.gif
│       │   ├── tool_images
│       │   │   ├── 1.png
│       │   │   ├── 2.png
│       │   │   └── 3.png
│       │   └── Usecase.gif
│       ├── index.html
│       ├── installation
│       │   └── index.html
│       ├── license
│       │   └── index.html
│       ├── search
│       │   └── search_index.json
│       ├── server
│       │   └── index.html
│       ├── sitemap.xml
│       ├── sitemap.xml.gz
│       ├── tools
│       │   └── index.html
│       ├── troubleshooting
│       │   └── index.html
│       └── use_cases
│           └── index.html
├── images
│   ├── 1.png
│   ├── 11.png
│   ├── 12.png
│   ├── 13.png
│   ├── 14.png
│   ├── 16.png
│   ├── 19.png
│   ├── 2.png
│   ├── 20.png
│   ├── 21.png
│   ├── 22.png
│   ├── 23.png
│   ├── 24.png
│   ├── 26.png
│   ├── 28.png
│   ├── 29.png
│   ├── 3.png
│   ├── 30.png
│   ├── 31.png
│   ├── 32.png
│   ├── 33.png
│   ├── 34.png
│   ├── 35.png
│   ├── 36.png
│   ├── 38.png
│   ├── 39.png
│   ├── 4.png
│   ├── 40.png
│   ├── 41.png
│   ├── 42.png
│   ├── 43.png
│   ├── 44.png
│   ├── 5.png
│   ├── 6.png
│   ├── 7.png
│   ├── 8.png
│   ├── 9.png
│   ├── Indexing.gif
│   ├── tool_images
│   │   ├── 1.png
│   │   ├── 2.png
│   │   └── 3.png
│   └── Usecase.gif
├── LICENSE
├── MANIFEST.in
├── organizer
│   ├── CONTRIBUTING_LANGUAGES.md
│   ├── cookbook.md
│   ├── docs.md
│   ├── language_specific_nodes.md
│   ├── Tools_Exploration.md
│   └── troubleshoot.md
├── package-lock.json
├── pyproject.toml
├── README.md
├── scripts
│   ├── generate_lang_contributors.py
│   └── post_install_fix.sh
├── SECURITY.md
├── src
│   └── codegraphcontext
│       ├── __init__.py
│       ├── __main__.py
│       ├── cli
│       │   ├── __init__.py
│       │   ├── cli_helpers.py
│       │   ├── main.py
│       │   ├── setup_macos.py
│       │   └── setup_wizard.py
│       ├── core
│       │   ├── __init__.py
│       │   ├── database.py
│       │   ├── jobs.py
│       │   └── watcher.py
│       ├── prompts.py
│       ├── server.py
│       ├── tools
│       │   ├── __init__.py
│       │   ├── advanced_language_query_tool.py
│       │   ├── code_finder.py
│       │   ├── graph_builder.py
│       │   ├── languages
│       │   │   ├── c.py
│       │   │   ├── cpp.py
│       │   │   ├── go.py
│       │   │   ├── java.py
│       │   │   ├── javascript.py
│       │   │   ├── python.py
│       │   │   ├── ruby.py
│       │   │   ├── rust.py
│       │   │   └── typescript.py
│       │   ├── package_resolver.py
│       │   ├── query_tool_languages
│       │   │   ├── c_toolkit.py
│       │   │   ├── cpp_toolkit.py
│       │   │   ├── go_toolkit.py
│       │   │   ├── java_toolkit.py
│       │   │   ├── javascript_toolkit.py
│       │   │   ├── python_toolkit.py
│       │   │   ├── ruby_toolkit.py
│       │   │   ├── rust_toolkit.py
│       │   │   └── typescript_toolkit.py
│       │   └── system.py
│       └── utils
│           └── debug_log.py
├── tests
│   ├── __init__.py
│   ├── conftest.py
│   ├── sample_project
│   │   ├── advanced_calls.py
│   │   ├── advanced_classes.py
│   │   ├── advanced_classes2.py
│   │   ├── advanced_functions.py
│   │   ├── advanced_imports.py
│   │   ├── async_features.py
│   │   ├── callbacks_decorators.py
│   │   ├── circular1.py
│   │   ├── circular2.py
│   │   ├── class_instantiation.py
│   │   ├── cli_and_dunder.py
│   │   ├── complex_classes.py
│   │   ├── comprehensions_generators.py
│   │   ├── context_managers.py
│   │   ├── control_flow.py
│   │   ├── datatypes.py
│   │   ├── dynamic_dispatch.py
│   │   ├── dynamic_imports.py
│   │   ├── edge_cases
│   │   │   ├── comments_only.py
│   │   │   ├── docstring_only.py
│   │   │   ├── empty.py
│   │   │   ├── hardcoded_secrets.py
│   │   │   ├── long_functions.py
│   │   │   └── syntax_error.py
│   │   ├── function_chains.py
│   │   ├── generators.py
│   │   ├── import_reexports.py
│   │   ├── mapping_calls.py
│   │   ├── module_a.py
│   │   ├── module_b.py
│   │   ├── module_c
│   │   │   ├── __init__.py
│   │   │   ├── submodule1.py
│   │   │   └── submodule2.py
│   │   ├── namespace_pkg
│   │   │   └── ns_module.py
│   │   ├── pattern_matching.py
│   │   └── typing_examples.py
│   ├── sample_project_c
│   │   ├── cgc_sample
│   │   ├── include
│   │   │   ├── config.h
│   │   │   ├── math
│   │   │   │   └── vec.h
│   │   │   ├── module.h
│   │   │   ├── platform.h
│   │   │   └── util.h
│   │   ├── Makefile
│   │   ├── README.md
│   │   └── src
│   │       ├── main.c
│   │       ├── math
│   │       │   └── vec.c
│   │       ├── module.c
│   │       └── util.c
│   ├── sample_project_cpp
│   │   ├── class_features.cpp
│   │   ├── classes.cpp
│   │   ├── control_flow.cpp
│   │   ├── edge_cases.cpp
│   │   ├── enum_struct_union.cpp
│   │   ├── exceptions.cpp
│   │   ├── file_io.cpp
│   │   ├── function_chain.cpp
│   │   ├── function_chain.h
│   │   ├── function_types.cpp
│   │   ├── main.cpp
│   │   ├── main.exe
│   │   ├── namespaces.cpp
│   │   ├── raii_example.cpp
│   │   ├── README.md
│   │   ├── sample_project.exe
│   │   ├── stl_usage.cpp
│   │   ├── templates.cpp
│   │   └── types_variable_assignments.cpp
│   ├── sample_project_go
│   │   ├── advanced_types.go
│   │   ├── basic_functions.go
│   │   ├── embedded_composition.go
│   │   ├── error_handling.go
│   │   ├── generics.go
│   │   ├── go.mod
│   │   ├── goroutines_channels.go
│   │   ├── interfaces.go
│   │   ├── packages_imports.go
│   │   ├── README.md
│   │   ├── structs_methods.go
│   │   └── util
│   │       └── helpers.go
│   ├── sample_project_java
│   │   ├── out
│   │   │   └── com
│   │   │       └── example
│   │   │           └── app
│   │   │               ├── annotations
│   │   │               │   └── Logged.class
│   │   │               ├── Main.class
│   │   │               ├── misc
│   │   │               │   ├── Outer.class
│   │   │               │   └── Outer$Inner.class
│   │   │               ├── model
│   │   │               │   ├── Role.class
│   │   │               │   └── User.class
│   │   │               ├── service
│   │   │               │   ├── AbstractGreeter.class
│   │   │               │   ├── GreetingService.class
│   │   │               │   └── impl
│   │   │               │       └── GreetingServiceImpl.class
│   │   │               └── util
│   │   │                   ├── CollectionUtils.class
│   │   │                   └── IOHelper.class
│   │   ├── README.md
│   │   ├── sources.txt
│   │   └── src
│   │       └── com
│   │           └── example
│   │               └── app
│   │                   ├── annotations
│   │                   │   └── Logged.java
│   │                   ├── Main.java
│   │                   ├── misc
│   │                   │   └── Outer.java
│   │                   ├── model
│   │                   │   ├── Role.java
│   │                   │   └── User.java
│   │                   ├── service
│   │                   │   ├── AbstractGreeter.java
│   │                   │   ├── GreetingService.java
│   │                   │   └── impl
│   │                   │       └── GreetingServiceImpl.java
│   │                   └── util
│   │                       ├── CollectionUtils.java
│   │                       └── IOHelper.java
│   ├── sample_project_javascript
│   │   ├── arrays.js
│   │   ├── asyncAwait.js
│   │   ├── classes.js
│   │   ├── dom.js
│   │   ├── errorHandling.js
│   │   ├── events.js
│   │   ├── exporter.js
│   │   ├── fetchAPI.js
│   │   ├── fixtures
│   │   │   └── js
│   │   │       └── accessors.js
│   │   ├── functions.js
│   │   ├── importer.js
│   │   ├── objects.js
│   │   ├── promises.js
│   │   ├── README.md
│   │   └── variables.js
│   ├── sample_project_misc
│   │   ├── index.html
│   │   ├── README.md
│   │   ├── styles.css
│   │   ├── tables.css
│   │   └── tables.html
│   ├── sample_project_php
│   │   ├── classes_objects.php
│   │   ├── database.php
│   │   ├── edgecases.php
│   │   ├── error_handling.php
│   │   ├── file_handling.php
│   │   ├── functions.php
│   │   ├── generators_iterators.php
│   │   ├── globals_superglobals.php
│   │   ├── Inheritance.php
│   │   ├── interface_traits.php
│   │   └── README.md
│   ├── sample_project_ruby
│   │   ├── class_example.rb
│   │   ├── enumerables.rb
│   │   ├── error_handling.rb
│   │   ├── file_io.rb
│   │   ├── inheritance_example.rb
│   │   ├── main.rb
│   │   ├── metaprogramming.rb
│   │   ├── mixins_example.rb
│   │   ├── module_example.rb
│   │   └── tests
│   │       ├── test_mixins.py
│   │       └── test_sample.rb
│   ├── sample_project_rust
│   │   ├── Cargo.toml
│   │   ├── README.md
│   │   └── src
│   │       ├── basic_functions.rs
│   │       ├── concurrency.rs
│   │       ├── error_handling.rs
│   │       ├── generics.rs
│   │       ├── iterators_closures.rs
│   │       ├── lib.rs
│   │       ├── lifetimes_references.rs
│   │       ├── modules.rs
│   │       ├── smart_pointers.rs
│   │       ├── structs_enums.rs
│   │       └── traits.rs
│   ├── sample_project_typescript
│   │   ├── package.json
│   │   ├── README.md
│   │   ├── src
│   │   │   ├── advanced-types.ts
│   │   │   ├── async-promises.ts
│   │   │   ├── classes-inheritance.ts
│   │   │   ├── decorators-metadata.ts
│   │   │   ├── error-validation.ts
│   │   │   ├── functions-generics.ts
│   │   │   ├── index.ts
│   │   │   ├── modules-namespaces.ts
│   │   │   ├── types-interfaces.ts
│   │   │   └── utilities-helpers.ts
│   │   └── tsconfig.json
│   ├── test_cpp_parser.py
│   ├── test_database_validation.py
│   ├── test_end_to_end.py
│   ├── test_graph_indexing_js.py
│   ├── test_graph_indexing.py
│   ├── test_tree_sitter
│   │   ├── __init__.py
│   │   ├── class_instantiation.py
│   │   ├── complex_classes.py
│   │   └── test_file.py
│   └── test_typescript_parser.py
└── website
    ├── .example.env
    ├── .gitignore
    ├── api
    │   └── pypi.ts
    ├── bun.lockb
    ├── components.json
    ├── eslint.config.js
    ├── index.html
    ├── package-lock.json
    ├── package.json
    ├── postcss.config.js
    ├── public
    │   ├── favicon.ico
    │   ├── placeholder.svg
    │   └── robots.txt
    ├── README.md
    ├── src
    │   ├── App.css
    │   ├── App.tsx
    │   ├── assets
    │   │   ├── function-calls.png
    │   │   ├── graph-total.png
    │   │   ├── hero-graph.jpg
    │   │   └── hierarchy.png
    │   ├── components
    │   │   ├── ComparisonTable.tsx
    │   │   ├── CookbookSection.tsx
    │   │   ├── DemoSection.tsx
    │   │   ├── ExamplesSection.tsx
    │   │   ├── FeaturesSection.tsx
    │   │   ├── Footer.tsx
    │   │   ├── HeroSection.tsx
    │   │   ├── InstallationSection.tsx
    │   │   ├── MoveToTop.tsx
    │   │   ├── ShowDownloads.tsx
    │   │   ├── ShowStarGraph.tsx
    │   │   ├── TestimonialSection.tsx
    │   │   ├── ThemeProvider.tsx
    │   │   ├── ThemeToggle.tsx
    │   │   └── ui
    │   │       ├── accordion.tsx
    │   │       ├── alert-dialog.tsx
    │   │       ├── alert.tsx
    │   │       ├── aspect-ratio.tsx
    │   │       ├── avatar.tsx
    │   │       ├── badge.tsx
    │   │       ├── breadcrumb.tsx
    │   │       ├── button.tsx
    │   │       ├── calendar.tsx
    │   │       ├── card.tsx
    │   │       ├── carousel.tsx
    │   │       ├── chart.tsx
    │   │       ├── checkbox.tsx
    │   │       ├── collapsible.tsx
    │   │       ├── command.tsx
    │   │       ├── context-menu.tsx
    │   │       ├── dialog.tsx
    │   │       ├── drawer.tsx
    │   │       ├── dropdown-menu.tsx
    │   │       ├── form.tsx
    │   │       ├── hover-card.tsx
    │   │       ├── input-otp.tsx
    │   │       ├── input.tsx
    │   │       ├── label.tsx
    │   │       ├── menubar.tsx
    │   │       ├── navigation-menu.tsx
    │   │       ├── orbiting-circles.tsx
    │   │       ├── pagination.tsx
    │   │       ├── popover.tsx
    │   │       ├── progress.tsx
    │   │       ├── radio-group.tsx
    │   │       ├── resizable.tsx
    │   │       ├── scroll-area.tsx
    │   │       ├── select.tsx
    │   │       ├── separator.tsx
    │   │       ├── sheet.tsx
    │   │       ├── sidebar.tsx
    │   │       ├── skeleton.tsx
    │   │       ├── slider.tsx
    │   │       ├── sonner.tsx
    │   │       ├── switch.tsx
    │   │       ├── table.tsx
    │   │       ├── tabs.tsx
    │   │       ├── textarea.tsx
    │   │       ├── toast.tsx
    │   │       ├── toaster.tsx
    │   │       ├── toggle-group.tsx
    │   │       ├── toggle.tsx
    │   │       ├── tooltip.tsx
    │   │       └── use-toast.ts
    │   ├── hooks
    │   │   ├── use-mobile.tsx
    │   │   └── use-toast.ts
    │   ├── index.css
    │   ├── lib
    │   │   └── utils.ts
    │   ├── main.tsx
    │   ├── pages
    │   │   ├── Index.tsx
    │   │   └── NotFound.tsx
    │   └── vite-env.d.ts
    ├── tailwind.config.ts
    ├── tsconfig.app.json
    ├── tsconfig.json
    ├── tsconfig.node.json
    ├── vercel.json
    └── vite.config.ts
```

# Files

--------------------------------------------------------------------------------
/src/codegraphcontext/tools/code_finder.py:
--------------------------------------------------------------------------------

```python
  1 | # src/codegraphcontext/tools/code_finder.py
  2 | import logging
  3 | import re
  4 | from typing import Any, Dict, List, Literal
  5 | from pathlib import Path
  6 | 
  7 | from ..core.database import DatabaseManager
  8 | 
  9 | logger = logging.getLogger(__name__)
 10 | 
 11 | class CodeFinder:
 12 |     """Module for finding relevant code snippets and analyzing relationships."""
 13 | 
 14 |     def __init__(self, db_manager: DatabaseManager):
 15 |         self.db_manager = db_manager
 16 |         self.driver = self.db_manager.get_driver()
 17 | 
 18 |     def format_query(self, find_by: Literal["Class", "Function"], fuzzy_search:bool) -> str:
 19 |         """Format the search query based on the search type and fuzzy search settings."""
 20 |         return f"""
 21 |             CALL db.index.fulltext.queryNodes("code_search_index", $search_term) YIELD node, score
 22 |                 WITH node, score
 23 |                 WHERE node:{find_by} {'AND node.name CONTAINS $search_term' if not fuzzy_search else ''}
 24 |                 RETURN node.name as name, node.file_path as file_path, node.line_number as line_number,
 25 |                     node.source as source, node.docstring as docstring, node.is_dependency as is_dependency
 26 |                 ORDER BY score DESC
 27 |                 LIMIT 20
 28 |             """
 29 | 
 30 |     def find_by_function_name(self, search_term: str, fuzzy_search: bool) -> List[Dict]:
 31 |         """Find functions by name matching using the full-text index."""
 32 |         with self.driver.session() as session:
 33 |             if fuzzy_search:
 34 |                 formatted_search_term = f"name:{search_term}"
 35 |                 result = session.run(self.format_query("Function", fuzzy_search), search_term=formatted_search_term)
 36 |             else:
 37 |                 result = session.run(self.format_query("Function", fuzzy_search), search_term=search_term)
 38 |             return [dict(record) for record in result]
 39 | 
 40 |     def find_by_class_name(self, search_term: str, fuzzy_search: bool) -> List[Dict]:
 41 |         """Find classes by name matching using the full-text index."""
 42 |         with self.driver.session() as session:
 43 |             if fuzzy_search:
 44 |                 formatted_search_term = f"name:{search_term}"
 45 |                 result = session.run(self.format_query("Class", fuzzy_search), search_term=formatted_search_term)
 46 |             else:
 47 |                 result = session.run(self.format_query("Class", fuzzy_search), search_term=search_term)
 48 |             return [dict(record) for record in result]
 49 | 
 50 |     def find_by_variable_name(self, search_term: str) -> List[Dict]:
 51 |         """Find variables by name matching"""
 52 |         with self.driver.session() as session:
 53 |             result = session.run("""
 54 |                 MATCH (v:Variable)
 55 |                 WHERE v.name CONTAINS $search_term OR v.name =~ $regex_pattern
 56 |                 RETURN v.name as name, v.file_path as file_path, v.line_number as line_number,
 57 |                        v.value as value, v.context as context, v.is_dependency as is_dependency
 58 |                 ORDER BY v.is_dependency ASC, v.name
 59 |                 LIMIT 20
 60 |             """, search_term=search_term, regex_pattern=f"(?i).*{re.escape(search_term)}.*")
 61 |             
 62 |             return [dict(record) for record in result]
 63 | 
 64 |     def find_by_content(self, search_term: str) -> List[Dict]:
 65 |         """Find code by content matching in source or docstrings using the full-text index."""
 66 |         with self.driver.session() as session:
 67 |             result = session.run("""
 68 |                 CALL db.index.fulltext.queryNodes("code_search_index", $search_term) YIELD node, score
 69 |                 WITH node, score
 70 |                 WHERE node:Function OR node:Class OR node:Variable
 71 |                 RETURN
 72 |                     CASE 
 73 |                         WHEN node:Function THEN 'function'
 74 |                         WHEN node:Class THEN 'class'
 75 |                         ELSE 'variable' 
 76 |                     END as type,
 77 |                     node.name as name, node.file_path as file_path,
 78 |                     node.line_number as line_number, node.source as source,
 79 |                     node.docstring as docstring, node.is_dependency as is_dependency
 80 |                 ORDER BY score DESC
 81 |                 LIMIT 20
 82 |             """, search_term=search_term)
 83 |             return [dict(record) for record in result]
 84 | 
 85 |     def find_related_code(self, user_query: str, fuzzy_search: bool, edit_distance: int) -> Dict[str, Any]:
 86 |         """Find code related to a query using multiple search strategies"""
 87 |         if fuzzy_search:
 88 |             user_query_normalized = " ".join(map(lambda x: f"{x}~{edit_distance}", user_query.split(" ")))
 89 |         else:
 90 |             user_query_normalized = user_query
 91 | 
 92 |         results = {
 93 |             "query": user_query_normalized,
 94 |             "functions_by_name": self.find_by_function_name(user_query_normalized, fuzzy_search),
 95 |             "classes_by_name": self.find_by_class_name(user_query_normalized, fuzzy_search),
 96 |             "variables_by_name": self.find_by_variable_name(user_query),  # no fuzzy for variables as they are not using full-text index
 97 |             "content_matches": self.find_by_content(user_query_normalized)
 98 |         }
 99 |         
100 |         all_results = []
101 |         
102 |         for func in results["functions_by_name"]:
103 |             func["search_type"] = "function_name"
104 |             func["relevance_score"] = 0.9 if not func["is_dependency"] else 0.7
105 |             all_results.append(func)
106 |         
107 |         for cls in results["classes_by_name"]:
108 |             cls["search_type"] = "class_name"
109 |             cls["relevance_score"] = 0.8 if not cls["is_dependency"] else 0.6
110 |             all_results.append(cls)
111 | 
112 |         for var in results["variables_by_name"]:
113 |             var["search_type"] = "variable_name"
114 |             var["relevance_score"] = 0.7 if not var["is_dependency"] else 0.5
115 |             all_results.append(var)
116 |         
117 |         for content in results["content_matches"]:
118 |             content["search_type"] = "content"
119 |             content["relevance_score"] = 0.6 if not content["is_dependency"] else 0.4
120 |             all_results.append(content)
121 |         
122 |         all_results.sort(key=lambda x: x["relevance_score"], reverse=True)
123 |         
124 |         results["ranked_results"] = all_results[:15]
125 |         results["total_matches"] = len(all_results)
126 |         
127 |         return results
128 |     
129 |     def find_functions_by_argument(self, argument_name: str, file_path: str = None) -> List[Dict]:
130 |         """Find functions that take a specific argument name."""
131 |         with self.driver.session() as session:
132 |             if file_path:
133 |                 query = """
134 |                     MATCH (f:Function)-[:HAS_PARAMETER]->(p:Parameter)
135 |                     WHERE p.name = $argument_name AND f.file_path = $file_path
136 |                     RETURN f.name AS function_name, f.file_path AS file_path, f.line_number AS line_number,
137 |                            f.docstring AS docstring, f.is_dependency AS is_dependency
138 |                     ORDER BY f.is_dependency ASC, f.file_path, f.line_number
139 |                     LIMIT 20
140 |                 """
141 |                 result = session.run(query, argument_name=argument_name, file_path=file_path)
142 |             else:
143 |                 query = """
144 |                     MATCH (f:Function)-[:HAS_PARAMETER]->(p:Parameter)
145 |                     WHERE p.name = $argument_name
146 |                     RETURN f.name AS function_name, f.file_path AS file_path, f.line_number AS line_number,
147 |                            f.docstring AS docstring, f.is_dependency AS is_dependency
148 |                     ORDER BY f.is_dependency ASC, f.file_path, f.line_number
149 |                     LIMIT 20
150 |                 """
151 |                 result = session.run(query, argument_name=argument_name)
152 |             return [dict(record) for record in result]
153 | 
154 |     def find_functions_by_decorator(self, decorator_name: str, file_path: str = None) -> List[Dict]:
155 |         """Find functions that have a specific decorator applied to them."""
156 |         with self.driver.session() as session:
157 |             if file_path:
158 |                 query = """
159 |                     MATCH (f:Function)
160 |                     WHERE f.file_path = $file_path AND $decorator_name IN f.decorators
161 |                     RETURN f.name AS function_name, f.file_path AS file_path, f.line_number AS line_number,
162 |                            f.docstring AS docstring, f.is_dependency AS is_dependency, f.decorators AS decorators
163 |                     ORDER BY f.is_dependency ASC, f.file_path, f.line_number
164 |                     LIMIT 20
165 |                 """
166 |                 result = session.run(query, decorator_name=decorator_name, file_path=file_path)
167 |             else:
168 |                 query = """
169 |                     MATCH (f:Function)
170 |                     WHERE $decorator_name IN f.decorators
171 |                     RETURN f.name AS function_name, f.file_path AS file_path, f.line_number AS line_number,
172 |                            f.docstring AS docstring, f.is_dependency AS is_dependency, f.decorators AS decorators
173 |                     ORDER BY f.is_dependency ASC, f.file_path, f.line_number
174 |                     LIMIT 20
175 |                 """
176 |                 result = session.run(query, decorator_name=decorator_name)
177 |             return [dict(record) for record in result]
178 |     
179 |     def who_calls_function(self, function_name: str, file_path: str = None) -> List[Dict]:
180 |         """Find what functions call a specific function using CALLS relationships with improved matching"""
181 |         with self.driver.session() as session:
182 |             if file_path:
183 |                 result = session.run("""
184 |                     MATCH (caller:Function)-[call:CALLS]->(target:Function {name: $function_name, file_path: $file_path})
185 |                     OPTIONAL MATCH (caller_file:File)-[:CONTAINS]->(caller)
186 |                     RETURN DISTINCT
187 |                         caller.name as caller_function,
188 |                         caller.file_path as caller_file_path,
189 |                         caller.line_number as caller_line_number,
190 |                         caller.docstring as caller_docstring,
191 |                         caller.is_dependency as caller_is_dependency,
192 |                         call.line_number as call_line_number,
193 |                         call.args as call_args,
194 |                         call.full_call_name as full_call_name,
195 |                         call.call_type as call_type,
196 |                         target.file_path as target_file_path
197 |                     ORDER BY caller.is_dependency ASC, caller.file_path, caller.line_number
198 |                     LIMIT 20
199 |                 """, function_name=function_name, file_path=file_path)
200 |                 
201 |                 results = [dict(record) for record in result]
202 |                 if not results:
203 |                     result = session.run("""
204 |                         MATCH (target:Function {name: $function_name})
205 |                         MATCH (caller:Function)-[call:CALLS]->(target)
206 |                         OPTIONAL MATCH (caller_file:File)-[:CONTAINS]->(caller)
207 |                         RETURN DISTINCT
208 |                             caller.name as caller_function,
209 |                             caller.file_path as caller_file_path,
210 |                             caller.line_number as caller_line_number,
211 |                             caller.docstring as caller_docstring,
212 |                             caller.is_dependency as caller_is_dependency,
213 |                             call.line_number as call_line_number,
214 |                             call.args as call_args,
215 |                             call.full_call_name as full_call_name,
216 |                             call.call_type as call_type,
217 |                             target.file_path as target_file_path
218 |                         ORDER BY caller.is_dependency ASC, caller.file_path, caller.line_number
219 |                         LIMIT 20
220 |                     """, function_name=function_name)
221 |                     results = [dict(record) for record in result]
222 |             else:
223 |                 result = session.run("""
224 |                     MATCH (target:Function {name: $function_name})
225 |                     MATCH (caller:Function)-[call:CALLS]->(target)
226 |                     OPTIONAL MATCH (caller_file:File)-[:CONTAINS]->(caller)
227 |                     RETURN DISTINCT
228 |                         caller.name as caller_function,
229 |                         caller.file_path as caller_file_path,
230 |                         caller.line_number as caller_line_number,
231 |                         caller.docstring as caller_docstring,
232 |                         caller.is_dependency as caller_is_dependency,
233 |                         call.line_number as call_line_number,
234 |                         call.args as call_args,
235 |                         call.full_call_name as full_call_name,
236 |                         call.call_type as call_type,
237 |                         target.file_path as target_file_path
238 |                     ORDER BY caller.is_dependency ASC, caller.file_path, caller.line_number
239 |                     LIMIT 20
240 |                 """, function_name=function_name)
241 |                 results = [dict(record) for record in result]
242 |             
243 |             return results
244 |     
245 |     def what_does_function_call(self, function_name: str, file_path: str = None) -> List[Dict]:
246 |         """Find what functions a specific function calls using CALLS relationships"""
247 |         with self.driver.session() as session:
248 |             if file_path:
249 |                 # Convert file_path to absolute path
250 |                 absolute_file_path = str(Path(file_path).resolve())
251 |                 result = session.run("""
252 |                     MATCH (caller:Function {name: $function_name, file_path: $absolute_file_path})
253 |                     MATCH (caller)-[call:CALLS]->(called:Function)
254 |                     OPTIONAL MATCH (called_file:File)-[:CONTAINS]->(called)
255 |                     RETURN DISTINCT
256 |                         called.name as called_function,
257 |                         called.file_path as called_file_path,
258 |                         called.line_number as called_line_number,
259 |                         called.docstring as called_docstring,
260 |                         called.is_dependency as called_is_dependency,
261 |                         call.line_number as call_line_number,
262 |                         call.args as call_args,
263 |                         call.full_call_name as full_call_name,
264 |                         call.call_type as call_type
265 |                     ORDER BY called.is_dependency ASC, called.name
266 |                     LIMIT 20
267 |                 """, function_name=function_name, absolute_file_path=absolute_file_path)
268 |             else:
269 |                 result = session.run("""
270 |                     MATCH (caller:Function {name: $function_name})
271 |                     MATCH (caller)-[call:CALLS]->(called:Function)
272 |                     OPTIONAL MATCH (called_file:File)-[:CONTAINS]->(called)
273 |                     RETURN DISTINCT
274 |                         called.name as called_function,
275 |                         called.file_path as called_file_path,
276 |                         called.line_number as called_line_number,
277 |                         called.docstring as called_docstring,
278 |                         called.is_dependency as called_is_dependency,
279 |                         call.line_number as call_line_number,
280 |                         call.args as call_args,
281 |                         call.full_call_name as full_call_name,
282 |                         call.call_type as call_type
283 |                     ORDER BY called.is_dependency ASC, called.name
284 |                     LIMIT 20
285 |                 """, function_name=function_name)
286 |             
287 |             return [dict(record) for record in result]
288 |     
289 |     def who_imports_module(self, module_name: str) -> List[Dict]:
290 |         """Find what files import a specific module using IMPORTS relationships"""
291 |         with self.driver.session() as session:
292 |             result = session.run("""
293 |                 MATCH (file:File)-[imp:IMPORTS]->(module:Module)
294 |                 WHERE module.name = $module_name OR module.full_import_name CONTAINS $module_name
295 |                 OPTIONAL MATCH (repo:Repository)-[:CONTAINS]->(file)
296 |                 WITH file, repo, COLLECT({
297 |                     imported_module: module.name,
298 |                     import_alias: module.alias,
299 |                     full_import_name: module.full_import_name
300 |                 }) AS imports
301 |                 RETURN
302 |                     file.name AS file_name,
303 |                     file.path AS file_path,
304 |                     file.relative_path AS file_relative_path,
305 |                     file.is_dependency AS file_is_dependency,
306 |                     repo.name AS repository_name,
307 |                     imports
308 |                 ORDER BY file.is_dependency ASC, file.path
309 |                 LIMIT 20
310 |             """, module_name=module_name)
311 |             
312 |             return [dict(record) for record in result]
313 |     
314 |     def who_modifies_variable(self, variable_name: str) -> List[Dict]:
315 |         """Find what functions contain or modify a specific variable"""
316 |         with self.driver.session() as session:
317 |             result = session.run("""
318 |                 MATCH (var:Variable {name: $variable_name})
319 |                 MATCH (container)-[:CONTAINS]->(var)
320 |                 WHERE container:Function OR container:Class OR container:File
321 |                 OPTIONAL MATCH (file:File)-[:CONTAINS]->(container)
322 |                 RETURN DISTINCT
323 |                     CASE 
324 |                         WHEN container:Function THEN container.name
325 |                         WHEN container:Class THEN container.name
326 |                         ELSE 'file_level'
327 |                     END as container_name,
328 |                     CASE 
329 |                         WHEN container:Function THEN 'function'
330 |                         WHEN container:Class THEN 'class'
331 |                         ELSE 'file'
332 |                     END as container_type,
333 |                     COALESCE(container.file_path, file.path) as file_path,
334 |                     container.line_number as container_line_number,
335 |                     var.line_number as variable_line_number,
336 |                     var.value as variable_value,
337 |                     var.context as variable_context,
338 |                     COALESCE(container.is_dependency, file.is_dependency, false) as is_dependency
339 |                 ORDER BY is_dependency ASC, file_path, variable_line_number
340 |                 LIMIT 20
341 |             """, variable_name=variable_name)
342 |             
343 |             return [dict(record) for record in result]
344 |     
345 |     def find_class_hierarchy(self, class_name: str, file_path: str = None) -> Dict[str, Any]:
346 |         """Find class inheritance relationships using INHERITS relationships"""
347 |         with self.driver.session() as session:
348 |             if file_path:
349 |                 match_clause = "MATCH (child:Class {name: $class_name, file_path: $file_path})"
350 |             else:
351 |                 match_clause = "MATCH (child:Class {name: $class_name})"
352 | 
353 |             parents_query = f"""
354 |                 {match_clause}
355 |                 MATCH (child)-[:INHERITS]->(parent:Class)
356 |                 OPTIONAL MATCH (parent_file:File)-[:CONTAINS]->(parent)
357 |                 RETURN DISTINCT
358 |                     parent.name as parent_class,
359 |                     parent.file_path as parent_file_path,
360 |                     parent.line_number as parent_line_number,
361 |                     parent.docstring as parent_docstring,
362 |                     parent.is_dependency as parent_is_dependency
363 |                 ORDER BY parent.is_dependency ASC, parent.name
364 |             """
365 |             parents_result = session.run(parents_query, class_name=class_name, file_path=file_path)
366 |             
367 |             children_query = f"""
368 |                 {match_clause}
369 |                 MATCH (grandchild:Class)-[:INHERITS]->(child)
370 |                 OPTIONAL MATCH (child_file:File)-[:CONTAINS]->(grandchild)
371 |                 RETURN DISTINCT
372 |                     grandchild.name as child_class,
373 |                     grandchild.file_path as child_file_path,
374 |                     grandchild.line_number as child_line_number,
375 |                     grandchild.docstring as child_docstring,
376 |                     grandchild.is_dependency as child_is_dependency
377 |                 ORDER BY grandchild.is_dependency ASC, grandchild.name
378 |             """
379 |             children_result = session.run(children_query, class_name=class_name, file_path=file_path)
380 |             
381 |             methods_query = f"""
382 |                 {match_clause}
383 |                 MATCH (child)-[:CONTAINS]->(method:Function)
384 |                 RETURN DISTINCT
385 |                     method.name as method_name,
386 |                     method.file_path as method_file_path,
387 |                     method.line_number as method_line_number,
388 |                     method.args as method_args,
389 |                     method.docstring as method_docstring,
390 |                     method.is_dependency as method_is_dependency
391 |                 ORDER BY method.is_dependency ASC, method.line_number
392 |             """
393 |             methods_result = session.run(methods_query, class_name=class_name, file_path=file_path)
394 |             
395 |             return {
396 |                 "class_name": class_name,
397 |                 "parent_classes": [dict(record) for record in parents_result],
398 |                 "child_classes": [dict(record) for record in children_result],
399 |                 "methods": [dict(record) for record in methods_result]
400 |             }
401 |     
402 |     def find_function_overrides(self, function_name: str) -> List[Dict]:
403 |         """Find all implementations of a function across different classes"""
404 |         with self.driver.session() as session:
405 |             result = session.run("""
406 |                 MATCH (class:Class)-[:CONTAINS]->(func:Function {name: $function_name})
407 |                 OPTIONAL MATCH (file:File)-[:CONTAINS]->(class)
408 |                 RETURN DISTINCT
409 |                     class.name as class_name,
410 |                     class.file_path as class_file_path,
411 |                     func.name as function_name,
412 |                     func.line_number as function_line_number,
413 |                     func.args as function_args,
414 |                     func.docstring as function_docstring,
415 |                     func.is_dependency as is_dependency,
416 |                     file.name as file_name
417 |                 ORDER BY func.is_dependency ASC, class.name
418 |                 LIMIT 20
419 |             """, function_name=function_name)
420 |             
421 |             return [dict(record) for record in result]
422 |     
423 |     def find_dead_code(self, exclude_decorated_with: List[str] = None) -> Dict[str, Any]:
424 |         """Find potentially unused functions (not called by other functions in the project), optionally excluding those with specific decorators."""
425 |         if exclude_decorated_with is None:
426 |             exclude_decorated_with = []
427 | 
428 |         with self.driver.session() as session:
429 |             result = session.run("""
430 |                 MATCH (func:Function)
431 |                 WHERE func.is_dependency = false
432 |                   AND NOT func.name IN ['main', '__init__', '__main__', 'setup', 'run', '__new__', '__del__']
433 |                   AND NOT func.name STARTS WITH '_test'
434 |                   AND NOT func.name STARTS WITH 'test_'
435 |                   AND ALL(decorator_name IN $exclude_decorated_with WHERE NOT decorator_name IN func.decorators)
436 |                 WITH func
437 |                 OPTIONAL MATCH (caller:Function)-[:CALLS]->(func)
438 |                 WHERE caller.is_dependency = false
439 |                 WITH func, count(caller) as caller_count
440 |                 WHERE caller_count = 0
441 |                 OPTIONAL MATCH (file:File)-[:CONTAINS]->(func)
442 |                 RETURN
443 |                     func.name as function_name,
444 |                     func.file_path as file_path,
445 |                     func.line_number as line_number,
446 |                     func.docstring as docstring,
447 |                     func.context as context,
448 |                     file.name as file_name
449 |                 ORDER BY func.file_path, func.line_number
450 |                 LIMIT 50
451 |             """, exclude_decorated_with=exclude_decorated_with)
452 |             
453 |             return {
454 |                 "potentially_unused_functions": [dict(record) for record in result],
455 |                 "note": "These functions might be unused, but could be entry points, callbacks, or called dynamically"
456 |             }
457 |     
458 |     def find_all_callers(self, function_name: str, file_path: str = None) -> List[Dict]:
459 |         """Find all direct and indirect callers of a specific function."""
460 |         with self.driver.session() as session:
461 |             if file_path:
462 |                 # Find functions within the specified file_path that call the target function
463 |                 query = """
464 |                     MATCH (f:Function)-[:CALLS*]->(target:Function {name: $function_name, file_path: $file_path})
465 |                     RETURN DISTINCT f.name AS caller_name, f.file_path AS caller_file_path, f.line_number AS caller_line_number, f.is_dependency AS caller_is_dependency
466 |                     ORDER BY f.is_dependency ASC, f.file_path, f.line_number
467 |                     LIMIT 50
468 |                 """
469 |                 result = session.run(query, function_name=function_name, file_path=file_path)
470 |             else:
471 |                 # If no file_path (context) is provided, find all callers of the function by name
472 |                 query = """
473 |                     MATCH (f:Function)-[:CALLS*]->(target:Function {name: $function_name})
474 |                     RETURN DISTINCT f.name AS caller_name, f.file_path AS caller_file_path, f.line_number AS caller_line_number, f.is_dependency AS caller_is_dependency
475 |                     ORDER BY f.is_dependency ASC, f.file_path, f.line_number
476 |                     LIMIT 50
477 |                 """
478 |                 result = session.run(query, function_name=function_name)
479 |             return [dict(record) for record in result]
480 | 
481 |     def find_all_callees(self, function_name: str, file_path: str = None) -> List[Dict]:
482 |         """Find all direct and indirect callees of a specific function."""
483 |         with self.driver.session() as session:
484 |             if file_path:
485 |                 query = """
486 |                     MATCH (caller:Function {name: $function_name, file_path: $file_path})
487 |                     MATCH (caller)-[:CALLS*]->(f:Function)
488 |                     RETURN DISTINCT f.name AS callee_name, f.file_path AS callee_file_path, f.line_number AS callee_line_number, f.is_dependency AS callee_is_dependency
489 |                     ORDER BY f.is_dependency ASC, f.file_path, f.line_number
490 |                     LIMIT 50
491 |                 """
492 |                 result = session.run(query, function_name=function_name, file_path=file_path)
493 |             else:
494 |                 query = """
495 |                     MATCH (caller:Function {name: $function_name})
496 |                     MATCH (caller)-[:CALLS*]->(f:Function)
497 |                     RETURN DISTINCT f.name AS callee_name, f.file_path AS callee_file_path, f.line_number AS callee_line_number, f.is_dependency AS callee_is_dependency
498 |                     ORDER BY f.is_dependency ASC, f.file_path, f.line_number
499 |                     LIMIT 50
500 |                 """
501 |                 result = session.run(query, function_name=function_name)
502 |             return [dict(record) for record in result]
503 | 
504 |     def find_function_call_chain(self, start_function: str, end_function: str, max_depth: int = 5) -> List[Dict]:
505 |         """Find call chains between two functions"""
506 |         with self.driver.session() as session:
507 |             result = session.run(f"""
508 |                 MATCH path = shortestPath(
509 |                     (start:Function {{name: $start_function}})-[:CALLS*1..{max_depth}]->(end:Function {{name: $end_function}})
510 |                 )
511 |                 WITH path, nodes(path) as func_nodes, relationships(path) as call_rels
512 |                 RETURN 
513 |                     [node in func_nodes | {{
514 |                         name: node.name,
515 |                         file_path: node.file_path,
516 |                         line_number: node.line_number,
517 |                         is_dependency: node.is_dependency
518 |                     }}] as function_chain,
519 |                     [rel in call_rels | {{
520 |                         call_line: rel.line_number,
521 |                         args: rel.args,
522 |                         full_call_name: rel.full_call_name
523 |                     }}] as call_details,
524 |                     length(path) as chain_length
525 |                 ORDER BY chain_length ASC
526 |                 LIMIT 10
527 |             """, start_function=start_function, end_function=end_function)
528 |             
529 |             return [dict(record) for record in result]
530 |     
531 |     def find_module_dependencies(self, module_name: str) -> Dict[str, Any]:
532 |         """Find all dependencies and dependents of a module"""
533 |         with self.driver.session() as session:
534 |             importers_result = session.run("""
535 |                 MATCH (file:File)-[:IMPORTS]->(module:Module {name: $module_name})
536 |                 OPTIONAL MATCH (repo:Repository)-[:CONTAINS]->(file)
537 |                 RETURN DISTINCT
538 |                     file.name as file_name,
539 |                     file.path as file_path,
540 |                     file.is_dependency as file_is_dependency,
541 |                     repo.name as repository_name
542 |                 ORDER BY file.is_dependency ASC, file.path
543 |                 LIMIT 20
544 |             """, module_name=module_name)
545 |             
546 |             related_imports_result = session.run("""
547 |                 MATCH (file:File)-[:IMPORTS]->(target_module:Module {name: $module_name})
548 |                 MATCH (file)-[:IMPORTS]->(other_module:Module)
549 |                 WHERE other_module <> target_module
550 |                 RETURN DISTINCT
551 |                     other_module.name as related_module,
552 |                     other_module.alias as module_alias,
553 |                     count(file) as usage_count
554 |                 ORDER BY usage_count DESC
555 |                 LIMIT 20
556 |             """, module_name=module_name)
557 |             
558 |             return {
559 |                 "module_name": module_name,
560 |                 "imported_by_files": [dict(record) for record in importers_result],
561 |                 "frequently_used_with": [dict(record) for record in related_imports_result]
562 |             }
563 |     
564 |     def find_variable_usage_scope(self, variable_name: str) -> Dict[str, Any]:
565 |         """Find the scope and usage patterns of a variable"""
566 |         with self.driver.session() as session:
567 |             variable_instances = session.run("""
568 |                 MATCH (var:Variable {name: $variable_name})
569 |                 OPTIONAL MATCH (container)-[:CONTAINS]->(var)
570 |                 WHERE container:Function OR container:Class OR container:File
571 |                 OPTIONAL MATCH (file:File)-[:CONTAINS]->(var)
572 |                 RETURN DISTINCT
573 |                     var.name as variable_name,
574 |                     var.value as variable_value,
575 |                     var.line_number as line_number,
576 |                     var.context as context,
577 |                     COALESCE(var.file_path, file.path) as file_path,
578 |                     CASE 
579 |                         WHEN container:Function THEN 'function'
580 |                         WHEN container:Class THEN 'class'
581 |                         ELSE 'module'
582 |                     END as scope_type,
583 |                     CASE 
584 |                         WHEN container:Function THEN container.name
585 |                         WHEN container:Class THEN container.name
586 |                         ELSE 'module_level'
587 |                     END as scope_name,
588 |                     var.is_dependency as is_dependency
589 |                 ORDER BY var.is_dependency ASC, file_path, line_number
590 |             """, variable_name=variable_name)
591 |             
592 |             return {
593 |                 "variable_name": variable_name,
594 |                 "instances": [dict(record) for record in variable_instances]
595 |             }
596 |     
597 |     def analyze_code_relationships(self, query_type: str, target: str, context: str = None) -> Dict[str, Any]:
598 |         """Main method to analyze different types of code relationships with fixed return types"""
599 |         query_type = query_type.lower().strip()
600 |         
601 |         try:
602 |             if query_type == "find_callers":
603 |                 results = self.who_calls_function(target, context)
604 |                 return {
605 |                     "query_type": "find_callers", "target": target, "context": context, "results": results,
606 |                     "summary": f"Found {len(results)} functions that call '{target}'"
607 |                 }
608 |             
609 |             elif query_type == "find_callees":
610 |                 results = self.what_does_function_call(target, context)
611 |                 return {
612 |                     "query_type": "find_callees", "target": target, "context": context, "results": results,
613 |                     "summary": f"Function '{target}' calls {len(results)} other functions"
614 |                 }
615 |                 
616 |             elif query_type == "find_importers":
617 |                 results = self.who_imports_module(target)
618 |                 return {
619 |                     "query_type": "find_importers", "target": target, "results": results,
620 |                     "summary": f"Found {len(results)} files that import '{target}'"
621 |                 }
622 |                 
623 |             elif query_type == "find_functions_by_argument":
624 |                 results = self.find_functions_by_argument(target, context)
625 |                 return {
626 |                     "query_type": "find_functions_by_argument", "target": target, "context": context, "results": results,
627 |                     "summary": f"Found {len(results)} functions that take '{target}' as an argument"
628 |                 }
629 |             
630 |             elif query_type == "find_functions_by_decorator":
631 |                 results = self.find_functions_by_decorator(target, context)
632 |                 return {
633 |                     "query_type": "find_functions_by_decorator", "target": target, "context": context, "results": results,
634 |                     "summary": f"Found {len(results)} functions decorated with '{target}'"
635 |                 }
636 |                 
637 |             elif query_type in ["who_modifies", "modifies", "mutations", "changes", "variable_usage"]:
638 |                 results = self.who_modifies_variable(target)
639 |                 return {
640 |                     "query_type": "who_modifies", "target": target, "results": results,
641 |                     "summary": f"Found {len(results)} containers that hold variable '{target}'"
642 |                 }
643 |             
644 |             elif query_type in ["class_hierarchy", "inheritance", "extends"]:
645 |                 results = self.find_class_hierarchy(target, context)
646 |                 return {
647 |                     "query_type": "class_hierarchy", "target": target, "results": results,
648 |                     "summary": f"Class '{target}' has {len(results['parent_classes'])} parents, {len(results['child_classes'])} children, and {len(results['methods'])} methods"
649 |                 }
650 |             
651 |             elif query_type in ["overrides", "implementations", "polymorphism"]:
652 |                 results = self.find_function_overrides(target)
653 |                 return {
654 |                     "query_type": "overrides", "target": target, "results": results,
655 |                     "summary": f"Found {len(results)} implementations of function '{target}'"
656 |                 }
657 |             
658 |             elif query_type in ["dead_code", "unused", "unreachable"]:
659 |                 results = self.find_dead_code()
660 |                 return {
661 |                     "query_type": "dead_code", "results": results,
662 |                     "summary": f"Found {len(results['potentially_unused_functions'])} potentially unused functions"
663 |                 }
664 |             
665 |             elif query_type == "find_complexity":
666 |                 limit = int(context) if context and context.isdigit() else 10
667 |                 results = self.find_most_complex_functions(limit)
668 |                 return {
669 |                     "query_type": "find_complexity", "limit": limit, "results": results,
670 |                     "summary": f"Found the top {len(results)} most complex functions"
671 |                 }
672 |             
673 |             elif query_type == "find_all_callers":
674 |                 results = self.find_all_callers(target, context)
675 |                 return {
676 |                     "query_type": "find_all_callers", "target": target, "context": context, "results": results,
677 |                     "summary": f"Found {len(results)} direct and indirect callers of '{target}'"
678 |                 }
679 | 
680 |             elif query_type == "find_all_callees":
681 |                 results = self.find_all_callees(target, context)
682 |                 return {
683 |                     "query_type": "find_all_callees", "target": target, "context": context, "results": results,
684 |                     "summary": f"Found {len(results)} direct and indirect callees of '{target}'"
685 |                 }
686 |                 
687 |             elif query_type in ["call_chain", "path", "chain"]:
688 |                 if '->' in target:
689 |                     start_func, end_func = target.split('->', 1)
690 |                     # max_depth can be passed as context, default to 5 if not provided or invalid
691 |                     max_depth = int(context) if context and context.isdigit() else 5
692 |                     results = self.find_function_call_chain(start_func.strip(), end_func.strip(), max_depth)
693 |                     return {
694 |                         "query_type": "call_chain", "target": target, "results": results,
695 |                         "summary": f"Found {len(results)} call chains from '{start_func.strip()}' to '{end_func.strip()}' (max depth: {max_depth})"
696 |                     }
697 |                 else:
698 |                     return {
699 |                         "error": "For call_chain queries, use format 'start_function->end_function'",
700 |                         "example": "main->process_data"
701 |                     }
702 |             
703 |             elif query_type in ["module_deps", "module_dependencies", "module_usage"]:
704 |                 results = self.find_module_dependencies(target)
705 |                 return {
706 |                     "query_type": "module_dependencies", "target": target, "results": results,
707 |                     "summary": f"Module '{target}' is imported by {len(results['imported_by_files'])} files"
708 |                 }
709 |             
710 |             elif query_type in ["variable_scope", "var_scope", "variable_usage_scope"]:
711 |                 results = self.find_variable_usage_scope(target)
712 |                 return {
713 |                     "query_type": "variable_scope", "target": target, "results": results,
714 |                     "summary": f"Variable '{target}' has {len(results['instances'])} instances across different scopes"
715 |                 }
716 |             
717 |             else:
718 |                 return {
719 |                     "error": f"Unknown query type: {query_type}",
720 |                     "supported_types": [
721 |                         "find_callers", "find_callees", "find_importers", "who_modifies",
722 |                         "class_hierarchy", "overrides", "dead_code", "call_chain",
723 |                         "module_deps", "variable_scope", "find_complexity"
724 |                     ]
725 |                 }
726 |         
727 |         except Exception as e:
728 |             return {
729 |                 "error": f"Error executing relationship query: {str(e)}",
730 |                 "query_type": query_type,
731 |                 "target": target
732 |             }
733 | 
734 |     def get_cyclomatic_complexity(self, function_name: str, file_path: str = None) -> List[Dict]:
735 |         """Get the cyclomatic complexity of a function."""
736 |         with self.driver.session() as session:
737 |             if file_path:
738 |                 # Use ENDS WITH for flexible path matching
739 |                 query = """
740 |                     MATCH (f:Function {name: $function_name})
741 |                     WHERE f.file_path ENDS WITH $file_path
742 |                     RETURN f.name as function_name, f.file_path as file_path, f.cyclomatic_complexity as complexity
743 |                 """
744 |                 result = session.run(query, function_name=function_name, file_path=file_path)
745 |             else:
746 |                 query = """
747 |                     MATCH (f:Function {name: $function_name})
748 |                     RETURN f.name as function_name, f.file_path as file_path, f.cyclomatic_complexity as complexity
749 |                 """
750 |                 result = session.run(query, function_name=function_name)
751 |             
752 |             return [dict(record) for record in result]
753 | 
754 |     def find_most_complex_functions(self, limit: int = 10) -> List[Dict]:
755 |         """Find the most complex functions based on cyclomatic complexity."""
756 |         with self.driver.session() as session:
757 |             query = """
758 |                 MATCH (f:Function)
759 |                 WHERE f.cyclomatic_complexity IS NOT NULL AND f.is_dependency = false
760 |                 RETURN f.name as function_name, f.file_path as file_path, f.cyclomatic_complexity as complexity, f.line_number as line_number
761 |                 ORDER BY f.cyclomatic_complexity DESC
762 |                 LIMIT $limit
763 |             """
764 |             result = session.run(query, limit=limit)
765 |             return [dict(record) for record in result]
766 | 
767 |     def list_indexed_repositories(self) -> List[Dict]:
768 |         """List all indexed repositories."""
769 |         with self.driver.session() as session:
770 |             result = session.run("""
771 |                 MATCH (r:Repository)
772 |                 RETURN r.name as name, r.path as path, r.is_dependency as is_dependency
773 |                 ORDER BY r.name
774 |             """)
775 |             return [dict(record) for record in result]
776 | 
```

--------------------------------------------------------------------------------
/tests/test_graph_indexing.py:
--------------------------------------------------------------------------------

```python
  1 | 
  2 | import pytest
  3 | import os
  4 | 
  5 | from .conftest import SAMPLE_PROJECT_PATH
  6 | 
  7 | # ==============================================================================
  8 | # == EXPECTED RELATIONSHIPS
  9 | # ==============================================================================
 10 | 
 11 | EXPECTED_STRUCTURE = [
 12 |     ("module_a.py", "foo", "Function"),
 13 |     ("module_a.py", "bar", "Function"),
 14 |     ("module_a.py", "outer", "Function"),
 15 |     ("module_b.py", "helper", "Function"),
 16 |     ("module_b.py", "process_data", "Function"),
 17 |     ("module_b.py", "factorial", "Function"),
 18 |     ("advanced_classes.py", "A", "Class"),
 19 |     ("advanced_classes.py", "B", "Class"),
 20 |     ("advanced_classes.py", "C", "Class"),
 21 |     ("module_a.py", "nested", "Function"),
 22 |     ("advanced_calls.py", "square", "Function"),
 23 |     ("advanced_calls.py", "calls", "Function"),
 24 |     ("advanced_calls.py", "Dummy", "Class"),
 25 |     ("advanced_classes2.py", "Base", "Class"),
 26 |     ("advanced_classes2.py", "Mid", "Class"),
 27 |     ("advanced_classes2.py", "Final", "Class"),
 28 |     ("advanced_classes2.py", "Mixin1", "Class"),
 29 |     ("advanced_classes2.py", "Mixin2", "Class"),
 30 |     ("advanced_classes2.py", "Combined", "Class"),
 31 |     ("advanced_classes2.py", "Point", "Class"),
 32 |     ("advanced_classes2.py", "Color", "Class"),
 33 |     ("advanced_classes2.py", "handle", "Function"),
 34 |     ("advanced_functions.py", "with_defaults", "Function"),
 35 |     ("advanced_functions.py", "with_args_kwargs", "Function"),
 36 |     ("advanced_functions.py", "higher_order", "Function"),
 37 |     ("advanced_functions.py", "return_function", "Function"),
 38 |     ("advanced_imports.py", "outer_import", "Function"),
 39 |     ("advanced_imports.py", "use_random", "Function"),
 40 |     ("async_features.py", "fetch_data", "Function"),
 41 |     ("async_features.py", "main", "Function"),
 42 |     ("callbacks_decorators.py", "executor", "Function"),
 43 |     ("callbacks_decorators.py", "square", "Function"),
 44 |     ("callbacks_decorators.py", "log_decorator", "Function"),
 45 |     ("callbacks_decorators.py", "hello", "Function"),
 46 |     ("class_instantiation.py", "A", "Class"),
 47 |     ("class_instantiation.py", "B", "Class"),
 48 |     ("class_instantiation.py", "Fluent", "Class"),
 49 |     ("function_chains.py", "f1", "Function"),
 50 |     ("function_chains.py", "f2", "Function"),
 51 |     ("function_chains.py", "f3", "Function"),
 52 |     ("function_chains.py", "make_adder", "Function"),
 53 |     ("generators.py", "gen_numbers", "Function"),
 54 |     ("generators.py", "agen_numbers", "Function"),
 55 |     ("generators.py", "async_with_example", "Function"),
 56 |     ("generators.py", "AsyncCM", "Class"),
 57 |     ("datatypes.py", "Point", "Class"),
 58 |     ("datatypes.py", "Color", "Class"),
 59 |     ("complex_classes.py", "Base", "Class"),
 60 |     ("complex_classes.py", "Child", "Class"),
 61 |     ("complex_classes.py", "decorator", "Function"),
 62 |     ("complex_classes.py", "decorated_function", "Function"),
 63 |     ("control_flow.py", "choose_path", "Function"),
 64 |     ("control_flow.py", "ternary", "Function"),
 65 |     ("control_flow.py", "try_except_finally", "Function"),
 66 |     ("control_flow.py", "conditional_inner_import", "Function"),
 67 |     ("control_flow.py", "env_based_import", "Function"),
 68 |     ("circular1.py", "func1", "Function"),
 69 |     ("circular2.py", "func2", "Function"),
 70 |     ("cli_and_dunder.py", "run", "Function"),
 71 |     ("comprehensions_generators.py", "double", "Function"),
 72 |     ("context_managers.py", "FileOpener", "Class"),
 73 |     ("context_managers.py", "use_file", "Function"),
 74 |     ("dynamic_dispatch.py", "add", "Function"),
 75 |     ("dynamic_dispatch.py", "sub", "Function"),
 76 |     ("dynamic_dispatch.py", "mul", "Function"),
 77 |     ("dynamic_dispatch.py", "dispatch_by_key", "Function"),
 78 |     ("dynamic_dispatch.py", "dispatch_by_string", "Function"),
 79 |     ("dynamic_dispatch.py", "partial_example", "Function"),
 80 |     ("dynamic_dispatch.py", "C", "Class"),
 81 |     ("dynamic_dispatch.py", "methodcaller_example", "Function"),
 82 |     ("dynamic_dispatch.py", "dynamic_import_call", "Function"),
 83 |     ("dynamic_imports.py", "import_optional", "Function"),
 84 |     ("dynamic_imports.py", "import_by___import__", "Function"),
 85 |     ("dynamic_imports.py", "importlib_runtime", "Function"),
 86 |     ("import_reexports.py", "core_function", "Function"),
 87 |     ("import_reexports.py", "reexport", "Function"),
 88 |     ("mapping_calls.py", "Dispatcher", "Class"),
 89 |     ("mapping_calls.py", "use_dispatcher", "Function"),
 90 |     ("module_c/submodule1.py", "call_helper_twice", "Function"),
 91 |     ("module_c/submodule2.py", "wrapper", "Function"),
 92 |     ("namespace_pkg/ns_module.py", "ns_func", "Function"),
 93 |     ("pattern_matching.py", "matcher", "Function"),
 94 |     ("typing_examples.py", "typed_func", "Function"),
 95 |     ("typing_examples.py", "union_func", "Function"),
 96 |     ("typing_examples.py", "dict_func", "Function"),
 97 | ]
 98 | 
 99 | EXPECTED_INHERITANCE = [
100 |     pytest.param("C", "advanced_classes.py", "A", "advanced_classes.py", id="C inherits from A"),
101 |     pytest.param("C", "advanced_classes.py", "B", "advanced_classes.py", id="C inherits from B"),
102 |     pytest.param("ConcreteThing", "advanced_classes.py", "AbstractThing", "advanced_classes.py", id="ConcreteThing inherits from AbstractThing"),
103 |     pytest.param("Mid", "advanced_classes2.py", "Base", "advanced_classes2.py", id="Mid inherits from Base"),
104 |     pytest.param("Final", "advanced_classes2.py", "Mid", "advanced_classes2.py", id="Final inherits from Mid"),
105 |     pytest.param("Combined", "advanced_classes2.py", "Mixin1", "advanced_classes2.py", id="Combined inherits from Mixin1"),
106 |     pytest.param("Combined", "advanced_classes2.py", "Mixin2", "advanced_classes2.py", id="Combined inherits from Mixin2"),
107 |     pytest.param("B", "class_instantiation.py", "A", "class_instantiation.py", id="B inherits from A"),
108 |     pytest.param("B", "class_instantiation.py", "A", "class_instantiation.py", marks=pytest.mark.skip(reason="Indexer does not support inheritance via super() calls"), id="B inherits from A via super()"),
109 |     pytest.param("Child", "complex_classes.py", "Base", "complex_classes.py", id="Child inherits from Base"),
110 | ]
111 | 
112 | EXPECTED_CALLS = [
113 |     pytest.param("foo", "module_a.py", None, "helper", "module_b.py", None, id="module_a.foo->module_b.helper"),
114 |     pytest.param("foo", "module_a.py", None, "process_data", "module_b.py", None, id="module_a.foo->module_b.process_data"),
115 |     pytest.param("factorial", "module_b.py", None, "factorial", "module_b.py", None, id="module_b.factorial->recursive"),
116 |     pytest.param("calls", "advanced_calls.py", None, "square", "advanced_calls.py", None, id="advanced_calls.calls->square"),
117 |     pytest.param("call_helper_twice", "module_c/submodule1.py", None, "helper", "module_b.py", None, id="submodule1.call_helper_twice->module_b.helper"),
118 |     pytest.param("wrapper", "module_c/submodule2.py", None, "call_helper_twice", "module_c/submodule1.py", None, id="submodule2.wrapper->submodule1.call_helper_twice"),
119 |     pytest.param("main", "async_features.py", None, "fetch_data", "async_features.py", None, id="async.main->fetch_data"),
120 |     pytest.param("func1", "circular1.py", None, "func2", "circular2.py", None, id="circular1.func1->circular2.func2"),
121 |     pytest.param("run", "cli_and_dunder.py", None, "with_defaults", "advanced_functions.py", None, id="cli.run->with_defaults"),
122 |     pytest.param("use_dispatcher", "mapping_calls.py", None, "call", "mapping_calls.py", None, id="mapping.use_dispatcher->call"),
123 |     pytest.param("calls", "advanced_calls.py", None, "method", "advanced_calls.py", "Dummy", marks=pytest.mark.skip(reason="Dynamic call with getattr is not supported"), id="advanced_calls.calls->Dummy.method"),
124 |     pytest.param("both", "advanced_classes2.py", "Combined", "m1", "advanced_classes2.py", "Mixin1", id="advanced_classes2.both->m1"),
125 |     pytest.param("both", "advanced_classes2.py", "Combined", "m2", "advanced_classes2.py", "Mixin2", id="advanced_classes2.both->m2"),
126 |     pytest.param("executor", "callbacks_decorators.py", None, "square", "callbacks_decorators.py", None, marks=pytest.mark.skip(reason="Dynamic call passing function as argument is not supported"), id="callbacks.executor->square"),
127 |     pytest.param("reexport", "import_reexports.py", None, "core_function", "import_reexports.py", None, id="reexport->core_function"),
128 |     pytest.param("greet", "class_instantiation.py", "B", "greet", "class_instantiation.py", "A", marks=pytest.mark.skip(reason="super() calls are not supported yet"), id="B.greet->A.greet"),
129 |     pytest.param("greet", "complex_classes.py", "Child", "greet", "complex_classes.py", "Base", marks=pytest.mark.skip(reason="super() calls are not supported yet"), id="Child.greet->Base.greet"),
130 |     pytest.param("class_method", "complex_classes.py", "Child", "greet", "complex_classes.py", "Child", id="Child.class_method->Child.greet"),
131 |     pytest.param("use_file", "context_managers.py", None, "__enter__", "context_managers.py", "FileOpener", marks=pytest.mark.skip(reason="Implicit context manager calls not supported"), id="use_file->FileOpener.__enter__"),
132 |     pytest.param("partial_example", "dynamic_dispatch.py", None, "add", "dynamic_dispatch.py", None, marks=pytest.mark.skip(reason="Calls via functools.partial not supported yet"), id="partial_example->add"),
133 |     pytest.param("async_with_example", "generators.py", None, "__aenter__", "generators.py", "AsyncCM", marks=pytest.mark.skip(reason="Implicit async context manager calls not supported"), id="async_with_example->AsyncCM.__aenter__"),
134 |     pytest.param("call", "mapping_calls.py", "Dispatcher", "start", "mapping_calls.py", "Dispatcher", marks=pytest.mark.skip(reason="Dynamic call via dict lookup not supported"), id="Dispatcher.call->start"),
135 |     pytest.param("greet", "class_instantiation.py", None, "greet", "class_instantiation.py", "A", marks=pytest.mark.skip(reason="Indexer does not capture calls to methods of instantiated objects within the same file"), id="A.greet called"),
136 |     pytest.param("greet", "class_instantiation.py", None, "greet", "class_instantiation.py", "B", marks=pytest.mark.skip(reason="Indexer does not capture calls to methods of instantiated objects within the same file"), id="B.greet called"),
137 |     pytest.param("step1", "class_instantiation.py", "Fluent", "step1", "class_instantiation.py", "Fluent", marks=pytest.mark.skip(reason="Indexer does not capture method chaining calls"), id="Fluent.step1 called"),
138 |     pytest.param("step2", "class_instantiation.py", "Fluent", "step2", "class_instantiation.py", "Fluent", marks=pytest.mark.skip(reason="Indexer does not capture method chaining calls"), id="Fluent.step2 called"),
139 |     pytest.param("step3", "class_instantiation.py", "Fluent", "step3", "class_instantiation.py", "Fluent", marks=pytest.mark.skip(reason="Indexer does not capture method chaining calls"), id="Fluent.step3 called"),
140 |     pytest.param("dynamic", "class_instantiation.py", "B", "lambda", "class_instantiation.py", None, marks=pytest.mark.skip(reason="Dynamic attribute assignment and lambda calls not supported"), id="B.dynamic called"),
141 |     pytest.param("add_argument", "cli_and_dunder.py", None, "add_argument", None, None, marks=pytest.mark.skip(reason="Calls to external library methods not fully supported"), id="ArgumentParser.add_argument called"),
142 |     pytest.param("parse_args", "cli_and_dunder.py", None, "parse_args", None, None, marks=pytest.mark.skip(reason="Calls to external library methods not fully supported"), id="ArgumentParser.parse_args called"),
143 |     pytest.param("print", "cli_and_dunder.py", None, "print", None, None, marks=pytest.mark.skip(reason="Built-in function calls not explicitly indexed"), id="print called"),
144 |     pytest.param("double", "comprehensions_generators.py", None, "double", "comprehensions_generators.py", None, marks=pytest.mark.skip(reason="Indexer does not capture calls within comprehensions/generators"), id="double called in list comprehension"),
145 |     pytest.param("range", "comprehensions_generators.py", None, "range", None, None, marks=pytest.mark.skip(reason="Built-in function calls not explicitly indexed"), id="range called in list comprehension"),
146 |     pytest.param("double", "comprehensions_generators.py", None, "double", "comprehensions_generators.py", None, marks=pytest.mark.skip(reason="Indexer does not capture calls within comprehensions/generators"), id="double called in generator expression"),
147 |     pytest.param("range", "comprehensions_generators.py", None, "range", None, None, marks=pytest.mark.skip(reason="Built-in function calls not explicitly indexed"), id="range called in generator expression"),
148 |     pytest.param("list", "comprehensions_generators.py", None, "list", None, None, marks=pytest.mark.skip(reason="Built-in function calls not explicitly indexed"), id="list called"),
149 |     pytest.param("sorted", "comprehensions_generators.py", None, "sorted", None, None, marks=pytest.mark.skip(reason="Built-in function calls not explicitly indexed"), id="sorted called"),
150 |     pytest.param("len", "comprehensions_generators.py", None, "len", None, None, marks=pytest.mark.skip(reason="Built-in function calls not explicitly indexed"), id="len called"),
151 |     pytest.param("open", "comprehensions_generators.py", None, "open", None, None, marks=pytest.mark.skip(reason="Built-in function calls not explicitly indexed"), id="open called for write"),
152 |     pytest.param("write", "comprehensions_generators.py", None, "write", None, None, marks=pytest.mark.skip(reason="Method calls on built-in types not explicitly indexed"), id="write called"),
153 |     pytest.param("open", "comprehensions_generators.py", None, "open", None, None, marks=pytest.mark.skip(reason="Built-in function calls not explicitly indexed"), id="open called for read"),
154 |     pytest.param("read", "comprehensions_generators.py", None, "read", None, None, marks=pytest.mark.skip(reason="Method calls on built-in types not explicitly indexed"), id="read called"),
155 |     pytest.param("ValueError", "control_flow.py", None, "ValueError", None, None, marks=pytest.mark.skip(reason="Built-in exception constructors not explicitly indexed"), id="ValueError called"),
156 |     pytest.param("str", "control_flow.py", None, "str", None, None, marks=pytest.mark.skip(reason="Built-in type constructors not explicitly indexed"), id="str called"),
157 |     pytest.param("getenv", "control_flow.py", None, "getenv", None, None, marks=pytest.mark.skip(reason="Calls to external library methods not fully supported"), id="os.getenv called"),
158 |     pytest.param("dumps", "control_flow.py", None, "dumps", None, None, marks=pytest.mark.skip(reason="Calls to external library methods not fully supported"), id="json.dumps called"),
159 |     pytest.param("namedtuple", "datatypes.py", None, "namedtuple", None, None, marks=pytest.mark.skip(reason="Calls to external library functions not fully supported"), id="namedtuple called"),
160 |     pytest.param("DISPATCH", "dynamic_dispatch.py", None, "add", "dynamic_dispatch.py", None, marks=pytest.mark.skip(reason="Dynamic dispatch via dictionary lookup not supported"), id="dispatch_by_key calls add dynamically"),
161 |     pytest.param("DISPATCH", "dynamic_dispatch.py", None, "sub", "dynamic_dispatch.py", None, marks=pytest.mark.skip(reason="Dynamic dispatch via dictionary lookup not supported"), id="dispatch_by_key calls sub dynamically"),
162 |     pytest.param("DISPATCH", "dynamic_dispatch.py", None, "mul", "dynamic_dispatch.py", None, marks=pytest.mark.skip(reason="Dynamic dispatch via dictionary lookup not supported"), id="dispatch_by_key calls mul dynamically"),
163 |     pytest.param("get", "dynamic_dispatch.py", None, "get", None, None, marks=pytest.mark.skip(reason="Calls to built-in dictionary methods not explicitly indexed"), id="globals().get called"),
164 |     pytest.param("callable", "dynamic_dispatch.py", None, "callable", None, None, marks=pytest.mark.skip(reason="Built-in function calls not explicitly indexed"), id="callable called"),
165 |     pytest.param("partial", "dynamic_dispatch.py", None, "partial", None, None, marks=pytest.mark.skip(reason="Calls to external library functions not fully supported"), id="partial called"),
166 |     pytest.param("methodcaller", "dynamic_dispatch.py", None, "methodcaller", None, None, marks=pytest.mark.skip(reason="Calls to external library functions not fully supported"), id="methodcaller called"),
167 |     pytest.param("method", "dynamic_dispatch.py", "C", "method", "dynamic_dispatch.py", "C", marks=pytest.mark.skip(reason="Dynamic call via operator.methodcaller not supported"), id="C.method called via methodcaller"),
168 |     pytest.param("import_module", "dynamic_dispatch.py", None, "import_module", None, None, marks=pytest.mark.skip(reason="Calls to external library functions not fully supported"), id="importlib.import_module called"),
169 |     pytest.param("getattr", "dynamic_dispatch.py", None, "getattr", None, None, marks=pytest.mark.skip(reason="Built-in function calls not explicitly indexed"), id="getattr called"),
170 |     pytest.param("dumps", "dynamic_imports.py", None, "dumps", None, None, marks=pytest.mark.skip(reason="Calls to external library methods not fully supported"), id="json.dumps called in import_optional"),
171 |     pytest.param("__import__", "dynamic_imports.py", None, "__import__", None, None, marks=pytest.mark.skip(reason="Built-in function calls not explicitly indexed"), id="__import__ called"),
172 |     pytest.param("getattr", "dynamic_imports.py", None, "getattr", None, None, marks=pytest.mark.skip(reason="Built-in function calls not explicitly indexed"), id="getattr called in import_by___import__"),
173 |     pytest.param("import_module", "dynamic_imports.py", None, "import_module", None, None, marks=pytest.mark.skip(reason="Calls to external library functions not fully supported"), id="importlib.import_module called in importlib_runtime"),
174 |     pytest.param("getattr", "dynamic_imports.py", None, "getattr", None, None, marks=pytest.mark.skip(reason="Built-in function calls not explicitly indexed"), id="getattr called in importlib_runtime"),
175 |     pytest.param("f3", "function_chains.py", None, "f3", "function_chains.py", None, marks=pytest.mark.skip(reason="Indexer does not capture chained function calls"), id="f3 called"),
176 |     pytest.param("f2", "function_chains.py", None, "f2", "function_chains.py", None, marks=pytest.mark.skip(reason="Indexer does not capture chained function calls"), id="f2 called"),
177 |     pytest.param("f1", "function_chains.py", None, "f1", "function_chains.py", None, marks=pytest.mark.skip(reason="Indexer does not capture chained function calls"), id="f1 called"),
178 |     pytest.param("strip", "function_chains.py", None, "strip", None, None, marks=pytest.mark.skip(reason="Method calls on built-in types not explicitly indexed"), id="strip called"),
179 |     pytest.param("lower", "function_chains.py", None, "lower", None, None, marks=pytest.mark.skip(reason="Method calls on built-in types not explicitly indexed"), id="lower called"),
180 |     pytest.param("replace", "function_chains.py", None, "replace", None, None, marks=pytest.mark.skip(reason="Method calls on built-in types not explicitly indexed"), id="replace called"),
181 |     pytest.param("make_adder", "function_chains.py", None, "make_adder", "function_chains.py", None, marks=pytest.mark.skip(reason="Indexer does not capture calls to functions that return functions"), id="make_adder called"),
182 |     pytest.param("adder", "function_chains.py", None, "adder", "function_chains.py", None, marks=pytest.mark.skip(reason="Indexer does not capture calls to inner functions returned by other functions"), id="adder called"),
183 |     pytest.param("make_adder", "function_chains.py", None, "make_adder", "function_chains.py", None, marks=pytest.mark.skip(reason="Indexer does not capture calls to functions that return functions in a chain"), id="make_adder called in chain"),
184 |     pytest.param("adder", "function_chains.py", None, "adder", "function_chains.py", None, marks=pytest.mark.skip(reason="Indexer does not capture calls to inner functions returned by other functions in a chain"), id="adder called in chain"),
185 |     pytest.param("sqrt", "import_reexports.py", None, "sqrt", None, None, marks=pytest.mark.skip(reason="Calls to aliased imported functions not fully supported"), id="m.sqrt called"),
186 |     pytest.param("Dispatcher", "mapping_calls.py", None, "Dispatcher", "mapping_calls.py", None, marks=pytest.mark.skip(reason="Indexer does not capture class constructor calls"), id="Dispatcher constructor called"),
187 |     pytest.param("str", "pattern_matching.py", None, "str", None, None, marks=pytest.mark.skip(reason="Built-in type constructors not explicitly indexed"), id="str called in pattern matching"),
188 | ]
189 | 
190 | EXPECTED_IMPORTS = [
191 |     pytest.param("module_a.py", "math", id="module_a imports math"),
192 |     pytest.param("module_a.py", "module_b", id="module_a imports module_b"),
193 |     pytest.param("advanced_imports.py", "math", id="advanced_imports imports math"),
194 |     pytest.param("advanced_imports.py", "random", id="advanced_imports imports random"),
195 |     pytest.param("advanced_imports.py", "sys", id="advanced_imports imports sys"),
196 |     pytest.param("async_features.py", "asyncio", id="async_features imports asyncio"),
197 |     pytest.param("circular1.py", "func2", id="circular1 imports func2"),
198 |     pytest.param("circular2.py", "func1", id="circular2 imports func1"),
199 |     pytest.param("cli_and_dunder.py", "argparse", id="cli_and_dunder imports argparse"),
200 |     pytest.param("cli_and_dunder.py", "advanced_functions", id="cli_and_dunder imports advanced_functions"),
201 |     pytest.param("control_flow.py", "os", id="control_flow imports os"),
202 |     pytest.param("datatypes.py", "dataclass", id="datatypes imports dataclass"),
203 |     pytest.param("datatypes.py", "enum", id="datatypes imports enum"),
204 |     pytest.param("datatypes.py", "namedtuple", id="datatypes imports namedtuple"),
205 |     pytest.param("dynamic_dispatch.py", "partial", id="dynamic_dispatch imports partial"),
206 |     pytest.param("dynamic_dispatch.py", "operator", id="dynamic_dispatch imports operator"),
207 |     pytest.param("dynamic_dispatch.py", "importlib", id="dynamic_dispatch imports importlib"),
208 |     pytest.param("dynamic_imports.py", "importlib", id="dynamic_imports imports importlib"),
209 |     pytest.param("import_reexports.py", "math", marks=pytest.mark.skip(reason="Indexer does not support aliased imports (e.g., 'import math as m')"), id="import_reexports imports math"),
210 |     pytest.param("module_c/submodule1.py", "helper", id="submodule1 imports helper"),
211 |     pytest.param("module_c/submodule2.py", "call_helper_twice", id="submodule2 imports call_helper_twice"),
212 |     pytest.param("typing_examples.py", "List", id="typing_examples imports List"),
213 |     pytest.param("typing_examples.py", "Dict", marks=pytest.mark.skip(reason="Indexer does not capture imports after ','"), id="typing_examples imports Dict"),
214 |     pytest.param("typing_examples.py", "Union", marks=pytest.mark.skip(reason="Indexer does not capture imports after ','"), id="typing_examples imports Union"),
215 |     pytest.param("control_flow.py", "numpy", marks=pytest.mark.skip(reason="Indexer does not capture conditional imports"), id="control_flow imports numpy (conditional)"),
216 |     pytest.param("control_flow.py", "ujson", marks=pytest.mark.skip(reason="Indexer does not capture conditional imports"), id="control_flow imports ujson (conditional)"),
217 |     pytest.param("control_flow.py", "json", id="control_flow imports json (conditional)"),
218 |     pytest.param("dynamic_imports.py", "ujson", marks=pytest.mark.skip(reason="Indexer does not capture conditional imports"), id="dynamic_imports imports ujson (conditional)"),
219 |     pytest.param("dynamic_imports.py", "json", id="dynamic_imports imports json (conditional)"),
220 | ]
221 | 
222 | EXPECTED_PARAMETERS = [
223 |     pytest.param("foo", "module_a.py", "x", id="foo has parameter x"),
224 |     pytest.param("helper", "module_b.py", "x", id="helper has parameter x"),
225 |     pytest.param("process_data", "module_b.py", "data", id="process_data has parameter data"),
226 |     pytest.param("factorial", "module_b.py", "n", id="factorial has parameter n"),
227 |     pytest.param("square", "advanced_calls.py", "x", id="square has parameter x"),
228 |     pytest.param("method", "advanced_calls.py", "x", id="Dummy.method has parameter x"),
229 |     pytest.param("with_defaults", "advanced_functions.py", "a", id="with_defaults has parameter a"),
230 |     pytest.param("with_defaults", "advanced_functions.py", "b", id="with_defaults has parameter b"),
231 |     pytest.param("with_defaults", "advanced_functions.py", "c", id="with_defaults has parameter c"),
232 |     pytest.param("higher_order", "advanced_functions.py", "func", id="higher_order has parameter func"),
233 |     pytest.param("higher_order", "advanced_functions.py", "data", id="higher_order has parameter data"),
234 |     pytest.param("return_function", "advanced_functions.py", "x", id="return_function has parameter x"),
235 |     pytest.param("executor", "callbacks_decorators.py", "func", id="executor has parameter func"),
236 |     pytest.param("executor", "callbacks_decorators.py", "val", id="executor has parameter val"),
237 |     pytest.param("square", "callbacks_decorators.py", "x", id="square has parameter x"),
238 |     pytest.param("log_decorator", "callbacks_decorators.py", "fn", id="log_decorator has parameter fn"),
239 |     pytest.param("hello", "callbacks_decorators.py", "name", id="hello has parameter name"),
240 |     pytest.param("greet", "class_instantiation.py", "self", id="A.greet has parameter self"),
241 |     pytest.param("greet", "class_instantiation.py", "self", id="B.greet has parameter self"),
242 |     pytest.param("step1", "class_instantiation.py", "self", id="Fluent.step1 has parameter self"),
243 |     pytest.param("step2", "class_instantiation.py", "self", id="Fluent.step2 has parameter self"),
244 |     pytest.param("step3", "class_instantiation.py", "self", id="Fluent.step3 has parameter self"),
245 |     pytest.param("run", "cli_and_dunder.py", "argv", id="run has parameter argv"),
246 |     pytest.param("greet", "complex_classes.py", "self", id="Base.greet has self"),
247 |     pytest.param("greet", "complex_classes.py", "self", id="Child.greet has self"),
248 |     pytest.param("static_method", "complex_classes.py", "x", id="Child.static_method has x"),
249 |     pytest.param("class_method", "complex_classes.py", "cls", id="Child.class_method has cls"),
250 |     pytest.param("class_method", "complex_classes.py", "y", id="Child.class_method has y"),
251 |     pytest.param("decorator", "complex_classes.py", "func", id="decorator has func"),
252 |     pytest.param("decorated_function", "complex_classes.py", "x", id="decorated_function has x"),
253 |     pytest.param("double", "comprehensions_generators.py", "x", id="double has parameter x"),
254 |     pytest.param("__enter__", "context_managers.py", "self", id="FileOpener.__enter__ has self"),
255 |     pytest.param("__exit__", "context_managers.py", "self", id="FileOpener.__exit__ has self"),
256 |     pytest.param("__exit__", "context_managers.py", "exc_type", id="FileOpener.__exit__ has exc_type"),
257 |     pytest.param("__exit__", "context_managers.py", "exc_val", id="FileOpener.__exit__ has exc_val"),
258 |     pytest.param("__exit__", "context_managers.py", "exc_tb", id="FileOpener.__exit__ has exc_tb"),
259 |     pytest.param("choose_path", "control_flow.py", "x", id="choose_path has parameter x"),
260 |     pytest.param("ternary", "control_flow.py", "x", id="ternary has parameter x"),
261 |     pytest.param("try_except_finally", "control_flow.py", "x", id="try_except_finally has parameter x"),
262 |     pytest.param("conditional_inner_import", "control_flow.py", "use_numpy", id="conditional_inner_import has use_numpy"),
263 |     pytest.param("add", "dynamic_dispatch.py", "a", id="add has a"),
264 |     pytest.param("add", "dynamic_dispatch.py", "b", id="add has b"),
265 |     pytest.param("sub", "dynamic_dispatch.py", "a", id="sub has a"),
266 |     pytest.param("sub", "dynamic_dispatch.py", "b", id="sub has b"),
267 |     pytest.param("mul", "dynamic_dispatch.py", "a", id="mul has a"),
268 |     pytest.param("mul", "dynamic_dispatch.py", "b", id="mul has b"),
269 |     pytest.param("dispatch_by_key", "dynamic_dispatch.py", "name", id="dispatch_by_key has name"),
270 |     pytest.param("dispatch_by_key", "dynamic_dispatch.py", "a", id="dispatch_by_key has a"),
271 |     pytest.param("dispatch_by_key", "dynamic_dispatch.py", "b", id="dispatch_by_key has b"),
272 |     pytest.param("method", "dynamic_dispatch.py", "x", id="C.method has x"),
273 |     pytest.param("methodcaller_example", "dynamic_dispatch.py", "x", id="methodcaller_example has x"),
274 |     pytest.param("import_by___import__", "dynamic_imports.py", "name", id="import_by___import__ has name"),
275 |     pytest.param("importlib_runtime", "dynamic_imports.py", "name", id="importlib_runtime has name"),
276 |     pytest.param("importlib_runtime", "dynamic_imports.py", "attr", id="importlib_runtime has attr"),
277 |     pytest.param("f1", "function_chains.py", "x", id="f1 has x"),
278 |     pytest.param("f2", "function_chains.py", "x", id="f2 has x"),
279 |     pytest.param("f3", "function_chains.py", "x", id="f3 has x"),
280 |     pytest.param("make_adder", "function_chains.py", "n", id="make_adder has n"),
281 |     pytest.param("gen_numbers", "generators.py", "n", id="gen_numbers has n"),
282 |     pytest.param("agen_numbers", "generators.py", "n", id="agen_numbers has n"),
283 |     pytest.param("call", "mapping_calls.py", "cmd", id="Dispatcher.call has cmd"),
284 |     pytest.param("use_dispatcher", "mapping_calls.py", "cmd", id="use_dispatcher has cmd"),
285 |     pytest.param("call_helper_twice", "module_c/submodule1.py", "x", id="call_helper_twice has x"),
286 |     pytest.param("wrapper", "module_c/submodule2.py", "x", id="wrapper has x"),
287 |     pytest.param("matcher", "pattern_matching.py", "x", id="matcher has x"),
288 |     pytest.param("typed_func", "typing_examples.py", "a", marks=pytest.mark.skip(reason="Indexer does not support parameters with type hints"), id="typed_func has a"),
289 |     pytest.param("typed_func", "typing_examples.py", "b", marks=pytest.mark.skip(reason="Indexer does not support parameters with type hints"), id="typed_func has b"),
290 |     pytest.param("union_func", "typing_examples.py", "x", marks=pytest.mark.skip(reason="Indexer does not support parameters with type hints"), id="union_func has x"),
291 |     pytest.param("dict_func", "typing_examples.py", "d", marks=pytest.mark.skip(reason="Indexer does not support parameters with type hints"), id="dict_func has d"),
292 |     pytest.param("wrapper", "complex_classes.py", "*args", marks=pytest.mark.skip(reason="Indexer does not capture variadic parameters (*args)"), id="wrapper has *args"),
293 |     pytest.param("wrapper", "complex_classes.py", "**kwargs", marks=pytest.mark.skip(reason="Indexer does not capture variadic parameters (**kwargs)"), id="wrapper has **kwargs"),
294 |     pytest.param("dispatch_by_string", "dynamic_dispatch.py", "*args", marks=pytest.mark.skip(reason="Indexer does not capture variadic parameters (*args)"), id="dispatch_by_string has *args"),
295 |     pytest.param("dispatch_by_string", "dynamic_dispatch.py", "**kwargs", marks=pytest.mark.skip(reason="Indexer does not capture variadic parameters (**kwargs)"), id="dispatch_by_string has **kwargs"),
296 |     pytest.param("dynamic_import_call", "dynamic_dispatch.py", "*args", marks=pytest.mark.skip(reason="Indexer does not capture variadic parameters (*args)"), id="dynamic_import_call has *args"),
297 |     pytest.param("dynamic_import_call", "dynamic_dispatch.py", "**kwargs", marks=pytest.mark.skip(reason="Indexer does not capture variadic parameters (**kwargs)"), id="dynamic_import_call has **kwargs"),
298 |     pytest.param("adder", "function_chains.py", "x", id="adder has x"),
299 |     pytest.param("__aenter__", "generators.py", "self", id="AsyncCM.__aenter__ has self"),
300 |     pytest.param("__aexit__", "generators.py", "self", id="AsyncCM.__aexit__ has self"),
301 |     pytest.param("__aexit__", "generators.py", "exc_type", id="AsyncCM.__aexit__ has exc_type"),
302 |     pytest.param("__aexit__", "generators.py", "exc_val", id="AsyncCM.__aexit__ has exc_val"),
303 |     pytest.param("__aexit__", "generators.py", "exc_tb", id="AsyncCM.__aexit__ has exc_tb"),
304 |     pytest.param("__init__", "mapping_calls.py", "self", id="Dispatcher.__init__ has self"),
305 |     pytest.param("start", "mapping_calls.py", "self", id="Dispatcher.start has self"),
306 |     pytest.param("stop", "mapping_calls.py", "self", id="Dispatcher.stop has self"),
307 |     pytest.param("ns_func", "namespace_pkg/ns_module.py", None, marks=pytest.mark.skip(reason="Functions with no parameters are not explicitly tested for parameter existence"), id="ns_func has no parameters"),
308 | ]
309 | 
310 | EXPECTED_CLASS_METHODS = [
311 |     pytest.param("A", "advanced_classes.py", "foo", id="A contains foo"),
312 |     pytest.param("B", "advanced_classes.py", "foo", id="B contains foo"),
313 |     pytest.param("C", "advanced_classes.py", "bar", id="C contains bar"),
314 |     pytest.param("AbstractThing", "advanced_classes.py", "do", id="AbstractThing contains do"),
315 |     pytest.param("ConcreteThing", "advanced_classes.py", "do", id="ConcreteThing contains do"),
316 |     pytest.param("Dummy", "advanced_calls.py", "method", id="Dummy contains method"),
317 |     pytest.param("Mixin1", "advanced_classes2.py", "m1", id="Mixin1 contains m1"),
318 |     pytest.param("Mixin2", "advanced_classes2.py", "m2", id="Mixin2 contains m2"),
319 |     pytest.param("Combined", "advanced_classes2.py", "both", id="Combined contains both"),
320 |     pytest.param("Point", "advanced_classes2.py", "magnitude", id="Point contains magnitude"),
321 |     pytest.param("Color", "advanced_classes2.py", "is_primary", id="Color contains is_primary"),
322 |     pytest.param("A", "class_instantiation.py", "greet", id="A contains greet"),
323 |     pytest.param("B", "class_instantiation.py", "greet", id="B contains greet"),
324 |     pytest.param("Fluent", "class_instantiation.py", "step1", id="Fluent contains step1"),
325 |     pytest.param("Fluent", "class_instantiation.py", "step2", id="Fluent contains step2"),
326 |     pytest.param("Fluent", "class_instantiation.py", "step3", id="Fluent contains step3"),
327 |     pytest.param("AsyncCM", "generators.py", "__aenter__", id="AsyncCM contains __aenter__"),
328 |     pytest.param("AsyncCM", "generators.py", "__aexit__", id="AsyncCM contains __aexit__"),
329 |     pytest.param("Base", "complex_classes.py", "greet", id="Base contains greet"),
330 |     pytest.param("Child", "complex_classes.py", "greet", id="Child contains greet"),
331 |     pytest.param("Child", "complex_classes.py", "static_method", id="Child contains static_method"),
332 |     pytest.param("Child", "complex_classes.py", "class_method", id="Child contains class_method"),
333 |     pytest.param("FileOpener", "context_managers.py", "__enter__", id="FileOpener contains __enter__"),
334 |     pytest.param("FileOpener", "context_managers.py", "__exit__", id="FileOpener contains __exit__"),
335 |     pytest.param("C", "dynamic_dispatch.py", "method", id="C contains method"),
336 |     pytest.param("Dispatcher", "mapping_calls.py", "__init__", id="Dispatcher contains __init__"),
337 |     pytest.param("Dispatcher", "mapping_calls.py", "start", id="Dispatcher contains start"),
338 |     pytest.param("Dispatcher", "mapping_calls.py", "stop", id="Dispatcher contains stop"),
339 |     pytest.param("Dispatcher", "mapping_calls.py", "call", id="Dispatcher contains call"),
340 | ]
341 | 
342 | EXPECTED_FUNCTION_CONTAINS = [
343 |     pytest.param("return_function", "advanced_functions.py", "inner", id="return_function contains inner"),
344 |     pytest.param("log_decorator", "callbacks_decorators.py", "wrapper", id="log_decorator contains wrapper"),
345 |     pytest.param("make_adder", "function_chains.py", "adder", id="make_adder contains adder"),
346 |     pytest.param("decorator", "complex_classes.py", "wrapper", id="decorator contains wrapper"),
347 | ]
348 | 
349 | EXPECTED_DECORATORS = [
350 |     pytest.param("decorated_function", "complex_classes.py", "decorator", "complex_classes.py", id="decorated_function decorated by decorator"),
351 |     pytest.param("hello", "callbacks_decorators.py", "log_decorator", "callbacks_decorators.py", id="hello decorated by log_decorator"),
352 | ]
353 | 
354 | EXPECTED_EMPTY_FILES = [
355 |     pytest.param("edge_cases/comments_only.py", id="comments_only.py is empty"),
356 |     pytest.param("edge_cases/docstring_only.py", id="docstring_only.py is empty"),
357 |     pytest.param("edge_cases/empty.py", id="empty.py is empty"),
358 |     pytest.param("edge_cases/syntax_error.py", marks=pytest.mark.skip(reason="File with syntax error should be skipped or handled gracefully"), id="syntax_error.py is skipped"),
359 |     pytest.param("module_c/__init__.py", id="module_c/__init__.py is empty"),
360 | ]
361 | 
362 | 
363 | 
364 | # ==============================================================================
365 | # == TEST IMPLEMENTATIONS
366 | # ==============================================================================
367 | 
368 | def check_query(graph, query, description):
369 |     """Helper function to execute a Cypher query and assert that a match is found."""
370 |     try:
371 |         result = graph.query(query)
372 |     except Exception as e:
373 |         pytest.fail(f"Query failed for {description} with error: {e}\nQuery was:\n{query}")
374 | 
375 |     assert result is not None, f"Query for {description} returned None.\nQuery was:\n{query}"
376 |     assert len(result) > 0, f"Query for {description} returned no records.\nQuery was:\n{query}"
377 |     assert result[0].get('count', 0) > 0, f"No match found for {description}.\nQuery was:\n{query}"
378 | 
379 | @pytest.mark.parametrize("file_name, item_name, item_label", EXPECTED_STRUCTURE)
380 | def test_file_contains_item(graph, file_name, item_name, item_label):
381 |     """Verifies that a File node correctly CONTAINS a Function or Class node."""
382 |     description = f"CONTAINS from [{file_name}] to [{item_name}]"
383 |     abs_file_path = os.path.join(SAMPLE_PROJECT_PATH, file_name)
384 |     query = f"""
385 |     MATCH (f:File {{path: '{abs_file_path}'}})-[:CONTAINS]->(item:{item_label} {{name: '{item_name}'}})
386 |     RETURN count(*) AS count
387 |     """
388 |     check_query(graph, query, description)
389 | 
390 | @pytest.mark.parametrize("child_name, child_file, parent_name, parent_file", EXPECTED_INHERITANCE)
391 | def test_inheritance_relationship(graph, child_name, child_file, parent_name, parent_file):
392 |     """Verifies that an INHERITS relationship exists between two classes."""
393 |     description = f"INHERITS from [{child_name}] to [{parent_name}]"
394 |     child_path = os.path.join(SAMPLE_PROJECT_PATH, child_file)
395 |     parent_path = os.path.join(SAMPLE_PROJECT_PATH, parent_file)
396 |     query = f"""
397 |     MATCH (child:Class {{name: '{child_name}', file_path: '{child_path}'}})-[:INHERITS]->(parent:Class {{name: '{parent_name}', file_path: '{parent_path}'}})
398 |     RETURN count(*) as count
399 |     """
400 |     check_query(graph, query, description)
401 | 
402 | @pytest.mark.parametrize("caller_name, caller_file, caller_class, callee_name, callee_file, callee_class", EXPECTED_CALLS)
403 | def test_function_call_relationship(graph, caller_name, caller_file, caller_class, callee_name, callee_file, callee_class):
404 |     """Verifies that a CALLS relationship exists by checking for nodes first, then the relationship."""
405 |     caller_path = os.path.join(SAMPLE_PROJECT_PATH, caller_file)
406 |     callee_path = os.path.join(SAMPLE_PROJECT_PATH, callee_file)
407 | 
408 |     # Build match clauses for caller and callee
409 |     if caller_class:
410 |         caller_match = f"(caller_class:Class {{name: '{caller_class}', file_path: '{caller_path}'}})-[:CONTAINS]->(caller:Function {{name: '{caller_name}'}})"
411 |     else:
412 |         caller_match = f"(caller:Function {{name: '{caller_name}', file_path: '{caller_path}'}})"
413 | 
414 |     if callee_class:
415 |         callee_match = f"(callee_class:Class {{name: '{callee_class}', file_path: '{callee_path}'}})-[:CONTAINS]->(callee:Function {{name: '{callee_name}'}})"
416 |     else:
417 |         callee_match = f"(callee:Function {{name: '{callee_name}', file_path: '{callee_path}'}})"
418 | 
419 |     # 1. Check that the caller node exists
420 |     caller_description = f"existence of caller {caller_class or 'Function'} {{name: '{caller_name}'}} in [{caller_file}]"
421 |     caller_query = f"""
422 |     MATCH {caller_match}
423 |     RETURN count(caller) as count
424 |     """
425 |     check_query(graph, caller_query, caller_description)
426 | 
427 |     # 2. Check that the callee node exists
428 |     callee_description = f"existence of callee {callee_class or 'Function'} {{name: '{callee_name}'}} in [{callee_file}]"
429 |     callee_query = f"""
430 |     MATCH {callee_match}
431 |     RETURN count(callee) as count
432 |     """
433 |     check_query(graph, callee_query, callee_description)
434 | 
435 |     # 3. Check that the CALLS relationship exists between them
436 |     relationship_description = f"CALLS from [{caller_name}] to [{callee_name}]"
437 |     relationship_query = f"""
438 |     MATCH {caller_match}
439 |     MATCH {callee_match}
440 |     MATCH (caller)-[:CALLS]->(callee)
441 |     RETURN count(*) as count
442 |     """
443 |     check_query(graph, relationship_query, relationship_description)
444 | 
445 | @pytest.mark.parametrize("file_name, module_name", EXPECTED_IMPORTS)
446 | def test_import_relationship(graph, file_name, module_name):
447 |     """Verifies that an IMPORTS relationship exists between a file and a module."""
448 |     description = f"IMPORTS from [{file_name}] to [{module_name}]"
449 |     abs_file_path = os.path.join(SAMPLE_PROJECT_PATH, file_name)
450 |     query = f"""
451 |     MATCH (f:File {{path: '{abs_file_path}'}})-[:IMPORTS]->(m:Module {{name: '{module_name}'}})
452 |     RETURN count(*) as count
453 |     """
454 |     check_query(graph, query, description)
455 | 
456 | @pytest.mark.parametrize("function_name, file_name, parameter_name", EXPECTED_PARAMETERS)
457 | def test_parameter_relationship(graph, function_name, file_name, parameter_name):
458 |     """Verifies that a HAS_PARAMETER relationship exists between a function and a parameter."""
459 |     description = f"HAS_PARAMETER from [{function_name}] to [{parameter_name}]"
460 |     abs_file_path = os.path.join(SAMPLE_PROJECT_PATH, file_name)
461 |     query = f"""
462 |     MATCH (f:Function {{name: '{function_name}', file_path: '{abs_file_path}'}})-[:HAS_PARAMETER]->(p:Parameter {{name: '{parameter_name}'}})
463 |     RETURN count(*) as count
464 |     """
465 |     check_query(graph, query, description)
466 | 
467 | @pytest.mark.parametrize("class_name, file_name, method_name", EXPECTED_CLASS_METHODS)
468 | def test_class_method_relationship(graph, class_name, file_name, method_name):
469 |     """Verifies that a CONTAINS relationship exists between a class and a method."""
470 |     description = f"CONTAINS from [{class_name}] to [{method_name}]"
471 |     abs_file_path = os.path.join(SAMPLE_PROJECT_PATH, file_name)
472 |     query = f"""
473 |     MATCH (c:Class {{name: '{class_name}', file_path: '{abs_file_path}'}})-[:CONTAINS]->(m:Function {{name: '{method_name}'}})
474 |     RETURN count(*) as count
475 |     """
476 |     check_query(graph, query, description)
477 | 
478 | @pytest.mark.parametrize("outer_function_name, file_name, inner_function_name", EXPECTED_FUNCTION_CONTAINS)
479 | def test_function_contains_relationship(graph, outer_function_name, file_name, inner_function_name):
480 |     """Verifies that a CONTAINS relationship exists between an outer function and an inner function."""
481 |     description = f"CONTAINS from [{outer_function_name}] to [{inner_function_name}]"
482 |     abs_file_path = os.path.join(SAMPLE_PROJECT_PATH, file_name)
483 |     query = f"""
484 |     MATCH (outer:Function {{name: '{outer_function_name}', file_path: '{abs_file_path}'}})-[:CONTAINS]->(inner:Function {{name: '{inner_function_name}'}})
485 |     RETURN count(*) as count
486 |     """
487 |     check_query(graph, query, description)
488 | 
```
Page 14/18FirstPrevNextLast