<commit_before>//! This calculates the types which has storage which lives across a suspension point in a
//! generator from the perspective of typeck. The actual types used at runtime
//! is calculated in `rustc_mir::transform::generator` and may be a subset of the
//! types computed here.

use super::FnCtxt;
use rustc_data_structures::fx::{FxHashSet, FxIndexSet};
use rustc_hir as hir;
use rustc_hir::def::{CtorKind, DefKind, Res};
use rustc_hir::def_id::DefId;
use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor};
use rustc_hir::{Expr, ExprKind, Pat, PatKind};
use rustc_middle::middle::region::{self, YieldData};
use rustc_middle::ty::{self, Ty};
use rustc_span::Span;

struct InteriorVisitor<'a, 'tcx> {
    fcx: &'a FnCtxt<'a, 'tcx>,
    types: FxIndexSet<ty::GeneratorInteriorTypeCause<'tcx>>,
    region_scope_tree: &'tcx region::ScopeTree,
    expr_count: usize,
    kind: hir::GeneratorKind,
    prev_unresolved_span: Option<Span>,
}

impl<'a, 'tcx> InteriorVisitor<'a, 'tcx> {
    fn record(
        &mut self,
        ty: Ty<'tcx>,
        scope: Option<region::Scope>,
        expr: Option<&'tcx Expr<'tcx>>,
        source_span: Span,
    ) {
        use rustc_span::DUMMY_SP;

        debug!(
            "generator_interior: attempting to record type {:?} {:?} {:?} {:?}",
            ty, scope, expr, source_span
        );

        let live_across_yield = scope
            .map(|s| {
                self.region_scope_tree.yield_in_scope(s).and_then(|yield_data| {
                    // If we are recording an expression that is the last yield
                    // in the scope, or that has a postorder CFG index larger
                    // than the one of all of the yields, then its value can't
                    // be storage-live (and therefore live) at any of the yields.
                    //
                    // See the mega-comment at `yield_in_scope` for a proof.

                    debug!(
                        "comparing counts yield: {} self: {}, source_span = {:?}",
                        yield_data.expr_and_pat_count, self.expr_count, source_span
                    );

                    if yield_data.expr_and_pat_count >= self.expr_count {
                        Some(yield_data)
                    } else {
                        None
                    }
                })
            })
            .unwrap_or_else(|| {
                Some(YieldData { span: DUMMY_SP, expr_and_pat_count: 0, source: self.kind.into() })
            });

        if let Some(yield_data) = live_across_yield {
            let ty = self.fcx.resolve_vars_if_possible(&ty);
            debug!(
                "type in expr = {:?}, scope = {:?}, type = {:?}, count = {}, yield_span = {:?}",
                expr, scope, ty, self.expr_count, yield_data.span
            );

            if let Some((unresolved_type, unresolved_type_span)) =
                self.fcx.unresolved_type_vars(&ty)
            {
                let note = format!(
                    "the type is part of the {} because of this {}",
                    self.kind, yield_data.source
                );

                // If unresolved type isn't a ty_var then unresolved_type_span is None
                let span = self
                    .prev_unresolved_span
                    .unwrap_or_else(|| unresolved_type_span.unwrap_or(source_span));
                self.fcx
                    .need_type_info_err_in_generator(self.kind, span, unresolved_type)
                    .span_note(yield_data.span, &*note)
                    .emit();
            } else {
                // Insert the type into the ordered set.
                let scope_span = scope.map(|s| s.span(self.fcx.tcx, self.region_scope_tree));
                self.types.insert(ty::GeneratorInteriorTypeCause {
                    span: source_span,
                    ty: &ty,
                    scope_span,
                    yield_span: yield_data.span,
                    expr: expr.map(|e| e.hir_id),
                });
            }
        } else {
            debug!(
                "no type in expr = {:?}, count = {:?}, span = {:?}",
                expr,
                self.expr_count,
                expr.map(|e| e.span)
            );
            let ty = self.fcx.resolve_vars_if_possible(&ty);
            if let Some((unresolved_type, unresolved_type_span)) =
                self.fcx.unresolved_type_vars(&ty)
            {
                debug!(
                    "remained unresolved_type = {:?}, unresolved_type_span: {:?}",
                    unresolved_type, unresolved_type_span
                );
                self.prev_unresolved_span = unresolved_type_span;
            }
        }
    }
}

pub fn resolve_interior<'a, 'tcx>(
    fcx: &'a FnCtxt<'a, 'tcx>,
    def_id: DefId,
    body_id: hir::BodyId,
    interior: Ty<'tcx>,
    kind: hir::GeneratorKind,
) {
    let body = fcx.tcx.hir().body(body_id);
    let mut visitor = InteriorVisitor {
        fcx,
        types: FxIndexSet::default(),
        region_scope_tree: fcx.tcx.region_scope_tree(def_id),
        expr_count: 0,
        kind,
        prev_unresolved_span: None,
    };
    intravisit::walk_body(&mut visitor, body);

    // Check that we visited the same amount of expressions and the RegionResolutionVisitor
    let region_expr_count = visitor.region_scope_tree.body_expr_count(body_id).unwrap();
    assert_eq!(region_expr_count, visitor.expr_count);

    // The types are already kept in insertion order.
    let types = visitor.types;

    // The types in the generator interior contain lifetimes local to the generator itself,
    // which should not be exposed outside of the generator. Therefore, we replace these
    // lifetimes with existentially-bound lifetimes, which reflect the exact value of the
    // lifetimes not being known by users.
    //
    // These lifetimes are used in auto trait impl checking (for example,
    // if a Sync generator contains an &'α T, we need to check whether &'α T: Sync),
    // so knowledge of the exact relationships between them isn't particularly important.

    debug!("types in generator {:?}, span = {:?}", types, body.value.span);

    let mut counter = 0;
    let mut captured_tys = FxHashSet::default();
    let type_causes: Vec<_> = types
        .into_iter()
        .filter_map(|mut cause| {
            // Erase regions and canonicalize late-bound regions to deduplicate as many types as we
            // can.
            let erased = fcx.tcx.erase_regions(&cause.ty);
            if captured_tys.insert(erased) {
                // Replace all regions inside the generator interior with late bound regions.
                // Note that each region slot in the types gets a new fresh late bound region,
                // which means that none of the regions inside relate to any other, even if
                // typeck had previously found constraints that would cause them to be related.
                let folded = fcx.tcx.fold_regions(&erased, &mut false, |_, current_depth| {
                    counter += 1;
                    fcx.tcx.mk_region(ty::ReLateBound(current_depth, ty::BrAnon(counter)))
                });

                cause.ty = folded;
                Some(cause)
            } else {
                None
            }
        })
        .collect();

    // Extract type components to build the witness type.
    let type_list = fcx.tcx.mk_type_list(type_causes.iter().map(|cause| cause.ty));
    let witness = fcx.tcx.mk_generator_witness(ty::Binder::bind(type_list));

    // Store the generator types and spans into the typeck results for this generator.
    visitor.fcx.inh.typeck_results.borrow_mut().generator_interior_types = type_causes;

    debug!(
        "types in generator after region replacement {:?}, span = {:?}",
        witness, body.value.span
    );

    // Unify the type variable inside the generator with the new witness
    match fcx.at(&fcx.misc(body.value.span), fcx.param_env).eq(interior, witness) {
        Ok(ok) => fcx.register_infer_ok_obligations(ok),
        _ => bug!(),
    }
}

// This visitor has to have the same visit_expr calls as RegionResolutionVisitor in
// librustc_middle/middle/region.rs since `expr_count` is compared against the results
// there.
impl<'a, 'tcx> Visitor<'tcx> for InteriorVisitor<'a, 'tcx> {
    type Map = intravisit::ErasedMap<'tcx>;

    fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
        NestedVisitorMap::None
    }

    fn visit_pat(&mut self, pat: &'tcx Pat<'tcx>) {
        intravisit::walk_pat(self, pat);

        self.expr_count += 1;

        if let PatKind::Binding(..) = pat.kind {
            let scope = self.region_scope_tree.var_scope(pat.hir_id.local_id);
            let ty = self.fcx.typeck_results.borrow().pat_ty(pat);
            self.record(ty, Some(scope), None, pat.span);
        }
    }

    fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) {
        match &expr.kind {
            ExprKind::Call(callee, args) => match &callee.kind {
                ExprKind::Path(qpath) => {
                    let res = self.fcx.typeck_results.borrow().qpath_res(qpath, callee.hir_id);
                    match res {
                        // Direct calls never need to keep the callee `ty::FnDef`
                        // ZST in a temporary, so skip its type, just in case it
                        // can significantly complicate the generator type.
                        Res::Def(
                            DefKind::Fn | DefKind::AssocFn | DefKind::Ctor(_, CtorKind::Fn),
                            _,
                        ) => {
                            // NOTE(eddyb) this assumes a path expression has
                            // no nested expressions to keep track of.
                            self.expr_count += 1;

                            // Record the rest of the call expression normally.
                            for arg in *args {
                                self.visit_expr(arg);
                            }
                        }
                        _ => intravisit::walk_expr(self, expr),
                    }
                }
                _ => intravisit::walk_expr(self, expr),
            },
            _ => intravisit::walk_expr(self, expr),
        }

        self.expr_count += 1;

        let scope = self.region_scope_tree.temporary_scope(expr.hir_id.local_id);

        // If there are adjustments, then record the final type --
        // this is the actual value that is being produced.
        if let Some(adjusted_ty) = self.fcx.typeck_results.borrow().expr_ty_adjusted_opt(expr) {
            self.record(adjusted_ty, scope, Some(expr), expr.span);
        }

        // Also record the unadjusted type (which is the only type if
        // there are no adjustments). The reason for this is that the
        // unadjusted value is sometimes a "temporary" that would wind
        // up in a MIR temporary.
        //
        // As an example, consider an expression like `vec![].push()`.
        // Here, the `vec![]` would wind up MIR stored into a
        // temporary variable `t` which we can borrow to invoke
        // `<Vec<_>>::push(&mut t)`.
        //
        // Note that an expression can have many adjustments, and we
        // are just ignoring those intermediate types. This is because
        // those intermediate values are always linearly "consumed" by
        // the other adjustments, and hence would never be directly
        // captured in the MIR.
        //
        // (Note that this partly relies on the fact that the `Deref`
        // traits always return references, which means their content
        // can be reborrowed without needing to spill to a temporary.
        // If this were not the case, then we could conceivably have
        // to create intermediate temporaries.)
        //
        // The type table might not have information for this expression
        // if it is in a malformed scope. (#66387)
        if let Some(ty) = self.fcx.typeck_results.borrow().expr_ty_opt(expr) {
            self.record(ty, scope, Some(expr), expr.span);
        } else {
            self.fcx.tcx.sess.delay_span_bug(expr.span, "no type for node");
        }
    }
}
<commit_msg>also record the types of borrows from the pattern locals in match guards<commit_after>//! This calculates the types which has storage which lives across a suspension point in a
//! generator from the perspective of typeck. The actual types used at runtime
//! is calculated in `rustc_mir::transform::generator` and may be a subset of the
//! types computed here.

