Skip to content
Closed
Show file tree
Hide file tree
Changes from 3 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
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,23 @@ export abstract class PrimitiveComponent<
pcb_component_id: string | null = null
cad_component_id: string | null = null
_reportedInvalidPcbCalcWarnings = new Set<string>()

private _reportInvalidComponentPropertyError(
propertyName: string,
message: string,
): void {
if (!this.root || this._reportedInvalidPcbCalcWarnings.has(propertyName)) {
return
}

this.root.db.source_invalid_component_property_error.insert({
source_component_id: this.source_component_id || "",
property_name: propertyName,
message,
error_type: "source_invalid_component_property_error",
})
this._reportedInvalidPcbCalcWarnings.add(propertyName)
}
fallbackUnassignedName?: string

constructor(props: z.input<ZodProps>) {
Expand Down Expand Up @@ -227,7 +244,18 @@ export abstract class PrimitiveComponent<
} = {},
): number {
if (rawValue == null) return 0
if (typeof rawValue === "number") return rawValue
if (typeof rawValue === "number") {
if (Number.isNaN(rawValue)) {
const rawAxisProp = (this.props as any)?.[axis]
const hasRawCalcString =
typeof rawAxisProp === "string" &&
rawAxisProp.trim().toLowerCase().startsWith("calc")
if (hasRawCalcString) {
return this._resolvePcbCoordinate(rawAxisProp, axis, options)
}
}
return rawValue
}
if (typeof rawValue !== "string") {
throw new Error(
`Invalid ${axis} value for ${this.componentName}: ${String(rawValue)}`,
Expand Down Expand Up @@ -282,30 +310,24 @@ export abstract class PrimitiveComponent<
)

if (includesComponentVariable && !allowComponentVariables) {
if (
this._isInsideFootprint() &&
this.root &&
!this._reportedInvalidPcbCalcWarnings.has(axis)
) {
this.root.db.source_invalid_component_property_error.insert({
source_component_id: this.source_component_id || "",
property_name: axis,
message:
`component-relative calc references are not supported for footprint elements (${this.componentName}); ` +
if (this._isInsideFootprint() && this.root) {
this._reportInvalidComponentPropertyError(
axis,
`component-relative calc references are not supported for footprint elements (${this.componentName}); ` +
`${axis} will be ignored. expression="${rawValue}"`,
error_type: "source_invalid_component_property_error",
})
this._reportedInvalidPcbCalcWarnings.add(axis)
)
}
return 0
}

return evaluateCalcString(rawValue, { knownVariables })
} catch (error) {
const message = error instanceof Error ? error.message : String(error)
throw new Error(
`Invalid ${axis} value for ${this.componentName}: ${message}`,
this._reportInvalidComponentPropertyError(
axis,
`Invalid ${axis} value for ${this.componentName}: ${message}. expression="${rawValue}"`,
)
return 0
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -303,7 +303,12 @@ function getComponentRefsForCalcPlacement(

const addRefs = (rawValue: unknown) => {
if (typeof rawValue !== "string") return
const identifiers = extractCalcIdentifiers(rawValue)
let identifiers: string[] = []
try {
identifiers = extractCalcIdentifiers(rawValue)
} catch {
return
}
for (const identifier of identifiers) {
if (!identifier.startsWith("board.")) {
refs.add(identifier)
Expand Down
109 changes: 109 additions & 0 deletions tests/components/pcb/calc-pcb-position-malformed-wrapper.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
import { expect, test } from "bun:test"
import { getTestFixture } from "tests/fixtures/get-test-fixture"

test("pcb calc rejects missing closing paren in calc wrapper for normal component", () => {
const { circuit } = getTestFixture()

circuit.add(
<board width="40mm" height="20mm">
<resistor
name="R1"
footprint="0402"
resistance="1k"
pcbX="calc(board.minX + 1mm"
/>
</board>,
)

circuit.render()

const invalidPropertyErrors =
circuit.db.source_invalid_component_property_error.list()
expect(invalidPropertyErrors.length).toBeGreaterThan(0)

const message = invalidPropertyErrors
.filter((element) => "message" in element)
.map((element) => element.message)
.join("\n")

expect(message).toContain("Invalid pcbX value")

expect(
invalidPropertyErrors.some(
(element) =>
"property_name" in element && element.property_name === "pcbX",
),
).toBe(true)
})

test("pcb calc rejects malformed calc wrapper token for normal component", () => {
const { circuit } = getTestFixture()

circuit.add(
<board width="40mm" height="20mm">
<resistor
name="R1"
footprint="0402"
resistance="1k"
pcbX="calc board.minX + 1mm)"
/>
</board>,
)

circuit.render()

const invalidPropertyErrors =
circuit.db.source_invalid_component_property_error.list()
expect(invalidPropertyErrors.length).toBeGreaterThan(0)

const message = invalidPropertyErrors
.filter((element) => "message" in element)
.map((element) => element.message)
.join("\n")

expect(message).toContain("Invalid pcbX value")

expect(
invalidPropertyErrors.some(
(element) =>
"property_name" in element && element.property_name === "pcbX",
),
).toBe(true)
})

test("pcb calc reports invalid inner calc expression for normal component", () => {
const { circuit } = getTestFixture()

circuit.add(
<board width="40mm" height="20mm">
<resistor
name="R1"
footprint="0402"
resistance="1k"
pcbX="calc(board.minX + )"
/>
</board>,
)

circuit.render()

const invalidPropertyErrors =
circuit.db.source_invalid_component_property_error.list()
expect(invalidPropertyErrors.length).toBeGreaterThan(0)

const message = invalidPropertyErrors
.filter((element) => "message" in element)
.map((element) => element.message)
.join("\n")

expect(message).toContain("Invalid pcbX value")
expect(message).toContain("Unexpected end of expression")
expect(message).toContain('expression="calc(board.minX + )"')

expect(
invalidPropertyErrors.some(
(element) =>
"property_name" in element && element.property_name === "pcbX",
),
).toBe(true)
})
Loading