<commit_before>use crate::check::{FnCtxt, LocalTy, UserType};
use rustc_hir as hir;
use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor};
use rustc_hir::PatKind;
use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
use rustc_middle::ty::Ty;
use rustc_span::Span;
use rustc_trait_selection::traits;
use std::mem;

pub(super) struct GatherLocalsVisitor<'a, 'tcx> {
    fcx: &'a FnCtxt<'a, 'tcx>,
    parent_id: hir::HirId,
    // params are special cases of pats, but we want to handle them as
    // *distinct* cases. so track when we are hitting a pat *within* an fn
    // param.
    outermost_fn_param_pat: bool,
}

impl<'a, 'tcx> GatherLocalsVisitor<'a, 'tcx> {
    pub(super) fn new(
        fcx: &'a FnCtxt<'a, 'tcx>,
        parent_id: hir::HirId,
        outermost_fn_param_pat: bool,
    ) -> Self {
        Self { fcx, parent_id, outermost_fn_param_pat }
    }

    fn assign(&mut self, span: Span, nid: hir::HirId, ty_opt: Option<LocalTy<'tcx>>) -> Ty<'tcx> {
        match ty_opt {
            None => {
                // Infer the variable's type.
                let var_ty = self.fcx.next_ty_var(TypeVariableOrigin {
                    kind: TypeVariableOriginKind::TypeInference,
                    span,
                });
                self.fcx
                    .locals
                    .borrow_mut()
                    .insert(nid, LocalTy { decl_ty: var_ty, revealed_ty: var_ty });
                var_ty
            }
            Some(typ) => {
                // Take type that the user specified.
                self.fcx.locals.borrow_mut().insert(nid, typ);
                typ.revealed_ty
            }
        }
    }
}

impl<'a, 'tcx> Visitor<'tcx> for GatherLocalsVisitor<'a, 'tcx> {
    type Map = intravisit::ErasedMap<'tcx>;

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

    // Add explicitly-declared locals.
    fn visit_local(&mut self, local: &'tcx hir::Local<'tcx>) {
        let local_ty = match local.ty {
            Some(ref ty) => {
                let o_ty = self.fcx.to_ty(&ty);

                let revealed_ty = if self.fcx.tcx.features().impl_trait_in_bindings {
                    self.fcx.instantiate_opaque_types_from_value(self.parent_id, &o_ty, ty.span)
                } else {
                    o_ty
                };

                let c_ty = self
                    .fcx
                    .inh
                    .infcx
                    .canonicalize_user_type_annotation(&UserType::Ty(revealed_ty));
                debug!(
                    "visit_local: ty.hir_id={:?} o_ty={:?} revealed_ty={:?} c_ty={:?}",
                    ty.hir_id, o_ty, revealed_ty, c_ty
                );
                self.fcx
                    .typeck_results
                    .borrow_mut()
                    .user_provided_types_mut()
                    .insert(ty.hir_id, c_ty);

                Some(LocalTy { decl_ty: o_ty, revealed_ty })
            }
            None => None,
        };
        self.assign(local.span, local.hir_id, local_ty);

        debug!(
            "local variable {:?} is assigned type {}",
            local.pat,
            self.fcx.ty_to_string(&*self.fcx.locals.borrow().get(&local.hir_id).unwrap().decl_ty)
        );
        intravisit::walk_local(self, local);
    }

    fn visit_param(&mut self, param: &'tcx hir::Param<'tcx>) {
        let old_outermost_fn_param_pat = mem::replace(&mut self.outermost_fn_param_pat, true);
        intravisit::walk_param(self, param);
        self.outermost_fn_param_pat = old_outermost_fn_param_pat;
    }

    // Add pattern bindings.
    fn visit_pat(&mut self, p: &'tcx hir::Pat<'tcx>) {
        if let PatKind::Binding(_, _, ident, _) = p.kind {
            let var_ty = self.assign(p.span, p.hir_id, None);

            if self.outermost_fn_param_pat {
                if !self.fcx.tcx.features().unsized_fn_params {
                    self.fcx.require_type_is_sized(
                        var_ty,
                        p.span,
                        traits::SizedArgumentType(Some(p.span)),
                    );
                }
            } else {
                if !self.fcx.tcx.features().unsized_locals {
                    self.fcx.require_type_is_sized(var_ty, p.span, traits::VariableType(p.hir_id));
                }
            }

            debug!(
                "pattern binding {} is assigned to {} with type {:?}",
                ident,
                self.fcx.ty_to_string(&*self.fcx.locals.borrow().get(&p.hir_id).unwrap().decl_ty),
                var_ty
            );
        }
        let old_outermost_fn_param_pat = mem::replace(&mut self.outermost_fn_param_pat, false);
        intravisit::walk_pat(self, p);
        self.outermost_fn_param_pat = old_outermost_fn_param_pat;
    }

    // Don't descend into the bodies of nested closures.
    fn visit_fn(
        &mut self,
        _: intravisit::FnKind<'tcx>,
        _: &'tcx hir::FnDecl<'tcx>,
        _: hir::BodyId,
        _: Span,
        _: hir::HirId,
    ) {
    }
}
<commit_msg>param -> parameter, pat -> pattern<commit_after>use crate::check::{FnCtxt, LocalTy, UserType};
use rustc_hir as hir;
use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor};
use rustc_hir::PatKind;
use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
use rustc_middle::ty::Ty;
use rustc_span::Span;
use rustc_trait_selection::traits;
use std::mem;

pub(super) struct GatherLocalsVisitor<'a, 'tcx> {
    fcx: &'a FnCtxt<'a, 'tcx>,
    parent_id: hir::HirId,
    // parameters are special cases of patterns, but we want to handle them as
    // *distinct* cases. so track when we are hitting a pattern *within* an fn
    // parameter.
    outermost_fn_param_pat: bool,
}

impl<'a, 'tcx> GatherLocalsVisitor<'a, 'tcx> {
    pub(super) fn new(
        fcx: &'a FnCtxt<'a, 'tcx>,
        parent_id: hir::HirId,
        outermost_fn_param_pat: bool,
    ) -> Self {
        Self { fcx, parent_id, outermost_fn_param_pat }
    }

    fn assign(&mut self, span: Span, nid: hir::HirId, ty_opt: Option<LocalTy<'tcx>>) -> Ty<'tcx> {
        match ty_opt {
            None => {
                // Infer the variable's type.
                let var_ty = self.fcx.next_ty_var(TypeVariableOrigin {
                    kind: TypeVariableOriginKind::TypeInference,
                    span,
                });
                self.fcx
                    .locals
                    .borrow_mut()
                    .insert(nid, LocalTy { decl_ty: var_ty, revealed_ty: var_ty });
                var_ty
            }
            Some(typ) => {
                // Take type that the user specified.
                self.fcx.locals.borrow_mut().insert(nid, typ);
                typ.revealed_ty
            }
        }
    }
}

impl<'a, 'tcx> Visitor<'tcx> for GatherLocalsVisitor<'a, 'tcx> {
    type Map = intravisit::ErasedMap<'tcx>;

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

    // Add explicitly-declared locals.
    fn visit_local(&mut self, local: &'tcx hir::Local<'tcx>) {
        let local_ty = match local.ty {
            Some(ref ty) => {
                let o_ty = self.fcx.to_ty(&ty);

                let revealed_ty = if self.fcx.tcx.features().impl_trait_in_bindings {
                    self.fcx.instantiate_opaque_types_from_value(self.parent_id, &o_ty, ty.span)
                } else {
                    o_ty
                };

                let c_ty = self
                    .fcx
                    .inh
                    .infcx
                    .canonicalize_user_type_annotation(&UserType::Ty(revealed_ty));
                debug!(
                    "visit_local: ty.hir_id={:?} o_ty={:?} revealed_ty={:?} c_ty={:?}",
                    ty.hir_id, o_ty, revealed_ty, c_ty
                );
                self.fcx
                    .typeck_results
                    .borrow_mut()
                    .user_provided_types_mut()
                    .insert(ty.hir_id, c_ty);

                Some(LocalTy { decl_ty: o_ty, revealed_ty })
            }
            None => None,
        };
        self.assign(local.span, local.hir_id, local_ty);

        debug!(
            "local variable {:?} is assigned type {}",
            local.pat,
            self.fcx.ty_to_string(&*self.fcx.locals.borrow().get(&local.hir_id).unwrap().decl_ty)
        );
        intravisit::walk_local(self, local);
    }

    fn visit_param(&mut self, param: &'tcx hir::Param<'tcx>) {
        let old_outermost_fn_param_pat = mem::replace(&mut self.outermost_fn_param_pat, true);
        intravisit::walk_param(self, param);
        self.outermost_fn_param_pat = old_outermost_fn_param_pat;
    }

    // Add pattern bindings.
    fn visit_pat(&mut self, p: &'tcx hir::Pat<'tcx>) {
        if let PatKind::Binding(_, _, ident, _) = p.kind {
            let var_ty = self.assign(p.span, p.hir_id, None);

            if self.outermost_fn_param_pat {
                if !self.fcx.tcx.features().unsized_fn_params {
                    self.fcx.require_type_is_sized(
                        var_ty,
                        p.span,
                        traits::SizedArgumentType(Some(p.span)),
                    );
                }
            } else {
                if !self.fcx.tcx.features().unsized_locals {
                    self.fcx.require_type_is_sized(var_ty, p.span, traits::VariableType(p.hir_id));
                }
            }

            debug!(
                "pattern binding {} is assigned to {} with type {:?}",
                ident,
                self.fcx.ty_to_string(&*self.fcx.locals.borrow().get(&p.hir_id).unwrap().decl_ty),
                var_ty
            );
        }
        let old_outermost_fn_param_pat = mem::replace(&mut self.outermost_fn_param_pat, false);
        intravisit::walk_pat(self, p);
        self.outermost_fn_param_pat = old_outermost_fn_param_pat;
    }

    // Don't descend into the bodies of nested closures.
    fn visit_fn(
        &mut self,
        _: intravisit::FnKind<'tcx>,
        _: &'tcx hir::FnDecl<'tcx>,
        _: hir::BodyId,
        _: Span,
        _: hir::HirId,
    ) {
    }
}
<|endoftext|>


<commit_before>use super::{ExecutionStrategy,
            Scope};
use crate::{config,
            error::{Error,
                    Result}};
use futures::stream::StreamExt;
use habitat_common::{cli::FS_ROOT,
                     package_graph::PackageGraph,
                     types::ListenCtlAddr,
                     ui::{Status,
                          UIWriter}};
use habitat_core::{error as herror,
                   fs as hfs,
                   package::{list::temp_package_directory,
                             Identifiable,
                             PackageIdent,
                             PackageInstall}};
use habitat_sup_client::{SrvClient,
                         SrvClientError};
use habitat_sup_protocol;
use std::{fs,
          path::Path,
          str::FromStr};

pub async fn uninstall<U>(ui: &mut U,
                          ident: impl AsRef<PackageIdent>,
                          fs_root_path: &Path,
                          execution_strategy: ExecutionStrategy,
                          scope: Scope,
                          excludes: &[PackageIdent],
                          even_if_loaded: bool)
                          -> Result<()>
    where U: UIWriter
{
    uninstall_many(ui,
                   &[ident],
                   fs_root_path,
                   execution_strategy,
                   scope,
                   excludes,
                   even_if_loaded).await
}

/// Delete packages and all dependencies which are not used by the packages.
/// We do an ordered traverse of the dependencies, updating the graph as we delete a
/// package. This lets us use simple logic where we continually check if the package
/// we're trying to delete currently has no packages depending on it.
///
/// The full logic is:
/// 1. We find all packages on the filesystem and convert them into a graph
/// 2. We find the fully qualified package ident and all its dependencies
/// 3. We do a BFS on the graph to get the dependencies in order
/// 4. We check if the specified package has any reverse deps
///     4a. If there are, we throw an error
///     4b. If not, we delete the package
/// 5. For each dependency we check if there are any packages which depend on it
///     5a. If there are, we skip it
///     5b. If there are not, we delete it from disk and the graph
///
/// `excludes` is a list of user-supplied `PackageIdent`s.
/// `even_if_loaded` indictates that the package should be uninstalled even if it is loaded by the
/// supervisor. This only applies to packages specified in `idents`. It does not apply to their
/// dependencies.
pub async fn uninstall_many<U>(ui: &mut U,
                               idents: &[impl AsRef<PackageIdent>],
                               fs_root_path: &Path,
                               execution_strategy: ExecutionStrategy,
                               scope: Scope,
                               excludes: &[PackageIdent],
                               even_if_loaded: bool)
                               -> Result<()>
    where U: UIWriter
{
    // 1.
    let mut graph = PackageGraph::from_root_path(fs_root_path)?;

    let loaded_services = supervisor_services().await?;
    if !loaded_services.is_empty() {
        ui.status(Status::Determining, "list of loaded services in supervisor")?;
        for s in loaded_services.iter() {
            ui.status(Status::Found, format!("loaded service {}", s))?;
        }
    }
    let uninstall_if_loaded = if even_if_loaded {
        UninstallIfLoaded::EvenIfLoaded
    } else {
        UninstallIfLoaded::SkipIfLoaded(&loaded_services)
    };
    // Never uninstall a dependency if it is loaded
    let dependency_uninstall_if_loaded = UninstallIfLoaded::SkipIfLoaded(&loaded_services);

    for ident in idents {
        // 2.
        let ident = ident.as_ref();
        let pkg_install = PackageInstall::load(ident, Some(fs_root_path))?;
        let ident = pkg_install.ident();
        ui.begin(format!("Uninstalling {}", &ident))?;

        // 3.
        let deps = graph.owned_ordered_deps(&ident);

        // 4.
        match graph.count_rdeps(&ident) {
            None => {
                // package not in graph - this shouldn't happen but could be a race condition in
                // Step 2 with another hab uninstall. We can continue as what we
                // wanted (package to be removed) has already happened. We're going
                // to continue and try and delete down through the dependency tree
                // anyway
                ui.warn(format!("Tried to find dependant packages of {} but it wasn't in \
                                 graph.  Maybe another uninstall command was run at the same \
                                 time?",
                                &ident))?;
            }
            Some(0) => {
                maybe_delete(ui,
                             &fs_root_path,
                             &pkg_install,
                             execution_strategy,
                             &excludes,
                             uninstall_if_loaded)?;
                graph.remove(&ident);
            }
            Some(c) => {
                return Err(Error::CannotRemovePackage(ident.clone(), c));
            }
        }

        // 5.
        let mut count = 0;
        match scope {
            Scope::Package => {
                ui.status(Status::Skipping, "dependencies (--no-deps specified)")?;
            }
            Scope::PackageAndDependencies => {
                for p in &deps {
                    match graph.count_rdeps(&p) {
                        None => {
                            // package not in graph - this shouldn't happen but could be a race
                            // condition in Step 2 with another hab uninstall. We can
                            // continue as what we wanted (package to be removed) has already
                            // happened. We're going to continue and try
                            // and delete down through the dependency
                            // tree anyway
                            ui.warn(format!("Tried to find dependant packages of {} but it \
                                             wasn't in graph.  Maybe another uninstall command \
                                             was run at the same time?",
                                            &p))?;
                        }
                        Some(0) => {
                            let install = PackageInstall::load(&p, Some(fs_root_path))?;
                            maybe_delete(ui,
                                         &fs_root_path,
                                         &install,
                                         execution_strategy,
                                         &excludes,
                                         dependency_uninstall_if_loaded)?;

                            graph.remove(&p);
                            count += 1;
                        }
                        Some(c) => {
                            ui.status(Status::Skipping,
                                      format!("{}. It is a dependency of {} packages", &p, c))?;
                        }
                    }
                }
            }
        }
        match execution_strategy {
            ExecutionStrategy::DryRun => {
                ui.end(format!("Would uninstall {} and {} dependencies (Dry run)",
                               &ident, count))?;
            }
            ExecutionStrategy::Run => {
                ui.end(format!("Uninstall of {} and {} dependencies complete",
                               &ident, count))?;
            }
        };
    }
    Ok(())
}

/// Check if we have a launcher/supervisor running out of this habitat root.
/// If the launcher PID file exists then the supervisor is up and running
fn launcher_is_running(fs_root_path: &Path) -> bool {
    let launcher_root = hfs::launcher_root_path(Some(fs_root_path));
    let pid_file_path = launcher_root.join("PID");

    pid_file_path.is_file()
}

async fn supervisor_services() -> Result<Vec<PackageIdent>> {
    if !launcher_is_running(&*FS_ROOT) {
        return Ok(vec![]);
    }

    let cfg = config::load()?;
    let secret_key = config::ctl_secret_key(&cfg)?;
    let listen_ctl_addr = ListenCtlAddr::default();
    let msg = habitat_sup_protocol::ctl::SvcStatus::default();

    let mut out: Vec<PackageIdent> = vec![];
    let mut response = SrvClient::request(&listen_ctl_addr, &secret_key, msg).await?;
    while let Some(message_result) = response.next().await {
        let reply = message_result?;
        match reply.message_id() {
            "ServiceStatus" => {
                let m = reply.parse::<habitat_sup_protocol::types::ServiceStatus>()
                             .map_err(SrvClientError::Decode)?;
                out.push(m.ident.into());
            }
            "NetOk" => (),
            "NetErr" => {
                let err = reply.parse::<habitat_sup_protocol::net::NetErr>()
                               .map_err(SrvClientError::Decode)?;
                return Err(SrvClientError::from(err).into());
            }
            _ => {
                warn!("Unexpected status message, {:?}", reply);
            }
        }
    }
    Ok(out)
}

#[derive(Clone, Copy)]
enum UninstallIfLoaded<'a> {
    EvenIfLoaded,
    SkipIfLoaded(&'a [PackageIdent]),
}

impl UninstallIfLoaded<'_> {
    fn should_skip(&self, ident: &PackageIdent) -> bool {
        if let Self::SkipIfLoaded(services) = self {
            services.iter().any(|i| i.satisfies(ident))
        } else {
            false
        }
    }
}

/// Delete a package from disk, depending upon the ExecutionStrategy supplied
///
/// Returns:
///   Ok(true) - package is deleted
///   Ok(false) - package would be deleted but it's a dry run
///   Err(_) -  IO problem deleting package from filesystem
fn maybe_delete<U>(ui: &mut U,
                   fs_root_path: &Path,
                   install: &PackageInstall,
                   strategy: ExecutionStrategy,
                   excludes: &[PackageIdent],
                   uninstall_if_loaded: UninstallIfLoaded)
                   -> Result<bool>
    where U: UIWriter
{
    let ident = install.ident();
    let pkg_root_path = hfs::pkg_root_path(Some(fs_root_path));

    let hab = PackageIdent::from_str("core/hab")?;
    if ident.satisfies(&hab) {
        ui.status(Status::Skipping,
                  format!("{}. You can't uninstall core/hab", &ident))?;
        return Ok(false);
    }

    if uninstall_if_loaded.should_skip(ident) {
        ui.status(Status::Skipping,
                  format!("{}. It is currently loaded by the supervisor", &ident))?;
        return Ok(false);
    }

    // The excludes list could be looser than the fully qualified idents.  E.g. if core/redis is on
    // the exclude list then we should exclude core/redis/1.1.0/20180608091936.  We use the
    // `Identifiable` trait which supplies this logic for PackageIdents
    let should_exclude = excludes.iter().any(|i| i.satisfies(ident));
    if should_exclude {
        ui.status(Status::Skipping,
                  format!("{}. It is on the exclusion list", &ident))?;
        return Ok(false);
    }

    match strategy {
        ExecutionStrategy::DryRun => {
            ui.status(Status::DryRunDeleting, &ident)?;
            Ok(false)
        }
        ExecutionStrategy::Run => {
            ui.status(Status::Deleting, &ident)?;
            let pkg_dir = install.installed_path();
            do_clean_delete(&pkg_root_path, &pkg_dir)
        }
    }
}

/// Delete empty parent directories from a given path. don't traverse above
/// the `pkg_root_path`
fn do_clean_delete(pkg_root_path: &Path, real_install_path: &Path) -> Result<bool> {
    // This match will always return Ok(Path) as the install path is always 4 levels
    // below the pkg_root_path
    match real_install_path.parent() {
        Some(real_install_base) => {
            let temp_install_path = temp_package_directory(real_install_path)?.path()
                                                                              .to_path_buf();
            fs::rename(&real_install_path, &temp_install_path)?;
            fs::remove_dir_all(&temp_install_path)?;

            for p in real_install_base.ancestors() {
                // Let's be safe and not rm below the package directories
                if p == pkg_root_path {
                    break;
                }
                match p.read_dir() {
                    Ok(contents) => {
                        // This will calculate the amount of items in the directory
                        match contents.count() {
                            0 => fs::remove_dir(&p)?,
                            _ => break,
                        }
                    }
                    Err(e) => return Err(Error::HabitatCore(herror::Error::IO(e))),
                }
            }
            Ok(true)
        }
        None => unreachable!("Install path doesn't have a parent"),
    }
}
<commit_msg>Rename UninstallIfLoaded<commit_after>use super::{ExecutionStrategy,
            Scope};
use crate::{config,
            error::{Error,
                    Result}};
use futures::stream::StreamExt;
use habitat_common::{cli::FS_ROOT,
                     package_graph::PackageGraph,
                     types::ListenCtlAddr,
                     ui::{Status,
                          UIWriter}};
use habitat_core::{error as herror,
                   fs as hfs,
                   package::{list::temp_package_directory,
                             Identifiable,
                             PackageIdent,
                             PackageInstall}};
use habitat_sup_client::{SrvClient,
                         SrvClientError};
use habitat_sup_protocol;
use std::{fs,
          path::Path,
          str::FromStr};

pub async fn uninstall<U>(ui: &mut U,
                          ident: impl AsRef<PackageIdent>,
                          fs_root_path: &Path,
                          execution_strategy: ExecutionStrategy,
                          scope: Scope,
                          excludes: &[PackageIdent],
                          even_if_loaded: bool)
                          -> Result<()>
    where U: UIWriter
{
    uninstall_many(ui,
                   &[ident],
                   fs_root_path,
                   execution_strategy,
                   scope,
                   excludes,
                   even_if_loaded).await
}

/// Delete packages and all dependencies which are not used by the packages.
/// We do an ordered traverse of the dependencies, updating the graph as we delete a
/// package. This lets us use simple logic where we continually check if the package
/// we're trying to delete currently has no packages depending on it.
///
/// The full logic is:
/// 1. We find all packages on the filesystem and convert them into a graph
/// 2. We find the fully qualified package ident and all its dependencies
/// 3. We do a BFS on the graph to get the dependencies in order
/// 4. We check if the specified package has any reverse deps
///     4a. If there are, we throw an error
///     4b. If not, we delete the package
/// 5. For each dependency we check if there are any packages which depend on it
///     5a. If there are, we skip it
///     5b. If there are not, we delete it from disk and the graph
///
/// `excludes` is a list of user-supplied `PackageIdent`s.
/// `even_if_loaded` indictates that the package should be uninstalled even if it is loaded by the
/// supervisor. This only applies to packages specified in `idents`. It does not apply to their
/// dependencies.
pub async fn uninstall_many<U>(ui: &mut U,
                               idents: &[impl AsRef<PackageIdent>],
                               fs_root_path: &Path,
                               execution_strategy: ExecutionStrategy,
                               scope: Scope,
                               excludes: &[PackageIdent],
                               even_if_loaded: bool)
                               -> Result<()>
    where U: UIWriter
{
    // 1.
    let mut graph = PackageGraph::from_root_path(fs_root_path)?;

    let loaded_services = supervisor_services().await?;
    if !loaded_services.is_empty() {
        ui.status(Status::Determining, "list of loaded services in supervisor")?;
        for s in loaded_services.iter() {
            ui.status(Status::Found, format!("loaded service {}", s))?;
        }
    }
    let uninstall_if_loaded = if even_if_loaded {
        UninstallMode::Force
    } else {
        UninstallMode::SkipIfLoaded(&loaded_services)
    };
    // Never uninstall a dependency if it is loaded
    let dependency_uninstall_if_loaded = UninstallMode::SkipIfLoaded(&loaded_services);

    for ident in idents {
        // 2.
        let ident = ident.as_ref();
        let pkg_install = PackageInstall::load(ident, Some(fs_root_path))?;
        let ident = pkg_install.ident();
        ui.begin(format!("Uninstalling {}", &ident))?;

        // 3.
        let deps = graph.owned_ordered_deps(&ident);

        // 4.
        match graph.count_rdeps(&ident) {
            None => {
                // package not in graph - this shouldn't happen but could be a race condition in
                // Step 2 with another hab uninstall. We can continue as what we
                // wanted (package to be removed) has already happened. We're going
                // to continue and try and delete down through the dependency tree
                // anyway
                ui.warn(format!("Tried to find dependant packages of {} but it wasn't in \
                                 graph.  Maybe another uninstall command was run at the same \
                                 time?",
                                &ident))?;
            }
            Some(0) => {
                maybe_delete(ui,
                             &fs_root_path,
                             &pkg_install,
                             execution_strategy,
                             &excludes,
                             uninstall_if_loaded)?;
                graph.remove(&ident);
            }
            Some(c) => {
                return Err(Error::CannotRemovePackage(ident.clone(), c));
            }
        }

        // 5.
        let mut count = 0;
        match scope {
            Scope::Package => {
                ui.status(Status::Skipping, "dependencies (--no-deps specified)")?;
            }
            Scope::PackageAndDependencies => {
                for p in &deps {
                    match graph.count_rdeps(&p) {
                        None => {
                            // package not in graph - this shouldn't happen but could be a race
                            // condition in Step 2 with another hab uninstall. We can
                            // continue as what we wanted (package to be removed) has already
                            // happened. We're going to continue and try
                            // and delete down through the dependency
                            // tree anyway
                            ui.warn(format!("Tried to find dependant packages of {} but it \
                                             wasn't in graph.  Maybe another uninstall command \
                                             was run at the same time?",
                                            &p))?;
                        }
                        Some(0) => {
                            let install = PackageInstall::load(&p, Some(fs_root_path))?;
                            maybe_delete(ui,
                                         &fs_root_path,
                                         &install,
                                         execution_strategy,
                                         &excludes,
                                         dependency_uninstall_if_loaded)?;

                            graph.remove(&p);
                            count += 1;
                        }
                        Some(c) => {
                            ui.status(Status::Skipping,
                                      format!("{}. It is a dependency of {} packages", &p, c))?;
                        }
                    }
                }
            }
        }
        match execution_strategy {
            ExecutionStrategy::DryRun => {
                ui.end(format!("Would uninstall {} and {} dependencies (Dry run)",
                               &ident, count))?;
            }
            ExecutionStrategy::Run => {
                ui.end(format!("Uninstall of {} and {} dependencies complete",
                               &ident, count))?;
            }
        };
    }
    Ok(())
}

/// Check if we have a launcher/supervisor running out of this habitat root.
/// If the launcher PID file exists then the supervisor is up and running
fn launcher_is_running(fs_root_path: &Path) -> bool {
    let launcher_root = hfs::launcher_root_path(Some(fs_root_path));
    let pid_file_path = launcher_root.join("PID");

    pid_file_path.is_file()
}

async fn supervisor_services() -> Result<Vec<PackageIdent>> {
    if !launcher_is_running(&*FS_ROOT) {
        return Ok(vec![]);
    }

    let cfg = config::load()?;
    let secret_key = config::ctl_secret_key(&cfg)?;
    let listen_ctl_addr = ListenCtlAddr::default();
    let msg = habitat_sup_protocol::ctl::SvcStatus::default();

    let mut out: Vec<PackageIdent> = vec![];
    let mut response = SrvClient::request(&listen_ctl_addr, &secret_key, msg).await?;
    while let Some(message_result) = response.next().await {
        let reply = message_result?;
        match reply.message_id() {
            "ServiceStatus" => {
                let m = reply.parse::<habitat_sup_protocol::types::ServiceStatus>()
                             .map_err(SrvClientError::Decode)?;
                out.push(m.ident.into());
            }
            "NetOk" => (),
            "NetErr" => {
                let err = reply.parse::<habitat_sup_protocol::net::NetErr>()
                               .map_err(SrvClientError::Decode)?;
                return Err(SrvClientError::from(err).into());
            }
            _ => {
                warn!("Unexpected status message, {:?}", reply);
            }
        }
    }
    Ok(out)
}

#[derive(Clone, Copy)]
enum UninstallMode<'a> {
    Force,
    SkipIfLoaded(&'a [PackageIdent]),
}

impl UninstallMode<'_> {
    fn should_skip(&self, ident: &PackageIdent) -> bool {
        if let Self::SkipIfLoaded(services) = self {
            services.iter().any(|i| i.satisfies(ident))
        } else {
            false
        }
    }
}

/// Delete a package from disk, depending upon the ExecutionStrategy supplied
///
/// Returns:
///   Ok(true) - package is deleted
///   Ok(false) - package would be deleted but it's a dry run
///   Err(_) -  IO problem deleting package from filesystem
fn maybe_delete<U>(ui: &mut U,
                   fs_root_path: &Path,
                   install: &PackageInstall,
                   strategy: ExecutionStrategy,
                   excludes: &[PackageIdent],
                   uninstall_if_loaded: UninstallMode)
                   -> Result<bool>
    where U: UIWriter
{
    let ident = install.ident();
    let pkg_root_path = hfs::pkg_root_path(Some(fs_root_path));

    let hab = PackageIdent::from_str("core/hab")?;
    if ident.satisfies(&hab) {
        ui.status(Status::Skipping,
                  format!("{}. You can't uninstall core/hab", &ident))?;
        return Ok(false);
    }

    if uninstall_if_loaded.should_skip(ident) {
        ui.status(Status::Skipping,
                  format!("{}. It is currently loaded by the supervisor", &ident))?;
        return Ok(false);
    }

    // The excludes list could be looser than the fully qualified idents.  E.g. if core/redis is on
    // the exclude list then we should exclude core/redis/1.1.0/20180608091936.  We use the
    // `Identifiable` trait which supplies this logic for PackageIdents
    let should_exclude = excludes.iter().any(|i| i.satisfies(ident));
    if should_exclude {
        ui.status(Status::Skipping,
                  format!("{}. It is on the exclusion list", &ident))?;
        return Ok(false);
    }

    match strategy {
        ExecutionStrategy::DryRun => {
            ui.status(Status::DryRunDeleting, &ident)?;
            Ok(false)
        }
        ExecutionStrategy::Run => {
            ui.status(Status::Deleting, &ident)?;
            let pkg_dir = install.installed_path();
            do_clean_delete(&pkg_root_path, &pkg_dir)
        }
    }
}

/// Delete empty parent directories from a given path. don't traverse above
/// the `pkg_root_path`
fn do_clean_delete(pkg_root_path: &Path, real_install_path: &Path) -> Result<bool> {
    // This match will always return Ok(Path) as the install path is always 4 levels
    // below the pkg_root_path
    match real_install_path.parent() {
        Some(real_install_base) => {
            let temp_install_path = temp_package_directory(real_install_path)?.path()
                                                                              .to_path_buf();
            fs::rename(&real_install_path, &temp_install_path)?;
            fs::remove_dir_all(&temp_install_path)?;

            for p in real_install_base.ancestors() {
                // Let's be safe and not rm below the package directories
                if p == pkg_root_path {
                    break;
                }
                match p.read_dir() {
                    Ok(contents) => {
                        // This will calculate the amount of items in the directory
                        match contents.count() {
                            0 => fs::remove_dir(&p)?,
                            _ => break,
                        }
                    }
                    Err(e) => return Err(Error::HabitatCore(herror::Error::IO(e))),
                }
            }
            Ok(true)
        }
        None => unreachable!("Install path doesn't have a parent"),
    }
}
<|endoftext|>


<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */

use rustc::hir::{self, HirId};
use rustc::lint::{LateContext, LateLintPass, LintArray, LintContext, LintPass};
use rustc::ty;
use std::boxed;
use std::env;
use std::error::Error;
use std::fmt;
use std::fs;
use std::io;
use std::path;
use syntax::ast;
use weedle;

declare_lint!(
    WEBIDL_INHERIT_CORRECT,
    Deny,
    "Warn and report usage of incorrect webidl inheritance"
);

pub(crate) struct WebIdlPass {
    symbols: crate::Symbols,
}

#[derive(Clone, Debug)]
pub struct ParentMismatchError {
    name: String,
    rust_parent: String,
    webidl_parent: String,
}

impl fmt::Display for ParentMismatchError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(
            f,
            "webidl-rust inheritance mismatch, rust: {:?}, rust parent: {:?}, webidl parent: {:?}",
            self.name, self.rust_parent, self.webidl_parent
        )
    }
}

impl Error for ParentMismatchError {
    fn description(&self) -> &str {
        "ParentMismatchError"
    }

    fn cause(&self) -> Option<&Error> {
        None
    }
}

impl WebIdlPass {
    pub fn new(symbols: crate::Symbols) -> WebIdlPass {
        WebIdlPass { symbols }
    }
}

fn get_ty_name(ty: &str) -> &str {
    if let Some(i) = ty.rfind(':') {
        &ty[i + 1..]
    } else {
        ty
    }
}

fn get_manifest_dir() -> io::Result<path::PathBuf> {
    match env::var("CARGO_MANIFEST_DIR") {
        Ok(var) => {
            let mut dir = path::PathBuf::new();
            dir.push(var);
            Ok(dir)
        },
        Err(env::VarError::NotPresent) => {
            Err(io::Error::new(io::ErrorKind::NotFound, "CARGO_MANIFEST_DIR environment variable was not found"))
        },
        Err(env::VarError::NotUnicode(_)) => {
            Err(io::Error::new(io::ErrorKind::InvalidData, "CARGO_MANIFEST_DIR environment variable's contents are non valid UTF-8"))
        },
    }
}

fn get_webidl_path(struct_name: &str) -> io::Result<path::PathBuf> {
    let mut dir = get_manifest_dir()?;
    dir.push("dom/webidls/");
    dir.push(format!("{}.webidl", struct_name));

    Ok(dir)
}

fn is_webidl_ty(symbols: &crate::Symbols, cx: &LateContext, ty: &ty::TyS) -> bool {
    let mut ret = false;
    ty.maybe_walk(|t| {
        match t.sty {
            ty::Adt(did, _substs) => {
                if cx.tcx.has_attr(did.did, symbols.webidl) {
                    ret = true;
                }
                false
            },
            ty::Ref(..) => false,    // don't recurse down &ptrs
            ty::RawPtr(..) => false, // don't recurse down *ptrs
            ty::FnDef(..) | ty::FnPtr(_) => false,
            _ => true,
        }
    });
    ret
}

fn check_inherits(code: &str, name: &str, parent_name: &str) -> Result<(), Box<Error>> {
    let idl = weedle::parse(code).expect("Invalid webidl provided");
    let mut inherits = "";

    for def in idl {
        if let weedle::Definition::Interface(def) = def {
            if let Some(parent) = def.inheritance {
                inherits = parent.identifier.0;
                break;
            }
        } else if let weedle::Definition::CallbackInterface(def) = def {
            if let Some(parent) = def.inheritance {
                inherits = parent.identifier.0;
                break;
            }
        }
    }

    if inherits == parent_name {
        return Ok(());
    }

    // If there is no parent, first field must be of type Reflector.
    if inherits == "" && parent_name == "Reflector" {
        return Ok(());
    }

    if inherits == "" &&
        name == "PaintRenderingContext2D" &&
        parent_name == "CanvasRenderingContext2D"
    {
        // PaintRenderingContext2D embeds a CanvasRenderingContext2D
        // instead of a Reflector as an optimization,
        // but this is fine since CanvasRenderingContext2D
        // also has a reflector
        return Ok(());
    }

    Err(boxed::Box::from(ParentMismatchError {
        name: name.to_string(),
        rust_parent: parent_name.to_string(),
        webidl_parent: inherits.to_string(),
    }))
}