use super::FnCtxt;
use rustc_data_structures::fx::{FxHashSet, FxIndexSet};
use rustc_hir as hir;
use rustc_hir::def::{CtorKind, DefKind, Res};
use rustc_hir::def_id::DefId;
use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor};
use rustc_hir::{Expr, ExprKind, Pat, PatKind, Arm, Guard, HirId};
use rustc_hir::hir_id::HirIdSet;
use rustc_middle::middle::region::{self, YieldData};
use rustc_middle::ty::{self, Ty};
use rustc_span::Span;
use smallvec::SmallVec;

struct InteriorVisitor<'a, 'tcx> {
    fcx: &'a FnCtxt<'a, 'tcx>,
    types: FxIndexSet<ty::GeneratorInteriorTypeCause<'tcx>>,
    region_scope_tree: &'tcx region::ScopeTree,
    expr_count: usize,
    kind: hir::GeneratorKind,
    prev_unresolved_span: Option<Span>,
    /// Match arm guards have temporary borrows from the pattern bindings.
    /// In case there is a yield point in a guard with a reference to such bindings,
    /// such borrows can span across this yield point.
    /// As such, we need to track these borrows and record them despite of the fact
    /// that they may succeed the said yield point in the post-order.
    nested_scope_of_guards: SmallVec<[SmallVec<[HirId; 4]>; 4]>,
    current_scope_of_guards: HirIdSet,
    arm_has_guard: bool,
}

impl<'a, 'tcx> InteriorVisitor<'a, 'tcx> {
    fn record(
        &mut self,
        ty: Ty<'tcx>,
        scope: Option<region::Scope>,
        expr: Option<&'tcx Expr<'tcx>>,
        source_span: Span,
        guard_borrowing_from_pattern: bool,
    ) {
        use rustc_span::DUMMY_SP;

        debug!(
            "generator_interior: attempting to record type {:?} {:?} {:?} {:?}",
            ty, scope, expr, source_span
        );

        let live_across_yield = scope
            .map(|s| {
                self.region_scope_tree.yield_in_scope(s).and_then(|yield_data| {
                    // If we are recording an expression that is the last yield
                    // in the scope, or that has a postorder CFG index larger
                    // than the one of all of the yields, then its value can't
                    // be storage-live (and therefore live) at any of the yields.
                    //
                    // See the mega-comment at `yield_in_scope` for a proof.

                    debug!(
                        "comparing counts yield: {} self: {}, source_span = {:?}",
                        yield_data.expr_and_pat_count, self.expr_count, source_span
                    );

                    if guard_borrowing_from_pattern || yield_data.expr_and_pat_count >= self.expr_count {
                        Some(yield_data)
                    } else {
                        None
                    }
                })
            })
            .unwrap_or_else(|| {
                Some(YieldData { span: DUMMY_SP, expr_and_pat_count: 0, source: self.kind.into() })
            });

        if let Some(yield_data) = live_across_yield {
            let ty = self.fcx.resolve_vars_if_possible(&ty);
            debug!(
                "type in expr = {:?}, scope = {:?}, type = {:?}, count = {}, yield_span = {:?}",
                expr, scope, ty, self.expr_count, yield_data.span
            );

            if let Some((unresolved_type, unresolved_type_span)) =
                self.fcx.unresolved_type_vars(&ty)
            {
                let note = format!(
                    "the type is part of the {} because of this {}",
                    self.kind, yield_data.source
                );

                // If unresolved type isn't a ty_var then unresolved_type_span is None
                let span = self
                    .prev_unresolved_span
                    .unwrap_or_else(|| unresolved_type_span.unwrap_or(source_span));
                self.fcx
                    .need_type_info_err_in_generator(self.kind, span, unresolved_type)
                    .span_note(yield_data.span, &*note)
                    .emit();
            } else {
                // Insert the type into the ordered set.
                let scope_span = scope.map(|s| s.span(self.fcx.tcx, self.region_scope_tree));
                self.types.insert(ty::GeneratorInteriorTypeCause {
                    span: source_span,
                    ty: &ty,
                    scope_span,
                    yield_span: yield_data.span,
                    expr: expr.map(|e| e.hir_id),
                });
            }
        } else {
            debug!(
                "no type in expr = {:?}, count = {:?}, span = {:?}",
                expr,
                self.expr_count,
                expr.map(|e| e.span)
            );
            let ty = self.fcx.resolve_vars_if_possible(&ty);
            if let Some((unresolved_type, unresolved_type_span)) =
                self.fcx.unresolved_type_vars(&ty)
            {
                debug!(
                    "remained unresolved_type = {:?}, unresolved_type_span: {:?}",
                    unresolved_type, unresolved_type_span
                );
                self.prev_unresolved_span = unresolved_type_span;
            }
        }
    }
}

pub fn resolve_interior<'a, 'tcx>(
    fcx: &'a FnCtxt<'a, 'tcx>,
    def_id: DefId,
    body_id: hir::BodyId,
    interior: Ty<'tcx>,
    kind: hir::GeneratorKind,
) {
    let body = fcx.tcx.hir().body(body_id);
    let mut visitor = InteriorVisitor {
        fcx,
        types: FxIndexSet::default(),
        region_scope_tree: fcx.tcx.region_scope_tree(def_id),
        expr_count: 0,
        kind,
        prev_unresolved_span: None,
        nested_scope_of_guards: <_>::default(),
        current_scope_of_guards: <_>::default(),
        arm_has_guard: false,
    };
    intravisit::walk_body(&mut visitor, body);

    // Check that we visited the same amount of expressions and the RegionResolutionVisitor
    let region_expr_count = visitor.region_scope_tree.body_expr_count(body_id).unwrap();
    assert_eq!(region_expr_count, visitor.expr_count);

    // The types are already kept in insertion order.
    let types = visitor.types;

    // The types in the generator interior contain lifetimes local to the generator itself,
    // which should not be exposed outside of the generator. Therefore, we replace these
    // lifetimes with existentially-bound lifetimes, which reflect the exact value of the
    // lifetimes not being known by users.
    //
    // These lifetimes are used in auto trait impl checking (for example,
    // if a Sync generator contains an &'α T, we need to check whether &'α T: Sync),
    // so knowledge of the exact relationships between them isn't particularly important.

    debug!("types in generator {:?}, span = {:?}", types, body.value.span);

    let mut counter = 0;
    let mut captured_tys = FxHashSet::default();
    let type_causes: Vec<_> = types
        .into_iter()
        .filter_map(|mut cause| {
            // Erase regions and canonicalize late-bound regions to deduplicate as many types as we
            // can.
            let erased = fcx.tcx.erase_regions(&cause.ty);
            if captured_tys.insert(erased) {
                // Replace all regions inside the generator interior with late bound regions.
                // Note that each region slot in the types gets a new fresh late bound region,
                // which means that none of the regions inside relate to any other, even if
                // typeck had previously found constraints that would cause them to be related.
                let folded = fcx.tcx.fold_regions(&erased, &mut false, |_, current_depth| {
                    counter += 1;
                    fcx.tcx.mk_region(ty::ReLateBound(current_depth, ty::BrAnon(counter)))
                });

                cause.ty = folded;
                Some(cause)
            } else {
                None
            }
        })
        .collect();

    // Extract type components to build the witness type.
    let type_list = fcx.tcx.mk_type_list(type_causes.iter().map(|cause| cause.ty));
    let witness = fcx.tcx.mk_generator_witness(ty::Binder::bind(type_list));

    // Store the generator types and spans into the typeck results for this generator.
    visitor.fcx.inh.typeck_results.borrow_mut().generator_interior_types = type_causes;

    debug!(
        "types in generator after region replacement {:?}, span = {:?}",
        witness, body.value.span
    );

    // Unify the type variable inside the generator with the new witness
    match fcx.at(&fcx.misc(body.value.span), fcx.param_env).eq(interior, witness) {
        Ok(ok) => fcx.register_infer_ok_obligations(ok),
        _ => bug!(),
    }
}

// This visitor has to have the same visit_expr calls as RegionResolutionVisitor in
// librustc_middle/middle/region.rs since `expr_count` is compared against the results
// there.
impl<'a, 'tcx> Visitor<'tcx> for InteriorVisitor<'a, 'tcx> {
    type Map = intravisit::ErasedMap<'tcx>;

    fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
        NestedVisitorMap::None
    }

    fn visit_arm(&mut self, arm: &'tcx Arm<'tcx>) {
        if arm.guard.is_some() {
            self.nested_scope_of_guards.push(<_>::default());
            self.arm_has_guard = true;
        }
        self.visit_pat(&arm.pat);
        if let Some(ref g) = arm.guard {
            match g {
                Guard::If(ref e) => {
                    self.visit_expr(e);
                }
            }
            let mut scope_var_ids =
                self.nested_scope_of_guards.pop().expect("should have pushed at least one earlier");
            for var_id in scope_var_ids.drain(..) {
                assert!(self.current_scope_of_guards.remove(&var_id), "variable should be placed in scope earlier");
            }
            self.arm_has_guard = false;
        }
        self.visit_expr(&arm.body);
    }

    fn visit_pat(&mut self, pat: &'tcx Pat<'tcx>) {
        intravisit::walk_pat(self, pat);

        self.expr_count += 1;

        if let PatKind::Binding(_, id, ..) = pat.kind {
            let scope = self.region_scope_tree.var_scope(pat.hir_id.local_id);
            let ty = self.fcx.typeck_results.borrow().pat_ty(pat);
            self.record(ty, Some(scope), None, pat.span, false);
            if self.arm_has_guard {
                self.nested_scope_of_guards.as_mut_slice().last_mut().unwrap().push(id);
                self.current_scope_of_guards.insert(id);
            }
        }
    }

    fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) {
        let mut guard_borrowing_from_pattern = false;
        match &expr.kind {
            ExprKind::Call(callee, args) => match &callee.kind {
                ExprKind::Path(qpath) => {
                    let res = self.fcx.typeck_results.borrow().qpath_res(qpath, callee.hir_id);
                    match res {
                        // Direct calls never need to keep the callee `ty::FnDef`
                        // ZST in a temporary, so skip its type, just in case it
                        // can significantly complicate the generator type.
                        Res::Def(
                            DefKind::Fn | DefKind::AssocFn | DefKind::Ctor(_, CtorKind::Fn),
                            _,
                        ) => {
                            // NOTE(eddyb) this assumes a path expression has
                            // no nested expressions to keep track of.
                            self.expr_count += 1;

                            // Record the rest of the call expression normally.
                            for arg in *args {
                                self.visit_expr(arg);
                            }
                        }
                        _ => intravisit::walk_expr(self, expr),
                    }
                }
                _ => intravisit::walk_expr(self, expr),
            }
            ExprKind::Path(qpath) => {
                intravisit::walk_expr(self, expr);
                let res = self.fcx.typeck_results.borrow().qpath_res(qpath, expr.hir_id);
                match res {
                    Res::Local(id) if self.current_scope_of_guards.contains(&id) => {
                        debug!("a borrow in guard from pattern local is detected");
                        guard_borrowing_from_pattern = true;
                    }
                    _ => {}
                }
            }
            _ => intravisit::walk_expr(self, expr),
        }

        self.expr_count += 1;

        let scope = self.region_scope_tree.temporary_scope(expr.hir_id.local_id);

        // If there are adjustments, then record the final type --
        // this is the actual value that is being produced.
        if let Some(adjusted_ty) = self.fcx.typeck_results.borrow().expr_ty_adjusted_opt(expr) {
            self.record(adjusted_ty, scope, Some(expr), expr.span, guard_borrowing_from_pattern);
        }

        // Also record the unadjusted type (which is the only type if
        // there are no adjustments). The reason for this is that the
        // unadjusted value is sometimes a "temporary" that would wind
        // up in a MIR temporary.
        //
        // As an example, consider an expression like `vec![].push(x)`.
        // Here, the `vec![]` would wind up MIR stored into a
        // temporary variable `t` which we can borrow to invoke
        // `<Vec<_>>::push(&mut t, x)`.
        //
        // Note that an expression can have many adjustments, and we
        // are just ignoring those intermediate types. This is because
        // those intermediate values are always linearly "consumed" by
        // the other adjustments, and hence would never be directly
        // captured in the MIR.
        //
        // (Note that this partly relies on the fact that the `Deref`
        // traits always return references, which means their content
        // can be reborrowed without needing to spill to a temporary.
        // If this were not the case, then we could conceivably have
        // to create intermediate temporaries.)
        //
        // The type table might not have information for this expression
        // if it is in a malformed scope. (#66387)
        if let Some(ty) = self.fcx.typeck_results.borrow().expr_ty_opt(expr) {
            self.record(ty, scope, Some(expr), expr.span, guard_borrowing_from_pattern);
        } else {
            self.fcx.tcx.sess.delay_span_bug(expr.span, "no type for node");
        }
    }
}
<|endoftext|>


