import {
CompilerDiagnostic,
CompilerError,
ErrorCategory,
} from '../CompilerError';
import {HIRFunction, IdentifierId, SourceLocation} from '../HIR';
import {Result} from '../Utils/Result';
export function validateStaticComponents(
fn: HIRFunction,
): Result<void, CompilerError> {
const error = new CompilerError();
const knownDynamicComponents = new Map<IdentifierId, SourceLocation>();
for (const block of fn.body.blocks.values()) {
phis: for (const phi of block.phis) {
for (const operand of phi.operands.values()) {
const loc = knownDynamicComponents.get(operand.identifier.id);
if (loc != null) {
knownDynamicComponents.set(phi.place.identifier.id, loc);
continue phis;
}
}
}
for (const instr of block.instructions) {
const {lvalue, value} = instr;
switch (value.kind) {
case 'FunctionExpression':
case 'NewExpression':
case 'MethodCall':
case 'CallExpression': {
knownDynamicComponents.set(lvalue.identifier.id, value.loc);
break;
}
case 'LoadLocal': {
const loc = knownDynamicComponents.get(value.place.identifier.id);
if (loc != null) {
knownDynamicComponents.set(lvalue.identifier.id, loc);
}
break;
}
case 'StoreLocal': {
const loc = knownDynamicComponents.get(value.value.identifier.id);
if (loc != null) {
knownDynamicComponents.set(lvalue.identifier.id, loc);
knownDynamicComponents.set(value.lvalue.place.identifier.id, loc);
}
break;
}
case 'JsxExpression': {
if (value.tag.kind === 'Identifier') {
const location = knownDynamicComponents.get(
value.tag.identifier.id,
);
if (location != null) {
error.pushDiagnostic(
CompilerDiagnostic.create({
category: ErrorCategory.StaticComponents,
reason: 'Cannot create components during render',
description: `Components created during render will reset their state each time they are created. Declare components outside of render`,
})
.withDetails({
kind: 'error',
loc: value.tag.loc,
message: 'This component is created during render',
})
.withDetails({
kind: 'error',
loc: location,
message: 'The component is created during render here',
}),
);
}
}
}
}
}
}
return error.asResult();
}