Skip to content

Commit

Permalink
Report all type mismatches (#939)
Browse files Browse the repository at this point in the history
  • Loading branch information
RunDevelopment committed Sep 12, 2022
1 parent a3d5a52 commit 0eb9b68
Show file tree
Hide file tree
Showing 2 changed files with 87 additions and 2 deletions.
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { Center, Icon, Spinner, Tooltip } from '@chakra-ui/react';
import { memo } from 'react';
import { BsCheck, BsExclamation } from 'react-icons/bs';
import ReactMarkdown from 'react-markdown';
import { Validity } from '../../../helpers/checkNodeValidity';

interface ValidityIndicatorProps {
Expand Down Expand Up @@ -29,7 +30,9 @@ export const ValidityIndicator = memo(({ validity, animated }: ValidityIndicator
borderRadius={8}
closeOnClick={false}
gutter={24}
label={validity.isValid ? 'Node valid' : validity.reason}
label={
<ReactMarkdown>{validity.isValid ? 'Node valid' : validity.reason}</ReactMarkdown>
}
px={2}
textAlign="center"
>
Expand Down
84 changes: 83 additions & 1 deletion src/renderer/helpers/checkNodeValidity.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { evaluate } from '../../common/types/evaluate';
import { IntersectionExpression, NamedExpression } from '../../common/types/expression';
import { FunctionInstance } from '../../common/types/function';
import { isDisjointWith } from '../../common/types/intersection';
import { IntIntervalType, NumericLiteralType, Type } from '../../common/types/types';
import { IntIntervalType, NumericLiteralType, StructType, Type } from '../../common/types/types';
import { IntNumberType, isImage } from '../../common/types/util';
import { parseTargetHandle } from '../../common/util';

Expand Down Expand Up @@ -77,6 +77,76 @@ const formatMissingInputs = (missingInputs: Input[]) => {
return `Missing required input data: ${missingInputs.map((input) => input.label).join(', ')}`;
};

type AssignmentErrorTrace = FieldAssignmentError | GeneralAssignmentError;
interface GeneralAssignmentError {
type: 'General';
assigned: Type;
definition: Type;
}
interface FieldAssignmentError {
type: 'Field';
assigned: StructType;
definition: StructType;
field: string;
inner: AssignmentErrorTrace;
}

const generateAssignmentErrorTrace = (assigned: Type, definition: Type): AssignmentErrorTrace => {
if (
assigned.type === 'struct' &&
definition.type === 'struct' &&
assigned.name === definition.name
) {
// find the field that causes the mismatch
const mismatchIndex = assigned.fields.findIndex((a, i) => {
return isDisjointWith(a.type, definition.fields[i].type);
});

if (mismatchIndex !== -1) {
const a = assigned.fields[mismatchIndex];
const d = definition.fields[mismatchIndex];

return {
type: 'Field',
assigned,
definition,
field: a.name,

inner: generateAssignmentErrorTrace(a.type, d.type),
};
}
}

return { type: 'General', assigned, definition };
};

const printErrorTrace = (trace: AssignmentErrorTrace): string[] => {
const { assigned, definition } = trace;

if (trace.type === 'General') {
return [
`The type **${assigned.toString()}** is not connectable with **${definition.toString()}**.`,
];
}

if (trace.inner.type === 'General') {
return [
`The **${trace.assigned.name}** types are incompatible because **${
trace.field
}: ${trace.inner.assigned.toString()}** is not connectable with **${
trace.field
}: ${trace.inner.definition.toString()}**.`,
];
}

return [
`The type **${assigned.toString()}** is not connectable with **${definition.toString()}** because the **${
trace.field
}** fields are incompatible.`,
...printErrorTrace(trace.inner),
];
};

export interface CheckNodeValidityOptions {
id: string;
schema: NodeSchema;
Expand Down Expand Up @@ -154,6 +224,18 @@ export const checkNodeValidity = ({
}
}

if (functionInstance.inputErrors.length > 0) {
const { inputId, assignedType, inputType } = functionInstance.inputErrors[0];
const input = schema.inputs.find((i) => i.id === inputId)!;
const trace = printErrorTrace(generateAssignmentErrorTrace(assignedType, inputType));
return {
isValid: false,
reason: `Input ${
input.label
} was connected with an incompatible value. ${trace.join(' ')}`,
};
}

// eslint-disable-next-line no-unreachable-loop
for (const { outputId } of functionInstance.outputErrors) {
const output = schema.outputs.find((o) => o.id === outputId)!;
Expand Down

0 comments on commit 0eb9b68

Please sign in to comment.