<commit_before>#[macro_use]
extern crate rustfbp;
extern crate capnp;

component! {
    nucleus_flow_parser_graph_check, contracts(fbp_graph, fbp_semantic_error)
    inputs(input: fbp_graph),
    inputs_array(),
    outputs(output: fbp_graph, error: fbp_semantic_error),
    outputs_array(),
    option(),
    acc(),
    fn run(&mut self) -> Result<()> {
        let error;
        let mut ip = try!(self.ports.recv("input"));
        {
            let graph: fbp_graph::Reader = try!(ip.get_root());

            let mut errors: Vec<String> = Vec::new();

            let mut nodes: HashMap<String, String> = HashMap::new();
            for n in try!(graph.borrow().get_nodes()).iter() {
                let name = try!(n.get_name());
                let sort = try!(n.get_sort());
                let insert = match nodes.get(name) {
                    Some(v) => {
                        if v == "" { true }
                        else {
                            if sort != "" {
                                errors.push(format!("The node \"{}()\" has its type declared more than once : {} and {}", name, v, sort));
                            }
                            false
                        }
                    }
                    None => { true }
                };
                if insert {
                    nodes.insert(name.into(), sort.into());
                }
            }

            let mut edges: HashMap<String, Vec<String>> = HashMap::new();
            for n in try!(graph.borrow().get_edges()).iter() {
                if !nodes.contains_key(try!(n.get_o_name())) {
                    errors.push(format!("Uninstantiated node \"{}()\"", try!(n.get_o_name())));
                }
                if !nodes.contains_key(try!(n.get_i_name())) {
                    errors.push(format!("Uninstantiated node \"{}()\"", try!(n.get_i_name())));
                }
                let sender = if try!(n.get_o_selection()) == "" {
                    format!("{}() {}", try!(n.get_o_name()), try!(n.get_o_port()))
                } else {
                    format!("{}() {}[{}]", try!(n.get_o_name()), try!(n.get_o_port()), try!(n.get_o_selection()))
                };
                let receiver = if try!(n.get_i_selection()) == "" {
                    format!("{}() {}", try!(n.get_i_name()), try!(n.get_i_port()))
                } else {
                    format!("{}() {}[{}]", try!(n.get_i_name()), try!(n.get_i_port()), try!(n.get_i_selection()))
                };
                {
                    let mut v = edges.entry(sender).or_insert(Vec::new());
                    v.push(receiver);
                }

                for (k, v) in &edges {
                    if v.len() > 1 {
                        let mut error: String = "There is a forbidden one-2-many simple port connection :\n".into();
                        for e in v {
                            error = format!("{}{} -> {}\n", error, k, e);
                        }
                        error = format!("{}Please use the ip_clone component\n", error);
                        errors.push(error);
                    }
                }
            }
            let mut inputs: HashMap<String, Vec<String>> = HashMap::new();
            for n in try!(graph.borrow().get_external_inputs()).iter() {
                let input = try!(n.get_name());
                let receiver = if try!(n.get_selection()) == "" {
                    format!("{}() {}", try!(n.get_comp()), try!(n.get_port()))
                } else {
                    format!("{}() {}[{}]", try!(n.get_comp()), try!(n.get_port()), try!(n.get_selection()))
                };
                {
                    let mut v = inputs.entry(input.into()).or_insert(Vec::new());
                    v.push(receiver);
                }
                for (k, v) in &inputs {
                    if v.len() > 1 {
                        let mut error: String = "There is a forbidden one-2-many simple port connection :\n".into();
                        for e in v {
                            error = format!("{}{} => {}\n", error, k, e);
                        }
                        error = format!("{}Please use the ip_clone component\n", error);
                        errors.push(error);
                    }
                }
            }

            if errors.len() > 0 {
                let mut new_ip = IP::new();
                {
                    let mut new_ip = new_ip.init_root::<fbp_semantic_error::Builder>();
                    new_ip.set_path(try!(graph.get_path()));
                    {
                        let mut nodes = new_ip.init_parsing(errors.len() as u32);
                        let mut i = 0;
                        for n in &errors {
                            nodes.borrow().set(i, &n[..]);
                            i += 1;
                        }
                    }
                }
                let _ = self.ports.send("error", new_ip);
                error = true;
            } else { error = false }
        }

        if !error {
            let _ = self.ports.send("output", ip);
        }

        Ok(())
    }
}
<commit_msg>Problem: typo in error message<commit_after>#[macro_use]
extern crate rustfbp;
extern crate capnp;

component! {
    nucleus_flow_parser_graph_check, contracts(fbp_graph, fbp_semantic_error)
    inputs(input: fbp_graph),
    inputs_array(),
    outputs(output: fbp_graph, error: fbp_semantic_error),
    outputs_array(),
    option(),
    acc(),
    fn run(&mut self) -> Result<()> {
        let error;
        let mut ip = try!(self.ports.recv("input"));
        {
            let graph: fbp_graph::Reader = try!(ip.get_root());

            let mut errors: Vec<String> = Vec::new();

            let mut nodes: HashMap<String, String> = HashMap::new();
            for n in try!(graph.borrow().get_nodes()).iter() {
                let name = try!(n.get_name());
                let sort = try!(n.get_sort());
                let insert = match nodes.get(name) {
                    Some(v) => {
                        if v == "" { true }
                        else {
                            if sort != "" {
                                errors.push(format!("The node \"{}()\" has been declared more than once : {} and {}", name, v, sort));
                            }
                            false
                        }
                    }
                    None => { true }
                };
                if insert {
                    nodes.insert(name.into(), sort.into());
                }
            }

            let mut edges: HashMap<String, Vec<String>> = HashMap::new();
            for n in try!(graph.borrow().get_edges()).iter() {
                if !nodes.contains_key(try!(n.get_o_name())) {
                    errors.push(format!("Uninstantiated node \"{}()\"", try!(n.get_o_name())));
                }
                if !nodes.contains_key(try!(n.get_i_name())) {
                    errors.push(format!("Uninstantiated node \"{}()\"", try!(n.get_i_name())));
                }
                let sender = if try!(n.get_o_selection()) == "" {
                    format!("{}() {}", try!(n.get_o_name()), try!(n.get_o_port()))
                } else {
                    format!("{}() {}[{}]", try!(n.get_o_name()), try!(n.get_o_port()), try!(n.get_o_selection()))
                };
                let receiver = if try!(n.get_i_selection()) == "" {
                    format!("{}() {}", try!(n.get_i_name()), try!(n.get_i_port()))
                } else {
                    format!("{}() {}[{}]", try!(n.get_i_name()), try!(n.get_i_port()), try!(n.get_i_selection()))
                };
                {
                    let mut v = edges.entry(sender).or_insert(Vec::new());
                    v.push(receiver);
                }

                for (k, v) in &edges {
                    if v.len() > 1 {
                        let mut error: String = "There is a forbidden one-2-many simple port connection :\n".into();
                        for e in v {
                            error = format!("{}{} -> {}\n", error, k, e);
                        }
                        error = format!("{}Please use the ip_clone component\n", error);
                        errors.push(error);
                    }
                }
            }
            let mut inputs: HashMap<String, Vec<String>> = HashMap::new();
            for n in try!(graph.borrow().get_external_inputs()).iter() {
                let input = try!(n.get_name());
                let receiver = if try!(n.get_selection()) == "" {
                    format!("{}() {}", try!(n.get_comp()), try!(n.get_port()))
                } else {
                    format!("{}() {}[{}]", try!(n.get_comp()), try!(n.get_port()), try!(n.get_selection()))
                };
                {
                    let mut v = inputs.entry(input.into()).or_insert(Vec::new());
                    v.push(receiver);
                }
                for (k, v) in &inputs {
                    if v.len() > 1 {
                        let mut error: String = "There is a forbidden one-2-many simple port connection :\n".into();
                        for e in v {
                            error = format!("{}{} => {}\n", error, k, e);
                        }
                        error = format!("{}Please use the ip_clone component\n", error);
                        errors.push(error);
                    }
                }
            }

            if errors.len() > 0 {
                let mut new_ip = IP::new();
                {
                    let mut new_ip = new_ip.init_root::<fbp_semantic_error::Builder>();
                    new_ip.set_path(try!(graph.get_path()));
                    {
                        let mut nodes = new_ip.init_parsing(errors.len() as u32);
                        let mut i = 0;
                        for n in &errors {
                            nodes.borrow().set(i, &n[..]);
                            i += 1;
                        }
                    }
                }
                let _ = self.ports.send("error", new_ip);
                error = true;
            } else { error = false }
        }

        if !error {
            let _ = self.ports.send("output", ip);
        }

        Ok(())
    }
}
<|endoftext|>


<commit_before>use std::io;
use std::net::{SocketAddr, Ipv4Addr, SocketAddrV4};
use std::str;
use std::time::Duration;

use futures::{Future, IntoFuture, Stream};
use futures::future;
use tokio_core::reactor::Handle;
use tokio_core::net::UdpSocket;
use tokio_timer::Timer;
use hyper;
use xml::EventReader;
use xml::reader::XmlEvent;

use async::Gateway;
use errors::SearchError;
use search::{SEARCH_REQUEST, parse_result};

/// Search gateway, bind to all interfaces and use a timeout of 3 seconds.
///
/// Bind to all interfaces.
/// The request will timeout after 3 seconds.
pub fn search_gateway(handle: &Handle) -> Box<Future<Item = Gateway, Error = SearchError>> {
    search_gateway_timeout(Duration::from_secs(3), handle)
}

/// Search gateway, bind to all interfaces and use the given duration for the timeout.
///
/// Bind to all interfaces.
/// The request will timeout after the given duration.
pub fn search_gateway_timeout(
    timeout: Duration,
    handle: &Handle,
) -> Box<Future<Item = Gateway, Error = SearchError>> {
    search_gateway_from_timeout(Ipv4Addr::new(0, 0, 0, 0), timeout, handle)
}

/// Search gateway, bind to the given interface and use a time of 3 seconds.
///
/// Bind to the given interface.
/// The request will timeout after 3 seconds.
pub fn search_gateway_from(
    ip: Ipv4Addr,
    handle: &Handle,
) -> Box<Future<Item = Gateway, Error = SearchError>> {
    search_gateway_from_timeout(ip, Duration::from_secs(3), handle)
}

/// Search gateway, bind to the given interface and use the given duration for the timeout.
///
/// Bind to the given interface.
/// The request will timeout after the given duration.
pub fn search_gateway_from_timeout(
    ip: Ipv4Addr,
    timeout: Duration,
    handle: &Handle,
) -> Box<Future<Item = Gateway, Error = SearchError>> {
    let addr = SocketAddr::V4(SocketAddrV4::new(ip, 0));
    let handle = handle.clone();
    let task = UdpSocket::bind(&addr, &handle)
        .into_future()
        .and_then(|socket| {
            socket.send_dgram(
                SEARCH_REQUEST.as_bytes(),
                "239.255.255.250:1900".parse().unwrap(),
            )
        })
        .and_then(|(socket, _)| socket.recv_dgram(Vec::new()))
        .map_err(|err| SearchError::from(err))
        .and_then(|(_sock, buf, n, _addr)| {
            str::from_utf8(&buf[..n])
                .map_err(|err| SearchError::from(err))
                .and_then(|text| {
                    parse_result(text).ok_or(SearchError::InvalidResponse)
                })
        })
        .and_then(move |location| {
            get_control_url(&location, &handle).and_then(move |control_url| {
                Ok(Gateway::new(location.0, control_url, handle))
            })
        });
    let timeout = Timer::default().timeout(task, timeout);
    Box::new(timeout)
}

pub fn get_control_url(
    location: &(SocketAddrV4, String),
    handle: &Handle,
) -> Box<Future<Item = String, Error = SearchError>> {
    let client = hyper::Client::new(handle);
    let uri = match format!("http://{}{}", location.0, location.1).parse() {
        Ok(uri) => uri,
        Err(err) => return Box::new(future::err(SearchError::from(err))),
    };
    let future = client
        .get(uri)
        .and_then(|resp| resp.body().concat2())
        .then(|result| match result {
            Ok(body) => parse_control_url(body.as_ref()),
            Err(err) => Err(SearchError::from(err)),
        });
    Box::new(future)
}

fn parse_control_url<R>(resp: R) -> Result<String, SearchError>
where
    R: io::Read,
{

    let parser = EventReader::new(resp);
    let mut chain = Vec::<String>::with_capacity(4);

    struct Service {
        service_type: String,
        control_url: String,
    }

    let mut service = Service {
        service_type: "".to_string(),
        control_url: "".to_string(),
    };

    for e in parser.into_iter() {
        match try!(e) {
            XmlEvent::StartElement { name, .. } => {
                chain.push(name.borrow().to_repr());
                let tail = if chain.len() >= 3 {
                    chain.iter().skip(chain.len() - 3)
                } else {
                    continue;
                };

                if vec!["device", "serviceList", "service"]
                    .iter()
                    .zip(tail)
                    .all(|(l, r)| l == r)
                {
                    service.service_type.clear();
                    service.control_url.clear();
                }
            }
            XmlEvent::EndElement { .. } => {
                let top = chain.pop();
                let tail = if top == Some("service".to_string()) && chain.len() >= 2 {
                    chain.iter().skip(chain.len() - 2)
                } else {
                    continue;
                };

                if vec!["device", "serviceList"].iter().zip(tail).all(
                    |(l, r)| {
                        l == r
                    },
                )
                {
                    if ("urn:schemas-upnp-org:service:WANIPConnection:1" == service.service_type ||
                            "urn:schemas-upnp-org:service:WANPPPConnection:1" ==
                                service.service_type) &&
                        service.control_url.len() != 0
                    {
                        return Ok(service.control_url);
                    }
                }
            }
            XmlEvent::Characters(text) => {
                let tail = if chain.len() >= 4 {
                    chain.iter().skip(chain.len() - 4)
                } else {
                    continue;
                };

                if vec!["device", "serviceList", "service", "serviceType"]
                    .iter()
                    .zip(tail.clone())
                    .all(|(l, r)| l == r)
                {
                    service.service_type.push_str(&text);
                }
                if vec!["device", "serviceList", "service", "controlURL"]
                    .iter()
                    .zip(tail)
                    .all(|(l, r)| l == r)
                {
                    service.control_url.push_str(&text);
                }
            }
            _ => (),
        }
    }
    Err(SearchError::InvalidResponse)
}
<commit_msg>Use Vec with size > 0 so that response will be read<commit_after>use std::io;
use std::net::{SocketAddr, Ipv4Addr, SocketAddrV4};
use std::str;
use std::time::Duration;

use futures::{Future, IntoFuture, Stream};
use futures::future;
use tokio_core::reactor::Handle;
use tokio_core::net::UdpSocket;
use tokio_timer::Timer;
use hyper;
use xml::EventReader;
use xml::reader::XmlEvent;

use async::Gateway;
use errors::SearchError;
use search::{SEARCH_REQUEST, parse_result};

/// Search gateway, bind to all interfaces and use a timeout of 3 seconds.
///
/// Bind to all interfaces.
/// The request will timeout after 3 seconds.
pub fn search_gateway(handle: &Handle) -> Box<Future<Item = Gateway, Error = SearchError>> {
    search_gateway_timeout(Duration::from_secs(3), handle)
}

/// Search gateway, bind to all interfaces and use the given duration for the timeout.
///
/// Bind to all interfaces.
/// The request will timeout after the given duration.
pub fn search_gateway_timeout(
    timeout: Duration,
    handle: &Handle,
) -> Box<Future<Item = Gateway, Error = SearchError>> {
    search_gateway_from_timeout(Ipv4Addr::new(0, 0, 0, 0), timeout, handle)
}

/// Search gateway, bind to the given interface and use a time of 3 seconds.
///
/// Bind to the given interface.
/// The request will timeout after 3 seconds.
pub fn search_gateway_from(
    ip: Ipv4Addr,
    handle: &Handle,
) -> Box<Future<Item = Gateway, Error = SearchError>> {
    search_gateway_from_timeout(ip, Duration::from_secs(3), handle)
}

/// Search gateway, bind to the given interface and use the given duration for the timeout.
///
/// Bind to the given interface.
/// The request will timeout after the given duration.
pub fn search_gateway_from_timeout(
    ip: Ipv4Addr,
    timeout: Duration,
    handle: &Handle,
) -> Box<Future<Item = Gateway, Error = SearchError>> {
    let addr = SocketAddr::V4(SocketAddrV4::new(ip, 0));
    let handle = handle.clone();
    let task = UdpSocket::bind(&addr, &handle)
        .into_future()
        .and_then(|socket| {
            socket.send_dgram(
                SEARCH_REQUEST.as_bytes(),
                "239.255.255.250:1900".parse().unwrap(),
            )
        })
        .and_then(|(socket, _)| socket.recv_dgram(vec![0u8; 1500]))
        .map_err(|err| SearchError::from(err))
        .and_then(|(_sock, buf, n, _addr)| {
            str::from_utf8(&buf[..n])
                .map_err(|err| SearchError::from(err))
                .and_then(|text| {
                    parse_result(text).ok_or(SearchError::InvalidResponse)
                })
        })
        .and_then(move |location| {
            get_control_url(&location, &handle).and_then(move |control_url| {
                Ok(Gateway::new(location.0, control_url, handle))
            })
        });
    let timeout = Timer::default().timeout(task, timeout);
    Box::new(timeout)
}

pub fn get_control_url(
    location: &(SocketAddrV4, String),
    handle: &Handle,
) -> Box<Future<Item = String, Error = SearchError>> {
    let client = hyper::Client::new(handle);
    let uri = match format!("http://{}{}", location.0, location.1).parse() {
        Ok(uri) => uri,
        Err(err) => return Box::new(future::err(SearchError::from(err))),
    };
    let future = client
        .get(uri)
        .and_then(|resp| resp.body().concat2())
        .then(|result| match result {
            Ok(body) => parse_control_url(body.as_ref()),
            Err(err) => Err(SearchError::from(err)),
        });
    Box::new(future)
}

fn parse_control_url<R>(resp: R) -> Result<String, SearchError>
where
    R: io::Read,
{

    let parser = EventReader::new(resp);
    let mut chain = Vec::<String>::with_capacity(4);

    struct Service {
        service_type: String,
        control_url: String,
    }

    let mut service = Service {
        service_type: "".to_string(),
        control_url: "".to_string(),
    };

    for e in parser.into_iter() {
        match try!(e) {
            XmlEvent::StartElement { name, .. } => {
                chain.push(name.borrow().to_repr());
                let tail = if chain.len() >= 3 {
                    chain.iter().skip(chain.len() - 3)
                } else {
                    continue;
                };

                if vec!["device", "serviceList", "service"]
                    .iter()
                    .zip(tail)
                    .all(|(l, r)| l == r)
                {
                    service.service_type.clear();
                    service.control_url.clear();
                }
            }
            XmlEvent::EndElement { .. } => {
                let top = chain.pop();
                let tail = if top == Some("service".to_string()) && chain.len() >= 2 {
                    chain.iter().skip(chain.len() - 2)
                } else {
                    continue;
                };

                if vec!["device", "serviceList"].iter().zip(tail).all(
                    |(l, r)| {
                        l == r
                    },
                )
                {
                    if ("urn:schemas-upnp-org:service:WANIPConnection:1" == service.service_type ||
                            "urn:schemas-upnp-org:service:WANPPPConnection:1" ==
                                service.service_type) &&
                        service.control_url.len() != 0
                    {
                        return Ok(service.control_url);
                    }
                }
            }
            XmlEvent::Characters(text) => {
                let tail = if chain.len() >= 4 {
                    chain.iter().skip(chain.len() - 4)
                } else {
                    continue;
                };

                if vec!["device", "serviceList", "service", "serviceType"]
                    .iter()
                    .zip(tail.clone())
                    .all(|(l, r)| l == r)
                {
                    service.service_type.push_str(&text);
                }
                if vec!["device", "serviceList", "service", "controlURL"]
                    .iter()
                    .zip(tail)
                    .all(|(l, r)| l == r)
                {
                    service.control_url.push_str(&text);
                }
            }
            _ => (),
        }
    }
    Err(SearchError::InvalidResponse)
}
<|endoftext|>


<commit_before>#![deny(warnings)]

extern crate arg_parser;
extern crate coreutils;
extern crate extra;

use std::env;
use std::path::Path;
use std::io::{stdout, stderr, Write};
use std::fs::{remove_dir_all, hard_link};
use std::os::unix::fs::symlink;
use std::process::exit;

