diff --git a/lib/components/normal-components/Board.ts b/lib/components/normal-components/Board.ts index f2aea3b6..5c899459 100644 --- a/lib/components/normal-components/Board.ts +++ b/lib/components/normal-components/Board.ts @@ -149,6 +149,9 @@ export class Board extends Group { if (this.root?.pcbDisabled) return if (this.getInheritedProperty("routingDisabled")) return const { db } = this.root! + + super.doInitialPcbDesignRuleChecks() + const errors = checkEachPcbTraceNonOverlapping(db.toArray()) for (const error of errors) { db.pcb_trace_error.insert(error) diff --git a/lib/components/primitive-components/Group/Group.ts b/lib/components/primitive-components/Group/Group.ts index 769bd401..ff7400ca 100644 --- a/lib/components/primitive-components/Group/Group.ts +++ b/lib/components/primitive-components/Group/Group.ts @@ -25,6 +25,8 @@ import { TraceHint } from "../TraceHint" import type { ISubcircuit } from "./ISubcircuit" import { getSimpleRouteJsonFromCircuitJson } from "lib/utils/public-exports" import type { GenericLocalAutorouter } from "lib/utils/autorouting/GenericLocalAutorouter" +import { checkEachPcbTraceNonOverlapping } from "@tscircuit/checks" +import type { PrimitiveComponent } from "lib/components/base-components/PrimitiveComponent" export class Group = typeof groupProps> extends NormalComponent @@ -714,4 +716,41 @@ export class Group = typeof groupProps> const autorouter = this._getAutorouterConfig() return autorouter.groupMode === "sequential-trace" } + + doInitialPcbDesignRuleChecks() { + if (this.root?.pcbDisabled) return + if (this.getInheritedProperty("routingDisabled")) return + const { db } = this.root! + + if (this.isSubcircuit) { + const subcircuitComponentsByName = new Map() + + for (const child of this.children) { + // Skip if child is itself a subcircuit + if ((child as any).isSubcircuit) continue + + if (child._parsedProps.name) { + const components = + subcircuitComponentsByName.get(child._parsedProps.name) || [] + components.push(child) + subcircuitComponentsByName.set(child._parsedProps.name, components) + } + } + + for (const [name, components] of subcircuitComponentsByName.entries()) { + if (components.length > 1) { + db.pcb_trace_error.insert({ + error_type: "pcb_trace_error", + message: `Multiple components found with name "${name}" in subcircuit "${this._parsedProps.name || "unnamed"}". Component names must be unique within a subcircuit.`, + source_trace_id: "", + pcb_trace_id: "", + pcb_component_ids: components + .map((c) => c.pcb_component_id!) + .filter(Boolean), + pcb_port_ids: [], + }) + } + } + } + } } diff --git a/tests/repros/repro12-same-name-of-components.test.tsx b/tests/repros/repro12-same-name-of-components.test.tsx new file mode 100644 index 00000000..04ac31ae --- /dev/null +++ b/tests/repros/repro12-same-name-of-components.test.tsx @@ -0,0 +1,96 @@ +import { getTestFixture } from "tests/fixtures/get-test-fixture" +import { test } from "bun:test" +import { expect } from "bun:test" +test("repro-12: same name of components in subcircuit", async () => { + const { circuit } = getTestFixture() + + circuit.add( + + + + + , + ) + + await circuit.renderUntilSettled() + + const pcb_trace_errors = circuit.db.pcb_trace_error.list() + expect(pcb_trace_errors).toHaveLength(1) +}) + +test("repro-12: same name of components in board", async () => { + const { circuit } = getTestFixture() + + circuit.add( + + + + + , + ) + + await circuit.renderUntilSettled() + + const pcb_trace_errors = circuit.db.pcb_trace_error.list() + expect(pcb_trace_errors).toHaveLength(1) +}) + +test("repro-12: same name of components in different subcircuits should not be an error", async () => { + const { circuit } = getTestFixture() + + circuit.add( + + + + + + + + + + + , + ) + + await circuit.renderUntilSettled() + + // No errors because the components are in different subcircuits + const pcb_trace_errors = circuit.db.pcb_trace_error.list() + expect(pcb_trace_errors).toHaveLength(0) +})