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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,11 @@ uv run pytest tests/test_parser.py -v

CI runs the suite on Python 3.10, 3.11, 3.12 and 3.13 and fails below 65% coverage.

`tests/test_parser_grammar_contract.py` checks every built-in class, function, import,
and call dispatch name against its bundled Tree-sitter grammar. When updating a grammar
or adding dispatch entries, use the grammar's actual named node types. This contract
checks configured dispatch names; language fixtures still verify extraction behavior.

## Linting and Type Checking

```bash
Expand Down
10 changes: 5 additions & 5 deletions code_review_graph/parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -1173,13 +1173,13 @@ def __post_init__(self) -> None:
"csharp": [
"class_declaration", "interface_declaration",
"enum_declaration", "struct_declaration",
"record_declaration", "record_struct_declaration",
"record_declaration",
],
"ruby": ["class", "module"],
"r": [], # Classes detected via call pattern-matching, not AST node types
"perl": ["package_statement", "class_statement", "role_statement"],
"kotlin": ["class_declaration", "object_declaration"],
"swift": ["class_declaration", "struct_declaration", "protocol_declaration"],
"swift": ["class_declaration", "protocol_declaration"],
"php": [
"class_declaration", "interface_declaration",
"trait_declaration", "enum_declaration",
Expand All @@ -1197,7 +1197,7 @@ def __post_init__(self) -> None:
"luau": ["type_definition"], # Luau type aliases; table-based OOP via constructs handler
"objc": [
"class_interface", "class_implementation",
"category_interface", "protocol_declaration",
"protocol_declaration",
],
"bash": [], # Shell has no classes
# Elixir: `defmodule Name do ... end` is a ``call`` node whose first
Expand Down Expand Up @@ -1393,7 +1393,7 @@ def __post_init__(self) -> None:
"c": ["call_expression"],
"cpp": ["call_expression"],
"csharp": ["invocation_expression", "object_creation_expression"],
"ruby": ["call", "method_call"],
"ruby": ["call"],
"r": ["call"],
"perl": [
"function_call_expression", "method_call_expression",
Expand Down Expand Up @@ -1429,7 +1429,7 @@ def __post_init__(self) -> None:
# SuffixExpr); calls are walked explicitly in
# _extract_zig_calls_in_subtree from inside function bodies.
"zig": [],
"powershell": ["command_expression"],
"powershell": ["command"],
"julia": [
"call_expression",
"broadcast_call_expression",
Expand Down
25 changes: 25 additions & 0 deletions tests/test_parser_grammar_contract.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
"""Grammar dispatch names must exist in the bundled grammar (issue #986)."""

import pytest
from tree_sitter_language_pack import get_language

from code_review_graph.parser import _CALL_TYPES, _CLASS_TYPES, _FUNCTION_TYPES, _IMPORT_TYPES


@pytest.mark.parametrize(
("table", "language", "node_type"),
[
(table, language, node_type)
for table, mapping in (
("classes", _CLASS_TYPES), ("functions", _FUNCTION_TYPES),
("imports", _IMPORT_TYPES), ("calls", _CALL_TYPES),
)
for language, names in mapping.items()
for node_type in names
],
)
def test_dispatch_type_exists_in_grammar(table, language, node_type):
grammar = get_language(language)
assert grammar.id_for_node_kind(node_type, True) is not None, (
f"{table}[{language!r}] contains nonexistent named node {node_type!r}"
)
Loading