<commit_before>use hir::def_id::DefId;
use hir::HirId;
use hir::ItemKind;
use rustc_ast::Mutability;
use rustc_errors::Applicability;
use rustc_hir as hir;
use rustc_middle::ty::subst::InternalSubsts;
use rustc_middle::ty::{Adt, Array, Ref, Ty};
use rustc_session::lint::builtin::RUST_2021_PRELUDE_COLLISIONS;
use rustc_span::symbol::kw::{Empty, Underscore};
use rustc_span::symbol::{sym, Ident};
use rustc_span::Span;
use rustc_trait_selection::infer::InferCtxtExt;

use crate::check::{
    method::probe::{self, Pick},
    FnCtxt,
};

impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
    pub(super) fn lint_dot_call_from_2018(
        &self,
        self_ty: Ty<'tcx>,
        segment: &hir::PathSegment<'_>,
        span: Span,
        call_expr: &'tcx hir::Expr<'tcx>,
        self_expr: &'tcx hir::Expr<'tcx>,
        pick: &Pick<'tcx>,
        args: &'tcx [hir::Expr<'tcx>],
    ) {
        debug!(
            "lookup(method_name={}, self_ty={:?}, call_expr={:?}, self_expr={:?})",
            segment.ident, self_ty, call_expr, self_expr
        );

        // Rust 2021 and later is already using the new prelude
        if span.rust_2021() {
            return;
        }

        let prelude_or_array_lint = match segment.ident.name {
            // `try_into` was added to the prelude in Rust 2021.
            sym::try_into => RUST_2021_PRELUDE_COLLISIONS,
            // `into_iter` wasn't added to the prelude,
            // but `[T; N].into_iter()` doesn't resolve to IntoIterator::into_iter
            // before Rust 2021, which results in the same problem.
            // It is only a problem for arrays.
            sym::into_iter if let Array(..) = self_ty.kind() => {
                // In this case, it wasn't really a prelude addition that was the problem.
                // Instead, the problem is that the array-into_iter hack will no longer apply in Rust 2021.
                rustc_lint::ARRAY_INTO_ITER
            }
            _ => return,
        };

        // No need to lint if method came from std/core, as that will now be in the prelude
        if matches!(self.tcx.crate_name(pick.item.def_id.krate), sym::std | sym::core) {
            return;
        }

        if matches!(pick.kind, probe::PickKind::InherentImplPick | probe::PickKind::ObjectPick) {
            // avoid repeatedly adding unneeded `&*`s
            if pick.autoderefs == 1
                && matches!(
                    pick.autoref_or_ptr_adjustment,
                    Some(probe::AutorefOrPtrAdjustment::Autoref { .. })
                )
                && matches!(self_ty.kind(), Ref(..))
            {
                return;
            }

            // if it's an inherent `self` method (not `&self` or `&mut self`), it will take
            // precedence over the `TryInto` impl, and thus won't break in 2021 edition
            if pick.autoderefs == 0 && pick.autoref_or_ptr_adjustment.is_none() {
                return;
            }

            // Inherent impls only require not relying on autoref and autoderef in order to
            // ensure that the trait implementation won't be used
            self.tcx.struct_span_lint_hir(
                prelude_or_array_lint,
                self_expr.hir_id,
                self_expr.span,
                |lint| {
                    let sp = self_expr.span;

                    let mut lint = lint.build(&format!(
                        "trait method `{}` will become ambiguous in Rust 2021",
                        segment.ident.name
                    ));

                    let derefs = "*".repeat(pick.autoderefs);

                    let autoref = match pick.autoref_or_ptr_adjustment {
                        Some(probe::AutorefOrPtrAdjustment::Autoref {
                            mutbl: Mutability::Mut,
                            ..
                        }) => "&mut ",
                        Some(probe::AutorefOrPtrAdjustment::Autoref {
                            mutbl: Mutability::Not,
                            ..
                        }) => "&",
                        Some(probe::AutorefOrPtrAdjustment::ToConstPtr) | None => "",
                    };
                    if let Ok(self_expr) = self.sess().source_map().span_to_snippet(self_expr.span)
                    {
                        let self_adjusted = if let Some(probe::AutorefOrPtrAdjustment::ToConstPtr) =
                            pick.autoref_or_ptr_adjustment
                        {
                            format!("{}{} as *const _", derefs, self_expr)
                        } else {
                            format!("{}{}{}", autoref, derefs, self_expr)
                        };

                        lint.span_suggestion(
                            sp,
                            "disambiguate the method call",
                            format!("({})", self_adjusted),
                            Applicability::MachineApplicable,
                        );
                    } else {
                        let self_adjusted = if let Some(probe::AutorefOrPtrAdjustment::ToConstPtr) =
                            pick.autoref_or_ptr_adjustment
                        {
                            format!("{}(...) as *const _", derefs)
                        } else {
                            format!("{}{}...", autoref, derefs)
                        };
                        lint.span_help(
                            sp,
                            &format!("disambiguate the method call with `({})`", self_adjusted,),
                        );
                    }

                    lint.emit();
                },
            );
        } else {
            // trait implementations require full disambiguation to not clash with the new prelude
            // additions (i.e. convert from dot-call to fully-qualified call)
            self.tcx.struct_span_lint_hir(
                prelude_or_array_lint,
                call_expr.hir_id,
                call_expr.span,
                |lint| {
                    let sp = call_expr.span;
                    let trait_name = self.trait_path_or_bare_name(
                        span,
                        call_expr.hir_id,
                        pick.item.container.id(),
                    );

                    let mut lint = lint.build(&format!(
                        "trait method `{}` will become ambiguous in Rust 2021",
                        segment.ident.name
                    ));

                    let (self_adjusted, precise) = self.adjust_expr(pick, self_expr, sp);
                    if precise {
                        let args = args
                            .iter()
                            .skip(1)
                            .map(|arg| {
                                let span = arg.span.find_ancestor_inside(sp).unwrap_or_default();
                                format!(
                                    ", {}",
                                    self.sess().source_map().span_to_snippet(span).unwrap()
                                )
                            })
                            .collect::<String>();

                        lint.span_suggestion(
                            sp,
                            "disambiguate the associated function",
                            format!(
                                "{}::{}({}{})",
                                trait_name, segment.ident.name, self_adjusted, args
                            ),
                            Applicability::MachineApplicable,
                        );
                    } else {
                        lint.span_help(
                            sp,
                            &format!(
                                "disambiguate the associated function with `{}::{}(...)`",
                                trait_name, segment.ident,
                            ),
                        );
                    }

                    lint.emit();
                },
            );
        }
    }

    pub(super) fn lint_fully_qualified_call_from_2018(
        &self,
        span: Span,
        method_name: Ident,
        self_ty: Ty<'tcx>,
        self_ty_span: Span,
        expr_id: hir::HirId,
        pick: &Pick<'tcx>,
    ) {
        // Rust 2021 and later is already using the new prelude
        if span.rust_2021() {
            return;
        }

        // These are the fully qualified methods added to prelude in Rust 2021
        if !matches!(method_name.name, sym::try_into | sym::try_from | sym::from_iter) {
            return;
        }

        // No need to lint if method came from std/core, as that will now be in the prelude
        if matches!(self.tcx.crate_name(pick.item.def_id.krate), sym::std | sym::core) {
            return;
        }

        // For from_iter, check if the type actually implements FromIterator.
        // If we know it does not, we don't need to warn.
        if method_name.name == sym::from_iter {
            if let Some(trait_def_id) = self.tcx.get_diagnostic_item(sym::FromIterator) {
                if !self
                    .infcx
                    .type_implements_trait(
                        trait_def_id,
                        self_ty,
                        InternalSubsts::empty(),
                        self.param_env,
                    )
                    .may_apply()
                {
                    return;
                }
            }
        }

        // No need to lint if this is an inherent method called on a specific type, like `Vec::foo(...)`,
        // since such methods take precedence over trait methods.
        if matches!(pick.kind, probe::PickKind::InherentImplPick) {
            return;
        }

        self.tcx.struct_span_lint_hir(RUST_2021_PRELUDE_COLLISIONS, expr_id, span, |lint| {
            // "type" refers to either a type or, more likely, a trait from which
            // the associated function or method is from.
            let trait_path = self.trait_path_or_bare_name(span, expr_id, pick.item.container.id());
            let trait_generics = self.tcx.generics_of(pick.item.container.id());

            let trait_name =
                if trait_generics.params.len() <= trait_generics.has_self as usize {
                    trait_path
                } else {
                    let counts = trait_generics.own_counts();
                    format!(
                        "{}<{}>",
                        trait_path,
                        std::iter::repeat("'_")
                            .take(counts.lifetimes)
                            .chain(std::iter::repeat("_").take(
                                counts.types + counts.consts - trait_generics.has_self as usize
                            ))
                            .collect::<Vec<_>>()
                            .join(", ")
                    )
                };

            let mut lint = lint.build(&format!(
                "trait-associated function `{}` will become ambiguous in Rust 2021",
                method_name.name
            ));

            let mut self_ty_name = self
                .sess()
                .source_map()
                .span_to_snippet(self_ty_span.find_ancestor_inside(span).unwrap_or_default())
                .unwrap_or_else(|_| self_ty.to_string());

            // Get the number of generics the self type has (if an Adt) unless we can determine that
            // the user has written the self type with generics already which we (naively) do by looking
            // for a "<" in `self_ty_name`.
            if !self_ty_name.contains('<') {
                if let Adt(def, _) = self_ty.kind() {
                    let generics = self.tcx.generics_of(def.did);
                    if !generics.params.is_empty() {
                        let counts = generics.own_counts();
                        self_ty_name += &format!(
                            "<{}>",
                            std::iter::repeat("'_")
                                .take(counts.lifetimes)
                                .chain(std::iter::repeat("_").take(counts.types + counts.consts))
                                .collect::<Vec<_>>()
                                .join(", ")
                        );
                    }
                }
            }
            lint.span_suggestion(
                span,
                "disambiguate the associated function",
                format!("<{} as {}>::{}", self_ty_name, trait_name, method_name.name,),
                Applicability::MachineApplicable,
            );

            lint.emit();
        });
    }

    fn trait_path_or_bare_name(
        &self,
        span: Span,
        expr_hir_id: HirId,
        trait_def_id: DefId,
    ) -> String {
        self.trait_path(span, expr_hir_id, trait_def_id).unwrap_or_else(|| {
            let key = self.tcx.def_key(trait_def_id);
            format!("{}", key.disambiguated_data.data)
        })
    }

    fn trait_path(&self, span: Span, expr_hir_id: HirId, trait_def_id: DefId) -> Option<String> {
        let applicable_traits = self.tcx.in_scope_traits(expr_hir_id)?;
        let applicable_trait = applicable_traits.iter().find(|t| t.def_id == trait_def_id)?;
        if applicable_trait.import_ids.is_empty() {
            // The trait was declared within the module, we only need to use its name.
            return None;
        }

        let import_items: Vec<_> = applicable_trait
            .import_ids
            .iter()
            .map(|&import_id| {
                let hir_id = self.tcx.hir().local_def_id_to_hir_id(import_id);
                self.tcx.hir().expect_item(hir_id)
            })
            .collect();

        // Find an identifier with which this trait was imported (note that `_` doesn't count).
        let any_id = import_items
            .iter()
            .filter_map(|item| if item.ident.name != Underscore { Some(item.ident) } else { None })
            .next();
        if let Some(any_id) = any_id {
            if any_id.name == Empty {
                // Glob import, so just use its name.
                return None;
            } else {
                return Some(format!("{}", any_id));
            }
        }

        // All that is left is `_`! We need to use the full path. It doesn't matter which one we pick,
        // so just take the first one.
        match import_items[0].kind {
            ItemKind::Use(path, _) => Some(
                path.segments
                    .iter()
                    .map(|segment| segment.ident.to_string())
                    .collect::<Vec<_>>()
                    .join("::"),
            ),
            _ => {
                span_bug!(span, "unexpected item kind, expected a use: {:?}", import_items[0].kind);
            }
        }
    }

    /// Creates a string version of the `expr` that includes explicit adjustments.
    /// Returns the string and also a bool indicating whther this is a *precise*
    /// suggestion.
    fn adjust_expr(
        &self,
        pick: &Pick<'tcx>,
        expr: &hir::Expr<'tcx>,
        outer: Span,
    ) -> (String, bool) {
        let derefs = "*".repeat(pick.autoderefs);

        let autoref = match pick.autoref_or_ptr_adjustment {
            Some(probe::AutorefOrPtrAdjustment::Autoref { mutbl: Mutability::Mut, .. }) => "&mut ",
            Some(probe::AutorefOrPtrAdjustment::Autoref { mutbl: Mutability::Not, .. }) => "&",
            Some(probe::AutorefOrPtrAdjustment::ToConstPtr) | None => "",
        };

        let (expr_text, precise) = if let Ok(expr_text) = self
            .sess()
            .source_map()
            .span_to_snippet(expr.span.find_ancestor_inside(outer).unwrap_or_default())
        {
            (expr_text, true)
        } else {
            ("(..)".to_string(), false)
        };

        let adjusted_text = if let Some(probe::AutorefOrPtrAdjustment::ToConstPtr) =
            pick.autoref_or_ptr_adjustment
        {
            format!("{}{} as *const _", derefs, expr_text)
        } else {
            format!("{}{}{}", autoref, derefs, expr_text)
        };

        (adjusted_text, precise)
    }
}
<commit_msg>Use and_then instead of unwrap_or_default.<commit_after>use hir::def_id::DefId;
use hir::HirId;
use hir::ItemKind;
use rustc_ast::Mutability;
use rustc_errors::Applicability;
use rustc_hir as hir;
use rustc_middle::ty::subst::InternalSubsts;
use rustc_middle::ty::{Adt, Array, Ref, Ty};
use rustc_session::lint::builtin::RUST_2021_PRELUDE_COLLISIONS;
use rustc_span::symbol::kw::{Empty, Underscore};
use rustc_span::symbol::{sym, Ident};
use rustc_span::Span;
use rustc_trait_selection::infer::InferCtxtExt;

use crate::check::{
    method::probe::{self, Pick},
    FnCtxt,
};

impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
    pub(super) fn lint_dot_call_from_2018(
        &self,
        self_ty: Ty<'tcx>,
        segment: &hir::PathSegment<'_>,
        span: Span,
        call_expr: &'tcx hir::Expr<'tcx>,
        self_expr: &'tcx hir::Expr<'tcx>,
        pick: &Pick<'tcx>,
        args: &'tcx [hir::Expr<'tcx>],
    ) {
        debug!(
            "lookup(method_name={}, self_ty={:?}, call_expr={:?}, self_expr={:?})",
            segment.ident, self_ty, call_expr, self_expr
        );

        // Rust 2021 and later is already using the new prelude
        if span.rust_2021() {
            return;
        }

        let prelude_or_array_lint = match segment.ident.name {
            // `try_into` was added to the prelude in Rust 2021.
            sym::try_into => RUST_2021_PRELUDE_COLLISIONS,
            // `into_iter` wasn't added to the prelude,
            // but `[T; N].into_iter()` doesn't resolve to IntoIterator::into_iter
            // before Rust 2021, which results in the same problem.
            // It is only a problem for arrays.
            sym::into_iter if let Array(..) = self_ty.kind() => {
                // In this case, it wasn't really a prelude addition that was the problem.
                // Instead, the problem is that the array-into_iter hack will no longer apply in Rust 2021.
                rustc_lint::ARRAY_INTO_ITER
            }
            _ => return,
        };

        // No need to lint if method came from std/core, as that will now be in the prelude
        if matches!(self.tcx.crate_name(pick.item.def_id.krate), sym::std | sym::core) {
            return;
        }

        if matches!(pick.kind, probe::PickKind::InherentImplPick | probe::PickKind::ObjectPick) {
            // avoid repeatedly adding unneeded `&*`s
            if pick.autoderefs == 1
                && matches!(
                    pick.autoref_or_ptr_adjustment,
                    Some(probe::AutorefOrPtrAdjustment::Autoref { .. })
                )
                && matches!(self_ty.kind(), Ref(..))
            {
                return;
            }

            // if it's an inherent `self` method (not `&self` or `&mut self`), it will take
            // precedence over the `TryInto` impl, and thus won't break in 2021 edition
            if pick.autoderefs == 0 && pick.autoref_or_ptr_adjustment.is_none() {
                return;
            }

            // Inherent impls only require not relying on autoref and autoderef in order to
            // ensure that the trait implementation won't be used
            self.tcx.struct_span_lint_hir(
                prelude_or_array_lint,
                self_expr.hir_id,
                self_expr.span,
                |lint| {
                    let sp = self_expr.span;

                    let mut lint = lint.build(&format!(
                        "trait method `{}` will become ambiguous in Rust 2021",
                        segment.ident.name
                    ));

                    let derefs = "*".repeat(pick.autoderefs);

                    let autoref = match pick.autoref_or_ptr_adjustment {
                        Some(probe::AutorefOrPtrAdjustment::Autoref {
                            mutbl: Mutability::Mut,
                            ..
                        }) => "&mut ",
                        Some(probe::AutorefOrPtrAdjustment::Autoref {
                            mutbl: Mutability::Not,
                            ..
                        }) => "&",
                        Some(probe::AutorefOrPtrAdjustment::ToConstPtr) | None => "",
                    };
                    if let Ok(self_expr) = self.sess().source_map().span_to_snippet(self_expr.span)
                    {
                        let self_adjusted = if let Some(probe::AutorefOrPtrAdjustment::ToConstPtr) =
                            pick.autoref_or_ptr_adjustment
                        {
                            format!("{}{} as *const _", derefs, self_expr)
                        } else {
                            format!("{}{}{}", autoref, derefs, self_expr)
                        };

                        lint.span_suggestion(
                            sp,
                            "disambiguate the method call",
                            format!("({})", self_adjusted),
                            Applicability::MachineApplicable,
                        );
                    } else {
                        let self_adjusted = if let Some(probe::AutorefOrPtrAdjustment::ToConstPtr) =
                            pick.autoref_or_ptr_adjustment
                        {
                            format!("{}(...) as *const _", derefs)
                        } else {
                            format!("{}{}...", autoref, derefs)
                        };
                        lint.span_help(
                            sp,
                            &format!("disambiguate the method call with `({})`", self_adjusted,),
                        );
                    }

                    lint.emit();
                },
            );
        } else {
            // trait implementations require full disambiguation to not clash with the new prelude
            // additions (i.e. convert from dot-call to fully-qualified call)
            self.tcx.struct_span_lint_hir(
                prelude_or_array_lint,
                call_expr.hir_id,
                call_expr.span,
                |lint| {
                    let sp = call_expr.span;
                    let trait_name = self.trait_path_or_bare_name(
                        span,
                        call_expr.hir_id,
                        pick.item.container.id(),
                    );

                    let mut lint = lint.build(&format!(
                        "trait method `{}` will become ambiguous in Rust 2021",
                        segment.ident.name
                    ));

                    let (self_adjusted, precise) = self.adjust_expr(pick, self_expr, sp);
                    if precise {
                        let args = args
                            .iter()
                            .skip(1)
                            .map(|arg| {
                                let span = arg.span.find_ancestor_inside(sp).unwrap_or_default();
                                format!(
                                    ", {}",
                                    self.sess().source_map().span_to_snippet(span).unwrap()
                                )
                            })
                            .collect::<String>();

                        lint.span_suggestion(
                            sp,
                            "disambiguate the associated function",
                            format!(
                                "{}::{}({}{})",
                                trait_name, segment.ident.name, self_adjusted, args
                            ),
                            Applicability::MachineApplicable,
                        );
                    } else {
                        lint.span_help(
                            sp,
                            &format!(
                                "disambiguate the associated function with `{}::{}(...)`",
                                trait_name, segment.ident,
                            ),
                        );
                    }

                    lint.emit();
                },
            );
        }
    }

    pub(super) fn lint_fully_qualified_call_from_2018(
        &self,
        span: Span,
        method_name: Ident,
        self_ty: Ty<'tcx>,
        self_ty_span: Span,
        expr_id: hir::HirId,
        pick: &Pick<'tcx>,
    ) {
        // Rust 2021 and later is already using the new prelude
        if span.rust_2021() {
            return;
        }

        // These are the fully qualified methods added to prelude in Rust 2021
        if !matches!(method_name.name, sym::try_into | sym::try_from | sym::from_iter) {
            return;
        }

        // No need to lint if method came from std/core, as that will now be in the prelude
        if matches!(self.tcx.crate_name(pick.item.def_id.krate), sym::std | sym::core) {
            return;
        }

        // For from_iter, check if the type actually implements FromIterator.
        // If we know it does not, we don't need to warn.
        if method_name.name == sym::from_iter {
            if let Some(trait_def_id) = self.tcx.get_diagnostic_item(sym::FromIterator) {
                if !self
                    .infcx
                    .type_implements_trait(
                        trait_def_id,
                        self_ty,
                        InternalSubsts::empty(),
                        self.param_env,
                    )
                    .may_apply()
                {
                    return;
                }
            }
        }

        // No need to lint if this is an inherent method called on a specific type, like `Vec::foo(...)`,
        // since such methods take precedence over trait methods.
        if matches!(pick.kind, probe::PickKind::InherentImplPick) {
            return;
        }

        self.tcx.struct_span_lint_hir(RUST_2021_PRELUDE_COLLISIONS, expr_id, span, |lint| {
            // "type" refers to either a type or, more likely, a trait from which
            // the associated function or method is from.
            let trait_path = self.trait_path_or_bare_name(span, expr_id, pick.item.container.id());
            let trait_generics = self.tcx.generics_of(pick.item.container.id());

            let trait_name =
                if trait_generics.params.len() <= trait_generics.has_self as usize {
                    trait_path
                } else {
                    let counts = trait_generics.own_counts();
                    format!(
                        "{}<{}>",
                        trait_path,
                        std::iter::repeat("'_")
                            .take(counts.lifetimes)
                            .chain(std::iter::repeat("_").take(
                                counts.types + counts.consts - trait_generics.has_self as usize
                            ))
                            .collect::<Vec<_>>()
                            .join(", ")
                    )
                };

            let mut lint = lint.build(&format!(
                "trait-associated function `{}` will become ambiguous in Rust 2021",
                method_name.name
            ));

            let mut self_ty_name = self_ty_span
                .find_ancestor_inside(span)
                .and_then(|span| self.sess().source_map().span_to_snippet(span).ok())
                .unwrap_or_else(|| self_ty.to_string());

            // Get the number of generics the self type has (if an Adt) unless we can determine that
            // the user has written the self type with generics already which we (naively) do by looking
            // for a "<" in `self_ty_name`.
            if !self_ty_name.contains('<') {
                if let Adt(def, _) = self_ty.kind() {
                    let generics = self.tcx.generics_of(def.did);
                    if !generics.params.is_empty() {
                        let counts = generics.own_counts();
                        self_ty_name += &format!(
                            "<{}>",
                            std::iter::repeat("'_")
                                .take(counts.lifetimes)
                                .chain(std::iter::repeat("_").take(counts.types + counts.consts))
                                .collect::<Vec<_>>()
                                .join(", ")
                        );
                    }
                }
            }
            lint.span_suggestion(
                span,
                "disambiguate the associated function",
                format!("<{} as {}>::{}", self_ty_name, trait_name, method_name.name,),
                Applicability::MachineApplicable,
            );

            lint.emit();
        });
    }

    fn trait_path_or_bare_name(
        &self,
        span: Span,
        expr_hir_id: HirId,
        trait_def_id: DefId,
    ) -> String {
        self.trait_path(span, expr_hir_id, trait_def_id).unwrap_or_else(|| {
            let key = self.tcx.def_key(trait_def_id);
            format!("{}", key.disambiguated_data.data)
        })
    }

    fn trait_path(&self, span: Span, expr_hir_id: HirId, trait_def_id: DefId) -> Option<String> {
        let applicable_traits = self.tcx.in_scope_traits(expr_hir_id)?;
        let applicable_trait = applicable_traits.iter().find(|t| t.def_id == trait_def_id)?;
        if applicable_trait.import_ids.is_empty() {
            // The trait was declared within the module, we only need to use its name.
            return None;
        }

        let import_items: Vec<_> = applicable_trait
            .import_ids
            .iter()
            .map(|&import_id| {
                let hir_id = self.tcx.hir().local_def_id_to_hir_id(import_id);
                self.tcx.hir().expect_item(hir_id)
            })
            .collect();

        // Find an identifier with which this trait was imported (note that `_` doesn't count).
        let any_id = import_items
            .iter()
            .filter_map(|item| if item.ident.name != Underscore { Some(item.ident) } else { None })
            .next();
        if let Some(any_id) = any_id {
            if any_id.name == Empty {
                // Glob import, so just use its name.
                return None;
            } else {
                return Some(format!("{}", any_id));
            }
        }

        // All that is left is `_`! We need to use the full path. It doesn't matter which one we pick,
        // so just take the first one.
        match import_items[0].kind {
            ItemKind::Use(path, _) => Some(
                path.segments
                    .iter()
                    .map(|segment| segment.ident.to_string())
                    .collect::<Vec<_>>()
                    .join("::"),
            ),
            _ => {
                span_bug!(span, "unexpected item kind, expected a use: {:?}", import_items[0].kind);
            }
        }
    }

    /// Creates a string version of the `expr` that includes explicit adjustments.
    /// Returns the string and also a bool indicating whther this is a *precise*
    /// suggestion.
    fn adjust_expr(
        &self,
        pick: &Pick<'tcx>,
        expr: &hir::Expr<'tcx>,
        outer: Span,
    ) -> (String, bool) {
        let derefs = "*".repeat(pick.autoderefs);

        let autoref = match pick.autoref_or_ptr_adjustment {
            Some(probe::AutorefOrPtrAdjustment::Autoref { mutbl: Mutability::Mut, .. }) => "&mut ",
            Some(probe::AutorefOrPtrAdjustment::Autoref { mutbl: Mutability::Not, .. }) => "&",
            Some(probe::AutorefOrPtrAdjustment::ToConstPtr) | None => "",
        };

        let (expr_text, precise) = if let Some(expr_text) = expr
            .span
            .find_ancestor_inside(outer)
            .and_then(|span| self.sess().source_map().span_to_snippet(span).ok())
        {
            (expr_text, true)
        } else {
            ("(..)".to_string(), false)
        };

        let adjusted_text = if let Some(probe::AutorefOrPtrAdjustment::ToConstPtr) =
            pick.autoref_or_ptr_adjustment
        {
            format!("{}{} as *const _", derefs, expr_text)
        } else {
            format!("{}{}{}", autoref, derefs, expr_text)
        };

        (adjusted_text, precise)
    }
}
<|endoftext|>


<commit_before>use std::path::Path;

use html5ever::{LocalName, Namespace, QualName};
use kuchiki::traits::TendrilSink;
use kuchiki::{parse_fragment, Attribute, ExpandedName, NodeRef};

use crate::page::Page;
use crate::settings::Settings;
use crate::web;

use crate::string_ext::StringExt;

const START_HTML: &str = "";
const END_HTML: &str = "";

// Enhance the HTML
pub fn enhance_markup(html: &str, settings: &Settings) -> String {
    let doc = parse_markup(html);

    link_headings(&doc);
    process_custom_elements(&doc, &settings.pages_path);
    trim_pre_whitespace(&doc);

    let mut enhanced_html = doc.to_string();
    // HACK: The document ends up serialised with a wrapping `<html>` element around the content
    // strip that here. E.g.
    // https://github.com/kuchiki-rs/kuchiki/blob/f652e38b12cb0d33f7bb0565b6933a6e2823a0c5/src/tests.rs#L66
    if enhanced_html.ends_with(END_HTML) {
        enhanced_html.truncate(enhanced_html.len() - END_HTML.len());
    }
    if enhanced_html.starts_with(START_HTML) {
        enhanced_html[START_HTML.len()..].to_string()
    } else {
        enhanced_html
    }
}

fn link_headings(doc: &NodeRef) {
    for heading in doc.select("h1,h2,h3,h4,h5,h6").unwrap() {
        let identifier = heading.text_contents().to_slug();

        let a = NodeRef::new_element(
            el_name("a"),
            [
                attr("class", String::from("anchor")),
                attr("href", format!("#{}", identifier)),
                attr("id", identifier),
            ],
        );
        let span = NodeRef::new_element(
            el_name("span"),
            [attr("class", String::from("link-icon monospace"))],
        );
        span.append(NodeRef::new_text("#"));
        a.append(span);

        heading.as_node().prepend(a)
    }
}

fn process_custom_elements(doc: &NodeRef, basepath: &Path) {
    RecentlyChangedList::process(doc, basepath);
}

fn trim_pre_whitespace(doc: &NodeRef) {
    info!("{}", doc.to_string());
    // Have to collect notes to remove otherwise iteration stop after one pass through the block
    // https://github.com/kuchiki-rs/kuchiki/issues/57
    let mut to_detach = Vec::new();
    for code in doc.select("pre > code").unwrap() {
        let mut next_node = code.as_node().first_child();
        while let Some(node) = next_node {
            let tmp_next = node.next_sibling();
            if node.as_text().is_some() {
                to_detach.push(node);
            }
            next_node = tmp_next;
        }
    }
    to_detach.iter().for_each(|node| node.detach())
}

struct RecentlyChangedList;

const RECENTLY_MODIFIED_LIMIT: usize = 5;

impl RecentlyChangedList {
    fn process(doc: &NodeRef, basepath: &Path) {
        let p_qual = QualName::new(None, ns!(html), local_name!("p"));
        for elem in doc.select("recently-changed-list").unwrap() {
            // comrak wraps the element in
 tag so we need to replace it to avoid generating
            // invalid markup
            let parent = elem.as_node().parent();
            let node_to_replace = match parent {
                Some(ref parent) if parent.as_element().map_or(false, |e| e.name == p_qual) => {
                    parent
                }
                Some(_) | None => elem.as_node(),
            };

            let list = NodeRef::new_element(el_name("ul"), []);
            for page in Page::recently_modified(RECENTLY_MODIFIED_LIMIT, basepath) {
                let li = NodeRef::new_element(el_name("li"), []);
                let link = NodeRef::new_element(
                    el_name("a"),
                    [attr(
                        "href",
                        uri!(web::page::show(name = &page.name)).to_string(),
                    )],
                );
                link.append(NodeRef::new_text(page.title()));
                let span = NodeRef::new_element(
                    el_name("span"),
                    [attr("class", String::from("smaller-font lighten"))],
                );
                span.append(NodeRef::new_text(" updated "));
                let abbr =
                    NodeRef::new_element(el_name("abbr"), [attr("title", page.mtime_rfc3339())]);
                abbr.append(NodeRef::new_text(page.mtime_human()));

                span.append(abbr);
                li.append(link);
                li.append(span);
                list.append(li);
            }

            // replace elem with the list
            node_to_replace.insert_after(list);
            node_to_replace.detach();
        }
    }
}

fn attr(name: &str, value: String) -> (ExpandedName, Attribute) {
    (
        ExpandedName::new(Namespace::from(""), LocalName::from(name)),
        Attribute {
            prefix: None,
            value,
        },
    )
}

fn el_name(name: &str) -> QualName {
    QualName::new(None, ns!(html), LocalName::from(name))
}

fn parse_markup(html: &str) -> NodeRef {
    let ctx_name = QualName::new(None, ns!(html), local_name!("div"));
    parse_fragment(ctx_name.clone(), vec![]).one(html)
}

#[cfg(test)]
mod tests {
    use super::*;
    use regex::Regex;
    use rocket::form::validate::Contains;
    use std::path::PathBuf;

    const HTML: &str = "
Test
<recently-changed-list></recently-changed-list>";

    fn test_settings() -> Settings {
        Settings {
            pages_path: pages_path(),
            author: "Test".to_string(),
            author_url: "https://example.com/".to_string(),
            copyright_start_year: 2020,
            name: "Test Site".to_string(),
            domain: "example.com".to_string(),
            tagline: "For testing".to_string(),
            sentry_dsn: None,
        }
    }

    fn pages_path() -> PathBuf {
        let mut path = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
        path.extend(&["tests", "fixtures", "pages"]);
        path
    }

    #[test]
    fn recently_changed_list() {
        // Test that it replaces the custom element with a list of pages
        let doc = parse_markup(HTML);
        RecentlyChangedList::process(&doc, &pages_path());

        let processed = doc.to_string();
        let regex = Regex::new(r#"
(
[^<]+ updated [^<]+
)+
"#).unwrap();
        assert!(regex.is_match(&processed));
    }

    #[test]
    fn recently_changed_list_strips_parent_p_tag() {
        // When rendering the Markdown to HTML comrak wraps the custom element in a
, test that
        // this is stripped.
        let html = "
Recently Updated Pages
\n
<recently-changed-list></recently-changed-list>
\n";
        let doc = parse_markup(html);
        RecentlyChangedList::process(&doc, &pages_path());

        let processed = doc.to_string();
        assert!(!processed.contains("
"));
    }

    #[test]
    fn enhancing_markup_does_not_add_html_tag() {
        let markup = "
no HTML tag please
";
        let enhanced = enhance_markup(markup, &test_settings());
        assert_eq!(markup, enhanced);
    }
}
<commit_msg>Remove another debug log statement<commit_after>use std::path::Path;

use html5ever::{LocalName, Namespace, QualName};
use kuchiki::traits::TendrilSink;
use kuchiki::{parse_fragment, Attribute, ExpandedName, NodeRef};

use crate::page::Page;
use crate::settings::Settings;
use crate::web;

use crate::string_ext::StringExt;

const START_HTML: &str = "";
const END_HTML: &str = "";

// Enhance the HTML
pub fn enhance_markup(html: &str, settings: &Settings) -> String {
    let doc = parse_markup(html);

    link_headings(&doc);
    process_custom_elements(&doc, &settings.pages_path);
    trim_pre_whitespace(&doc);

    let mut enhanced_html = doc.to_string();
    // HACK: The document ends up serialised with a wrapping `<html>` element around the content
    // strip that here. E.g.
    // https://github.com/kuchiki-rs/kuchiki/blob/f652e38b12cb0d33f7bb0565b6933a6e2823a0c5/src/tests.rs#L66
    if enhanced_html.ends_with(END_HTML) {
        enhanced_html.truncate(enhanced_html.len() - END_HTML.len());
    }
    if enhanced_html.starts_with(START_HTML) {
        enhanced_html[START_HTML.len()..].to_string()
    } else {
        enhanced_html
    }
}

fn link_headings(doc: &NodeRef) {
    for heading in doc.select("h1,h2,h3,h4,h5,h6").unwrap() {
        let identifier = heading.text_contents().to_slug();

        let a = NodeRef::new_element(
            el_name("a"),
            [
                attr("class", String::from("anchor")),
                attr("href", format!("#{}", identifier)),
                attr("id", identifier),
            ],
        );
        let span = NodeRef::new_element(
            el_name("span"),
            [attr("class", String::from("link-icon monospace"))],
        );
        span.append(NodeRef::new_text("#"));
        a.append(span);

        heading.as_node().prepend(a)
    }
}

fn process_custom_elements(doc: &NodeRef, basepath: &Path) {
    RecentlyChangedList::process(doc, basepath);
}

fn trim_pre_whitespace(doc: &NodeRef) {
    // Have to collect notes to remove otherwise iteration stop after one pass through the block
    // https://github.com/kuchiki-rs/kuchiki/issues/57
    let mut to_detach = Vec::new();
    for code in doc.select("pre > code").unwrap() {
        let mut next_node = code.as_node().first_child();
        while let Some(node) = next_node {
            let tmp_next = node.next_sibling();
            if node.as_text().is_some() {
                to_detach.push(node);
            }
            next_node = tmp_next;
        }
    }
    to_detach.iter().for_each(|node| node.detach())
}

struct RecentlyChangedList;

const RECENTLY_MODIFIED_LIMIT: usize = 5;

impl RecentlyChangedList {
    fn process(doc: &NodeRef, basepath: &Path) {
        let p_qual = QualName::new(None, ns!(html), local_name!("p"));
        for elem in doc.select("recently-changed-list").unwrap() {
            // comrak wraps the element in
 tag so we need to replace it to avoid generating
            // invalid markup
            let parent = elem.as_node().parent();
            let node_to_replace = match parent {
                Some(ref parent) if parent.as_element().map_or(false, |e| e.name == p_qual) => {
                    parent
                }
                Some(_) | None => elem.as_node(),
            };

            let list = NodeRef::new_element(el_name("ul"), []);
            for page in Page::recently_modified(RECENTLY_MODIFIED_LIMIT, basepath) {
                let li = NodeRef::new_element(el_name("li"), []);
                let link = NodeRef::new_element(
                    el_name("a"),
                    [attr(
                        "href",
                        uri!(web::page::show(name = &page.name)).to_string(),
                    )],
                );
                link.append(NodeRef::new_text(page.title()));
                let span = NodeRef::new_element(
                    el_name("span"),
                    [attr("class", String::from("smaller-font lighten"))],
                );
                span.append(NodeRef::new_text(" updated "));
                let abbr =
                    NodeRef::new_element(el_name("abbr"), [attr("title", page.mtime_rfc3339())]);
                abbr.append(NodeRef::new_text(page.mtime_human()));

                span.append(abbr);
                li.append(link);
                li.append(span);
                list.append(li);
            }

            // replace elem with the list
            node_to_replace.insert_after(list);
            node_to_replace.detach();
        }
    }
}

fn attr(name: &str, value: String) -> (ExpandedName, Attribute) {
    (
        ExpandedName::new(Namespace::from(""), LocalName::from(name)),
        Attribute {
            prefix: None,
            value,
        },
    )
}

fn el_name(name: &str) -> QualName {
    QualName::new(None, ns!(html), LocalName::from(name))
}

fn parse_markup(html: &str) -> NodeRef {
    let ctx_name = QualName::new(None, ns!(html), local_name!("div"));
    parse_fragment(ctx_name.clone(), vec![]).one(html)
}

#[cfg(test)]
mod tests {
    use super::*;
    use regex::Regex;
    use rocket::form::validate::Contains;
    use std::path::PathBuf;

    const HTML: &str = "
Test
<recently-changed-list></recently-changed-list>";

    fn test_settings() -> Settings {
        Settings {
            pages_path: pages_path(),
            author: "Test".to_string(),
            author_url: "https://example.com/".to_string(),
            copyright_start_year: 2020,
            name: "Test Site".to_string(),
            domain: "example.com".to_string(),
            tagline: "For testing".to_string(),
            sentry_dsn: None,
        }
    }

    fn pages_path() -> PathBuf {
        let mut path = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
        path.extend(&["tests", "fixtures", "pages"]);
        path
    }

    #[test]
    fn recently_changed_list() {
        // Test that it replaces the custom element with a list of pages
        let doc = parse_markup(HTML);
        RecentlyChangedList::process(&doc, &pages_path());

        let processed = doc.to_string();
        let regex = Regex::new(r#"
(
[^<]+ updated [^<]+
)+
"#).unwrap();
        assert!(regex.is_match(&processed));
    }

    #[test]
    fn recently_changed_list_strips_parent_p_tag() {
        // When rendering the Markdown to HTML comrak wraps the custom element in a
, test that
        // this is stripped.
        let html = "
Recently Updated Pages
\n
<recently-changed-list></recently-changed-list>
\n";
        let doc = parse_markup(html);
        RecentlyChangedList::process(&doc, &pages_path());

        let processed = doc.to_string();
        assert!(!processed.contains("
"));
    }

    #[test]
    fn enhancing_markup_does_not_add_html_tag() {
        let markup = "
no HTML tag please
";
        let enhanced = enhance_markup(markup, &test_settings());
        assert_eq!(markup, enhanced);
    }
}
<|endoftext|>


<commit_before>use cgmath::Point2;
use game::get_rand_from_range;
use game::constants::TERRAIN_OBJECTS;
use graphics::coords_to_tile_offset;
use pathfinding::{astar::astar, utils::absdiff};
use shaders::Position;

fn tiles(p: &Point2<i32>) -> Vec<(Point2<i32>, i32)> {
  let e = Point2::new(p.x as i32, p.y as i32);
  let neighbours: Vec<Point2<i32>> = vec![Point2::new(e.x - 1, e.y),
                                          Point2::new(e.x - 1, e.y - 1),
                                          Point2::new(e.x, e.y - 1),
                                          Point2::new(e.x + 1, e.y),
                                          Point2::new(e.x + 1, e.y + 1),
                                          Point2::new(e.x, e.y + 1),
                                          Point2::new(e.x - 1, e.y + 1),
                                          Point2::new(e.x + 1, e.y - 1)
  ];

  neighbours.iter()
            .filter(|ref e| !TERRAIN_OBJECTS.contains(&[e.x as usize, e.y as usize]))
            .map(|p| (Point2::new(p.x, p.y), 1))
            .collect()
}

fn calc_route(start_point: Position, end_point: Position) -> Option<(Vec<Point2<i32>>, i32)> {
  let start = coords_to_tile_offset(start_point);
  let end = coords_to_tile_offset(end_point);

  astar(&start,
        |p: &Point2<i32>| tiles(p),
        |p: &Point2<i32>| absdiff(p.x, end.x) + absdiff(p.y, end.y),
        |p: &Point2<i32>| p.x == end.x && p.y == end.y)
}

pub fn calc_next_movement(start_point: Position, end_point: Position) -> i32 {
  let next_step: Point2<i32> = calc_route(start_point, end_point)
    .map_or_else(|| Point2::new(0 as i32, 0 as i32),
                 |(route, _)| {
                   if route.len() > 1 {
                     Point2::new(route[1].x as i32, route[1].y as i32)
                   } else {
                     Point2::new(route[0].x as i32, route[0].y as i32)
                   }
                 });

  let start = coords_to_tile_offset(start_point);

  let diff: (i32, i32) = (start.x - next_step.x, start.y - next_step.y);
  match diff {
    (1, 0) => 315,
    (1, 1) => 270,
    (0, 1) => 225,
    (-1, 1) => 180,
    (-1, 0) => 135,
    (-1, -1) => 90,
    (0, -1) => 45,
    (1, -1) => 0,
    _ => get_rand_from_range(0, 359),
  }
}
<commit_msg>Refactor "tiles" function so that impassable tiles is given as parameter<commit_after>use cgmath::Point2;
use game::get_rand_from_range;
use game::constants::TERRAIN_OBJECTS;
use graphics::coords_to_tile_offset;
use pathfinding::{astar::astar, utils::absdiff};
use shaders::Position;

fn tiles(p: &Point2<i32>, impassable_tiles: &Vec<[usize; 2]>) -> Vec<(Point2<i32>, i32)> {
  let e = Point2::new(p.x as i32, p.y as i32);
  let neighbours: Vec<Point2<i32>> = vec![Point2::new(e.x - 1, e.y),
                                          Point2::new(e.x - 1, e.y - 1),
                                          Point2::new(e.x, e.y - 1),
                                          Point2::new(e.x + 1, e.y),
                                          Point2::new(e.x + 1, e.y + 1),
                                          Point2::new(e.x, e.y + 1),
                                          Point2::new(e.x - 1, e.y + 1),
                                          Point2::new(e.x + 1, e.y - 1)
  ];

  neighbours.iter()
            .filter(|ref e| !impassable_tiles.contains(&[e.x as usize, e.y as usize]))
            .map(|p| (Point2::new(p.x, p.y), 1))
            .collect()
}

fn calc_route(start_point: Position, end_point: Position) -> Option<(Vec<Point2<i32>>, i32)> {
  let start = coords_to_tile_offset(start_point);
  let end = coords_to_tile_offset(end_point);

  astar(&start,
        |p: &Point2<i32>| tiles(p, &TERRAIN_OBJECTS.to_vec()),
        |p: &Point2<i32>| absdiff(p.x, end.x) + absdiff(p.y, end.y),
        |p: &Point2<i32>| p.x == end.x && p.y == end.y)
}

pub fn calc_next_movement(start_point: Position, end_point: Position) -> i32 {
  let next_step: Point2<i32> = calc_route(start_point, end_point)
    .map_or_else(|| Point2::new(0 as i32, 0 as i32),
                 |(route, _)| {
                   if route.len() > 1 {
                     Point2::new(route[1].x as i32, route[1].y as i32)
                   } else {
                     Point2::new(route[0].x as i32, route[0].y as i32)
                   }
                 });

  let start = coords_to_tile_offset(start_point);

  let diff: (i32, i32) = (start.x - next_step.x, start.y - next_step.y);
  match diff {
    (1, 0) => 315,
    (1, 1) => 270,
    (0, 1) => 225,
    (-1, 1) => 180,
    (-1, 0) => 135,
    (-1, -1) => 90,
    (0, -1) => 45,
    (1, -1) => 0,
    _ => get_rand_from_range(0, 359),
  }
}
<|endoftext|>


<commit_before>// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

#![deny(warnings)]

fn foo<F: FnOnce()>(_f: F) { }

fn main() {
    let mut var = Vec::new();
    foo(move|| {
        var.push(1);
    });
}
<commit_msg>Added test for spurious cannot borrow immutable item error with a closure<commit_after>// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//compile-flags: -Z borrowck=compare -Z emit-end-regions

#![deny(warnings)]

fn foo<F: FnOnce()>(_f: F) { }

fn main() {
    let mut var = Vec::new();
    foo(move|| {
        var.push(1);
    });
}
<|endoftext|>


<commit_before>extern crate kailua_diag;
extern crate kailua_syntax;
extern crate kailua_check;
#[macro_use] extern crate log;
extern crate env_logger;

use std::str;
use std::io;
use std::env;
use std::cell::RefCell;
use std::rc::Rc;
use std::path::{Path, PathBuf};

use kailua_diag::{Spanned, WithLoc, Source, Report, ConsoleReport};
use kailua_syntax::{parse_chunk, Block};

fn parse_and_check(mainpath: &Path) -> Result<(), String> {
    struct Options {
        source: Rc<RefCell<Source>>,
        root: PathBuf,
        report: Rc<Report>,
    }

    impl kailua_check::Options for Options {
        fn source(&self) -> &RefCell<Source> { &*self.source }

        fn require_block(&mut self, path: &[u8]) -> Result<Spanned<Block>, String> {
            const BUILTIN_MODS: &'static [&'static str] = &[
                "cjson",
                "url",
                "redis",
            ];

            if BUILTIN_MODS.iter().any(|&name| name.as_bytes() == path) {
                // dummy: they should not affect anything here
                Ok(Vec::new().without_loc())
            } else {
                let path = try!(str::from_utf8(path).map_err(|e| e.to_string()));

                let try = |opts: &mut Options, path: &Path| {
                    let path = opts.root.join(path);
                    let span = match opts.source.borrow_mut().add_file(&path) {
                        Ok(span) => span,
                        Err(e) => {
                            if e.kind() == io::ErrorKind::NotFound { return Ok(None); }
                            return Err(e.to_string());
                        }
                    };
                    let chunk = try!(parse_chunk(&opts.source.borrow(), span,
                                                 &*opts.report).map_err(|_| format!("parse error")));
                    Ok(Some(chunk))
                };

                macro_rules! try_path {
                    ($e:expr) => ({
                        if let Some(chunk) = try!(try(self, Path::new(&$e))) { return Ok(chunk); }
                    })
                }

                if !path.ends_with(".lua") {
                    try_path!(format!("{}.lua.kailua", path));
                    try_path!(format!("{}.lua", path));
                }
                try_path!(format!("{}.kailua", path));
                try_path!(path);
                Err(format!("module not found"))
            }
        }
    }

    let mut source = Source::new();
    let filespan = try!(source.add_file(mainpath).map_err(|e| e.to_string()));
    let source = Rc::new(RefCell::new(source));
    let report = Rc::new(ConsoleReport::new(source.clone()));
    let mut context = kailua_check::Context::new(report.clone());
    let root = mainpath.parent().unwrap_or(&Path::new(".."));
    let mut opts = Options { source: source, root: root.to_owned(), report: report };
    kailua_check::check_from_span(&mut context, filespan, &mut opts)
}

pub fn main() {
    env_logger::init().unwrap();
    for path in env::args().skip(1) {
        println!("--== {} ==--", path);
        if let Err(e) = parse_and_check(&Path::new(&path)) {
            // hide the internal error message, which will be eventually removed
            info!("error while checking {}: {}", path, e);
        } else {
            println!("done.");
        }
        println!("");
    }
}

<commit_msg>no more special casing for remaining environment-dependent modules.<commit_after>extern crate kailua_diag;
extern crate kailua_syntax;
extern crate kailua_check;
#[macro_use] extern crate log;
extern crate env_logger;

use std::str;
use std::io;
use std::env;
use std::cell::RefCell;
use std::rc::Rc;
use std::path::{Path, PathBuf};

use kailua_diag::{Spanned, Source, Report, ConsoleReport};
use kailua_syntax::{parse_chunk, Block};

fn parse_and_check(mainpath: &Path) -> Result<(), String> {
    struct Options {
        source: Rc<RefCell<Source>>,
        root: PathBuf,
        report: Rc<Report>,
    }

    impl kailua_check::Options for Options {
        fn source(&self) -> &RefCell<Source> { &*self.source }

        fn require_block(&mut self, path: &[u8]) -> Result<Spanned<Block>, String> {
            let path = try!(str::from_utf8(path).map_err(|e| e.to_string()));

            let try = |opts: &mut Options, path: &Path| {
                let path = opts.root.join(path);
                let span = match opts.source.borrow_mut().add_file(&path) {
                    Ok(span) => span,
                    Err(e) => {
                        if e.kind() == io::ErrorKind::NotFound { return Ok(None); }
                        return Err(e.to_string());
                    }
                };
                let chunk = try!(parse_chunk(&opts.source.borrow(), span,
                                             &*opts.report).map_err(|_| format!("parse error")));
                Ok(Some(chunk))
            };

            macro_rules! try_path {
                ($e:expr) => ({
                    if let Some(chunk) = try!(try(self, Path::new(&$e))) { return Ok(chunk); }
                })
            }

            if !path.ends_with(".lua") {
                try_path!(format!("{}.lua.kailua", path));
                try_path!(format!("{}.lua", path));
            }
            try_path!(format!("{}.kailua", path));
            try_path!(path);
            Err(format!("module not found"))
        }
    }

    let mut source = Source::new();
    let filespan = try!(source.add_file(mainpath).map_err(|e| e.to_string()));
    let source = Rc::new(RefCell::new(source));
    let report = Rc::new(ConsoleReport::new(source.clone()));
    let mut context = kailua_check::Context::new(report.clone());
    let root = mainpath.parent().unwrap_or(&Path::new(".."));
    let mut opts = Options { source: source, root: root.to_owned(), report: report };
    kailua_check::check_from_span(&mut context, filespan, &mut opts)
}

pub fn main() {
    env_logger::init().unwrap();
    for path in env::args().skip(1) {
        println!("--== {} ==--", path);
        if let Err(e) = parse_and_check(&Path::new(&path)) {
            // hide the internal error message, which will be eventually removed
            info!("error while checking {}: {}", path, e);
        } else {
            println!("done.");
        }
        println!("");
    }
}

<|endoftext|>


<commit_before>extern crate syntax;
extern crate rustc;
extern crate rustc_driver;
extern crate rustc_resolve as resolve;
extern crate rustc_typeck as typeck;

use self::syntax::{codemap, diagnostic};
pub use self::syntax::diagnostic::Level;
use self::syntax::print::pprust;
use self::rustc::ast_map::{self, NodePrinter};
use self::rustc::session::{self, config};
use self::rustc::metadata::creader::CrateReader;
use self::rustc::middle::{self, stability, ty};
use self::rustc_driver::driver;

use std::env;
use std::collections::HashMap;
use std::ops::Range;
use std::path::{Path, PathBuf};
use std::process::Command;
use std::str;
use std::sync::Arc;
use std::sync::mpsc::{channel, Sender, Receiver};
use std::thread;

#[cfg(windows)]
const EXE_SUFFIX: &'static str = "exe";
#[cfg(not(windows))]
const EXE_SUFFIX: &'static str = "";

fn get_rustc_dir_path() -> PathBuf {
    if cfg!(windows) {
        env::current_exe().unwrap().parent().unwrap().to_path_buf()
    } else {
        let out = Command::new("which").arg("rustc").output().unwrap();
        if !out.status.success() {
            panic!("Failed to get rustc path: {}", String::from_utf8(out.stderr).unwrap());
        }
        Path::new(str::from_utf8(&out.stdout).unwrap().trim()).parent().unwrap().to_path_buf()
    }
}

pub fn init_env() {
    if cfg!(windows) {
        // Set up %PATH% to be able to run the bundled `rustc.exe`.
        if let Some(path) = env::var_os("PATH") {
            let mut paths = env::split_paths(&path).collect::<Vec<_>>();
            paths.push(get_rustc_dir_path());
            let new_path = env::join_paths(paths.iter()).unwrap();
            env::set_var("PATH", &new_path);
        }
    }
}

pub fn compile_and_run(path: &Path) {
    let path = env::current_dir().unwrap().join(path);
    assert!(path.is_absolute());
    let exe = path.with_extension(EXE_SUFFIX);

    // There seems to be a bug that Rust programs (including `rustc.exe`) ran with inherited
    // std{in,out,err} via `.status()` or `.spawn()` produce a "`xyz.exe` has stopped working"
    // message unconditionally.
    if cfg!(windows) {
        let out = Command::new("rustc").arg(&path).arg("-o").arg(&exe).output().unwrap();
        println!("{}\n{}", String::from_utf8(out.stdout).unwrap(), String::from_utf8(out.stderr).unwrap());
        if !out.status.success() {
            println!("compilation failed");
        } else {
            match Command::new(&exe).output() {
                Err(e) => {
                    println!("failed to execute `{:?}`: {}", exe, e);
                }
                Ok(out) => {
                    println!("{}\n{}", String::from_utf8(out.stdout).unwrap(),
                                       String::from_utf8(out.stderr).unwrap());
                }
            }
        }
    } else {
        if let Ok(status) = Command::new("rustc").arg(&path).arg("-o").arg(&exe).status() {
            if status.success() {
                if let Err(e) = Command::new(&exe).spawn() {
                    println!("failed to execute `{:?}`: {}", exe, e);
                }
            }
        }
    }
}

enum Req {
    TypesAtOffset(usize, Range<usize>)
}

enum Res {
    Done,
    Aborted,
    Diagnostic(Diagnostic),
    TypesAtOffset(usize, Vec<(Range<usize>, String)>)
}

struct Diagnostic {
    line: usize,
    col: usize,
    level: Level,
    message: String
}

struct ErrorLogger {
    tx: Sender<Res>,
    file_end: usize
}

impl diagnostic::Emitter for ErrorLogger {
    fn emit(&mut self,
            cmsp: Option<(&codemap::CodeMap, codemap::Span)>,
            msg: &str, _code: Option<&str>, lvl: Level) {
        if msg.starts_with("aborting due to ") {
            return;
        }
        let (line, col) = match cmsp {
            Some((codemap, sp)) => {
                if sp.hi.0 as usize <= self.file_end {
                    let pos = codemap.lookup_char_pos(sp.lo);
                    (pos.line - 1, pos.col.0)
                } else {
                    (0, 0)
                }
            }
            None => (0, 0)
        };
        let _ = self.tx.send(Res::Diagnostic(Diagnostic {
            line: line,
            col: col,
            level: lvl,
            message: msg.to_owned()
        }));
    }

    fn custom_emit(&mut self, cm: &codemap::CodeMap,
                   sp: diagnostic::RenderSpan, msg: &str, lvl: Level) {
        let sp = match sp {
            diagnostic::FullSpan(s) | diagnostic::FileLine(s) |
            diagnostic::EndSpan(s) | diagnostic::Suggestion(s, _)  => s
        };
        self.emit(Some((cm, sp)), msg, None, lvl);
    }
}

fn rustc_thread(input: String, mut lifeline: Arc<()>, rx: Receiver<Req>, tx: Sender<Res>, emitter: ErrorLogger) {
    macro_rules! still_alive {
        () => (lifeline = match lifeline.downgrade().upgrade() { Some(x) => x, None => return })
    }
    let input = config::Input::Str(input);

    let rustc_dir_path = get_rustc_dir_path();

    let sessopts = config::Options {
        maybe_sysroot: Some(rustc_dir_path.parent().unwrap().to_path_buf()),
        ..config::basic_options().clone()
    };

    let codemap = codemap::CodeMap::new();
    let diagnostic_handler = diagnostic::Handler::with_emitter(true, Box::new(emitter));
    let span_diagnostic_handler = diagnostic::SpanHandler::new(diagnostic_handler, codemap);

    let sess = session::build_session_(sessopts,
                                       None,
                                       span_diagnostic_handler);

    let cfg = config::build_configuration(&sess);

    still_alive!();
    let krate = driver::phase_1_parse_input(&sess, cfg, &input);

    still_alive!();
    let krate = driver::phase_2_configure_and_expand(&sess, krate, "r3", None)
        .expect("phase_2_configure_and_expand aborted");

    let mut forest = ast_map::Forest::new(krate);
    let arenas = ty::CtxtArenas::new();

    still_alive!();
    let ast_map = driver::assign_node_ids_and_map(&sess, &mut forest);
    let krate = ast_map.krate();

    still_alive!();
    CrateReader::new(&sess).read_crates(krate);
    let lang_items = middle::lang_items::collect_language_items(krate, &sess);

    still_alive!();
    let resolve::CrateMap {
        def_map,
        freevars,
        trait_map,
        ..
    } = resolve::resolve_crate(&sess,
                               &ast_map,
                               resolve::MakeGlobMap::No);

    // Discard MTWT tables that aren't required past resolution.
    syntax::ext::mtwt::clear_tables();

    still_alive!();
    let named_region_map = middle::resolve_lifetime::krate(&sess, krate, &def_map);

    still_alive!();
    let region_map = middle::region::resolve_crate(&sess, krate);

    //middle::check_loop::check_crate(&sess, krate);

    //middle::check_static_recursion::check_crate(&sess, krate, &def_map, &ast_map);

    still_alive!();
    ty::ctxt::create_and_enter(sess,
                               &arenas,
                               def_map,
                               named_region_map,
                               ast_map,
                               freevars,
                               region_map,
                               lang_items,
                               stability::Index::new(krate), |tcx| {
        typeck::check_crate(tcx, trait_map);

        let _ = tx.send(Res::Done);

        for req in rx.iter() {
            still_alive!();
            match req {
                Req::TypesAtOffset(offset, line) => {
                    let mut out = vec![];
                    for (&id, ty) in tcx.node_types().iter() {
                        let node =  if let Some(node) = tcx.map.find(id) {
                            node
                        } else {
                            continue;
                        };
                        // Avoid statements, they're always ().
                        if let ast_map::NodeStmt(_) = node {
                            continue;
                        }
                        if let Some(sp) = tcx.map.opt_span(id) {
                            /*
                            // Avoid peeking at macro expansions.
                            if sp.expn_id != codemap::NO_EXPANSION {
                                continue;
                            }*/

                            let (lo, hi) = (sp.lo.0 as usize, sp.hi.0 as usize);
                            if line.start <= lo && lo <= offset && offset <= hi && hi <= line.end {
                                match node {
                                    // These cannot be reliably printed.
                                    ast_map::NodeLocal(_) | ast_map::NodeArg(_) | ast_map::NodeStructCtor(_) => {}
                                    // There is an associated NodeExpr(ExprBlock) where this actually matters.
                                    ast_map::NodeBlock(_) => continue,
                                    _ => {
                                        let node_string = pprust::to_string(|s| s.print_node(&node));
                                        let span_string = tcx.sess.codemap().span_to_snippet(sp).unwrap();
                                        let is_macro = regex!(r"^\w+\s*!\s*[\(\[\{]").is_match(&span_string);
                                        if !is_macro && node_string.replace(" ", "") != span_string.replace(" ", "") {
                                            continue;
                                        }
                                    }
                                }
                                let clean = regex!(concat![r"\b(",
                                    "core::(option::Option|result::Result)|",
                                    "collections::(vec::Vec|string::String)",
                                r")\b"]);
                                let ty_string = clean.replace_all(&ty.to_string(), |c: &::regex::Captures| {
                                    c.at(0).unwrap().split(':').next_back().unwrap().to_owned()
                                });
                                out.push((lo-line.start..hi-line.start, ty_string));
                            }
                        }
                    }
                    let _ = tx.send(Res::TypesAtOffset(offset, out));
                }
            }
        }
    });
}

#[derive(Copy, Clone, PartialEq, Debug)]
pub enum State {
    Compiling,
    Aborted,
    Waiting,
    TypesAtOffset(usize)
}

pub struct Rustc {
    pub file_end: usize,
    _lifeline: Arc<()>,
    req_tx: Sender<Req>,
    res_rx: Receiver<Res>,
    pub state: State,
    // True if error.
    pub diagnostics: HashMap<usize, Vec<(Level, usize, String)>>,
    pub errors: usize,
    pub types_at_offset: Option<Vec<(Range<usize>, String)>>
}

impl Rustc {
    pub fn start(input: String) -> Rustc {
        let lifeline = Arc::new(());
        let lifeline2 = lifeline.clone();
        let (req_tx, req_rx) = channel();
        let (res_tx, res_rx) = channel();
        let input_len = input.len();
        thread::spawn(move || {
            let res_tx2 = res_tx.clone();
            let res = thread::catch_panic(move || {
                rustc_thread(input, lifeline2, req_rx, res_tx.clone(), ErrorLogger {
                    tx: res_tx,
                    file_end: input_len
                });
            });
            if res.is_err() {
                let _ = res_tx2.send(Res::Aborted);
            }
        });
        Rustc {
            file_end: input_len,
            _lifeline: lifeline,
            req_tx: req_tx,
            res_rx: res_rx,
            state: State::Compiling,
            diagnostics: HashMap::new(),
            errors: 0,
            types_at_offset: None
        }
    }

    pub fn update(&mut self) -> bool {
        let mut dirty = false;
        while let Ok(res) = self.res_rx.try_recv() {
            match res {
                Res::Done => {
                    assert_eq!(self.state, State::Compiling);
                    self.state = State::Waiting;
                }
                Res::Aborted => {
                    assert!(self.errors > 0, "aborted without errors?!");
                    self.state = State::Aborted;
                }
                Res::Diagnostic(d) => {
                     match d.level {
                        Level::Bug | Level::Fatal | Level::Error => {
                            self.errors += 1;
                        }
                        _ => {}
                    }
                    self.diagnostics.entry(d.line).or_insert(vec![]).push((d.level, d.col, d.message));
                    dirty = true;
                }
                Res::TypesAtOffset(offset, result) => {
                    if self.state == State::TypesAtOffset(offset) {
                        self.state = State::Waiting;
                        self.types_at_offset = Some(result);
                        dirty = true;
                    }
                }
            }
        }
        dirty
    }

    pub fn types_at_offset(&mut self, offset: usize, line: Range<usize>) {
        self.types_at_offset = None;
        self.state = State::TypesAtOffset(offset);
        let _ = self.req_tx.send(Req::TypesAtOffset(offset, line));
    }
}
<commit_msg>Update to the latest nightly.<commit_after>extern crate syntax;
extern crate rustc;
extern crate rustc_front;
extern crate rustc_driver;
extern crate rustc_resolve as resolve;
extern crate rustc_typeck as typeck;

use self::syntax::{codemap, diagnostic};
pub use self::syntax::diagnostic::Level;
use self::rustc::front::map as hir_map;
use self::rustc::front::map::NodePrinter;
use self::rustc_front::lowering::lower_crate;
use self::rustc_front::print::pprust;
use self::rustc::session::{self, config};
use self::rustc::metadata::creader::LocalCrateReader;
use self::rustc::middle::{self, stability, ty};
use self::rustc_driver::driver;

//use std::cell::RefCell;
use std::env;
use std::collections::HashMap;
use std::ops::Range;
use std::path::{Path, PathBuf};
use std::process::Command;
use std::str;
use std::sync::{Arc, Weak};
use std::sync::mpsc::{channel, Sender, Receiver};
use std::thread;

#[cfg(windows)]
const EXE_SUFFIX: &'static str = "exe";
#[cfg(not(windows))]
const EXE_SUFFIX: &'static str = "";

fn get_rustc_dir_path() -> PathBuf {
    if cfg!(windows) {
        env::current_exe().unwrap().parent().unwrap().to_path_buf()
    } else {
        let out = Command::new("which").arg("rustc").output().unwrap();
        if !out.status.success() {
            panic!("Failed to get rustc path: {}", String::from_utf8(out.stderr).unwrap());
        }
        Path::new(str::from_utf8(&out.stdout).unwrap().trim()).parent().unwrap().to_path_buf()
    }
}

pub fn init_env() {
    if cfg!(windows) {
        // Set up %PATH% to be able to run the bundled `rustc.exe`.
        if let Some(path) = env::var_os("PATH") {
            let mut paths = env::split_paths(&path).collect::<Vec<_>>();
            paths.push(get_rustc_dir_path());
            let new_path = env::join_paths(paths.iter()).unwrap();
            env::set_var("PATH", &new_path);
        }
    }
}

pub fn compile_and_run(path: &Path) {
    let path = env::current_dir().unwrap().join(path);
    assert!(path.is_absolute());
    let exe = path.with_extension(EXE_SUFFIX);

    // There seems to be a bug that Rust programs (including `rustc.exe`) ran with inherited
    // std{in,out,err} via `.status()` or `.spawn()` produce a "`xyz.exe` has stopped working"
    // message unconditionally.
    if cfg!(windows) {
        let out = Command::new("rustc").arg(&path).arg("-o").arg(&exe).output().unwrap();
        println!("{}\n{}", String::from_utf8(out.stdout).unwrap(), String::from_utf8(out.stderr).unwrap());
        if !out.status.success() {
            println!("compilation failed");
        } else {
            match Command::new(&exe).output() {
                Err(e) => {
                    println!("failed to execute `{:?}`: {}", exe, e);
                }
                Ok(out) => {
                    println!("{}\n{}", String::from_utf8(out.stdout).unwrap(),
                                       String::from_utf8(out.stderr).unwrap());
                }
            }
        }
    } else {
        if let Ok(status) = Command::new("rustc").arg(&path).arg("-o").arg(&exe).status() {
            if status.success() {
                if let Err(e) = Command::new(&exe).spawn() {
                    println!("failed to execute `{:?}`: {}", exe, e);
                }
            }
        }
    }
}

enum Req {
    TypesAtOffset(usize, Range<usize>)
}

enum Res {
    Done,
    Aborted,
    Diagnostic(Diagnostic),
    TypesAtOffset(usize, Vec<(Range<usize>, String)>)
}

struct Diagnostic {
    line: usize,
    col: usize,
    level: Level,
    message: String
}

struct ErrorLogger {
    tx: Sender<Res>,
    file_end: usize
}

impl diagnostic::Emitter for ErrorLogger {
    fn emit(&mut self,
            cmsp: Option<(&codemap::CodeMap, codemap::Span)>,
            msg: &str, _code: Option<&str>, lvl: Level) {
        if msg.starts_with("aborting due to ") {
            return;
        }
        let (line, col) = match cmsp {
            Some((codemap, sp)) => {
                if sp.hi.0 as usize <= self.file_end {
                    let pos = codemap.lookup_char_pos(sp.lo);
                    (pos.line - 1, pos.col.0)
                } else {
                    (0, 0)
                }
            }
            None => (0, 0)
        };
        let _ = self.tx.send(Res::Diagnostic(Diagnostic {
            line: line,
            col: col,
            level: lvl,
            message: msg.to_owned()
        }));
    }

    fn custom_emit(&mut self, cm: &codemap::CodeMap,
                   sp: diagnostic::RenderSpan, msg: &str, lvl: Level) {
        let sp = match sp {
            diagnostic::FullSpan(s) | diagnostic::FileLine(s) |
            diagnostic::EndSpan(s) | diagnostic::Suggestion(s, _)  => s
        };
        self.emit(Some((cm, sp)), msg, None, lvl);
    }
}

fn rustc_thread(input: String, mut lifeline: Arc<()>, rx: Receiver<Req>, tx: Sender<Res>, emitter: ErrorLogger) {
    macro_rules! still_alive {
        () => (lifeline = match Weak::upgrade(&Arc::downgrade(&{lifeline})) {
                Some(x) => x, None => return
        })
    }
    let input = config::Input::Str(input);

    let rustc_dir_path = get_rustc_dir_path();

    let sessopts = config::Options {
        maybe_sysroot: Some(rustc_dir_path.parent().unwrap().to_path_buf()),
        ..config::basic_options().clone()
    };

    let codemap = codemap::CodeMap::new();
    let diagnostic_handler = diagnostic::Handler::with_emitter(true, Box::new(emitter));
    let span_diagnostic_handler = diagnostic::SpanHandler::new(diagnostic_handler, codemap);

    let sess = session::build_session_(sessopts,
                                       None,
                                       span_diagnostic_handler);

    let cfg = config::build_configuration(&sess);

    still_alive!();
    let krate = driver::phase_1_parse_input(&sess, cfg, &input);

    still_alive!();
    let krate = driver::phase_2_configure_and_expand(&sess, krate, "r3", None)
        .expect("phase_2_configure_and_expand aborted");

    still_alive!();
    let krate = driver::assign_node_ids(&sess, krate);

    let mut hir_forest = hir_map::Forest::new(lower_crate(&krate));
    let arenas = ty::CtxtArenas::new();

    still_alive!();
    let hir_map = hir_map::map_crate(&mut hir_forest);
    let krate = hir_map.krate();

    still_alive!();
    LocalCrateReader::new(&sess, &hir_map).read_crates(krate);
    let lang_items = middle::lang_items::collect_language_items(krate, &sess);

    still_alive!();
    let resolve::CrateMap {
        def_map,
        freevars,
        trait_map,
        ..
    } = resolve::resolve_crate(&sess,
                               &hir_map,
                               resolve::MakeGlobMap::No);

    // Discard MTWT tables that aren't required past resolution.
    syntax::ext::mtwt::clear_tables();

    still_alive!();
    let named_region_map = middle::resolve_lifetime::krate(&sess, krate, &def_map);

    still_alive!();
    let region_map = middle::region::resolve_crate(&sess, krate);

    //middle::check_loop::check_crate(&sess, krate);

    //middle::check_static_recursion::check_crate(&sess, krate, &def_map, &hir_map);

    still_alive!();
    ty::ctxt::create_and_enter(sess,
                               &arenas,
                               def_map,
                               named_region_map,
                               hir_map,
                               freevars,
                               region_map,
                               lang_items,
                               stability::Index::new(krate), |tcx| {
        typeck::check_crate(tcx, trait_map);

        let _ = tx.send(Res::Done);

        for req in rx.iter() {
            still_alive!();
            match req {
                Req::TypesAtOffset(offset, line) => {
                    let mut out = vec![];
                    for (&id, ty) in tcx.node_types().iter() {
                        let node =  if let Some(node) = tcx.map.find(id) {
                            node
                        } else {
                            continue;
                        };
                        // Avoid statements, they're always ().
                        if let hir_map::NodeStmt(_) = node {
                            continue;
                        }
                        if let Some(sp) = tcx.map.opt_span(id) {
                            /*
                            // Avoid peeking at macro expansions.
                            if sp.expn_id != codemap::NO_EXPANSION {
                                continue;
                            }*/

                            let (lo, hi) = (sp.lo.0 as usize, sp.hi.0 as usize);
                            if line.start <= lo && lo <= offset && offset <= hi && hi <= line.end {
                                match node {
                                    // These cannot be reliably printed.
                                    hir_map::NodeLocal(_) | hir_map::NodeArg(_) | hir_map::NodeStructCtor(_) => {}
                                    // There is an associated NodeExpr(ExprBlock) where this actually matters.
                                    hir_map::NodeBlock(_) => continue,
                                    _ => {
                                        let node_string = pprust::to_string(|s| s.print_node(&node));
                                        let span_string = tcx.sess.codemap().span_to_snippet(sp).unwrap();
                                        let is_macro = regex!(r"^\w+\s*!\s*[\(\[\{]").is_match(&span_string);
                                        if !is_macro && node_string.replace(" ", "") != span_string.replace(" ", "") {
                                            continue;
                                        }
                                    }
                                }
                                let clean = regex!(concat![r"\b(",
                                    "core::(option::Option|result::Result)|",
                                    "collections::(vec::Vec|string::String)",
                                r")\b"]);
                                let ty_string = clean.replace_all(&ty.to_string(), |c: &::regex::Captures| {
                                    c.at(0).unwrap().split(':').next_back().unwrap().to_owned()
                                });
                                out.push((lo-line.start..hi-line.start, ty_string));
                            }
                        }
                    }
                    let _ = tx.send(Res::TypesAtOffset(offset, out));
                }
            }
        }
    });
}

#[derive(Copy, Clone, PartialEq, Debug)]
pub enum State {
    Compiling,
    Aborted,
    Waiting,
    TypesAtOffset(usize)
}

pub struct Rustc {
    pub file_end: usize,
    _lifeline: Arc<()>,
    req_tx: Sender<Req>,
    res_rx: Receiver<Res>,
    pub state: State,
    // True if error.
    pub diagnostics: HashMap<usize, Vec<(Level, usize, String)>>,
    pub errors: usize,
    pub types_at_offset: Option<Vec<(Range<usize>, String)>>
}

impl Rustc {
    pub fn start(input: String) -> Rustc {
        let lifeline = Arc::new(());
        let lifeline2 = lifeline.clone();
        let (req_tx, req_rx) = channel();
        let (res_tx, res_rx) = channel();
        let input_len = input.len();
        thread::spawn(move || {
            let res_tx2 = res_tx.clone();
            let res = thread::catch_panic(move || {
                rustc_thread(input, lifeline2, req_rx, res_tx.clone(), ErrorLogger {
                    tx: res_tx,
                    file_end: input_len
                });
            });
            if res.is_err() {
                let _ = res_tx2.send(Res::Aborted);
            }
        });
        Rustc {
            file_end: input_len,
            _lifeline: lifeline,
            req_tx: req_tx,
            res_rx: res_rx,
            state: State::Compiling,
            diagnostics: HashMap::new(),
            errors: 0,
            types_at_offset: None
        }
    }

    pub fn update(&mut self) -> bool {
        let mut dirty = false;
        while let Ok(res) = self.res_rx.try_recv() {
            match res {
                Res::Done => {
                    assert_eq!(self.state, State::Compiling);
                    self.state = State::Waiting;
                }
                Res::Aborted => {
                    assert!(self.errors > 0, "aborted without errors?!");
                    self.state = State::Aborted;
                }
                Res::Diagnostic(d) => {
                     match d.level {
                        Level::Bug | Level::Fatal | Level::Error => {
                            self.errors += 1;
                        }
                        _ => {}
                    }
                    self.diagnostics.entry(d.line).or_insert(vec![]).push((d.level, d.col, d.message));
                    dirty = true;
                }
                Res::TypesAtOffset(offset, result) => {
                    if self.state == State::TypesAtOffset(offset) {
                        self.state = State::Waiting;
                        self.types_at_offset = Some(result);
                        dirty = true;
                    }
                }
            }
        }
        dirty
    }

    pub fn types_at_offset(&mut self, offset: usize, line: Range<usize>) {
        self.types_at_offset = None;
        self.state = State::TypesAtOffset(offset);
        let _ = self.req_tx.send(Req::TypesAtOffset(offset, line));
    }
}
<|endoftext|>


<commit_before>#![allow(dead_code, unused_variables)]

use piston::input::{Input, Button, Motion};
use piston::window::Size;
use viewport::Viewport;
use std::collections::HashMap;

pub trait Action: Copy + PartialEq + Eq { }

#[derive(Debug, Copy, Clone)]
pub enum Translated<A: Action> {
    Press(A),
    Release(A),
    Move(Motion)
}

#[derive(Clone)]
pub struct InputMap<A: Action> {
    keymap: KeyMap<A>,
    mouse_translator: MouseTranslator
}

impl<A: Action> InputMap<A> {
    pub fn new(size: Size) -> Self {
        InputMap {
            keymap: KeyMap::new(),
            mouse_translator: MouseTranslator::new(size)
        }
    }

    pub fn translate(&self, input: &Input) -> Option<Translated<A>> {
        macro_rules! translate_button(
            ($but_state:ident, $but_var:ident) => (
                match self.keymap.translate($but_var) {
                    Some(act) => Some(Translated::$but_state(act)),
                    None => None
                }
            );
        );

        match input {
            &Input::Press(button) => translate_button!(Press, button),
            &Input::Release(button) => translate_button!(Release, button),
            &Input::Move(motion) =>
                Some(Translated::Move(self.mouse_translator.translate(motion))),
            _ => None
        }
    }

    pub fn rebind_button(&mut self, but: Button, act: A) {
        // TODO implement
    }

    pub fn add_binding(&mut self, but: Button, act: A) {
        self.keymap.add_mapping(but, act);
    }

    pub fn get_bindings_for_action(&self, act: A) -> ButtonTuple {
        ButtonTuple(None, None, None) // TODO implement
    }

    pub fn set_size(&mut self, size: Size) {
        self.mouse_translator.viewport_size = size
    }

    pub fn set_size_from_viewport(&mut self, vp: Viewport) {
        self.set_size(Size::from(vp.draw_size));
    }
}

#[derive(Clone)]
struct MouseTranslator {
    pub x_axis_movement_inverted: bool,
    pub y_axis_movement_inverted: bool,
    pub x_axis_scroll_inverted: bool,
    pub y_axis_scroll_inverted: bool,
    pub viewport_size: Size
}

impl MouseTranslator {
    fn new(size: Size) -> Self {
        MouseTranslator {
            x_axis_movement_inverted: false,
            y_axis_movement_inverted: false,
            x_axis_scroll_inverted: false,
            y_axis_scroll_inverted: false,
            viewport_size: size
        }
    }

    fn translate(&self, motion: Motion) -> Motion {
        match motion {
            Motion::MouseCursor(x, y) => {
                let (sw, sh) = {
                    let Size {width, height} = self.viewport_size;
                    (width as f64, height as f64)
                };

                Motion::MouseCursor(x, y) // TODO implement
            },
            Motion::MouseScroll(x, y) => {
                let mx = if self.x_axis_scroll_inverted { -1.0f64 } else { 1.0 };
                let my = if self.y_axis_scroll_inverted { -1.0f64 } else { 1.0 };
                Motion::MouseScroll(x * mx, y * my)
            },
            relative => relative
        }
    }
}

#[derive(Clone, Debug)]
struct KeyMap<A: Action> {
    btn_map: HashMap<ButtonTuple, A>
}

impl<A: Action> KeyMap<A> {
    fn new() -> Self {
        KeyMap {
            btn_map: HashMap::new()
        }
    }

    fn add_mapping(&mut self, button: Button, action: A) {
        let mut bt = self.get_bindings_for_action(action).unwrap_or(ButtonTuple::new());
        let bt = if bt.insert_inplace(button) {bt} else {ButtonTuple::new()};
        self.btn_map.insert(bt, action);
    }

    fn with_mapping(mut self, button: Button, action: A) -> Self {
        self.add_mapping(button, action);
        self // TODO implement
    }

    fn get_bindings_for_action(&self, action: A) -> Option<ButtonTuple> {
        self.btn_map.iter().find(|&(_, &a)| a == action).map(|(&bt, _)| bt)
    }

    fn translate(&self, button: Button) -> Option<A> {
        self.btn_map.iter().find(|&(&bt, _)| bt.contains(button)).map(|(_, &a)| a)
    }
}

#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub struct ButtonTuple(Option<Button>, Option<Button>, Option<Button>);

impl ButtonTuple {
    fn new() -> Self {
        ButtonTuple(None, None, None)
    }

    fn contains(&self, btn: Button) -> bool {
        let sbtn = Some(btn);
        self.0 == sbtn || self.1 == sbtn || self.2 == sbtn
    }

    fn remove_inplace(&mut self, btn: Button) {
        let sbtn = Some(btn);
        if self.0 == sbtn {self.0 = None}
        if self.1 == sbtn {self.1 = None}
        if self.2 == sbtn {self.2 = None}
    }

    fn replace_inplace(&mut self, btn_idx: u32, btn: Button) -> bool {
        match btn_idx {
            0 => {self.0 = Some(btn); true},
            1 => {self.1 = Some(btn); true},
            2 => {self.2 = Some(btn); true},
            _ => false
        }
    }

    fn insert_inplace(&mut self, btn: Button) -> bool {
        let &mut ButtonTuple(a, b, c) = self;
        if a.is_none() {
            *self = ButtonTuple(Some(btn), b, c)
        } else if b.is_none() {
            *self = ButtonTuple(a, Some(btn), c)
        } else if c.is_none() {
            *self = ButtonTuple(a, b, Some(btn))
        } else {
            return false;
        }
        true
    }
}
<commit_msg>Remove outdated todo comment<commit_after>#![allow(dead_code, unused_variables)]

use piston::input::{Input, Button, Motion};
use piston::window::Size;
use viewport::Viewport;
use std::collections::HashMap;

pub trait Action: Copy + PartialEq + Eq { }

#[derive(Debug, Copy, Clone)]
pub enum Translated<A: Action> {
    Press(A),
    Release(A),
    Move(Motion)
}

#[derive(Clone)]
pub struct InputMap<A: Action> {
    keymap: KeyMap<A>,
    mouse_translator: MouseTranslator
}

impl<A: Action> InputMap<A> {
    pub fn new(size: Size) -> Self {
        InputMap {
            keymap: KeyMap::new(),
            mouse_translator: MouseTranslator::new(size)
        }
    }

    pub fn translate(&self, input: &Input) -> Option<Translated<A>> {
        macro_rules! translate_button(
            ($but_state:ident, $but_var:ident) => (
                match self.keymap.translate($but_var) {
                    Some(act) => Some(Translated::$but_state(act)),
                    None => None
                }
            );
        );

        match input {
            &Input::Press(button) => translate_button!(Press, button),
            &Input::Release(button) => translate_button!(Release, button),
            &Input::Move(motion) =>
                Some(Translated::Move(self.mouse_translator.translate(motion))),
            _ => None
        }
    }

    pub fn rebind_button(&mut self, but: Button, act: A) {
        // TODO implement
    }

    pub fn add_binding(&mut self, but: Button, act: A) {
        self.keymap.add_mapping(but, act);
    }

    pub fn get_bindings_for_action(&self, act: A) -> ButtonTuple {
        ButtonTuple(None, None, None) // TODO implement
    }

    pub fn set_size(&mut self, size: Size) {
        self.mouse_translator.viewport_size = size
    }

    pub fn set_size_from_viewport(&mut self, vp: Viewport) {
        self.set_size(Size::from(vp.draw_size));
    }
}

#[derive(Clone)]
struct MouseTranslator {
    pub x_axis_movement_inverted: bool,
    pub y_axis_movement_inverted: bool,
    pub x_axis_scroll_inverted: bool,
    pub y_axis_scroll_inverted: bool,
    pub viewport_size: Size
}

impl MouseTranslator {
    fn new(size: Size) -> Self {
        MouseTranslator {
            x_axis_movement_inverted: false,
            y_axis_movement_inverted: false,
            x_axis_scroll_inverted: false,
            y_axis_scroll_inverted: false,
            viewport_size: size
        }
    }

    fn translate(&self, motion: Motion) -> Motion {
        match motion {
            Motion::MouseCursor(x, y) => {
                let (sw, sh) = {
                    let Size {width, height} = self.viewport_size;
                    (width as f64, height as f64)
                };

                Motion::MouseCursor(x, y) // TODO implement
            },
            Motion::MouseScroll(x, y) => {
                let mx = if self.x_axis_scroll_inverted { -1.0f64 } else { 1.0 };
                let my = if self.y_axis_scroll_inverted { -1.0f64 } else { 1.0 };
                Motion::MouseScroll(x * mx, y * my)
            },
            relative => relative
        }
    }
}

#[derive(Clone, Debug)]
struct KeyMap<A: Action> {
    btn_map: HashMap<ButtonTuple, A>
}

impl<A: Action> KeyMap<A> {
    fn new() -> Self {
        KeyMap {
            btn_map: HashMap::new()
        }
    }

    fn add_mapping(&mut self, button: Button, action: A) {
        let mut bt = self.get_bindings_for_action(action).unwrap_or(ButtonTuple::new());
        let bt = if bt.insert_inplace(button) {bt} else {ButtonTuple::new()};
        self.btn_map.insert(bt, action);
    }

    fn with_mapping(mut self, button: Button, action: A) -> Self {
        self.add_mapping(button, action);
        self
    }

    fn get_bindings_for_action(&self, action: A) -> Option<ButtonTuple> {
        self.btn_map.iter().find(|&(_, &a)| a == action).map(|(&bt, _)| bt)
    }

    fn translate(&self, button: Button) -> Option<A> {
        self.btn_map.iter().find(|&(&bt, _)| bt.contains(button)).map(|(_, &a)| a)
    }
}

#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub struct ButtonTuple(Option<Button>, Option<Button>, Option<Button>);

impl ButtonTuple {
    fn new() -> Self {
        ButtonTuple(None, None, None)
    }

    fn contains(&self, btn: Button) -> bool {
        let sbtn = Some(btn);
        self.0 == sbtn || self.1 == sbtn || self.2 == sbtn
    }

    fn remove_inplace(&mut self, btn: Button) {
        let sbtn = Some(btn);
        if self.0 == sbtn {self.0 = None}
        if self.1 == sbtn {self.1 = None}
        if self.2 == sbtn {self.2 = None}
    }

    fn replace_inplace(&mut self, btn_idx: u32, btn: Button) -> bool {
        match btn_idx {
            0 => {self.0 = Some(btn); true},
            1 => {self.1 = Some(btn); true},
            2 => {self.2 = Some(btn); true},
            _ => false
        }
    }

    fn insert_inplace(&mut self, btn: Button) -> bool {
        let &mut ButtonTuple(a, b, c) = self;
        if a.is_none() {
            *self = ButtonTuple(Some(btn), b, c)
        } else if b.is_none() {
            *self = ButtonTuple(a, Some(btn), c)
        } else if c.is_none() {
            *self = ButtonTuple(a, b, Some(btn))
        } else {
            return false;
        }
        true
    }
}
<|endoftext|>


<commit_before><commit_msg>Add support for EXTENDED_SRGB_NONLINEAR_EXT color space (#1656)<commit_after><|endoftext|>


<commit_before><commit_msg>test(emulatir): Use get_register() in example<commit_after><|endoftext|>


<commit_before><commit_msg>Make JsArr type public<commit_after><|endoftext|>