fn check_webidl(name: &str, parent_name: &Option<String>) -> Result<(), Box<Error>> {
    let path = get_webidl_path(&name)?;
    if let Some(parent) = parent_name {
        let code = fs::read_to_string(path)?;
        return check_inherits(&code, name, &parent);
    }

    Ok(())
}

impl LintPass for WebIdlPass {
    fn name(&self) -> &'static str {
        "ServoWebIDLPass"
    }

    fn get_lints(&self) -> LintArray {
        lint_array!(WEBIDL_INHERIT_CORRECT)
    }
}

impl<'a, 'tcx> LateLintPass<'a, 'tcx> for WebIdlPass {
    fn check_struct_def(
        &mut self,
        cx: &LateContext<'a, 'tcx>,
        def: &'tcx hir::VariantData,
        n: ast::Name,
        _gen: &'tcx hir::Generics,
        id: HirId,
    ) {
        let def_id = cx.tcx.hir().local_def_id_from_hir_id(id);
        if !is_webidl_ty(&self.symbols, cx, cx.tcx.type_of(def_id)) {
            return;
        }

        let item = match cx.tcx.hir().get_by_hir_id(id) {
            hir::Node::Item(item) => item,
            _ => cx
                .tcx
                .hir()
                .expect_item_by_hir_id(cx.tcx.hir().get_parent_item(id)),
        };

        let parent_name = def.fields().iter().next().map(|field| {
            let def_id = cx.tcx.hir().local_def_id_from_hir_id(field.hir_id);
            let ty = cx.tcx.type_of(def_id).to_string();
            get_ty_name(&ty).to_string()
        });

        let struct_name = n.to_string();
        match check_webidl(&struct_name, &parent_name) {
            Ok(()) => {},
            Err(e) => {
                let description = format!("{}", e);
                cx.span_lint(WEBIDL_INHERIT_CORRECT, item.ident.span, &description)
            },
        };
    }
}
<commit_msg>Run rustfmt.<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */

use rustc::hir::{self, HirId};
use rustc::lint::{LateContext, LateLintPass, LintArray, LintContext, LintPass};
use rustc::ty;
use std::boxed;
use std::env;
use std::error::Error;
use std::fmt;
use std::fs;
use std::io;
use std::path;
use syntax::ast;
use weedle;

declare_lint!(
    WEBIDL_INHERIT_CORRECT,
    Deny,
    "Warn and report usage of incorrect webidl inheritance"
);

pub(crate) struct WebIdlPass {
    symbols: crate::Symbols,
}

#[derive(Clone, Debug)]
pub struct ParentMismatchError {
    name: String,
    rust_parent: String,
    webidl_parent: String,
}

impl fmt::Display for ParentMismatchError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(
            f,
            "webidl-rust inheritance mismatch, rust: {:?}, rust parent: {:?}, webidl parent: {:?}",
            self.name, self.rust_parent, self.webidl_parent
        )
    }
}

impl Error for ParentMismatchError {
    fn description(&self) -> &str {
        "ParentMismatchError"
    }

    fn cause(&self) -> Option<&Error> {
        None
    }
}

impl WebIdlPass {
    pub fn new(symbols: crate::Symbols) -> WebIdlPass {
        WebIdlPass { symbols }
    }
}

fn get_ty_name(ty: &str) -> &str {
    if let Some(i) = ty.rfind(':') {
        &ty[i + 1..]
    } else {
        ty
    }
}

fn get_manifest_dir() -> io::Result<path::PathBuf> {
    match env::var("CARGO_MANIFEST_DIR") {
        Ok(var) => {
            let mut dir = path::PathBuf::new();
            dir.push(var);
            Ok(dir)
        },
        Err(env::VarError::NotPresent) => Err(io::Error::new(
            io::ErrorKind::NotFound,
            "CARGO_MANIFEST_DIR environment variable was not found",
        )),
        Err(env::VarError::NotUnicode(_)) => Err(io::Error::new(
            io::ErrorKind::InvalidData,
            "CARGO_MANIFEST_DIR environment variable's contents are non valid UTF-8",
        )),
    }
}

fn get_webidl_path(struct_name: &str) -> io::Result<path::PathBuf> {
    let mut dir = get_manifest_dir()?;
    dir.push("dom/webidls/");
    dir.push(format!("{}.webidl", struct_name));

    Ok(dir)
}

fn is_webidl_ty(symbols: &crate::Symbols, cx: &LateContext, ty: &ty::TyS) -> bool {
    let mut ret = false;
    ty.maybe_walk(|t| {
        match t.sty {
            ty::Adt(did, _substs) => {
                if cx.tcx.has_attr(did.did, symbols.webidl) {
                    ret = true;
                }
                false
            },
            ty::Ref(..) => false,    // don't recurse down &ptrs
            ty::RawPtr(..) => false, // don't recurse down *ptrs
            ty::FnDef(..) | ty::FnPtr(_) => false,
            _ => true,
        }
    });
    ret
}

fn check_inherits(code: &str, name: &str, parent_name: &str) -> Result<(), Box<Error>> {
    let idl = weedle::parse(code).expect("Invalid webidl provided");
    let mut inherits = "";

    for def in idl {
        if let weedle::Definition::Interface(def) = def {
            if let Some(parent) = def.inheritance {
                inherits = parent.identifier.0;
                break;
            }
        } else if let weedle::Definition::CallbackInterface(def) = def {
            if let Some(parent) = def.inheritance {
                inherits = parent.identifier.0;
                break;
            }
        }
    }

    if inherits == parent_name {
        return Ok(());
    }

    // If there is no parent, first field must be of type Reflector.
    if inherits == "" && parent_name == "Reflector" {
        return Ok(());
    }

    if inherits == "" &&
        name == "PaintRenderingContext2D" &&
        parent_name == "CanvasRenderingContext2D"
    {
        // PaintRenderingContext2D embeds a CanvasRenderingContext2D
        // instead of a Reflector as an optimization,
        // but this is fine since CanvasRenderingContext2D
        // also has a reflector
        return Ok(());
    }

    Err(boxed::Box::from(ParentMismatchError {
        name: name.to_string(),
        rust_parent: parent_name.to_string(),
        webidl_parent: inherits.to_string(),
    }))
}

fn check_webidl(name: &str, parent_name: &Option<String>) -> Result<(), Box<Error>> {
    let path = get_webidl_path(&name)?;
    if let Some(parent) = parent_name {
        let code = fs::read_to_string(path)?;
        return check_inherits(&code, name, &parent);
    }

    Ok(())
}

impl LintPass for WebIdlPass {
    fn name(&self) -> &'static str {
        "ServoWebIDLPass"
    }

    fn get_lints(&self) -> LintArray {
        lint_array!(WEBIDL_INHERIT_CORRECT)
    }
}

impl<'a, 'tcx> LateLintPass<'a, 'tcx> for WebIdlPass {
    fn check_struct_def(
        &mut self,
        cx: &LateContext<'a, 'tcx>,
        def: &'tcx hir::VariantData,
        n: ast::Name,
        _gen: &'tcx hir::Generics,
        id: HirId,
    ) {
        let def_id = cx.tcx.hir().local_def_id_from_hir_id(id);
        if !is_webidl_ty(&self.symbols, cx, cx.tcx.type_of(def_id)) {
            return;
        }

        let item = match cx.tcx.hir().get_by_hir_id(id) {
            hir::Node::Item(item) => item,
            _ => cx
                .tcx
                .hir()
                .expect_item_by_hir_id(cx.tcx.hir().get_parent_item(id)),
        };

        let parent_name = def.fields().iter().next().map(|field| {
            let def_id = cx.tcx.hir().local_def_id_from_hir_id(field.hir_id);
            let ty = cx.tcx.type_of(def_id).to_string();
            get_ty_name(&ty).to_string()
        });

        let struct_name = n.to_string();
        match check_webidl(&struct_name, &parent_name) {
            Ok(()) => {},
            Err(e) => {
                let description = format!("{}", e);
                cx.span_lint(WEBIDL_INHERIT_CORRECT, item.ident.span, &description)
            },
        };
    }
}
<|endoftext|>


<commit_before>/* Byte at a time AES ECB decryption (Simple)
 *
 * Dmitry Vasiliev <dima@hlabs.org>
 */

extern crate serialize;

extern crate aes_lib;

use std::rand::random;
use std::fmt;
use std::collections::HashMap;
use serialize::base64::FromBase64;

use aes_lib::{AES_BLOCK_SIZE, encrypt_aes_ecb};

type Dict = HashMap<Vec<u8>, u8>;

#[deriving(PartialEq, Eq)]
enum Mode {
    ECB,
    CBC
}

impl fmt::Show for Mode {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            ECB => write!(f, "ECB"),
            CBC => write!(f, "CBC"),
        }
    }
}

struct Decryptor {
    unknown: Vec<u8>,
    key: Vec<u8>,
}

impl Decryptor {
    fn new() -> Decryptor {
        let unknown = unknown_string();
        let key = random_bytes(AES_BLOCK_SIZE);
        Decryptor{unknown: unknown, key: key}
    }

    #[inline]
    fn encrypt(&self, string: &[u8]) -> Vec<u8> {
        let data = string.to_vec() + self.unknown;
        encrypt_aes_ecb(data.as_slice(), self.key.as_slice())
    }

    fn guess_block_size(&self) -> Option<uint> {
        let mut prev = self.encrypt(['A' as u8].as_slice());
        for len in range(1, 256) {
            let s = Vec::from_elem(len + 1, 'A' as u8);
            let enc = self.encrypt(s.as_slice());
            if prev.slice_to(len) == enc.slice_to(len) {
                return Some(len);
            }
            prev = enc;
        }
        None
    }

    fn guess_aes_mode(&self, block_size: uint) -> Mode {
        let s = Vec::from_elem(block_size * 2, 'A' as u8);
        let enc = self.encrypt(s.as_slice());
        if enc.slice_to(block_size) == enc.slice(block_size, block_size * 2) {
            ECB
        } else {
            CBC
        }
    }

    fn make_dict(&self, block_size: uint) -> Dict {
        let mut dict = HashMap::with_capacity(256);
        let mut input = Vec::from_elem(block_size, 'A' as u8);
        for c in range(0, 255) {
            *input.last_mut().unwrap() = c;
            let enc = self.encrypt(input.as_slice());
            dict.insert(enc.slice_to(block_size).to_vec(), c);
        }
        dict
    }

    fn decrypt(&self, block_size: uint) -> Vec<u8> {
        let dict = self.make_dict(block_size);
        let mut input = Vec::from_elem(block_size, 'A' as u8);
        self.unknown.iter().map(|&c| {
            *input.last_mut().unwrap() = c;
            let enc = self.encrypt(input.as_slice());
            dict[enc.slice_to(block_size).to_vec()]
            }).collect()
    }
}

fn random_bytes(len: uint) -> Vec<u8> {
    Vec::from_fn(len, |_| random::<u8>())
}

fn unknown_string() -> Vec<u8> {
    let data = "Um9sbGluJyBpbiBteSA1LjAKV2l0aCBteSByYWctdG9wIGRvd24gc28gbXkg
aGFpciBjYW4gYmxvdwpUaGUgZ2lybGllcyBvbiBzdGFuZGJ5IHdhdmluZyBq
dXN0IHRvIHNheSBoaQpEaWQgeW91IHN0b3A/IE5vLCBJIGp1c3QgZHJvdmUg
YnkK";
    data.from_base64().unwrap()
}

fn main() {
    let decryptor = Decryptor::new();
    let block_size = decryptor.guess_block_size().unwrap();
    println!("Block size: {}", block_size);
    let mode = decryptor.guess_aes_mode(block_size);
    println!("AES mode: {}", mode);
    let dec = decryptor.decrypt(block_size);
    println!("Text: {}", String::from_utf8(dec).unwrap());
}
<commit_msg>Optimize code<commit_after>/* Byte at a time AES ECB decryption (Simple)
 *
 * Dmitry Vasiliev <dima@hlabs.org>
 */

extern crate serialize;

extern crate aes_lib;

use std::rand::random;
use std::fmt;
use std::collections::HashMap;
use serialize::base64::FromBase64;

use aes_lib::{AES_BLOCK_SIZE, encrypt_aes_ecb};

type Dict = HashMap<Vec<u8>, u8>;

#[deriving(PartialEq, Eq)]
enum Mode {
    ECB,
    CBC
}

impl fmt::Show for Mode {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            Mode::ECB => write!(f, "ECB"),
            Mode::CBC => write!(f, "CBC"),
        }
    }
}

struct Decryptor {
    unknown: Vec<u8>,
    key: Vec<u8>,
}

impl Decryptor {
    fn new() -> Decryptor {
        let unknown = unknown_string();
        let key = random_bytes(AES_BLOCK_SIZE);
        Decryptor{unknown: unknown, key: key}
    }

    #[inline]
    fn encrypt(&self, string: &[u8]) -> Vec<u8> {
        let data = string.to_vec() + self.unknown;
        encrypt_aes_ecb(data.as_slice(), self.key.as_slice())
    }

    fn guess_block_size(&self) -> Option<uint> {
        static MAX_KEY_SIZE: uint = 256;
        let data = Vec::from_elem(MAX_KEY_SIZE - 1, 0u8);
        let mut prev = self.encrypt(data.slice_to(1));
        for len in range(1, MAX_KEY_SIZE) {
            let enc = self.encrypt(data.slice_to(len + 1));
            if prev.slice_to(len) == enc.slice_to(len) {
                return Some(len);
            }
            prev = enc;
        }
        None
    }

    fn guess_aes_mode(&self, block_size: uint) -> Mode {
        let s = Vec::from_elem(block_size * 2, 0u8);
        let e = self.encrypt(s.as_slice());
        match e.slice_to(block_size) == e.slice(block_size, block_size * 2) {
            true => Mode::ECB,
            false => Mode::CBC
        }
    }

    fn make_dict(&self, block_size: uint) -> Dict {
        let mut input = Vec::from_elem(block_size, 0u8);
        range(0, 255).map(|c| {
            *input.last_mut().unwrap() = c;
            let enc = self.encrypt(input.as_slice());
            (enc.slice_to(block_size).to_vec(), c)
        }).collect()
    }

    fn decrypt(&self, block_size: uint) -> Vec<u8> {
        let dict = self.make_dict(block_size);
        let mut input = Vec::from_elem(block_size, 0u8);
        self.unknown.iter().map(|&c| {
            *input.last_mut().unwrap() = c;
            let enc = self.encrypt(input.as_slice());
            dict[enc.slice_to(block_size).to_vec()]
        }).collect()
    }
}

fn random_bytes(len: uint) -> Vec<u8> {
    Vec::from_fn(len, |_| random::<u8>())
}

fn unknown_string() -> Vec<u8> {
    "Um9sbGluJyBpbiBteSA1LjAKV2l0aCBteSByYWctdG9wIGRvd24gc28gbXkg
aGFpciBjYW4gYmxvdwpUaGUgZ2lybGllcyBvbiBzdGFuZGJ5IHdhdmluZyBq
dXN0IHRvIHNheSBoaQpEaWQgeW91IHN0b3A/IE5vLCBJIGp1c3QgZHJvdmUg
YnkK".from_base64().unwrap()
}

fn main() {
    let decryptor = Decryptor::new();
    let block_size = decryptor.guess_block_size().unwrap();
    println!("Block size: {}", block_size);
    let mode = decryptor.guess_aes_mode(block_size);
    println!("AES mode: {}", mode);
    let dec = decryptor.decrypt(block_size);
    println!("Text: {}", String::from_utf8(dec).unwrap());
}
<|endoftext|>


<commit_before>use crate::prelude::*;
use winapi::shared::guiddef;

#[cfg(feature = "guid")]
impl Uuid {
    /// Attempts to create a [`Uuid`] from a little endian winapi `GUID`
    ///
    /// [`Uuid`]: ../struct.Uuid.html
    pub fn from_guid(guid: guiddef::GUID) -> Result<Uuid, crate::Error> {
        Uuid::from_fields_le(
            guid.Data1 as u32,
            guid.Data2 as u16,
            guid.Data3 as u16,
            &(guid.Data4 as [u8; 8]),
        )
    }

    /// Converts a [`Uuid`] into a little endian winapi `GUID`
    ///
    /// [`Uuid`]: ../struct.Uuid.html
    pub fn to_guid(&self) -> guiddef::GUID {
        let (data1, data2, data3, data4) = self.to_fields_le();

        guiddef::GUID {
            Data1: data1,
            Data2: data2,
            Data3: data3,
            Data4: *data4,
        }
    }
}

#[cfg(feature = "guid")]
#[cfg(test)]
mod tests {
    use super::*;

    use crate::std::string::ToString;
    use winapi::shared::guiddef;

    #[test]
    fn test_from_guid() {
        let guid = guiddef::GUID {
            Data1: 0x4a35229d,
            Data2: 0x5527,
            Data3: 0x4f30,
            Data4: [0x86, 0x47, 0x9d, 0xc5, 0x4e, 0x1e, 0xe1, 0xe8],
        };

        let uuid = Uuid::from_guid(guid).unwrap();
        assert_eq!(
            "9d22354a-2755-304f-8647-9dc54e1ee1e8",
            uuid.to_hyphenated().to_string()
        );
    }

    #[test]
    fn test_guid_roundtrip() {
        let guid_in = guiddef::GUID {
            Data1: 0x4a35229d,
            Data2: 0x5527,
            Data3: 0x4f30,
            Data4: [0x86, 0x47, 0x9d, 0xc5, 0x4e, 0x1e, 0xe1, 0xe8],
        };

        let uuid = Uuid::from_guid(guid_in).unwrap();
        let guid_out = uuid.to_guid();

        assert_eq!(
            (guid_in.Data1, guid_in.Data2, guid_in.Data3, guid_in.Data4),
            (
                guid_out.Data1,
                guid_out.Data2,
                guid_out.Data3,
                guid_out.Data4
            )
        );
    }
}
<commit_msg>make `Uuid::from_guid` infallible<commit_after>use crate::prelude::*;
use winapi::shared::guiddef;

#[cfg(feature = "guid")]
impl Uuid {
    /// Attempts to create a [`Uuid`] from a little endian winapi `GUID`
    ///
    /// [`Uuid`]: ../struct.Uuid.html
    pub fn from_guid(guid: guiddef::GUID) -> Self {
        let Ok(uuid) = Uuid::from_fields_le(
            guid.Data1 as u32,
            guid.Data2 as u16,
            guid.Data3 as u16,
            &(guid.Data4 as [u8; 8]),
        );

        uuid
    }

    /// Converts a [`Uuid`] into a little endian winapi `GUID`
    ///
    /// [`Uuid`]: ../struct.Uuid.html
    pub fn to_guid(&self) -> guiddef::GUID {
        let (data1, data2, data3, data4) = self.to_fields_le();

        guiddef::GUID {
            Data1: data1,
            Data2: data2,
            Data3: data3,
            Data4: *data4,
        }
    }
}

#[cfg(feature = "guid")]
#[cfg(test)]
mod tests {
    use super::*;

    use crate::std::string::ToString;
    use winapi::shared::guiddef;

    #[test]
    fn test_from_guid() {
        let guid = guiddef::GUID {
            Data1: 0x4a35229d,
            Data2: 0x5527,
            Data3: 0x4f30,
            Data4: [0x86, 0x47, 0x9d, 0xc5, 0x4e, 0x1e, 0xe1, 0xe8],
        };

        let uuid = Uuid::from_guid(guid).unwrap();
        assert_eq!(
            "9d22354a-2755-304f-8647-9dc54e1ee1e8",
            uuid.to_hyphenated().to_string()
        );
    }

    #[test]
    fn test_guid_roundtrip() {
        let guid_in = guiddef::GUID {
            Data1: 0x4a35229d,
            Data2: 0x5527,
            Data3: 0x4f30,
            Data4: [0x86, 0x47, 0x9d, 0xc5, 0x4e, 0x1e, 0xe1, 0xe8],
        };

        let uuid = Uuid::from_guid(guid_in).unwrap();
        let guid_out = uuid.to_guid();

        assert_eq!(
            (guid_in.Data1, guid_in.Data2, guid_in.Data3, guid_in.Data4),
            (
                guid_out.Data1,
                guid_out.Data2,
                guid_out.Data3,
                guid_out.Data4
            )
        );
    }
}
<|endoftext|>


<commit_before>#![no_std]

use core::fmt;
use core::fmt::Write;
use log::info;

use kata_io as io;
use kata_line_reader::LineReader;

/// Error type indicating why a command line is not runnable.
enum CommandError {
    UnknownCommand,
    BadArgs,
    Formatter(fmt::Error),
}

impl fmt::Display for CommandError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            CommandError::UnknownCommand => write!(f, "unknown command"),
            CommandError::BadArgs => write!(f, "invalid arguments"),
            CommandError::Formatter(e) => write!(f, "{}", e),
        }
    }
}

impl From<core::num::ParseIntError> for CommandError {
    fn from(_err: core::num::ParseIntError) -> CommandError {
        CommandError::BadArgs
    }
}

impl From<core::num::ParseFloatError> for CommandError {
    fn from(_err: core::num::ParseFloatError) -> CommandError {
        CommandError::BadArgs
    }
}

impl From<fmt::Error> for CommandError {
    fn from(err: fmt::Error) -> CommandError {
        CommandError::Formatter(err)
    }
}

/// Read-eval-print loop for the DebugConsole command line interface.
pub fn repl(output: &mut dyn io::Write, input: &mut dyn io::Read) -> ! {
    //  let _ = write!(output, "DebugConsole::repl()\n");
    info!("DebugConsole::repl()");
    let mut line_reader = LineReader::new();
    loop {
        const PROMPT: &str = "KATA_PROMPT> ";
        let _ = output.write_str(PROMPT);
        match line_reader.read_line(output, input) {
            Ok(cmdline) => dispatch_command(cmdline, output),
            Err(e) => {
                let _ = write!(output, "\n{}\n", e);
            }
        }
    }
}

/// Runs a command line.
///
/// The line is split on whitespace. The first token is the command; the
/// remaining tokens are the arguments.
fn dispatch_command(cmdline: &str, output: &mut dyn io::Write) {
    let mut args = cmdline.split_ascii_whitespace();
    match args.nth(0) {
        Some(command) => {
            // Statically binds command names to implementations fns, which are
            // defined below.
            //
            // Since even the binding is static, it is fine for each command
            // implementation to use its own preferred signature.
            let result = match command {
                "add" => add_command(&mut args, output),
                "echo" => echo_command(cmdline, output),
                "clear" => clear_command(output),
                "ps" => ps_command(),

                "test_alloc" => test_alloc_command(output),
                "test_alloc_error" => test_alloc_error_command(output),
                "test_panic" => test_panic_command(),

                _ => Err(CommandError::UnknownCommand),
            };
            if let Err(e) = result {
                let _ = write!(output, "{}\n", e);
            };
        }
        None => {
            let _ = output.write_str("\n");
        }
    };
}

/// Implements an "echo" command which writes its arguments to output.
fn echo_command(cmdline: &str, output: &mut dyn io::Write) -> Result<(), CommandError> {
    const COMMAND_LENGTH: usize = 5; // "echo "
    if cmdline.len() < COMMAND_LENGTH {
        Ok(())
    } else {
        Ok(write!(
            output,
            "{}\n",
            &cmdline[COMMAND_LENGTH..cmdline.len()]
        )?)
    }
}

/// Implements a "ps" command that dumps seL4 scheduler state to the console.
fn ps_command() -> Result<(), CommandError> {
    extern "C" {
        fn sel4debug_dump_scheduler();
    }
    unsafe {
        sel4debug_dump_scheduler();
    }
    Ok(())
}

/// Implements a binary float addition command.
///
/// This is a toy to demonstrate that the CLI can operate on some very basic
/// dynamic input and that the Rust runtime provides floating point arithmetic
/// on integer-only hardware. It is also a prototype example of "command taking
/// arguments." It should be removed once actually useful system control
/// commands are implemented and done cribbing from it.
fn add_command(
    args: &mut dyn Iterator<Item = &str>,
    output: &mut dyn io::Write,
) -> Result<(), CommandError> {
    if let Some(x_str) = args.nth(0) {
        if let Some(y_str) = args.nth(0) {
            let x = x_str.parse::<f32>()?;
            let y = y_str.parse::<f32>()?;
            return Ok(write!(output, "{}\n", x + y)?);
        }
    }
    Err(CommandError::BadArgs)
}

/// Implements a command that outputs the ANSI "clear console" sequence.
fn clear_command(output: &mut dyn io::Write) -> Result<(), CommandError> {
    Ok(output.write_str("\x1b\x63")?)
}

/// Implements a command that tests facilities that use the global allocator.
/// Shamelessly cribbed from https://os.phil-opp.com/heap-allocation/
fn test_alloc_command(output: &mut dyn io::Write) -> Result<(), CommandError> {
    extern crate alloc;
    use alloc::{boxed::Box, rc::Rc, vec, vec::Vec};

    // allocate a number on the heap
    let heap_value = Box::new(41);
    writeln!(output, "heap_value at {:p}", heap_value).expect("Box failed");

    // create a dynamically sized vector
    let mut vec = Vec::new();
    for i in 0..500 {
        vec.push(i);
    }
    writeln!(output, "vec at {:p}", vec.as_slice()).expect("Vec failed");

    // create a reference counted vector -> will be freed when count reaches 0
    let reference_counted = Rc::new(vec![1, 2, 3]);
    let cloned_reference = reference_counted.clone();
    writeln!(
        output,
        "current reference count is {}",
        Rc::strong_count(&cloned_reference)
    )
    .expect("Rc 1 failed");
    core::mem::drop(reference_counted);
    writeln!(
        output,
        "reference count is {} now",
        Rc::strong_count(&cloned_reference)
    )
    .expect("Rc 2 failed");

    Ok(writeln!(output, "All tests passed!")?)
}

/// Implements a command that tests the global allocator error handling.
fn test_alloc_error_command(output: &mut dyn io::Write) -> Result<(), CommandError> {
    extern crate alloc;
    use alloc::vec::Vec;

    // Default heap holds 16KB.
    let mut vec = Vec::with_capacity(16384);
    for i in 0..16348 {
        vec.push(i);
    }
    Ok(writeln!(output, "vec at {:p}", vec.as_slice())?)
}

/// Implements a command that tests panic handling.
fn test_panic_command() -> Result<(), CommandError> {
    panic!("testing");
}
<commit_msg>kata-shell: add loglevel command<commit_after>#![no_std]

use core::fmt;
use core::fmt::Write;
use log::info;

use kata_io as io;
use kata_line_reader::LineReader;

/// Error type indicating why a command line is not runnable.
enum CommandError {
    UnknownCommand,
    BadArgs,
    Formatter(fmt::Error),
}

impl fmt::Display for CommandError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            CommandError::UnknownCommand => write!(f, "unknown command"),
            CommandError::BadArgs => write!(f, "invalid arguments"),
            CommandError::Formatter(e) => write!(f, "{}", e),
        }
    }
}

impl From<core::num::ParseIntError> for CommandError {
    fn from(_err: core::num::ParseIntError) -> CommandError {
        CommandError::BadArgs
    }
}

impl From<core::num::ParseFloatError> for CommandError {
    fn from(_err: core::num::ParseFloatError) -> CommandError {
        CommandError::BadArgs
    }
}

impl From<fmt::Error> for CommandError {
    fn from(err: fmt::Error) -> CommandError {
        CommandError::Formatter(err)
    }
}

/// Read-eval-print loop for the DebugConsole command line interface.
pub fn repl(output: &mut dyn io::Write, input: &mut dyn io::Read) -> ! {
    //  let _ = write!(output, "DebugConsole::repl()\n");
    info!("DebugConsole::repl()");
    let mut line_reader = LineReader::new();
    loop {
        const PROMPT: &str = "KATA_PROMPT> ";
        let _ = output.write_str(PROMPT);
        match line_reader.read_line(output, input) {
            Ok(cmdline) => dispatch_command(cmdline, output),
            Err(e) => {
                let _ = write!(output, "\n{}\n", e);
            }
        }
    }
}

/// Runs a command line.
///
/// The line is split on whitespace. The first token is the command; the
/// remaining tokens are the arguments.
fn dispatch_command(cmdline: &str, output: &mut dyn io::Write) {
    let mut args = cmdline.split_ascii_whitespace();
    match args.nth(0) {
        Some(command) => {
            // Statically binds command names to implementations fns, which are
            // defined below.
            //
            // Since even the binding is static, it is fine for each command
            // implementation to use its own preferred signature.
            let result = match command {
                "add" => add_command(&mut args, output),
                "echo" => echo_command(cmdline, output),
                "clear" => clear_command(output),
                "loglevel" => loglevel_command(&mut args, output),
                "ps" => ps_command(),

                "test_alloc" => test_alloc_command(output),
                "test_alloc_error" => test_alloc_error_command(output),
                "test_panic" => test_panic_command(),

                _ => Err(CommandError::UnknownCommand),
            };
            if let Err(e) = result {
                let _ = write!(output, "{}\n", e);
            };
        }
        None => {
            let _ = output.write_str("\n");
        }
    };
}

/// Implements an "echo" command which writes its arguments to output.
fn echo_command(cmdline: &str, output: &mut dyn io::Write) -> Result<(), CommandError> {
    const COMMAND_LENGTH: usize = 5; // "echo "
    if cmdline.len() < COMMAND_LENGTH {
        Ok(())
    } else {
        Ok(write!(
            output,
            "{}\n",
            &cmdline[COMMAND_LENGTH..cmdline.len()]
        )?)
    }
}

// Set/display the max log level for the DebugConsole.
// TODO(sleffler): support setting the log level in other components
fn loglevel_command(
    args: &mut dyn Iterator<Item = &str>,
    output: &mut dyn io::Write,
) -> Result<(), CommandError> {
    if let Some(level) = args.nth(0) {
        use log::LevelFilter;
        match level {
            "off" => log::set_max_level(LevelFilter::Off),
            "debug" => log::set_max_level(LevelFilter::Debug),
            "info" => log::set_max_level(LevelFilter::Info),
            "error" => log::set_max_level(LevelFilter::Error),
            "trace" => log::set_max_level(LevelFilter::Trace),
            "warn" => log::set_max_level(LevelFilter::Warn),
            _ => writeln!(output, "Unknown log level {}", level)?,
        }
    }
    Ok(writeln!(output, "{}", log::max_level())?)
}

/// Implements a "ps" command that dumps seL4 scheduler state to the console.
fn ps_command() -> Result<(), CommandError> {
    extern "C" {
        fn sel4debug_dump_scheduler();
    }
    unsafe {
        sel4debug_dump_scheduler();
    }
    Ok(())
}

/// Implements a binary float addition command.
///
/// This is a toy to demonstrate that the CLI can operate on some very basic
/// dynamic input and that the Rust runtime provides floating point arithmetic
/// on integer-only hardware. It is also a prototype example of "command taking
/// arguments." It should be removed once actually useful system control
/// commands are implemented and done cribbing from it.
fn add_command(
    args: &mut dyn Iterator<Item = &str>,
    output: &mut dyn io::Write,
) -> Result<(), CommandError> {
    if let Some(x_str) = args.nth(0) {
        if let Some(y_str) = args.nth(0) {
            let x = x_str.parse::<f32>()?;
            let y = y_str.parse::<f32>()?;
            return Ok(write!(output, "{}\n", x + y)?);
        }
    }
    Err(CommandError::BadArgs)
}

/// Implements a command that outputs the ANSI "clear console" sequence.
fn clear_command(output: &mut dyn io::Write) -> Result<(), CommandError> {
    Ok(output.write_str("\x1b\x63")?)
}

/// Implements a command that tests facilities that use the global allocator.
/// Shamelessly cribbed from https://os.phil-opp.com/heap-allocation/
fn test_alloc_command(output: &mut dyn io::Write) -> Result<(), CommandError> {
    extern crate alloc;
    use alloc::{boxed::Box, rc::Rc, vec, vec::Vec};

    // allocate a number on the heap
    let heap_value = Box::new(41);
    writeln!(output, "heap_value at {:p}", heap_value).expect("Box failed");

    // create a dynamically sized vector
    let mut vec = Vec::new();
    for i in 0..500 {
        vec.push(i);
    }
    writeln!(output, "vec at {:p}", vec.as_slice()).expect("Vec failed");

    // create a reference counted vector -> will be freed when count reaches 0
    let reference_counted = Rc::new(vec![1, 2, 3]);
    let cloned_reference = reference_counted.clone();
    writeln!(
        output,
        "current reference count is {}",
        Rc::strong_count(&cloned_reference)
    )
    .expect("Rc 1 failed");
    core::mem::drop(reference_counted);
    writeln!(
        output,
        "reference count is {} now",
        Rc::strong_count(&cloned_reference)
    )
    .expect("Rc 2 failed");

    Ok(writeln!(output, "All tests passed!")?)
}

/// Implements a command that tests the global allocator error handling.
fn test_alloc_error_command(output: &mut dyn io::Write) -> Result<(), CommandError> {
    extern crate alloc;
    use alloc::vec::Vec;

    // Default heap holds 16KB.
    let mut vec = Vec::with_capacity(16384);
    for i in 0..16348 {
        vec.push(i);
    }
    Ok(writeln!(output, "vec at {:p}", vec.as_slice())?)
}

/// Implements a command that tests panic handling.
fn test_panic_command() -> Result<(), CommandError> {
    panic!("testing");
}
<|endoftext|>


<commit_before>use crate::utils::{match_type, paths, span_lint_and_help};
use rustc::hir::map::Map;
use rustc_hir::intravisit::{self as visit, NestedVisitorMap, Visitor};
use rustc_hir::{Arm, Expr, ExprKind, MatchSource, StmtKind};
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_lint_pass, declare_tool_lint};

declare_clippy_lint! {
    /// **What it does:** Checks for `Mutex::lock` calls in `if let` expression
    /// with lock calls in any of the else blocks.
    ///
    /// **Why is this bad?** The Mutex lock remains held for the whole
    /// `if let ... else` block and deadlocks.
    ///
    /// **Known problems:** None.
    ///
    /// **Example:**
    ///
    /// ```rust,ignore
    /// if let Ok(thing) = mutex.lock() {
    ///     do_thing();
    /// } else {
    ///     mutex.lock();
    /// }
    /// ```
    /// Should be written
    /// ```rust,ignore
    /// let locked = mutex.lock();
    /// if let Ok(thing) = locked {
    ///     do_thing(thing);
    /// } else {
    ///     use_locked(locked);
    /// }
    /// ```
    pub IF_LET_MUTEX,
    correctness,
    "locking a `Mutex` in an `if let` block can cause deadlocks"
}

declare_lint_pass!(IfLetMutex => [IF_LET_MUTEX]);

impl LateLintPass<'_, '_> for IfLetMutex {
    fn check_expr(&mut self, cx: &LateContext<'_, '_>, ex: &'_ Expr<'_>) {
        let mut arm_visit = ArmVisitor {
            arm_mutex: false,
            arm_lock: false,
            cx,
        };
        let mut op_visit = IfLetMutexVisitor {
            op_mutex: false,
            op_lock: false,
            cx,
        };
        if let ExprKind::Match(
            ref op,
            ref arms,
            MatchSource::IfLetDesugar {
                contains_else_clause: true,
            },
        ) = ex.kind
        {
            op_visit.visit_expr(op);
            if op_visit.op_mutex && op_visit.op_lock {
                for arm in *arms {
                    arm_visit.visit_arm(arm);
                }

                if arm_visit.arm_mutex && arm_visit.arm_lock {
                    span_lint_and_help(
                        cx,
                        IF_LET_MUTEX,
                        ex.span,
                        "calling `Mutex::lock` inside the scope of another `Mutex::lock` causes a deadlock",
                        "move the lock call outside of the `if let ...` expression",
                    );
                }
            }
        }
    }
}

/// Checks if `Mutex::lock` is called in the `if let _ = expr.
pub struct IfLetMutexVisitor<'tcx, 'l> {
    pub op_mutex: bool,
    pub op_lock: bool,
    pub cx: &'tcx LateContext<'tcx, 'l>,
}

impl<'tcx, 'l> Visitor<'tcx> for IfLetMutexVisitor<'tcx, 'l> {
    type Map = Map<'tcx>;

    fn visit_expr(&mut self, expr: &'tcx Expr<'_>) {
        if let ExprKind::MethodCall(path, _span, args) = &expr.kind {
            if path.ident.to_string() == "lock" {
                self.op_lock = true;
            }
            let ty = self.cx.tables.expr_ty(&args[0]);
            if match_type(self.cx, ty, &paths::MUTEX) {
                self.op_mutex = true;
            }
        }
        visit::walk_expr(self, expr);
    }

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

/// Checks if `Mutex::lock` is called in any of the branches.
pub struct ArmVisitor<'tcx, 'l> {
    pub arm_mutex: bool,
    pub arm_lock: bool,
    pub cx: &'tcx LateContext<'tcx, 'l>,
}

impl<'tcx, 'l> Visitor<'tcx> for ArmVisitor<'tcx, 'l> {
    type Map = Map<'tcx>;

    fn visit_expr(&mut self, expr: &'tcx Expr<'_>) {
        if let ExprKind::MethodCall(path, _span, args) = &expr.kind {
            if path.ident.to_string() == "lock" {
                self.arm_lock = true;
            }
            let ty = self.cx.tables.expr_ty(&args[0]);
            if match_type(self.cx, ty, &paths::MUTEX) {
                self.arm_mutex = true;
            }
        }
        visit::walk_expr(self, expr);
    }

    fn visit_arm(&mut self, arm: &'tcx Arm<'_>) {
        if let ExprKind::Block(ref block, _l) = arm.body.kind {
            for stmt in block.stmts {
                match stmt.kind {
                    StmtKind::Local(loc) => {
                        if let Some(expr) = loc.init {
                            self.visit_expr(expr)
                        }
                    },
                    StmtKind::Expr(expr) => self.visit_expr(expr),
                    StmtKind::Semi(expr) => self.visit_expr(expr),
                    // we don't care about `Item`
                    _ => {},
                }
            }
        };
        visit::walk_arm(self, arm);
    }

    fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
        NestedVisitorMap::None
    }
}
<commit_msg>change visitor name to OppVisitor<commit_after>use crate::utils::{match_type, paths, span_lint_and_help};
use rustc::hir::map::Map;
use rustc_hir::intravisit::{self as visit, NestedVisitorMap, Visitor};
use rustc_hir::{Arm, Expr, ExprKind, MatchSource, StmtKind};
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_lint_pass, declare_tool_lint};

declare_clippy_lint! {
    /// **What it does:** Checks for `Mutex::lock` calls in `if let` expression
    /// with lock calls in any of the else blocks.
    ///
    /// **Why is this bad?** The Mutex lock remains held for the whole
    /// `if let ... else` block and deadlocks.
    ///
    /// **Known problems:** None.
    ///
    /// **Example:**
    ///
    /// ```rust,ignore
    /// if let Ok(thing) = mutex.lock() {
    ///     do_thing();
    /// } else {
    ///     mutex.lock();
    /// }
    /// ```
    /// Should be written
    /// ```rust,ignore
    /// let locked = mutex.lock();
    /// if let Ok(thing) = locked {
    ///     do_thing(thing);
    /// } else {
    ///     use_locked(locked);
    /// }
    /// ```
    pub IF_LET_MUTEX,
    correctness,
    "locking a `Mutex` in an `if let` block can cause deadlocks"
}

declare_lint_pass!(IfLetMutex => [IF_LET_MUTEX]);

impl LateLintPass<'_, '_> for IfLetMutex {
    fn check_expr(&mut self, cx: &LateContext<'_, '_>, ex: &'_ Expr<'_>) {
        let mut arm_visit = ArmVisitor {
            arm_mutex: false,
            arm_lock: false,
            cx,
        };
        let mut op_visit = OppVisitor {
            op_mutex: false,
            op_lock: false,
            cx,
        };
        if let ExprKind::Match(
            ref op,
            ref arms,
            MatchSource::IfLetDesugar {
                contains_else_clause: true,
            },
        ) = ex.kind
        {
            op_visit.visit_expr(op);
            if op_visit.op_mutex && op_visit.op_lock {
                for arm in *arms {
                    arm_visit.visit_arm(arm);
                }

                if arm_visit.arm_mutex && arm_visit.arm_lock {
                    span_lint_and_help(
                        cx,
                        IF_LET_MUTEX,
                        ex.span,
                        "calling `Mutex::lock` inside the scope of another `Mutex::lock` causes a deadlock",
                        "move the lock call outside of the `if let ...` expression",
                    );
                }
            }
        }
    }
}

/// Checks if `Mutex::lock` is called in the `if let _ = expr.
pub struct OppVisitor<'tcx, 'l> {
    pub op_mutex: bool,
    pub op_lock: bool,
    pub cx: &'tcx LateContext<'tcx, 'l>,
}

impl<'tcx, 'l> Visitor<'tcx> for OppVisitor<'tcx, 'l> {
    type Map = Map<'tcx>;

    fn visit_expr(&mut self, expr: &'tcx Expr<'_>) {
        if let ExprKind::MethodCall(path, _span, args) = &expr.kind {
            if path.ident.to_string() == "lock" {
                self.op_lock = true;
            }
            let ty = self.cx.tables.expr_ty(&args[0]);
            if match_type(self.cx, ty, &paths::MUTEX) {
                self.op_mutex = true;
            }
        }
        visit::walk_expr(self, expr);
    }

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

/// Checks if `Mutex::lock` is called in any of the branches.
pub struct ArmVisitor<'tcx, 'l> {
    pub arm_mutex: bool,
    pub arm_lock: bool,
    pub cx: &'tcx LateContext<'tcx, 'l>,
}

impl<'tcx, 'l> Visitor<'tcx> for ArmVisitor<'tcx, 'l> {
    type Map = Map<'tcx>;

    fn visit_expr(&mut self, expr: &'tcx Expr<'_>) {
        if let ExprKind::MethodCall(path, _span, args) = &expr.kind {
            if path.ident.to_string() == "lock" {
                self.arm_lock = true;
            }
            let ty = self.cx.tables.expr_ty(&args[0]);
            if match_type(self.cx, ty, &paths::MUTEX) {
                self.arm_mutex = true;
            }
        }
        visit::walk_expr(self, expr);
    }

    fn visit_arm(&mut self, arm: &'tcx Arm<'_>) {
        if let ExprKind::Block(ref block, _l) = arm.body.kind {
            for stmt in block.stmts {
                match stmt.kind {
                    StmtKind::Local(loc) => {
                        if let Some(expr) = loc.init {
                            self.visit_expr(expr)
                        }
                    },
                    StmtKind::Expr(expr) => self.visit_expr(expr),
                    StmtKind::Semi(expr) => self.visit_expr(expr),
                    // we don't care about `Item`
                    _ => {},
                }
            }
        };
        visit::walk_arm(self, arm);
    }

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


<commit_before>use rustc::lint::*;
use syntax::ast::*;
use std::ops::Deref;
use syntax::ext::quote::rt::Span;

/// **What it does:** Checks for
///  - () being assigned to a variable
///  - () being passed to a function
///
/// **Why is this bad?** It is extremely unlikely that a user intended to
/// assign '()' to valiable. Instead,
/// Unit is what a block evaluates to when it returns nothing. This is
/// typically caused by a trailing
///   unintended semicolon.
///
/// **Known problems:** None.
///
/// **Example:**
/// * `let x = {"foo" ;}` when the user almost certainly intended `let x
/// ={"foo"}`

declare_lint! {
    pub UNIT_EXPR,
    Warn,
    "unintended assignment or use of a unit typed value"
}

#[derive(Copy, Clone)]
pub struct UnitExpr;

impl LintPass for UnitExpr {
    fn get_lints(&self) -> LintArray {
        lint_array!(UNIT_EXPR)
    }
}

impl EarlyLintPass for UnitExpr {
    fn check_expr(&mut self, cx: &EarlyContext, expr: &Expr) {
        if let ExprKind::Assign(ref _left, ref right) = expr.node {
            if let Some(span) = is_unit_expr(right) {
                cx.span_lint(UNIT_EXPR, span, "Consider removing the trailing semicolon");
            }
        }
        if let ExprKind::MethodCall(ref _left, ref args) = expr.node {
            for ref arg in args {
                if let Some(span) = is_unit_expr(arg) {
                    cx.span_lint(UNIT_EXPR, span, "Consider removing the trailing semicolon");
                }
            }
        }
        if let ExprKind::Call(_, ref args) = expr.node {
            for ref arg in args {
                if let Some(span) = is_unit_expr(arg) {
                    cx.span_lint(UNIT_EXPR, span, "Consider removing the trailing semicolon");
                }
            }
        }
    }

    fn check_stmt(&mut self, cx: &EarlyContext, stmt: &Stmt) {
        if let StmtKind::Local(ref local) = stmt.node {
            if local.pat.node == PatKind::Wild {
                return;
            }
            if let Some(ref expr) = local.init {
                if let Some(span) = is_unit_expr(expr) {
                    cx.span_lint(UNIT_EXPR, span, "Consider removing the trailing semicolon");
                }
            }
        }
    }
}
fn is_unit_expr(expr: &Expr) -> Option<Span> {
    match expr.node {
        ExprKind::Block(ref block) => if check_last_stmt_in_block(block) {
            return Some(block.stmts[block.stmts.len() - 1].span.clone());
        } else {
            return None;
        },
        ExprKind::If(_, ref then, ref else_) => {
            let check_then = check_last_stmt_in_block(then);
            if let Some(ref else_) = *else_ {
                let check_else = is_unit_expr(else_.deref());
                if let Some(ref expr_else) = check_else {
                    return Some(expr_else.clone());
                }
            }
            if check_then {
                return Some(expr.span.clone());
            } else {
                return None;
            }
        },
        ExprKind::Match(ref _pattern, ref arms) => {
            for ref arm in arms {
                if let Some(expr) = is_unit_expr(&arm.body) {
                    return Some(expr);
                }
            }
            return None;
        },
        _ => return None,
    }
}

fn check_last_stmt_in_block(block: &Block) -> bool {
    let ref final_stmt = &block.stmts[block.stmts.len() - 1];
    if let StmtKind::Expr(_) = final_stmt.node {
        return false;
    } else {
        return true;
    }
}
<commit_msg>Improve the lint message<commit_after>use rustc::lint::*;
use syntax::ast::*;
use std::ops::Deref;
use syntax::ext::quote::rt::Span;
use utils::span_note_and_lint;

/// **What it does:** Checks for
///  - () being assigned to a variable
///  - () being passed to a function
///
/// **Why is this bad?** It is extremely unlikely that a user intended to
/// assign '()' to valiable. Instead,
/// Unit is what a block evaluates to when it returns nothing. This is
/// typically caused by a trailing
///   unintended semicolon.
///
/// **Known problems:** None.
///
/// **Example:**
/// * `let x = {"foo" ;}` when the user almost certainly intended `let x
/// ={"foo"}`

declare_lint! {
    pub UNIT_EXPR,
    Warn,
    "unintended assignment or use of a unit typed value"
}

#[derive(Copy, Clone)]
pub struct UnitExpr;

impl LintPass for UnitExpr {
    fn get_lints(&self) -> LintArray {
        lint_array!(UNIT_EXPR)
    }
}

impl EarlyLintPass for UnitExpr {
    fn check_expr(&mut self, cx: &EarlyContext, expr: &Expr) {
        if let ExprKind::Assign(ref _left, ref right) = expr.node {
            if let Some(span) = is_unit_expr(right) {
                span_note_and_lint(cx, UNIT_EXPR, expr.span,"This expression assigns the Unit type ()",span,"Consider removing the trailing semicolon");
            }
        }
        if let ExprKind::MethodCall(ref _left, ref args) = expr.node {
            for ref arg in args {
                if let Some(span) = is_unit_expr(arg) {
                span_note_and_lint(cx, UNIT_EXPR, expr.span,"This expression assigns the Unit type ()",span,"Consider removing the trailing semicolon");
                }
            }
        }
        if let ExprKind::Call(_, ref args) = expr.node {
            for ref arg in args {
                if let Some(span) = is_unit_expr(arg) {
                span_note_and_lint(cx, UNIT_EXPR, expr.span,"This expression assigns the Unit type ()",span,"Consider removing the trailing semicolon");
                }
            }
        }
    }

    fn check_stmt(&mut self, cx: &EarlyContext, stmt: &Stmt) {
        if let StmtKind::Local(ref local) = stmt.node {
            if local.pat.node == PatKind::Wild {
                return;
            }
            if let Some(ref expr) = local.init {
                if let Some(span) = is_unit_expr(expr) {
                span_note_and_lint(cx, UNIT_EXPR, expr.span,"This expression assigns the Unit type ()",span,"Consider removing the trailing semicolon");
                }
            }
        }
    }
}
fn is_unit_expr(expr: &Expr) -> Option<Span> {
    match expr.node {
        ExprKind::Block(ref block) => if check_last_stmt_in_block(block) {
            return Some(block.stmts[block.stmts.len() - 1].span.clone());
        } else {
            return None;
        },
        ExprKind::If(_, ref then, ref else_) => {
            let check_then = check_last_stmt_in_block(then);
            if let Some(ref else_) = *else_ {
                let check_else = is_unit_expr(else_.deref());
                if let Some(ref expr_else) = check_else {
                    return Some(expr_else.clone());
                }
            }
            if check_then {
                return Some(expr.span.clone());
            } else {
                return None;
            }
        },
        ExprKind::Match(ref _pattern, ref arms) => {
            for ref arm in arms {
                if let Some(expr) = is_unit_expr(&arm.body) {
                    return Some(expr);
                }
            }
            return None;
        },
        _ => return None,
    }
}

fn check_last_stmt_in_block(block: &Block) -> bool {
    let ref final_stmt = &block.stmts[block.stmts.len() - 1];
    if let StmtKind::Expr(_) = final_stmt.node {
        return false;
    } else {
        return true;
    }
}
<|endoftext|>


<commit_before>//! Data structures and methods for parsing and applying Knuth-Liang
//! hyphenation patterns and exceptions.

#![cfg_attr(feature = "serde_derive", feature(proc_macro))]

#[cfg(feature = "serde_derive")]
#[macro_use]
extern crate serde_derive;

extern crate serde;
extern crate fnv;

use std::borrow::Cow;
use std::cmp::{max};
use std::collections::hash_map::{HashMap, Entry};
use std::hash::BuildHasherDefault;
use std::iter::{once};
use std::mem;

use fnv::FnvHasher;

/// A pair representing a Knuth-Liang hyphenation pattern. It comprises
/// alphabetical characters for subword matching and the score of each
/// hyphenation point.
pub type KLPair = (String, Vec<u8>);

#[cfg(feature = "serde_derive")]
include!("serde_types.in.rs");

#[cfg(feature = "serde_codegen")]
include!(concat!(env!("OUT_DIR"), "/serde_types.rs"));

pub trait KLPTrie<'a> {
    type Score;

    fn new() -> Self;

    fn insert(&mut self, KLPair) -> Option<Vec<u8>>;

    fn score(&'a self, &str) -> Self::Score;

    fn is_empty(&self) -> bool;

}

impl<'a> KLPTrie<'a> for Patterns {
    type Score = Vec<u8>;

    /// Creates an empty `Patterns` trie.
    fn new() -> Patterns {
        let fnv = BuildHasherDefault::<FnvHasher>::default();

        Patterns {
            tally: None,
            descendants: HashMap::with_hasher(fnv)
        }
    }

    /// Inserts a Knuth-Liang hyphenation pair into the trie.
    ///
    /// If the pattern already exists, the old tally is returned; if not, `None` is.
    fn insert(&mut self, (p, tally): KLPair) -> Option<Vec<u8>> {
        let node = p.chars().fold(self, |t, c| {
            match t.descendants.entry(c) {
                Entry::Vacant(e) => e.insert(Patterns::new()),
                Entry::Occupied(e) => e.into_mut()
            }
        });

        let mut retv = None;
        match node.tally {
            Some(ref mut old) => retv = Some(mem::replace(old, tally)),
            None => node.tally = Some(tally)
        }

        retv
    }

    /// Assigns a score to each potential hyphenation point.
    ///
    /// All patterns matching a substring of `word` are compounded, and for
    /// each hyphenation point, the highest competing value is selected.
    fn score(&self, word: &str) -> Self::Score {
        let w = match word.chars().any(|c| c.is_uppercase()) {
            true => Cow::Owned(word.to_lowercase()),
            false => Cow::Borrowed(word)
        };
        let cs = once('.').chain(w.chars()).chain(once('.'));
        let match_length = cs.clone().count();

        if match_length <= 3 {
            return vec![];
        }

        let hyphenable_length = match_length - 2;
        let mut points: Vec<u8> = vec![0; hyphenable_length - 1];

        for i in 0..match_length {
            let mut m = &self.descendants;
            for c in cs.clone().skip(i) {
                match m.get(&c) {
                    Some(&Patterns { tally: Some(ref t), descendants: ref m1 }) => {
                        m = m1;
                        for (j, &p) in t.iter().enumerate() {
                            let k = i + j;
                            if k > 1 && k <= hyphenable_length {
                                let p1 = points[k - 2];
                                points[k - 2] = max(p, p1)
                            }
                        }
                    },
                    Some(patterns) => m = &patterns.descendants,
                    _ => break
                }
            }
        }

        points
    }

    fn is_empty(&self) -> bool {
        self.descendants.is_empty()
    }
}

impl<'a> KLPTrie<'a> for Exceptions {
    type Score = Option<&'a Vec<u8>>;

    /// Creates an empty `Exceptions` map.
    fn new() -> Exceptions {
        Exceptions(HashMap::new())
    }

    /// Inserts a Knuth-Liang exception pair into the map.
    ///
    /// If the pattern already exists, the old score is returned; if not, `None` is.
    fn insert(&mut self, klpair: KLPair) -> Option<Vec<u8>> {
        let (p, score) = klpair;
        let Exceptions(ref mut m) = *self;

        m.insert(p, score)
    }

    /// Retrieves the score for each hyphenation point of `word`.
    fn score(&'a self, word: &str) -> Self::Score {
        let Exceptions(ref m) = *self;
        let w = word.to_lowercase();

        m.get(&w)
    }

    fn is_empty(&self) -> bool {
        self.0.is_empty()
    }
}
<commit_msg>Avoid allocations when matching lowercase exceptions<commit_after>//! Data structures and methods for parsing and applying Knuth-Liang
//! hyphenation patterns and exceptions.

#![cfg_attr(feature = "serde_derive", feature(proc_macro))]

#[cfg(feature = "serde_derive")]
#[macro_use]
extern crate serde_derive;

extern crate serde;
extern crate fnv;

use std::borrow::Cow;
use std::cmp::{max};
use std::collections::hash_map::{HashMap, Entry};
use std::hash::BuildHasherDefault;
use std::iter::{once};
use std::mem;

use fnv::FnvHasher;

/// A pair representing a Knuth-Liang hyphenation pattern. It comprises
/// alphabetical characters for subword matching and the score of each
/// hyphenation point.
pub type KLPair = (String, Vec<u8>);

#[cfg(feature = "serde_derive")]
include!("serde_types.in.rs");

#[cfg(feature = "serde_codegen")]
include!(concat!(env!("OUT_DIR"), "/serde_types.rs"));

pub trait KLPTrie<'a> {
    type Score;

    fn new() -> Self;

    fn insert(&mut self, KLPair) -> Option<Vec<u8>>;

    fn score(&'a self, &str) -> Self::Score;

    fn is_empty(&self) -> bool;

}

impl<'a> KLPTrie<'a> for Patterns {
    type Score = Vec<u8>;

    /// Creates an empty `Patterns` trie.
    fn new() -> Patterns {
        let fnv = BuildHasherDefault::<FnvHasher>::default();

        Patterns {
            tally: None,
            descendants: HashMap::with_hasher(fnv)
        }
    }

    /// Inserts a Knuth-Liang hyphenation pair into the trie.
    ///
    /// If the pattern already exists, the old tally is returned; if not, `None` is.
    fn insert(&mut self, (p, tally): KLPair) -> Option<Vec<u8>> {
        let node = p.chars().fold(self, |t, c| {
            match t.descendants.entry(c) {
                Entry::Vacant(e) => e.insert(Patterns::new()),
                Entry::Occupied(e) => e.into_mut()
            }
        });

        let mut retv = None;
        match node.tally {
            Some(ref mut old) => retv = Some(mem::replace(old, tally)),
            None => node.tally = Some(tally)
        }

        retv
    }

    /// Assigns a score to each potential hyphenation point.
    ///
    /// All patterns matching a substring of `word` are compounded, and for
    /// each hyphenation point, the highest competing value is selected.
    fn score(&self, word: &str) -> Self::Score {
        let w = match word.chars().any(|c| c.is_uppercase()) {
            true => Cow::Owned(word.to_lowercase()),
            false => Cow::Borrowed(word)
        };
        let cs = once('.').chain(w.chars()).chain(once('.'));
        let match_length = cs.clone().count();

        if match_length <= 3 {
            return vec![];
        }

        let hyphenable_length = match_length - 2;
        let mut points: Vec<u8> = vec![0; hyphenable_length - 1];

        for i in 0..match_length {
            let mut m = &self.descendants;
            for c in cs.clone().skip(i) {
                match m.get(&c) {
                    Some(&Patterns { tally: Some(ref t), descendants: ref m1 }) => {
                        m = m1;
                        for (j, &p) in t.iter().enumerate() {
                            let k = i + j;
                            if k > 1 && k <= hyphenable_length {
                                let p1 = points[k - 2];
                                points[k - 2] = max(p, p1)
                            }
                        }
                    },
                    Some(patterns) => m = &patterns.descendants,
                    _ => break
                }
            }
        }

        points
    }

    fn is_empty(&self) -> bool {
        self.descendants.is_empty()
    }
}

impl<'a> KLPTrie<'a> for Exceptions {
    type Score = Option<&'a Vec<u8>>;

    /// Creates an empty `Exceptions` map.
    fn new() -> Exceptions {
        Exceptions(HashMap::new())
    }

    /// Inserts a Knuth-Liang exception pair into the map.
    ///
    /// If the pattern already exists, the old score is returned; if not, `None` is.
    fn insert(&mut self, (pattern, score): KLPair) -> Option<Vec<u8>> {
        let Exceptions(ref mut m) = *self;

        m.insert(pattern, score)
    }

    /// Retrieves the score for each hyphenation point of `word`.
    fn score(&'a self, word: &str) -> Self::Score {
        let Exceptions(ref m) = *self;
        let w = match word.chars().any(|c| c.is_uppercase()) {
            true => Cow::Owned(word.to_lowercase()),
            false => Cow::Borrowed(word)
        };

        m.get(w.as_ref())
    }

    fn is_empty(&self) -> bool {
        self.0.is_empty()
    }
}
<|endoftext|>