use arg_parser::ArgParser;
use extra::option::OptionalExt;
use extra::io::fail;

const MAN_PAGE: &'static str = r#"
NAME
    ln - make links between files

SYNOPSIS
    ln [OPTIONS] TARGET LINK_NAME
    ln [OPTIONS] TARGET

DESCRIPTION
    Create links between files.

OPTIONS
    -f --force
        Force removing existing destination fila
    -s --symbolic
        Make symbolic link instead of hard link
    --help
        show this help and exit
"#; /* @MANEND */

fn main() {
    let stdout = stdout();
    let mut stdout = stdout.lock();
    let mut stderr = stderr();

    let mut parser = ArgParser::new(6)
        .add_flag(&["f", "force"])
        .add_flag(&["s", "symbolic"])
        .add_flag(&["", "help"]);
    parser.parse(env::args());

    if parser.found("help") || parser.args.len() == 0 {
        stdout.write(MAN_PAGE.as_bytes()).try(&mut stderr);
        stdout.flush().try(&mut stderr);
        exit(0);
    } else {

        let mut cwd = env::current_dir().expect("can't get cwd");
        let src = Path::new(&parser.args[0]);
        let dst = match parser.args.len() {
            2 => Path::new(&parser.args[1]),
            1 => {
                cwd.push(src.file_name().try(&mut stderr));
                cwd.as_path()
            }
            _ => fail("use --help", &mut stderr),
        };

        if parser.found("force") {
            remove_dir_all(dst).try(&mut stderr);
        }
        if parser.found("symbolic") {
            symlink(src, dst).try(&mut stderr);
        } else {
            hard_link(src, dst).try(&mut stderr);
        }
    }

}
<commit_msg>Allow directory as second argument to ln<commit_after>#![deny(warnings)]

extern crate arg_parser;
extern crate coreutils;
extern crate extra;

use std::env;
use std::path::Path;
use std::io::{stdout, stderr, Write};
use std::fs::{remove_dir_all, hard_link};
use std::os::unix::fs::symlink;
use std::process::exit;

use arg_parser::ArgParser;
use extra::option::OptionalExt;
use extra::io::fail;

const MAN_PAGE: &'static str = r#"
NAME
    ln - make links between files

SYNOPSIS
    ln [OPTIONS] TARGET LINK_NAME
    ln [OPTIONS] TARGET

DESCRIPTION
    Create links between files.

OPTIONS
    -f --force
        Force removing existing destination fila
    -s --symbolic
        Make symbolic link instead of hard link
    --help
        show this help and exit
"#; /* @MANEND */

fn main() {
    let stdout = stdout();
    let mut stdout = stdout.lock();
    let mut stderr = stderr();

    let mut parser = ArgParser::new(6)
        .add_flag(&["f", "force"])
        .add_flag(&["s", "symbolic"])
        .add_flag(&["", "help"]);
    parser.parse(env::args());

    if parser.found("help") || parser.args.len() == 0 {
        stdout.write(MAN_PAGE.as_bytes()).try(&mut stderr);
        stdout.flush().try(&mut stderr);
        exit(0);
    } else {

        let mut cwd = env::current_dir().expect("can't get cwd");
        let src = Path::new(&parser.args[0]);
        let dst = match parser.args.len() {
            2 => Path::new(&parser.args[1]),
            1 => {
                cwd.push(src.file_name().try(&mut stderr));
                cwd.as_path()
            }
            _ => fail("use --help", &mut stderr),
        };

        let mut dst = dst.to_owned();
        if dst.is_dir() {
            dst.push(src.file_name().unwrap_or(src.as_os_str()));
        }

        if parser.found("force") {
            remove_dir_all(&dst).try(&mut stderr);
        }

        if parser.found("symbolic") {
            symlink(src, &dst).try(&mut stderr);
        } else {
            hard_link(src, &dst).try(&mut stderr);
        }
    }

}
<|endoftext|>


<commit_before>//! A stably addressed token buffer supporting efficient traversal based on a
//! cheaply copyable cursor.
//!
//! *This module is available only if Syn is built with the `"parsing"` feature.*

// This module is heavily commented as it contains most of the unsafe code in
// Syn, and caution should be used when editing it. The public-facing interface
// is 100% safe but the implementation is fragile internally.

#[cfg(all(
    not(all(target_arch = "wasm32", any(target_os = "unknown", target_os = "wasi"))),
    feature = "proc-macro"
))]
use crate::proc_macro as pm;
use crate::Lifetime;
use proc_macro2::{Delimiter, Group, Ident, Literal, Punct, Spacing, Span, TokenStream, TokenTree};
use std::hint;
use std::marker::PhantomData;

/// Internal type which is used instead of `TokenTree` to represent a token tree
/// within a `TokenBuffer`.
enum Entry {
    // Mimicking types from proc-macro.
    // Group entries contain the offset to the matching End entry.
    Group(Group, usize),
    Ident(Ident),
    Punct(Punct),
    Literal(Literal),
    End,
}

/// A buffer that can be efficiently traversed multiple times, unlike
/// `TokenStream` which requires a deep copy in order to traverse more than
/// once.
///
/// *This type is available only if Syn is built with the `"parsing"` feature.*
pub struct TokenBuffer {
    // NOTE: Do not implement clone on this - while the current design could be
    // cloned, other designs which could be desirable may not be cloneable.
    entries: Box<[Entry]>,
}

// Keep the explicit impl for backwards compatibility.
impl Drop for TokenBuffer {
    fn drop(&mut self) {}
}

impl TokenBuffer {
    fn new_inner(entries: &mut Vec<Entry>, stream: TokenStream) {
        for tt in stream {
            match tt {
                TokenTree::Ident(ident) => entries.push(Entry::Ident(ident)),
                TokenTree::Punct(punct) => entries.push(Entry::Punct(punct)),
                TokenTree::Literal(literal) => entries.push(Entry::Literal(literal)),
                TokenTree::Group(group) => {
                    let group_start_index = entries.len();
                    entries.push(Entry::End); // we replace this below
                    Self::new_inner(entries, group.stream());
                    let group_end_index = entries.len();
                    entries.push(Entry::End);
                    let group_end_offset = group_end_index - group_start_index;
                    entries[group_start_index] = Entry::Group(group, group_end_offset);
                }
            }
        }
    }

    /// Creates a `TokenBuffer` containing all the tokens from the input
    /// `proc_macro::TokenStream`.
    ///
    /// *This method is available only if Syn is built with both the `"parsing"` and
    /// `"proc-macro"` features.*
    #[cfg(all(
        not(all(target_arch = "wasm32", any(target_os = "unknown", target_os = "wasi"))),
        feature = "proc-macro"
    ))]
    pub fn new(stream: pm::TokenStream) -> Self {
        Self::new2(stream.into())
    }

    /// Creates a `TokenBuffer` containing all the tokens from the input
    /// `proc_macro2::TokenStream`.
    pub fn new2(stream: TokenStream) -> Self {
        let mut entries = Vec::new();
        Self::new_inner(&mut entries, stream);
        entries.push(Entry::End);
        Self {
            entries: entries.into_boxed_slice(),
        }
    }

    /// Creates a cursor referencing the first token in the buffer and able to
    /// traverse until the end of the buffer.
    pub fn begin(&self) -> Cursor {
        let ptr = self.entries.as_ptr();
        unsafe { Cursor::create(ptr, ptr.add(self.entries.len() - 1)) }
    }
}

/// A cheaply copyable cursor into a `TokenBuffer`.
///
/// This cursor holds a shared reference into the immutable data which is used
/// internally to represent a `TokenStream`, and can be efficiently manipulated
/// and copied around.
///
/// An empty `Cursor` can be created directly, or one may create a `TokenBuffer`
/// object and get a cursor to its first token with `begin()`.
///
/// Two cursors are equal if they have the same location in the same input
/// stream, and have the same scope.
///
/// *This type is available only if Syn is built with the `"parsing"` feature.*
pub struct Cursor<'a> {
    // The current entry which the `Cursor` is pointing at.
    ptr: *const Entry,
    // This is the only `Entry::End` object which this cursor is allowed to
    // point at. All other `End` objects are skipped over in `Cursor::create`.
    scope: *const Entry,
    // Cursor is covariant in 'a. This field ensures that our pointers are still
    // valid.
    marker: PhantomData<&'a Entry>,
}

impl<'a> Cursor<'a> {
    /// Creates a cursor referencing a static empty TokenStream.
    pub fn empty() -> Self {
        // It's safe in this situation for us to put an `Entry` object in global
        // storage, despite it not actually being safe to send across threads
        // (`Ident` is a reference into a thread-local table). This is because
        // this entry never includes a `Ident` object.
        //
        // This wrapper struct allows us to break the rules and put a `Sync`
        // object in global storage.
        struct UnsafeSyncEntry(Entry);
        unsafe impl Sync for UnsafeSyncEntry {}
        static EMPTY_ENTRY: UnsafeSyncEntry = UnsafeSyncEntry(Entry::End);

        Cursor {
            ptr: &EMPTY_ENTRY.0,
            scope: &EMPTY_ENTRY.0,
            marker: PhantomData,
        }
    }

    /// This create method intelligently exits non-explicitly-entered
    /// `None`-delimited scopes when the cursor reaches the end of them,
    /// allowing for them to be treated transparently.
    unsafe fn create(mut ptr: *const Entry, scope: *const Entry) -> Self {
        // NOTE: If we're looking at a `End`, we want to advance the cursor
        // past it, unless `ptr == scope`, which means that we're at the edge of
        // our cursor's scope. We should only have `ptr != scope` at the exit
        // from None-delimited groups entered with `ignore_none`.
        while let Entry::End = *ptr {
            if ptr == scope {
                break;
            }
            ptr = ptr.add(1);
        }

        Cursor {
            ptr,
            scope,
            marker: PhantomData,
        }
    }

    /// Get the current entry.
    fn entry(self) -> &'a Entry {
        unsafe { &*self.ptr }
    }

    /// Bump the cursor to point at the next token after the current one. This
    /// is undefined behavior if the cursor is currently looking at an
    /// `Entry::End`.
    ///
    /// If the cursor is looking at an `Entry::Group`, the bumped cursor will
    /// point at the first token in the group (with the same scope end).
    unsafe fn bump_ignore_group(self) -> Cursor<'a> {
        Cursor::create(self.ptr.offset(1), self.scope)
    }

    /// Bump the cursor to point at the next token after the current one. This
    /// is undefined behavior if the cursor is currently looking at an
    /// `Entry::End`.
    ///
    /// If the cursor is looking at an `Entry::Group`, the bumped cursor will
    /// point at the first token after the group.
    unsafe fn bump_over_group(self) -> Cursor<'a> {
        match self.entry() {
            &Entry::Group(_, end_offset) => Cursor::create(self.ptr.add(end_offset), self.scope),
            _ => self.bump_ignore_group(),
        }
    }

    /// Bump the cursor to enter the current group. This is undefined behavior
    /// if the cursor is not currently looking at an `Entry::Group`.
    unsafe fn bump_into_group(self) -> Cursor<'a> {
        match self.entry() {
            &Entry::Group(_, end_offset) => {
                Cursor::create(self.ptr.add(1), self.ptr.add(end_offset))
            }
            _ => hint::unreachable_unchecked(),
        }
    }

    /// While the cursor is looking at a `None`-delimited group, move it to look
    /// at the first token inside instead. If the group is empty, this will move
    /// the cursor past the `None`-delimited group.
    ///
    /// WARNING: This mutates its argument.
    fn ignore_none(&mut self) {
        while let Entry::Group(group, _) = self.entry() {
            if group.delimiter() == Delimiter::None {
                unsafe { *self = self.bump_ignore_group() };
            } else {
                break;
            }
        }
    }

    /// Checks whether the cursor is currently pointing at the end of its valid
    /// scope.
    pub fn eof(self) -> bool {
        // We're at eof if we're at the end of our scope.
        self.ptr == self.scope
    }

    /// If the cursor is pointing at a `Group` with the given delimiter, returns
    /// a cursor into that group and one pointing to the next `TokenTree`.
    pub fn group(mut self, delim: Delimiter) -> Option<(Cursor<'a>, Span, Cursor<'a>)> {
        // If we're not trying to enter a none-delimited group, we want to
        // ignore them. We have to make sure to _not_ ignore them when we want
        // to enter them, of course. For obvious reasons.
        if delim != Delimiter::None {
            self.ignore_none();
        }

        if let Entry::Group(group, _) = self.entry() {
            if group.delimiter() == delim {
                let (into, over) = unsafe { (self.bump_into_group(), self.bump_over_group()) };
                return Some((into, group.span(), over));
            }
        }

        None
    }

    /// If the cursor is pointing at a `Ident`, returns it along with a cursor
    /// pointing at the next `TokenTree`.
    pub fn ident(mut self) -> Option<(Ident, Cursor<'a>)> {
        self.ignore_none();
        match self.entry() {
            Entry::Ident(ident) => Some((ident.clone(), unsafe { self.bump_ignore_group() })),
            _ => None,
        }
    }

    /// If the cursor is pointing at a `Punct`, returns it along with a cursor
    /// pointing at the next `TokenTree`.
    pub fn punct(mut self) -> Option<(Punct, Cursor<'a>)> {
        self.ignore_none();
        match self.entry() {
            Entry::Punct(punct) if punct.as_char() != '\'' => {
                Some((punct.clone(), unsafe { self.bump_ignore_group() }))
            }
            _ => None,
        }
    }

    /// If the cursor is pointing at a `Literal`, return it along with a cursor
    /// pointing at the next `TokenTree`.
    pub fn literal(mut self) -> Option<(Literal, Cursor<'a>)> {
        self.ignore_none();
        match self.entry() {
            Entry::Literal(literal) => Some((literal.clone(), unsafe { self.bump_ignore_group() })),
            _ => None,
        }
    }

    /// If the cursor is pointing at a `Lifetime`, returns it along with a
    /// cursor pointing at the next `TokenTree`.
    pub fn lifetime(mut self) -> Option<(Lifetime, Cursor<'a>)> {
        self.ignore_none();
        match self.entry() {
            Entry::Punct(punct) if punct.as_char() == '\'' && punct.spacing() == Spacing::Joint => {
                let next = unsafe { self.bump_ignore_group() };
                match next.ident() {
                    Some((ident, rest)) => {
                        let lifetime = Lifetime {
                            apostrophe: punct.span(),
                            ident,
                        };
                        Some((lifetime, rest))
                    }
                    None => None,
                }
            }
            _ => None,
        }
    }

    /// Copies all remaining tokens visible from this cursor into a
    /// `TokenStream`.
    pub fn token_stream(self) -> TokenStream {
        let mut tts = Vec::new();
        let mut cursor = self;
        while let Some((tt, rest)) = cursor.token_tree() {
            tts.push(tt);
            cursor = rest;
        }
        tts.into_iter().collect()
    }

    /// If the cursor is pointing at a `TokenTree`, returns it along with a
    /// cursor pointing at the next `TokenTree`.
    ///
    /// Returns `None` if the cursor has reached the end of its stream.
    ///
    /// This method does not treat `None`-delimited groups as transparent, and
    /// will return a `Group(None, ..)` if the cursor is looking at one.
    pub fn token_tree(self) -> Option<(TokenTree, Cursor<'a>)> {
        let tree = match self.entry() {
            Entry::Group(group, _) => group.clone().into(),
            Entry::Literal(literal) => literal.clone().into(),
            Entry::Ident(ident) => ident.clone().into(),
            Entry::Punct(punct) => punct.clone().into(),
            Entry::End => return None,
        };

        Some((tree, unsafe { self.bump_over_group() }))
    }

    /// Returns the `Span` of the current token, or `Span::call_site()` if this
    /// cursor points to eof.
    pub fn span(self) -> Span {
        match self.entry() {
            Entry::Group(group, _) => group.span(),
            Entry::Literal(literal) => literal.span(),
            Entry::Ident(ident) => ident.span(),
            Entry::Punct(punct) => punct.span(),
            Entry::End => Span::call_site(),
        }
    }

    /// Skip over the next token without cloning it. Returns `None` if this
    /// cursor points to eof.
    ///
    /// This method treats `'lifetimes` as a single token.
    pub(crate) fn skip(self) -> Option<Cursor<'a>> {
        match self.entry() {
            Entry::End => None,

            // Treat lifetimes as a single tt for the purposes of 'skip'.
            Entry::Punct(punct) if punct.as_char() == '\'' && punct.spacing() == Spacing::Joint => {
                let next = unsafe { self.bump_ignore_group() };
                match next.entry() {
                    Entry::Ident(_) => Some(unsafe { next.bump_ignore_group() }),
                    _ => Some(next),
                }
            }
            _ => Some(unsafe { self.bump_over_group() }),
        }
    }
}

impl<'a> Copy for Cursor<'a> {}

impl<'a> Clone for Cursor<'a> {
    fn clone(&self) -> Self {
        *self
    }
}

impl<'a> Eq for Cursor<'a> {}

impl<'a> PartialEq for Cursor<'a> {
    fn eq(&self, other: &Self) -> bool {
        let Cursor { ptr, scope, marker } = self;
        let _ = marker;
        *ptr == other.ptr && *scope == other.scope
    }
}

pub(crate) fn same_scope(a: Cursor, b: Cursor) -> bool {
    a.scope == b.scope
}

pub(crate) fn open_span_of_group(cursor: Cursor) -> Span {
    match cursor.entry() {
        Entry::Group(group, _) => group.span_open(),
        _ => cursor.span(),
    }
}

pub(crate) fn close_span_of_group(cursor: Cursor) -> Span {
    match cursor.entry() {
        Entry::Group(group, _) => group.span_close(),
        _ => cursor.span(),
    }
}
<commit_msg>Touch up PR 1223<commit_after>//! A stably addressed token buffer supporting efficient traversal based on a
//! cheaply copyable cursor.
//!
//! *This module is available only if Syn is built with the `"parsing"` feature.*

// This module is heavily commented as it contains most of the unsafe code in
// Syn, and caution should be used when editing it. The public-facing interface
// is 100% safe but the implementation is fragile internally.

#[cfg(all(
    not(all(target_arch = "wasm32", any(target_os = "unknown", target_os = "wasi"))),
    feature = "proc-macro"
))]
use crate::proc_macro as pm;
use crate::Lifetime;
use proc_macro2::{Delimiter, Group, Ident, Literal, Punct, Spacing, Span, TokenStream, TokenTree};
use std::hint;
use std::marker::PhantomData;

/// Internal type which is used instead of `TokenTree` to represent a token tree
/// within a `TokenBuffer`.
enum Entry {
    // Mimicking types from proc-macro.
    // Group entries contain the offset to the matching End entry.
    Group(Group, usize),
    Ident(Ident),
    Punct(Punct),
    Literal(Literal),
    End,
}

/// A buffer that can be efficiently traversed multiple times, unlike
/// `TokenStream` which requires a deep copy in order to traverse more than
/// once.
///
/// *This type is available only if Syn is built with the `"parsing"` feature.*
pub struct TokenBuffer {
    // NOTE: Do not implement clone on this - while the current design could be
    // cloned, other designs which could be desirable may not be cloneable.
    entries: Box<[Entry]>,
}

// Keep the explicit impl for backwards compatibility.
impl Drop for TokenBuffer {
    fn drop(&mut self) {}
}

impl TokenBuffer {
    fn new_inner(entries: &mut Vec<Entry>, stream: TokenStream) {
        for tt in stream {
            match tt {
                TokenTree::Ident(ident) => entries.push(Entry::Ident(ident)),
                TokenTree::Punct(punct) => entries.push(Entry::Punct(punct)),
                TokenTree::Literal(literal) => entries.push(Entry::Literal(literal)),
                TokenTree::Group(group) => {
                    let group_start_index = entries.len();
                    entries.push(Entry::End); // we replace this below
                    Self::new_inner(entries, group.stream());
                    let group_end_index = entries.len();
                    entries.push(Entry::End);
                    let group_end_offset = group_end_index - group_start_index;
                    entries[group_start_index] = Entry::Group(group, group_end_offset);
                }
            }
        }
    }

    /// Creates a `TokenBuffer` containing all the tokens from the input
    /// `proc_macro::TokenStream`.
    ///
    /// *This method is available only if Syn is built with both the `"parsing"` and
    /// `"proc-macro"` features.*
    #[cfg(all(
        not(all(target_arch = "wasm32", any(target_os = "unknown", target_os = "wasi"))),
        feature = "proc-macro"
    ))]
    pub fn new(stream: pm::TokenStream) -> Self {
        Self::new2(stream.into())
    }

    /// Creates a `TokenBuffer` containing all the tokens from the input
    /// `proc_macro2::TokenStream`.
    pub fn new2(stream: TokenStream) -> Self {
        let mut entries = Vec::new();
        Self::new_inner(&mut entries, stream);
        entries.push(Entry::End);
        Self {
            entries: entries.into_boxed_slice(),
        }
    }

    /// Creates a cursor referencing the first token in the buffer and able to
    /// traverse until the end of the buffer.
    pub fn begin(&self) -> Cursor {
        let ptr = self.entries.as_ptr();
        unsafe { Cursor::create(ptr, ptr.add(self.entries.len() - 1)) }
    }
}

/// A cheaply copyable cursor into a `TokenBuffer`.
///
/// This cursor holds a shared reference into the immutable data which is used
/// internally to represent a `TokenStream`, and can be efficiently manipulated
/// and copied around.
///
/// An empty `Cursor` can be created directly, or one may create a `TokenBuffer`
/// object and get a cursor to its first token with `begin()`.
///
/// Two cursors are equal if they have the same location in the same input
/// stream, and have the same scope.
///
/// *This type is available only if Syn is built with the `"parsing"` feature.*
pub struct Cursor<'a> {
    // The current entry which the `Cursor` is pointing at.
    ptr: *const Entry,
    // This is the only `Entry::End` object which this cursor is allowed to
    // point at. All other `End` objects are skipped over in `Cursor::create`.
    scope: *const Entry,
    // Cursor is covariant in 'a. This field ensures that our pointers are still
    // valid.
    marker: PhantomData<&'a Entry>,
}

impl<'a> Cursor<'a> {
    /// Creates a cursor referencing a static empty TokenStream.
    pub fn empty() -> Self {
        // It's safe in this situation for us to put an `Entry` object in global
        // storage, despite it not actually being safe to send across threads
        // (`Ident` is a reference into a thread-local table). This is because
        // this entry never includes a `Ident` object.
        //
        // This wrapper struct allows us to break the rules and put a `Sync`
        // object in global storage.
        struct UnsafeSyncEntry(Entry);
        unsafe impl Sync for UnsafeSyncEntry {}
        static EMPTY_ENTRY: UnsafeSyncEntry = UnsafeSyncEntry(Entry::End);

        Cursor {
            ptr: &EMPTY_ENTRY.0,
            scope: &EMPTY_ENTRY.0,
            marker: PhantomData,
        }
    }

    /// This create method intelligently exits non-explicitly-entered
    /// `None`-delimited scopes when the cursor reaches the end of them,
    /// allowing for them to be treated transparently.
    unsafe fn create(mut ptr: *const Entry, scope: *const Entry) -> Self {
        // NOTE: If we're looking at a `End`, we want to advance the cursor
        // past it, unless `ptr == scope`, which means that we're at the edge of
        // our cursor's scope. We should only have `ptr != scope` at the exit
        // from None-delimited groups entered with `ignore_none`.
        while let Entry::End = *ptr {
            if ptr == scope {
                break;
            }
            ptr = ptr.add(1);
        }

        Cursor {
            ptr,
            scope,
            marker: PhantomData,
        }
    }

    /// Get the current entry.
    fn entry(self) -> &'a Entry {
        unsafe { &*self.ptr }
    }

    /// Bump the cursor to point at the next token after the current one. This
    /// is undefined behavior if the cursor is currently looking at an
    /// `Entry::End`.
    ///
    /// If the cursor is looking at an `Entry::Group`, the bumped cursor will
    /// point at the first token in the group (with the same scope end).
    unsafe fn bump_ignore_group(self) -> Cursor<'a> {
        Cursor::create(self.ptr.offset(1), self.scope)
    }

    /// Bump the cursor to point at the next token after the current one. This
    /// is undefined behavior if the cursor is currently looking at an
    /// `Entry::End`.
    ///
    /// If the cursor is looking at an `Entry::Group`, the bumped cursor will
    /// point at the first token after the group.
    unsafe fn bump_over_group(self) -> Cursor<'a> {
        match self.entry() {
            Entry::Group(_, end_offset) => Cursor::create(self.ptr.add(*end_offset), self.scope),
            _ => self.bump_ignore_group(),
        }
    }

    /// Bump the cursor to enter the current group. This is undefined behavior
    /// if the cursor is not currently looking at an `Entry::Group`.
    unsafe fn bump_into_group(self) -> Cursor<'a> {
        match self.entry() {
            Entry::Group(_, end_offset) => {
                Cursor::create(self.ptr.add(1), self.ptr.add(*end_offset))
            }
            _ => hint::unreachable_unchecked(),
        }
    }

    /// While the cursor is looking at a `None`-delimited group, move it to look
    /// at the first token inside instead. If the group is empty, this will move
    /// the cursor past the `None`-delimited group.
    ///
    /// WARNING: This mutates its argument.
    fn ignore_none(&mut self) {
        while let Entry::Group(group, _) = self.entry() {
            if group.delimiter() == Delimiter::None {
                unsafe { *self = self.bump_ignore_group() };
            } else {
                break;
            }
        }
    }

    /// Checks whether the cursor is currently pointing at the end of its valid
    /// scope.
    pub fn eof(self) -> bool {
        // We're at eof if we're at the end of our scope.
        self.ptr == self.scope
    }

    /// If the cursor is pointing at a `Group` with the given delimiter, returns
    /// a cursor into that group and one pointing to the next `TokenTree`.
    pub fn group(mut self, delim: Delimiter) -> Option<(Cursor<'a>, Span, Cursor<'a>)> {
        // If we're not trying to enter a none-delimited group, we want to
        // ignore them. We have to make sure to _not_ ignore them when we want
        // to enter them, of course. For obvious reasons.
        if delim != Delimiter::None {
            self.ignore_none();
        }

        if let Entry::Group(group, _) = self.entry() {
            if group.delimiter() == delim {
                let into = unsafe { self.bump_into_group() };
                let over = unsafe { self.bump_over_group() };
                return Some((into, group.span(), over));
            }
        }

        None
    }

    /// If the cursor is pointing at a `Ident`, returns it along with a cursor
    /// pointing at the next `TokenTree`.
    pub fn ident(mut self) -> Option<(Ident, Cursor<'a>)> {
        self.ignore_none();
        match self.entry() {
            Entry::Ident(ident) => Some((ident.clone(), unsafe { self.bump_ignore_group() })),
            _ => None,
        }
    }

    /// If the cursor is pointing at a `Punct`, returns it along with a cursor
    /// pointing at the next `TokenTree`.
    pub fn punct(mut self) -> Option<(Punct, Cursor<'a>)> {
        self.ignore_none();
        match self.entry() {
            Entry::Punct(punct) if punct.as_char() != '\'' => {
                Some((punct.clone(), unsafe { self.bump_ignore_group() }))
            }
            _ => None,
        }
    }

    /// If the cursor is pointing at a `Literal`, return it along with a cursor
    /// pointing at the next `TokenTree`.
    pub fn literal(mut self) -> Option<(Literal, Cursor<'a>)> {
        self.ignore_none();
        match self.entry() {
            Entry::Literal(literal) => Some((literal.clone(), unsafe { self.bump_ignore_group() })),
            _ => None,
        }
    }

    /// If the cursor is pointing at a `Lifetime`, returns it along with a
    /// cursor pointing at the next `TokenTree`.
    pub fn lifetime(mut self) -> Option<(Lifetime, Cursor<'a>)> {
        self.ignore_none();
        match self.entry() {
            Entry::Punct(punct) if punct.as_char() == '\'' && punct.spacing() == Spacing::Joint => {
                let next = unsafe { self.bump_ignore_group() };
                match next.ident() {
                    Some((ident, rest)) => {
                        let lifetime = Lifetime {
                            apostrophe: punct.span(),
                            ident,
                        };
                        Some((lifetime, rest))
                    }
                    None => None,
                }
            }
            _ => None,
        }
    }

    /// Copies all remaining tokens visible from this cursor into a
    /// `TokenStream`.
    pub fn token_stream(self) -> TokenStream {
        let mut tts = Vec::new();
        let mut cursor = self;
        while let Some((tt, rest)) = cursor.token_tree() {
            tts.push(tt);
            cursor = rest;
        }
        tts.into_iter().collect()
    }

    /// If the cursor is pointing at a `TokenTree`, returns it along with a
    /// cursor pointing at the next `TokenTree`.
    ///
    /// Returns `None` if the cursor has reached the end of its stream.
    ///
    /// This method does not treat `None`-delimited groups as transparent, and
    /// will return a `Group(None, ..)` if the cursor is looking at one.
    pub fn token_tree(self) -> Option<(TokenTree, Cursor<'a>)> {
        let tree = match self.entry() {
            Entry::Group(group, _) => group.clone().into(),
            Entry::Literal(literal) => literal.clone().into(),
            Entry::Ident(ident) => ident.clone().into(),
            Entry::Punct(punct) => punct.clone().into(),
            Entry::End => return None,
        };

        Some((tree, unsafe { self.bump_over_group() }))
    }

    /// Returns the `Span` of the current token, or `Span::call_site()` if this
    /// cursor points to eof.
    pub fn span(self) -> Span {
        match self.entry() {
            Entry::Group(group, _) => group.span(),
            Entry::Literal(literal) => literal.span(),
            Entry::Ident(ident) => ident.span(),
            Entry::Punct(punct) => punct.span(),
            Entry::End => Span::call_site(),
        }
    }

    /// Skip over the next token without cloning it. Returns `None` if this
    /// cursor points to eof.
    ///
    /// This method treats `'lifetimes` as a single token.
    pub(crate) fn skip(self) -> Option<Cursor<'a>> {
        match self.entry() {
            Entry::End => None,

            // Treat lifetimes as a single tt for the purposes of 'skip'.
            Entry::Punct(punct) if punct.as_char() == '\'' && punct.spacing() == Spacing::Joint => {
                let next = unsafe { self.bump_ignore_group() };
                match next.entry() {
                    Entry::Ident(_) => Some(unsafe { next.bump_ignore_group() }),
                    _ => Some(next),
                }
            }
            _ => Some(unsafe { self.bump_over_group() }),
        }
    }
}

impl<'a> Copy for Cursor<'a> {}

impl<'a> Clone for Cursor<'a> {
    fn clone(&self) -> Self {
        *self
    }
}

impl<'a> Eq for Cursor<'a> {}

impl<'a> PartialEq for Cursor<'a> {
    fn eq(&self, other: &Self) -> bool {
        let Cursor { ptr, scope, marker } = self;
        let _ = marker;
        *ptr == other.ptr && *scope == other.scope
    }
}

pub(crate) fn same_scope(a: Cursor, b: Cursor) -> bool {
    a.scope == b.scope
}

pub(crate) fn open_span_of_group(cursor: Cursor) -> Span {
    match cursor.entry() {
        Entry::Group(group, _) => group.span_open(),
        _ => cursor.span(),
    }
}

pub(crate) fn close_span_of_group(cursor: Cursor) -> Span {
    match cursor.entry() {
        Entry::Group(group, _) => group.span_close(),
        _ => cursor.span(),
    }
}
<|endoftext|>


<commit_before>use std::ops::{Add, Div};
use std::time;

use euclid::{TypedPoint2D, TypedRect, TypedSize2D, TypedTransform2D};

use defs::*;
use math;
use math::*;
use shape::*;

// TODO: Do I need a custom AATransform that manages precision
// somehow?

// TODO: think about how movement will work: this is tricky. When a
// chunk is broken in to pieces, we need to make sure our
// current_shape and target_shape are switched to being ones that are
// in the correct chunk

#[derive(Debug)]
pub struct CameraState {
    pub camera_bounds: TypedRect<f32, DrawSpace>,

    pub current_chunk: TopChunk,
    pub current_neutral_transform: TypedTransform2D<f32, UniverseSpace, DrawSpace>,
    pub current_transform: TypedTransform2D<f32, UniverseSpace, DrawSpace>,

    pub target_chunk: TopChunk, // This should always be the parent chunk of the player chunk
    pub target_neutral_transform: TypedTransform2D<f32, UniverseSpace, DrawSpace>,

    pub current_to_target_path: MonotonePath, // This should stay in sync with the above...
}

impl CameraState {
    fn center<T, U>(rect: &TypedRect<T, U>) -> TypedPoint2D<T, U>
    where
        T: Copy + Add<T, Output = T> + Div<T, Output = T> + From<f32>,
    {
        TypedPoint2D::new(
            rect.origin.x + rect.size.width / T::from(2.0),
            rect.origin.y + rect.size.height / T::from(2.0),
        )
    }

    fn intended_target_chunk(logical_state: &LogicalState) -> TopChunk {
        logical_state
            .universe
            .parent_of(&logical_state.player_chunk)
    }

    fn target_transform_for(
        chunk: &TopChunk,
        logical_state: &LogicalState,
        bounds: TypedRect<f32, DrawSpace>,
    ) -> TypedTransform2D<f32, UniverseSpace, DrawSpace> {
        let chunk_bounds = logical_state
            .universe
            .region_of_chunk(chunk)
            .bounding_box(&logical_state.universe);

        transform::fit_rect_in(
            &chunk_bounds
                .to_f32()
                .inflate(DRAW_PARENT_MARGIN, DRAW_PARENT_MARGIN),
            &bounds,
        )
    }

    pub fn new(
        resolution: &TypedSize2D<u32, ScreenSpace>,
        logical_state: &LogicalState,
    ) -> CameraState {
        let camera_bounds = TypedRect::new(
            TypedPoint2D::zero(),
            TypedSize2D::new(resolution.width as f32, resolution.height as f32),
        );

        let start_chunk = CameraState::intended_target_chunk(&logical_state);
        let transform =
            CameraState::target_transform_for(&start_chunk, logical_state, camera_bounds);

        CameraState {
            camera_bounds: camera_bounds,

            current_chunk: start_chunk.clone(),
            current_transform: transform,
            current_neutral_transform: transform,

            target_chunk: start_chunk.clone(),
            target_neutral_transform: transform,

            current_to_target_path: MonotonePath::Zero,
        }
    }

    pub fn do_zoom(&mut self, logical_state: &LogicalState) -> () {
        let new_target = CameraState::intended_target_chunk(logical_state);
        self.target_chunk = new_target.clone();
        self.target_neutral_transform =
            CameraState::target_transform_for(&new_target, logical_state, self.camera_bounds);

        self.current_to_target_path = self.current_to_target_path.up_target();
    }

    pub fn scale_from_neutral(&self) -> f32 {
        transform::scale(
            &self
                .current_transform
                .post_mul(&self.current_neutral_transform.inverse().unwrap()),
        )
    }

    pub fn current_visual_level(&self) -> f32 {
        self.scale_from_neutral().log(ZOOM_SCALE as f32)
    }

    fn target_to_current_transform(
        &self,
        logical_state: &LogicalState,
    ) -> TypedTransform2D<f32, UniverseSpace, UniverseSpace> {
        self.current_to_target_path
            .as_delta_from(&logical_state.universe, &self.current_chunk)
            .0
            .to_scale_transform()
    }

    fn true_target_transform(
        &self,
        logical_state: &LogicalState,
    ) -> TypedTransform2D<f32, UniverseSpace, DrawSpace> {
        self.target_to_current_transform(logical_state)
            .post_mul(&self.target_neutral_transform)
    }

    pub fn update(&mut self, logical_state: &LogicalState, time_delta: time::Duration) -> () {
        // TODO: This is currently busted but somehow I get away with it?

        self.current_transform = transform::lerp(
            &self.current_transform,
            &self.true_target_transform(logical_state),
            1.0 - (-math::time::duration_to_secs(time_delta) * CAMERA_LERP_SPEED).exp(),
        );

        self.normalise(logical_state);
    }

    fn normalise(&mut self, logical_state: &LogicalState) -> () {
        let scale_from_neutral = self.scale_from_neutral();

        // MUST TODO: This will break if the individual chunks get too
        // big, need to scale by the size of the target/current chunk?
        // println!("{:#?}", scale_from_neutral);

        let need_normalisation =
            (scale_from_neutral > CAMERA_UPPER_NORMALISE_TRIGGER && self.current_to_target_path.zdelta() < 0) ||
            (scale_from_neutral < CAMERA_LOWER_NORMALISE_TRIGGER && self.current_to_target_path.zdelta() > 0);

        if need_normalisation
        {
            let (adjustment, new_current_chunk) = self
                .current_to_target_path
                .take(1)
                .as_delta_from(&logical_state.universe, &self.current_chunk);

            self.current_neutral_transform = CameraState::target_transform_for(
                &new_current_chunk,
                logical_state,
                self.camera_bounds,
            );
            self.current_chunk = new_current_chunk;

            self.current_transform = self
                .current_transform
                .pre_mul(&adjustment.invert().to_scale_transform());

            self.current_to_target_path = self.current_to_target_path.drop(1);
        }
    }

    // MUST TODO: This will start to matter when chunks are made
    pub fn recenter(&mut self, logical_state: &LogicalState) -> () {
        unimplemented!();

        // let old_target_chunk = &self.target_chunk;
        // let new_target_chunk = CameraState::intended_target(logical_state);

        // self.target_chunk = new_target_chunk;

        // let old_current_chunk = &self.current_chunk;
        // let new_current_chunk = CameraState::intended_target(logical_state);

        // self.current_chunk = new_current_chunk;

        //unimplemented!();
    }
}
<commit_msg>rustfmt<commit_after>use std::ops::{Add, Div};
use std::time;

use euclid::{TypedPoint2D, TypedRect, TypedSize2D, TypedTransform2D};

use defs::*;
use math;
use math::*;
use shape::*;

// TODO: Do I need a custom AATransform that manages precision
// somehow?

// TODO: think about how movement will work: this is tricky. When a
// chunk is broken in to pieces, we need to make sure our
// current_shape and target_shape are switched to being ones that are
// in the correct chunk

#[derive(Debug)]
pub struct CameraState {
    pub camera_bounds: TypedRect<f32, DrawSpace>,

    pub current_chunk: TopChunk,
    pub current_neutral_transform: TypedTransform2D<f32, UniverseSpace, DrawSpace>,
    pub current_transform: TypedTransform2D<f32, UniverseSpace, DrawSpace>,

    pub target_chunk: TopChunk, // This should always be the parent chunk of the player chunk
    pub target_neutral_transform: TypedTransform2D<f32, UniverseSpace, DrawSpace>,

    pub current_to_target_path: MonotonePath, // This should stay in sync with the above...
}

impl CameraState {
    fn center<T, U>(rect: &TypedRect<T, U>) -> TypedPoint2D<T, U>
    where
        T: Copy + Add<T, Output = T> + Div<T, Output = T> + From<f32>,
    {
        TypedPoint2D::new(
            rect.origin.x + rect.size.width / T::from(2.0),
            rect.origin.y + rect.size.height / T::from(2.0),
        )
    }

    fn intended_target_chunk(logical_state: &LogicalState) -> TopChunk {
        logical_state
            .universe
            .parent_of(&logical_state.player_chunk)
    }

    fn target_transform_for(
        chunk: &TopChunk,
        logical_state: &LogicalState,
        bounds: TypedRect<f32, DrawSpace>,
    ) -> TypedTransform2D<f32, UniverseSpace, DrawSpace> {
        let chunk_bounds = logical_state
            .universe
            .region_of_chunk(chunk)
            .bounding_box(&logical_state.universe);

        transform::fit_rect_in(
            &chunk_bounds
                .to_f32()
                .inflate(DRAW_PARENT_MARGIN, DRAW_PARENT_MARGIN),
            &bounds,
        )
    }

    pub fn new(
        resolution: &TypedSize2D<u32, ScreenSpace>,
        logical_state: &LogicalState,
    ) -> CameraState {
        let camera_bounds = TypedRect::new(
            TypedPoint2D::zero(),
            TypedSize2D::new(resolution.width as f32, resolution.height as f32),
        );

        let start_chunk = CameraState::intended_target_chunk(&logical_state);
        let transform =
            CameraState::target_transform_for(&start_chunk, logical_state, camera_bounds);

        CameraState {
            camera_bounds: camera_bounds,

            current_chunk: start_chunk.clone(),
            current_transform: transform,
            current_neutral_transform: transform,

            target_chunk: start_chunk.clone(),
            target_neutral_transform: transform,

            current_to_target_path: MonotonePath::Zero,
        }
    }

    pub fn do_zoom(&mut self, logical_state: &LogicalState) -> () {
        let new_target = CameraState::intended_target_chunk(logical_state);
        self.target_chunk = new_target.clone();
        self.target_neutral_transform =
            CameraState::target_transform_for(&new_target, logical_state, self.camera_bounds);

        self.current_to_target_path = self.current_to_target_path.up_target();
    }

    pub fn scale_from_neutral(&self) -> f32 {
        transform::scale(
            &self
                .current_transform
                .post_mul(&self.current_neutral_transform.inverse().unwrap()),
        )
    }

    pub fn current_visual_level(&self) -> f32 {
        self.scale_from_neutral().log(ZOOM_SCALE as f32)
    }

    fn target_to_current_transform(
        &self,
        logical_state: &LogicalState,
    ) -> TypedTransform2D<f32, UniverseSpace, UniverseSpace> {
        self.current_to_target_path
            .as_delta_from(&logical_state.universe, &self.current_chunk)
            .0
            .to_scale_transform()
    }

    fn true_target_transform(
        &self,
        logical_state: &LogicalState,
    ) -> TypedTransform2D<f32, UniverseSpace, DrawSpace> {
        self.target_to_current_transform(logical_state)
            .post_mul(&self.target_neutral_transform)
    }

    pub fn update(&mut self, logical_state: &LogicalState, time_delta: time::Duration) -> () {
        // TODO: This is currently busted but somehow I get away with it?

        self.current_transform = transform::lerp(
            &self.current_transform,
            &self.true_target_transform(logical_state),
            1.0 - (-math::time::duration_to_secs(time_delta) * CAMERA_LERP_SPEED).exp(),
        );

        self.normalise(logical_state);
    }

    fn normalise(&mut self, logical_state: &LogicalState) -> () {
        let scale_from_neutral = self.scale_from_neutral();

        // MUST TODO: This will break if the individual chunks get too
        // big, need to scale by the size of the target/current chunk?
        // println!("{:#?}", scale_from_neutral);

        let need_normalisation = (scale_from_neutral > CAMERA_UPPER_NORMALISE_TRIGGER
            && self.current_to_target_path.zdelta() < 0)
            || (scale_from_neutral < CAMERA_LOWER_NORMALISE_TRIGGER
                && self.current_to_target_path.zdelta() > 0);

        if need_normalisation {
            let (adjustment, new_current_chunk) = self
                .current_to_target_path
                .take(1)
                .as_delta_from(&logical_state.universe, &self.current_chunk);

            self.current_neutral_transform = CameraState::target_transform_for(
                &new_current_chunk,
                logical_state,
                self.camera_bounds,
            );
            self.current_chunk = new_current_chunk;

            self.current_transform = self
                .current_transform
                .pre_mul(&adjustment.invert().to_scale_transform());

            self.current_to_target_path = self.current_to_target_path.drop(1);
        }
    }

    // MUST TODO: This will start to matter when chunks are made
    pub fn recenter(&mut self, logical_state: &LogicalState) -> () {
        unimplemented!();

        // let old_target_chunk = &self.target_chunk;
        // let new_target_chunk = CameraState::intended_target(logical_state);

        // self.target_chunk = new_target_chunk;

        // let old_current_chunk = &self.current_chunk;
        // let new_current_chunk = CameraState::intended_target(logical_state);

        // self.current_chunk = new_current_chunk;

        //unimplemented!();
    }
}
<|endoftext|>