From 6b09d93df2755cbcd8dfb107612369c4f02d85eb Mon Sep 17 00:00:00 2001 From: Eugene Mikhantyev Date: Sun, 15 Mar 2026 12:46:50 +0000 Subject: [PATCH] fix: check_wire_collisions now detects wires that short two pins of the same component Previously, the endpoint suppression logic skipped any wire where at least one endpoint touched a pin, hiding the case where both endpoints are pins of the same component (a direct pin-to-pin short through the body). Replace the single endpoint_matches_pin loop with separate start_at_pin / end_at_pin checks; suppress only when exactly one endpoint is at a pin. Add regression test to cover this case. Co-Authored-By: Claude Sonnet 4.6 --- python/commands/schematic_analysis.py | 30 +++++++++++++++---------- python/tests/test_schematic_analysis.py | 17 ++++++++++++++ 2 files changed, 35 insertions(+), 12 deletions(-) diff --git a/python/commands/schematic_analysis.py b/python/commands/schematic_analysis.py index b8a3b82..5c7fe7f 100644 --- a/python/commands/schematic_analysis.py +++ b/python/commands/schematic_analysis.py @@ -654,19 +654,25 @@ def check_wire_collisions(schematic_path: Path) -> List[Dict[str, Any]]: if not _line_segment_intersects_aabb(sx, sy, ex, ey, bx1, by1, bx2, by2): continue - # Check if either wire endpoint matches a pin of this symbol - endpoint_matches_pin = False - for px, py in sd["pin_set"]: - if (abs(sx - px) < pin_tolerance and abs(sy - py) < pin_tolerance): - endpoint_matches_pin = True - break - if (abs(ex - px) < pin_tolerance and abs(ey - py) < pin_tolerance): - endpoint_matches_pin = True - break + # Check which endpoints land on a pin of this symbol + start_at_pin = any( + abs(sx - px) < pin_tolerance and abs(sy - py) < pin_tolerance + for px, py in sd["pin_set"] + ) + end_at_pin = any( + abs(ex - px) < pin_tolerance and abs(ey - py) < pin_tolerance + for px, py in sd["pin_set"] + ) - if not endpoint_matches_pin: - sym = sd["sym"] - collisions.append({ + # Suppress only when exactly ONE endpoint is at a pin: the wire arrives + # from elsewhere and terminates at this component (a valid connection). + # If BOTH endpoints match pins of this same component, the wire shorts + # two pins while traversing the body — that IS a collision. + if (start_at_pin or end_at_pin) and not (start_at_pin and end_at_pin): + continue + + sym = sd["sym"] + collisions.append({ "wire": { "start": {"x": sx, "y": sy}, "end": {"x": ex, "y": ey}, diff --git a/python/tests/test_schematic_analysis.py b/python/tests/test_schematic_analysis.py index 35b6d1e..d9cdc30 100644 --- a/python/tests/test_schematic_analysis.py +++ b/python/tests/test_schematic_analysis.py @@ -415,6 +415,23 @@ class TestIntegrationCheckWireCollisions: "likely caused by reference-lookup always returning the first 'R?'" ) + def test_wire_shorts_component_pins_detected_as_collision(self): + """Regression: a wire connecting pin1→pin2 of the same component + must be reported even though both endpoints land on pins.""" + r_sexp = _make_resistor_sexp("R_short", 100.0, 100.0) + wire_sexp = ( + '(wire (pts (xy 100 103.81) (xy 100 96.19))\n' + ' (stroke (width 0) (type default))\n' + ' (uuid "aaaaaaaa-0000-0000-0000-000000000001"))' + ) + sch = _make_temp_schematic(r_sexp + "\n" + wire_sexp) + collisions = check_wire_collisions(sch) + assert len(collisions) == 1 + w = collisions[0]["wire"] + assert w["start"]["x"] == pytest.approx(100.0) + assert w["start"]["y"] == pytest.approx(103.81) + assert collisions[0]["component"]["reference"] == "R_short" + @pytest.mark.integration class TestIntegrationGetElementsInRegion: