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
7 changes: 6 additions & 1 deletion code_review_graph/changes.py
Original file line number Diff line number Diff line change
Expand Up @@ -309,10 +309,15 @@ def analyze_changes(
for fp in changed_files:
changed_nodes.extend(store.get_nodes_by_file(fp))

# Filter to functions/tests for risk scoring (skip File nodes).
# Filter to functions/tests for risk scoring (skip File nodes). Verilog/
# SystemVerilog signal-level nodes (ports/nets/params, modeled as Function
# nodes) are declarations, not callable functions: they have no TESTED_BY
# edges, so including them would flood the output with bogus "test gap"
# entries on any .sv edit.
changed_funcs = [
n for n in changed_nodes
if n.kind in ("Function", "Test", "Class")
and not n.extra.get("verilog_kind")
]

# Cap to prevent O(N*M) query explosion on large PRs.
Expand Down
6 changes: 6 additions & 0 deletions code_review_graph/flows.py
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,12 @@ def detect_entry_points(
if not include_tests and (node.is_test or _is_test_file(node.file_path)):
continue

# Skip Verilog/SystemVerilog signal-level nodes (ports/nets/params,
# modeled as Function nodes): they have no incoming CALLS edges and
# would otherwise all register as bogus entry-point roots.
if node.extra.get("verilog_kind"):
continue

is_entry = False

# True root: no one calls this function.
Expand Down
26 changes: 24 additions & 2 deletions code_review_graph/graph.py
Original file line number Diff line number Diff line change
Expand Up @@ -719,6 +719,14 @@ def get_impact_radius_sql(
# Batch-fetch nodes
changed_nodes = self._batch_get_nodes(seeds)
impacted_nodes = self._batch_get_nodes(impacted_qns)
# Drop Verilog/SystemVerilog signal-level nodes (ports/nets/params,
# modeled as Function nodes). They are leaves reached via CONTAINS/
# REFERENCES edges from a touched module, so excluding them removes no
# real downstream impact but stops every signal of that module from
# inflating impacted_files and the impact count.
impacted_nodes = [
n for n in impacted_nodes if not n.extra.get("verilog_kind")
]

total_impacted = len(impacted_nodes)
truncated = total_impacted > max_nodes
Expand Down Expand Up @@ -830,9 +838,19 @@ def get_stats(self) -> GraphStats:
total_nodes = self._conn.execute("SELECT COUNT(*) FROM nodes").fetchone()[0]
total_edges = self._conn.execute("SELECT COUNT(*) FROM edges").fetchone()[0]

# Verilog/SystemVerilog signal-level nodes (ports/nets/params/etc.) are
# stored as kind='Function' with a ``verilog_kind`` discriminator in
# ``extra``. Re-bucket them under 'Signal' so they do not inflate the
# user-facing Function count. (LIKE on the JSON text avoids a json1
# dependency; the key is a fixed literal, not user input.)
nodes_by_kind: dict[str, int] = {}
for row in self._conn.execute("SELECT kind, COUNT(*) as cnt FROM nodes GROUP BY kind"):
nodes_by_kind[row["kind"]] = row["cnt"]
for row in self._conn.execute(
"SELECT CASE WHEN extra LIKE '%\"verilog_kind\"%' THEN 'Signal'"
" ELSE kind END AS k, COUNT(*) as cnt FROM nodes"
" GROUP BY CASE WHEN extra LIKE '%\"verilog_kind\"%' THEN 'Signal'"
" ELSE kind END"
):
nodes_by_kind[row["k"]] = row["cnt"]

edges_by_kind: dict[str, int] = {}
for row in self._conn.execute("SELECT kind, COUNT(*) as cnt FROM edges GROUP BY kind"):
Expand Down Expand Up @@ -884,6 +902,10 @@ def get_nodes_by_size(
"line_start IS NOT NULL",
"line_end IS NOT NULL",
"(line_end - line_start + 1) >= ?",
# Exclude Verilog/SystemVerilog signal-level nodes (ports/nets/params,
# modeled as Function nodes): a multi-line covergroup/typedef is not
# an oversized function to decompose.
"extra NOT LIKE '%\"verilog_kind\"%'",
]
params: list = [min_lines]

Expand Down
Loading
Loading