/**
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

import {CompilerError} from '..';
import {BlockId, ReactiveFunction, ReactiveTerminalStatement} from '../HIR';
import {ReactiveFunctionVisitor, visitReactiveFunction} from './visitors';

/**
 * Assert that all break/continue targets reference existent labels.
 */
export function assertWellFormedBreakTargets(fn: ReactiveFunction): void {
  visitReactiveFunction(fn, new Visitor(), new Set());
}

class Visitor extends ReactiveFunctionVisitor<Set<BlockId>> {
  override visitTerminal(
    stmt: ReactiveTerminalStatement,
    seenLabels: Set<BlockId>,
  ): void {
    if (stmt.label != null) {
      seenLabels.add(stmt.label.id);
    }
    const terminal = stmt.terminal;
    if (terminal.kind === 'break' || terminal.kind === 'continue') {
      CompilerError.invariant(seenLabels.has(terminal.target), {
        reason: 'Unexpected break to invalid label',
        description: null,
        details: [
          {
            kind: 'error',
            loc: stmt.terminal.loc,
            message: null,
          },
        ],
      });
    }
  }
}
