/// Correctness for protocols

/*

This section of code makes sure the protocol is likely to generate
correct code. The correctness criteria include:

  * No protocols transition to states that don't exist.
  * Messages step to states with the right number of type parameters.

In addition, this serves as a lint pass. Lint warns for the following
things.

  * States with no messages, it's better to step to !.

It would also be nice to warn about unreachable states, but the
visitor infrastructure for protocols doesn't currently work well for
that.

*/

use ext::base::ext_ctxt;

use proto::{state, protocol, next_state};
use ast_builder::empty_span;

impl ext_ctxt: proto::visitor<(), (), ()>  {
    fn visit_proto(_proto: protocol,
                   _states: &[()]) { }

    fn visit_state(state: state, _m: &[()]) {
        if state.messages.len() == 0 {
            self.span_warn(
                state.span, // use a real span!
                fmt!("state %s contains no messages, \
                      consider stepping to a terminal state instead",
                      state.name))
        }
    }

    fn visit_message(name: ~str, _span: span, _tys: &[@ast::ty],
                     this: state, next: next_state) {
        match next {
          Some({state: next, tys: next_tys}) => {
            let proto = this.proto;
            if !proto.has_state(next) {
                // This should be a span fatal, but then we need to
                // track span information.
                self.span_err(
                    proto.get_state(next).span,
                    fmt!("message %s steps to undefined state, %s",
                         name, next));
            }
            else {
                let next = proto.get_state(next);

                if next.ty_params.len() != next_tys.len() {
                    self.span_err(
                        next.span, // use a real span
                        fmt!("message %s target (%s) \
                              needs %u type parameters, but got %u",
                             name, next.name,
                             next.ty_params.len(),
                             next_tys.len()));
                }
            }
          }
          None => ()
        }
    }
}