<commit_before>use std::convert::TryFrom;

use rustc_middle::mir::interpret::{InterpResult, Pointer, PointerArithmetic, Scalar};
use rustc_middle::ty::{self, Instance, Ty};
use rustc_target::abi::{Align, LayoutOf, Size};

use super::util::ensure_monomorphic_enough;
use super::{FnVal, InterpCx, Machine, MemoryKind};

impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
    /// Creates a dynamic vtable for the given type and vtable origin. This is used only for
    /// objects.
    ///
    /// The `trait_ref` encodes the erased self type. Hence, if we are
    /// making an object `Foo<Trait>` from a value of type `Foo<T>`, then
    /// `trait_ref` would map `T: Trait`.
    pub fn get_vtable(
        &mut self,
        ty: Ty<'tcx>,
        poly_trait_ref: Option<ty::PolyExistentialTraitRef<'tcx>>,
    ) -> InterpResult<'tcx, Pointer<M::PointerTag>> {
        trace!("get_vtable(trait_ref={:?})", poly_trait_ref);

        let (ty, poly_trait_ref) = self.tcx.erase_regions((ty, poly_trait_ref));

        // All vtables must be monomorphic, bail out otherwise.
        ensure_monomorphic_enough(*self.tcx, ty)?;
        ensure_monomorphic_enough(*self.tcx, poly_trait_ref)?;

        if let Some(&vtable) = self.vtables.get(&(ty, poly_trait_ref)) {
            // This means we guarantee that there are no duplicate vtables, we will
            // always use the same vtable for the same (Type, Trait) combination.
            // That's not what happens in rustc, but emulating per-crate deduplication
            // does not sound like it actually makes anything any better.
            return Ok(vtable);
        }

        let methods = if let Some(poly_trait_ref) = poly_trait_ref {
            let trait_ref = poly_trait_ref.with_self_ty(*self.tcx, ty);
            let trait_ref = self.tcx.erase_regions(trait_ref);

            self.tcx.vtable_methods(trait_ref)
        } else {
            &[]
        };

        let layout = self.layout_of(ty)?;
        assert!(!layout.is_unsized(), "can't create a vtable for an unsized type");
        let size = layout.size.bytes();
        let align = layout.align.abi.bytes();

        let tcx = *self.tcx;
        let ptr_size = self.pointer_size();
        let ptr_align = tcx.data_layout.pointer_align.abi;
        // /////////////////////////////////////////////////////////////////////////////////////////
        // If you touch this code, be sure to also make the corresponding changes to
        // `get_vtable` in `rust_codegen_llvm/meth.rs`.
        // /////////////////////////////////////////////////////////////////////////////////////////
        let vtable_size = ptr_size * u64::try_from(methods.len()).unwrap().checked_add(3).unwrap();
        let vtable = self.memory.allocate(
            vtable_size,
            ptr_align,
            MemoryKind::Vtable,
        );

        let drop = Instance::resolve_drop_in_place(tcx, ty);
        let drop = self.memory.create_fn_alloc(FnVal::Instance(drop));

        // No need to do any alignment checks on the memory accesses below, because we know the
        // allocation is correctly aligned as we created it above. Also we're only offsetting by
        // multiples of `ptr_align`, which means that it will stay aligned to `ptr_align`.
        let vtable_alloc = self.memory.get_raw_mut(vtable.alloc_id)?;
        vtable_alloc.write_ptr_sized(&tcx, vtable, drop.into())?;

        let size_ptr = vtable.offset(ptr_size, &tcx)?;
        vtable_alloc.write_ptr_sized(&tcx, size_ptr, Scalar::from_uint(size, ptr_size).into())?;
        let align_ptr = vtable.offset(ptr_size * 2, &tcx)?;
        vtable_alloc.write_ptr_sized(&tcx, align_ptr, Scalar::from_uint(align, ptr_size).into())?;

        for (i, method) in methods.iter().enumerate() {
            if let Some((def_id, substs)) = *method {
                // resolve for vtable: insert shims where needed
                let instance =
                    ty::Instance::resolve_for_vtable(tcx, self.param_env, def_id, substs)
                        .ok_or_else(|| err_inval!(TooGeneric))?;
                let fn_ptr = self.memory.create_fn_alloc(FnVal::Instance(instance));
                // We cannot use `vtable_allic` as we are creating fn ptrs in this loop.
                let method_ptr = vtable.offset(ptr_size * (3 + i as u64), &tcx)?;
                self.memory.get_raw_mut(vtable.alloc_id)?.write_ptr_sized(
                    &tcx,
                    method_ptr,
                    fn_ptr.into(),
                )?;
            }
        }

        M::after_static_mem_initialized(self, vtable, vtable_size)?;

        self.memory.mark_immutable(vtable.alloc_id)?;
        assert!(self.vtables.insert((ty, poly_trait_ref), vtable).is_none());

        Ok(vtable)
    }

    /// Resolves the function at the specified slot in the provided
    /// vtable. An index of '0' corresponds to the first method
    /// declared in the trait of the provided vtable.
    pub fn get_vtable_slot(
        &self,
        vtable: Scalar<M::PointerTag>,
        idx: u64,
    ) -> InterpResult<'tcx, FnVal<'tcx, M::ExtraFnVal>> {
        let ptr_size = self.pointer_size();
        // Skip over the 'drop_ptr', 'size', and 'align' fields.
        let vtable_slot = vtable.ptr_offset(ptr_size * idx.checked_add(3).unwrap(), self)?;
        let vtable_slot = self
            .memory
            .check_ptr_access(vtable_slot, ptr_size, self.tcx.data_layout.pointer_align.abi)?
            .expect("cannot be a ZST");
        let fn_ptr = self
            .memory
            .get_raw(vtable_slot.alloc_id)?
            .read_ptr_sized(self, vtable_slot)?
            .check_init()?;
        Ok(self.memory.get_fn(fn_ptr)?)
    }

    /// Returns the drop fn instance as well as the actual dynamic type.
    pub fn read_drop_type_from_vtable(
        &self,
        vtable: Scalar<M::PointerTag>,
    ) -> InterpResult<'tcx, (ty::Instance<'tcx>, Ty<'tcx>)> {
        // We don't care about the pointee type; we just want a pointer.
        let vtable = self
            .memory
            .check_ptr_access(
                vtable,
                self.tcx.data_layout.pointer_size,
                self.tcx.data_layout.pointer_align.abi,
            )?
            .expect("cannot be a ZST");
        let drop_fn =
            self.memory.get_raw(vtable.alloc_id)?.read_ptr_sized(self, vtable)?.check_init()?;
        // We *need* an instance here, no other kind of function value, to be able
        // to determine the type.
        let drop_instance = self.memory.get_fn(drop_fn)?.as_instance()?;
        trace!("Found drop fn: {:?}", drop_instance);
        let fn_sig = drop_instance.ty(*self.tcx, self.param_env).fn_sig(*self.tcx);
        let fn_sig = self.tcx.normalize_erasing_late_bound_regions(self.param_env, fn_sig);
        // The drop function takes `*mut T` where `T` is the type being dropped, so get that.
        let args = fn_sig.inputs();
        if args.len() != 1 {
            throw_ub!(InvalidDropFn(fn_sig));
        }
        let ty = args[0].builtin_deref(true).ok_or_else(|| err_ub!(InvalidDropFn(fn_sig)))?.ty;
        Ok((drop_instance, ty))
    }

    pub fn read_size_and_align_from_vtable(
        &self,
        vtable: Scalar<M::PointerTag>,
    ) -> InterpResult<'tcx, (Size, Align)> {
        let pointer_size = self.pointer_size();
        // We check for `size = 3 * ptr_size`, which covers the drop fn (unused here),
        // the size, and the align (which we read below).
        let vtable = self
            .memory
            .check_ptr_access(vtable, 3 * pointer_size, self.tcx.data_layout.pointer_align.abi)?
            .expect("cannot be a ZST");
        let alloc = self.memory.get_raw(vtable.alloc_id)?;
        let size = alloc.read_ptr_sized(self, vtable.offset(pointer_size, self)?)?.check_init()?;
        let size = u64::try_from(self.force_bits(size, pointer_size)?).unwrap();
        let align =
            alloc.read_ptr_sized(self, vtable.offset(pointer_size * 2, self)?)?.check_init()?;
        let align = u64::try_from(self.force_bits(align, pointer_size)?).unwrap();

        if size >= self.tcx.data_layout.obj_size_bound() {
            throw_ub_format!(
                "invalid vtable: \
                size is bigger than largest supported object"
            );
        }
        Ok((Size::from_bytes(size), Align::from_bytes(align).unwrap()))
    }
}
<commit_msg>Rustfmt<commit_after>use std::convert::TryFrom;

use rustc_middle::mir::interpret::{InterpResult, Pointer, PointerArithmetic, Scalar};
use rustc_middle::ty::{self, Instance, Ty};
use rustc_target::abi::{Align, LayoutOf, Size};

use super::util::ensure_monomorphic_enough;
use super::{FnVal, InterpCx, Machine, MemoryKind};

impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
    /// Creates a dynamic vtable for the given type and vtable origin. This is used only for
    /// objects.
    ///
    /// The `trait_ref` encodes the erased self type. Hence, if we are
    /// making an object `Foo<Trait>` from a value of type `Foo<T>`, then
    /// `trait_ref` would map `T: Trait`.
    pub fn get_vtable(
        &mut self,
        ty: Ty<'tcx>,
        poly_trait_ref: Option<ty::PolyExistentialTraitRef<'tcx>>,
    ) -> InterpResult<'tcx, Pointer<M::PointerTag>> {
        trace!("get_vtable(trait_ref={:?})", poly_trait_ref);

        let (ty, poly_trait_ref) = self.tcx.erase_regions((ty, poly_trait_ref));

        // All vtables must be monomorphic, bail out otherwise.
        ensure_monomorphic_enough(*self.tcx, ty)?;
        ensure_monomorphic_enough(*self.tcx, poly_trait_ref)?;

        if let Some(&vtable) = self.vtables.get(&(ty, poly_trait_ref)) {
            // This means we guarantee that there are no duplicate vtables, we will
            // always use the same vtable for the same (Type, Trait) combination.
            // That's not what happens in rustc, but emulating per-crate deduplication
            // does not sound like it actually makes anything any better.
            return Ok(vtable);
        }

        let methods = if let Some(poly_trait_ref) = poly_trait_ref {
            let trait_ref = poly_trait_ref.with_self_ty(*self.tcx, ty);
            let trait_ref = self.tcx.erase_regions(trait_ref);

            self.tcx.vtable_methods(trait_ref)
        } else {
            &[]
        };

        let layout = self.layout_of(ty)?;
        assert!(!layout.is_unsized(), "can't create a vtable for an unsized type");
        let size = layout.size.bytes();
        let align = layout.align.abi.bytes();

        let tcx = *self.tcx;
        let ptr_size = self.pointer_size();
        let ptr_align = tcx.data_layout.pointer_align.abi;
        // /////////////////////////////////////////////////////////////////////////////////////////
        // If you touch this code, be sure to also make the corresponding changes to
        // `get_vtable` in `rust_codegen_llvm/meth.rs`.
        // /////////////////////////////////////////////////////////////////////////////////////////
        let vtable_size = ptr_size * u64::try_from(methods.len()).unwrap().checked_add(3).unwrap();
        let vtable = self.memory.allocate(vtable_size, ptr_align, MemoryKind::Vtable);

        let drop = Instance::resolve_drop_in_place(tcx, ty);
        let drop = self.memory.create_fn_alloc(FnVal::Instance(drop));

        // No need to do any alignment checks on the memory accesses below, because we know the
        // allocation is correctly aligned as we created it above. Also we're only offsetting by
        // multiples of `ptr_align`, which means that it will stay aligned to `ptr_align`.
        let vtable_alloc = self.memory.get_raw_mut(vtable.alloc_id)?;
        vtable_alloc.write_ptr_sized(&tcx, vtable, drop.into())?;

        let size_ptr = vtable.offset(ptr_size, &tcx)?;
        vtable_alloc.write_ptr_sized(&tcx, size_ptr, Scalar::from_uint(size, ptr_size).into())?;
        let align_ptr = vtable.offset(ptr_size * 2, &tcx)?;
        vtable_alloc.write_ptr_sized(&tcx, align_ptr, Scalar::from_uint(align, ptr_size).into())?;

        for (i, method) in methods.iter().enumerate() {
            if let Some((def_id, substs)) = *method {
                // resolve for vtable: insert shims where needed
                let instance =
                    ty::Instance::resolve_for_vtable(tcx, self.param_env, def_id, substs)
                        .ok_or_else(|| err_inval!(TooGeneric))?;
                let fn_ptr = self.memory.create_fn_alloc(FnVal::Instance(instance));
                // We cannot use `vtable_allic` as we are creating fn ptrs in this loop.
                let method_ptr = vtable.offset(ptr_size * (3 + i as u64), &tcx)?;
                self.memory.get_raw_mut(vtable.alloc_id)?.write_ptr_sized(
                    &tcx,
                    method_ptr,
                    fn_ptr.into(),
                )?;
            }
        }

        M::after_static_mem_initialized(self, vtable, vtable_size)?;

        self.memory.mark_immutable(vtable.alloc_id)?;
        assert!(self.vtables.insert((ty, poly_trait_ref), vtable).is_none());

        Ok(vtable)
    }

    /// Resolves the function at the specified slot in the provided
    /// vtable. An index of '0' corresponds to the first method
    /// declared in the trait of the provided vtable.
    pub fn get_vtable_slot(
        &self,
        vtable: Scalar<M::PointerTag>,
        idx: u64,
    ) -> InterpResult<'tcx, FnVal<'tcx, M::ExtraFnVal>> {
        let ptr_size = self.pointer_size();
        // Skip over the 'drop_ptr', 'size', and 'align' fields.
        let vtable_slot = vtable.ptr_offset(ptr_size * idx.checked_add(3).unwrap(), self)?;
        let vtable_slot = self
            .memory
            .check_ptr_access(vtable_slot, ptr_size, self.tcx.data_layout.pointer_align.abi)?
            .expect("cannot be a ZST");
        let fn_ptr = self
            .memory
            .get_raw(vtable_slot.alloc_id)?
            .read_ptr_sized(self, vtable_slot)?
            .check_init()?;
        Ok(self.memory.get_fn(fn_ptr)?)
    }

    /// Returns the drop fn instance as well as the actual dynamic type.
    pub fn read_drop_type_from_vtable(
        &self,
        vtable: Scalar<M::PointerTag>,
    ) -> InterpResult<'tcx, (ty::Instance<'tcx>, Ty<'tcx>)> {
        // We don't care about the pointee type; we just want a pointer.
        let vtable = self
            .memory
            .check_ptr_access(
                vtable,
                self.tcx.data_layout.pointer_size,
                self.tcx.data_layout.pointer_align.abi,
            )?
            .expect("cannot be a ZST");
        let drop_fn =
            self.memory.get_raw(vtable.alloc_id)?.read_ptr_sized(self, vtable)?.check_init()?;
        // We *need* an instance here, no other kind of function value, to be able
        // to determine the type.
        let drop_instance = self.memory.get_fn(drop_fn)?.as_instance()?;
        trace!("Found drop fn: {:?}", drop_instance);
        let fn_sig = drop_instance.ty(*self.tcx, self.param_env).fn_sig(*self.tcx);
        let fn_sig = self.tcx.normalize_erasing_late_bound_regions(self.param_env, fn_sig);
        // The drop function takes `*mut T` where `T` is the type being dropped, so get that.
        let args = fn_sig.inputs();
        if args.len() != 1 {
            throw_ub!(InvalidDropFn(fn_sig));
        }
        let ty = args[0].builtin_deref(true).ok_or_else(|| err_ub!(InvalidDropFn(fn_sig)))?.ty;
        Ok((drop_instance, ty))
    }

    pub fn read_size_and_align_from_vtable(
        &self,
        vtable: Scalar<M::PointerTag>,
    ) -> InterpResult<'tcx, (Size, Align)> {
        let pointer_size = self.pointer_size();
        // We check for `size = 3 * ptr_size`, which covers the drop fn (unused here),
        // the size, and the align (which we read below).
        let vtable = self
            .memory
            .check_ptr_access(vtable, 3 * pointer_size, self.tcx.data_layout.pointer_align.abi)?
            .expect("cannot be a ZST");
        let alloc = self.memory.get_raw(vtable.alloc_id)?;
        let size = alloc.read_ptr_sized(self, vtable.offset(pointer_size, self)?)?.check_init()?;
        let size = u64::try_from(self.force_bits(size, pointer_size)?).unwrap();
        let align =
            alloc.read_ptr_sized(self, vtable.offset(pointer_size * 2, self)?)?.check_init()?;
        let align = u64::try_from(self.force_bits(align, pointer_size)?).unwrap();

        if size >= self.tcx.data_layout.obj_size_bound() {
            throw_ub_format!(
                "invalid vtable: \
                size is bigger than largest supported object"
            );
        }
        Ok((Size::from_bytes(size), Align::from_bytes(align).unwrap()))
    }
}
<|endoftext|>


<commit_before>// Copyright (c) Aptos
// SPDX-License-Identifier: Apache-2.0

//! This module provides a generic set of traits for dealing with cryptographic primitives.
//!
//! For examples on how to use these traits, see the implementations of the [`ed25519`] or
//! [`bls12381`] modules.

use crate::hash::{CryptoHash, CryptoHasher};
use anyhow::Result;
use core::convert::{From, TryFrom};
use rand::{rngs::StdRng, CryptoRng, RngCore, SeedableRng};
use serde::{de::DeserializeOwned, Serialize};
use std::{fmt::Debug, hash::Hash};
use thiserror::Error;

/// An error type for key and signature validation issues, see [`ValidCryptoMaterial`][ValidCryptoMaterial].
///
/// This enum reflects there are two interesting causes of validation
/// failure for the ingestion of key or signature material: deserialization errors
/// (often, due to mangled material or curve equation failure for ECC) and
/// validation errors (material recognizable but unacceptable for use,
/// e.g. unsafe).
#[derive(Clone, Debug, PartialEq, Eq, Error)]
#[error("{:?}", self)]
pub enum CryptoMaterialError {
    /// Struct to be signed does not serialize correctly.
    SerializationError,
    /// Key or signature material does not deserialize correctly.
    DeserializationError,
    /// Key or signature material deserializes, but is otherwise not valid.
    ValidationError,
    /// Key, threshold or signature material does not have the expected size.
    WrongLengthError,
    /// Part of the signature or key is not canonical resulting to malleability issues.
    CanonicalRepresentationError,
    /// A curve point (i.e., a public key) lies on a small group.
    SmallSubgroupError,
    /// A curve point (i.e., a public key) does not satisfy the curve equation.
    PointNotOnCurveError,
    /// BitVec errors in accountable multi-sig schemes.
    BitVecError(String),
}

/// The serialized length of the data that enables macro derived serialization and deserialization.
pub trait Length {
    /// The serialized length of the data
    fn length(&self) -> usize;
}

/// Key or more generally crypto material with a notion of byte validation.
///
/// A type family for material that knows how to serialize and
/// deserialize, as well as validate byte-encoded material. The
/// validation must be implemented as a [`TryFrom`][TryFrom] which
/// classifies its failures against the above
/// [`CryptoMaterialError`][CryptoMaterialError].
///
/// This provides an implementation for a validation that relies on a
/// round-trip to bytes and corresponding [`TryFrom`][TryFrom].
pub trait ValidCryptoMaterial:
    // The for<'a> exactly matches the assumption "deserializable from any lifetime".
    for<'a> TryFrom<&'a [u8], Error = CryptoMaterialError> + Serialize + DeserializeOwned
{
    /// Convert the valid crypto material to bytes.
    fn to_bytes(&self) -> Vec<u8>;
}

/// An extension to to/from Strings for [`ValidCryptoMaterial`][ValidCryptoMaterial].
///
/// Relies on [`hex`][::hex] for string encoding / decoding.
/// No required fields, provides a default implementation.
pub trait ValidCryptoMaterialStringExt: ValidCryptoMaterial {
    /// When trying to convert from bytes, we simply decode the string into
    /// bytes before checking if we can convert.
    fn from_encoded_string(encoded_str: &str) -> std::result::Result<Self, CryptoMaterialError> {
        let bytes_out = ::hex::decode(encoded_str);
        // We defer to `try_from` to make sure we only produce valid crypto materials.
        bytes_out
            // We reinterpret a failure to serialize: key is mangled someway.
            .or(Err(CryptoMaterialError::DeserializationError))
            .and_then(|ref bytes| Self::try_from(bytes))
    }
    /// A function to encode into hex-string after serializing.
    fn to_encoded_string(&self) -> Result<String> {
        Ok(::hex::encode(&self.to_bytes()))
    }
}

// There's nothing required in this extension, so let's just derive it
// for anybody that has a ValidCryptoMaterial.
impl<T: ValidCryptoMaterial> ValidCryptoMaterialStringExt for T {}

/// A type family for key material that should remain secret and has an
/// associated type of the [`PublicKey`][PublicKey] family.
pub trait PrivateKey: Sized {
    /// We require public / private types to be coupled, i.e. their
    /// associated type is each other.
    type PublicKeyMaterial: PublicKey<PrivateKeyMaterial = Self>;

    /// Returns the associated public key
    fn public_key(&self) -> Self::PublicKeyMaterial {
        self.into()
    }
}

/// A type family of valid keys that know how to sign.
///
/// This trait has a requirement on a `pub(crate)` marker trait meant to
/// specifically limit its implementations to the present crate.
///
/// A trait for a [`ValidCryptoMaterial`][ValidCryptoMaterial] which knows how to sign a
/// message, and return an associated `Signature` type.
pub trait SigningKey:
    PrivateKey<PublicKeyMaterial = <Self as SigningKey>::VerifyingKeyMaterial>
    + ValidCryptoMaterial
    + private::Sealed
{
    /// The associated verifying key type for this signing key.
    type VerifyingKeyMaterial: VerifyingKey<SigningKeyMaterial = Self>;
    /// The associated signature type for this signing key.
    type SignatureMaterial: Signature<SigningKeyMaterial = Self>;

    /// Signs an object that has an distinct domain-separation hasher and
    /// that we know how to serialize. There is no pre-hashing into a
    /// `HashValue` to be done by the caller.
    ///
    /// Note: this assumes serialization is infallible. See crates::bcs::ser
    /// for a discussion of this assumption.
    fn sign<T: CryptoHash + Serialize>(&self, message: &T) -> Self::SignatureMaterial;

    /// Signs a non-hash input message. For testing only.
    #[cfg(any(test, feature = "fuzzing"))]
    fn sign_arbitrary_message(&self, message: &[u8]) -> Self::SignatureMaterial;

    /// Returns the associated verifying key
    fn verifying_key(&self) -> Self::VerifyingKeyMaterial {
        self.public_key()
    }
}

/// Returns the signing message for the given message.
/// It is used by `SigningKey#sign` function.
pub fn signing_message<T: CryptoHash + Serialize>(message: &T) -> Vec<u8> {
    let mut bytes = <T::Hasher as CryptoHasher>::seed().to_vec();
    bcs::serialize_into(&mut bytes, &message)
        .map_err(|_| CryptoMaterialError::SerializationError)
        .expect("Serialization of signable material should not fail.");
    bytes
}

/// A type for key material that can be publicly shared, and in asymmetric
/// fashion, can be obtained from a [`PrivateKey`][PrivateKey]
/// reference.
/// This convertibility requirement ensures the existence of a
/// deterministic, canonical public key construction from a private key.
pub trait PublicKey: Sized + Clone + Eq + Hash +
    // This unsightly turbofish type parameter is the precise constraint
    // needed to require that there exists an
    //
    // ```
    // impl From<&MyPrivateKeyMaterial> for MyPublicKeyMaterial
    // ```
    //
    // declaration, for any `MyPrivateKeyMaterial`, `MyPublicKeyMaterial`
    // on which we register (respectively) `PublicKey` and `PrivateKey`
    // implementations.
    for<'a> From<&'a <Self as PublicKey>::PrivateKeyMaterial> {
    /// We require public / private types to be coupled, i.e. their
    /// associated type is each other.
    type PrivateKeyMaterial: PrivateKey<PublicKeyMaterial = Self>;
}

/// A type family of public keys that are used for signing.
///
/// This trait has a requirement on a `pub(crate)` marker trait meant to
/// specifically limit its implementations to the present crate.
///
/// It is linked to a type of the Signature family, which carries the
/// verification implementation.
pub trait VerifyingKey:
    PublicKey<PrivateKeyMaterial = <Self as VerifyingKey>::SigningKeyMaterial>
    + ValidCryptoMaterial
    + private::Sealed
{
    /// The associated signing key type for this verifying key.
    type SigningKeyMaterial: SigningKey<VerifyingKeyMaterial = Self>;
    /// The associated signature type for this verifying key.
    type SignatureMaterial: Signature<VerifyingKeyMaterial = Self>;

    /// We provide the striaghtfoward implementation which dispatches to the signature.
    fn verify_struct_signature<T: CryptoHash + Serialize>(
        &self,
        message: &T,
        signature: &Self::SignatureMaterial,
    ) -> Result<()> {
        signature.verify(message, self)
    }

    /// We provide the implementation which dispatches to the signature.
    fn batch_verify<T: CryptoHash + Serialize>(
        message: &T,
        keys_and_signatures: Vec<(Self, Self::SignatureMaterial)>,
    ) -> Result<()> {
        Self::SignatureMaterial::batch_verify(message, keys_and_signatures)
    }
}

/// A type family for signature material that knows which public key type
/// is needed to verify it, and given such a public key, knows how to
/// verify.
///
/// This trait simply requires an association to some type of the
/// [`PublicKey`][PublicKey] family of which we are the `SignatureMaterial`.
///
/// This trait has a requirement on a `pub(crate)` marker trait meant to
/// specifically limit its implementations to the present crate.
///
/// It should be possible to write a generic signature function that
/// checks signature material passed as `&[u8]` and only returns Ok when
/// that material de-serializes to a signature of the expected concrete
/// scheme. This would be done as an extension trait of
/// [`Signature`][Signature].
pub trait Signature:
    for<'a> TryFrom<&'a [u8], Error = CryptoMaterialError>
    + Sized
    + Debug
    + Clone
    + Eq
    + Hash
    + private::Sealed
{
    /// The associated verifying key type for this signature.
    type VerifyingKeyMaterial: VerifyingKey<SignatureMaterial = Self>;
    /// The associated signing key type for this signature
    type SigningKeyMaterial: SigningKey<SignatureMaterial = Self>;

    /// Verification for a struct we unabmiguously know how to serialize and
    /// that we have a domain separation prefix for.
    fn verify<T: CryptoHash + Serialize>(
        &self,
        message: &T,
        public_key: &Self::VerifyingKeyMaterial,
    ) -> Result<()>;

    /// Native verification function.
    fn verify_arbitrary_msg(
        &self,
        message: &[u8],
        public_key: &Self::VerifyingKeyMaterial,
    ) -> Result<()>;

    /// Convert the signature into a byte representation.
    fn to_bytes(&self) -> Vec<u8>;

    /// The implementer can override a batch verification implementation
    /// that by default iterates over each signature. More efficient
    /// implementations exist and should be implemented for many schemes.
    fn batch_verify<T: CryptoHash + Serialize>(
        message: &T,
        keys_and_signatures: Vec<(Self::VerifyingKeyMaterial, Self)>,
    ) -> Result<()> {
        for (key, signature) in keys_and_signatures {
            signature.verify(message, &key)?
        }
        Ok(())
    }
}

/// A type family for schemes which know how to generate key material from
/// a cryptographically-secure [`CryptoRng`][::rand::CryptoRng].
pub trait Uniform {
    /// Generate key material from an RNG. This should generally not be used for production
    /// purposes even with a good source of randomness. When possible use hardware crypto to generate and
    /// store private keys.
    fn generate<R>(rng: &mut R) -> Self
    where
        R: RngCore + CryptoRng;

    /// Generate a random key using the shared TEST_SEED
    fn generate_for_testing() -> Self
    where
        Self: Sized,
    {
        let mut rng: StdRng = SeedableRng::from_seed(crate::test_utils::TEST_SEED);
        Self::generate(&mut rng)
    }
}

/// A type family with a by-convention notion of genesis private key.
pub trait Genesis: PrivateKey {
    /// Produces the genesis private key.
    fn genesis() -> Self;
}

/// A pub(crate) mod hiding a Sealed trait and its implementations, allowing
/// us to make sure implementations are constrained to the crypto crate.
// See https://rust-lang.github.io/api-guidelines/future-proofing.html#sealed-traits-protect-against-downstream-implementations-c-sealed
pub(crate) mod private {
    pub trait Sealed {}

    // Implement for the ed25519, multi-ed25519 signatures
    impl Sealed for crate::ed25519::Ed25519PrivateKey {}
    impl Sealed for crate::ed25519::Ed25519PublicKey {}
    impl Sealed for crate::ed25519::Ed25519Signature {}

    impl Sealed for crate::multi_ed25519::MultiEd25519PrivateKey {}
    impl Sealed for crate::multi_ed25519::MultiEd25519PublicKey {}
    impl Sealed for crate::multi_ed25519::MultiEd25519Signature {}
}
<commit_msg>[crypto] Allow PublicKeys to be serialized like PrivateKey<commit_after>// Copyright (c) Aptos
// SPDX-License-Identifier: Apache-2.0

//! This module provides a generic set of traits for dealing with cryptographic primitives.
//!
//! For examples on how to use these traits, see the implementations of the [`ed25519`] or
//! [`bls12381`] modules.

use crate::hash::{CryptoHash, CryptoHasher};
use anyhow::Result;
use core::convert::{From, TryFrom};
use rand::{rngs::StdRng, CryptoRng, RngCore, SeedableRng};
use serde::{de::DeserializeOwned, Serialize};
use std::{fmt::Debug, hash::Hash};
use thiserror::Error;

/// An error type for key and signature validation issues, see [`ValidCryptoMaterial`][ValidCryptoMaterial].
///
/// This enum reflects there are two interesting causes of validation
/// failure for the ingestion of key or signature material: deserialization errors
/// (often, due to mangled material or curve equation failure for ECC) and
/// validation errors (material recognizable but unacceptable for use,
/// e.g. unsafe).
#[derive(Clone, Debug, PartialEq, Eq, Error)]
#[error("{:?}", self)]
pub enum CryptoMaterialError {
    /// Struct to be signed does not serialize correctly.
    SerializationError,
    /// Key or signature material does not deserialize correctly.
    DeserializationError,
    /// Key or signature material deserializes, but is otherwise not valid.
    ValidationError,
    /// Key, threshold or signature material does not have the expected size.
    WrongLengthError,
    /// Part of the signature or key is not canonical resulting to malleability issues.
    CanonicalRepresentationError,
    /// A curve point (i.e., a public key) lies on a small group.
    SmallSubgroupError,
    /// A curve point (i.e., a public key) does not satisfy the curve equation.
    PointNotOnCurveError,
    /// BitVec errors in accountable multi-sig schemes.
    BitVecError(String),
}

/// The serialized length of the data that enables macro derived serialization and deserialization.
pub trait Length {
    /// The serialized length of the data
    fn length(&self) -> usize;
}

/// Key or more generally crypto material with a notion of byte validation.
///
/// A type family for material that knows how to serialize and
/// deserialize, as well as validate byte-encoded material. The
/// validation must be implemented as a [`TryFrom`][TryFrom] which
/// classifies its failures against the above
/// [`CryptoMaterialError`][CryptoMaterialError].
///
/// This provides an implementation for a validation that relies on a
/// round-trip to bytes and corresponding [`TryFrom`][TryFrom].
pub trait ValidCryptoMaterial:
    // The for<'a> exactly matches the assumption "deserializable from any lifetime".
    for<'a> TryFrom<&'a [u8], Error = CryptoMaterialError> + Serialize + DeserializeOwned
{
    /// Convert the valid crypto material to bytes.
    fn to_bytes(&self) -> Vec<u8>;
}

/// An extension to to/from Strings for [`ValidCryptoMaterial`][ValidCryptoMaterial].
///
/// Relies on [`hex`][::hex] for string encoding / decoding.
/// No required fields, provides a default implementation.
pub trait ValidCryptoMaterialStringExt: ValidCryptoMaterial {
    /// When trying to convert from bytes, we simply decode the string into
    /// bytes before checking if we can convert.
    fn from_encoded_string(encoded_str: &str) -> std::result::Result<Self, CryptoMaterialError> {
        let bytes_out = ::hex::decode(encoded_str);
        // We defer to `try_from` to make sure we only produce valid crypto materials.
        bytes_out
            // We reinterpret a failure to serialize: key is mangled someway.
            .or(Err(CryptoMaterialError::DeserializationError))
            .and_then(|ref bytes| Self::try_from(bytes))
    }
    /// A function to encode into hex-string after serializing.
    fn to_encoded_string(&self) -> Result<String> {
        Ok(::hex::encode(&self.to_bytes()))
    }
}

// There's nothing required in this extension, so let's just derive it
// for anybody that has a ValidCryptoMaterial.
impl<T: ValidCryptoMaterial> ValidCryptoMaterialStringExt for T {}

/// A type family for key material that should remain secret and has an
/// associated type of the [`PublicKey`][PublicKey] family.
pub trait PrivateKey: Sized {
    /// We require public / private types to be coupled, i.e. their
    /// associated type is each other.
    type PublicKeyMaterial: PublicKey<PrivateKeyMaterial = Self>;

    /// Returns the associated public key
    fn public_key(&self) -> Self::PublicKeyMaterial {
        self.into()
    }
}

/// A type family of valid keys that know how to sign.
///
/// This trait has a requirement on a `pub(crate)` marker trait meant to
/// specifically limit its implementations to the present crate.
///
/// A trait for a [`ValidCryptoMaterial`][ValidCryptoMaterial] which knows how to sign a
/// message, and return an associated `Signature` type.
pub trait SigningKey:
    PrivateKey<PublicKeyMaterial = <Self as SigningKey>::VerifyingKeyMaterial>
    + ValidCryptoMaterial
    + private::Sealed
{
    /// The associated verifying key type for this signing key.
    type VerifyingKeyMaterial: VerifyingKey<SigningKeyMaterial = Self>;
    /// The associated signature type for this signing key.
    type SignatureMaterial: Signature<SigningKeyMaterial = Self>;

    /// Signs an object that has an distinct domain-separation hasher and
    /// that we know how to serialize. There is no pre-hashing into a
    /// `HashValue` to be done by the caller.
    ///
    /// Note: this assumes serialization is infallible. See crates::bcs::ser
    /// for a discussion of this assumption.
    fn sign<T: CryptoHash + Serialize>(&self, message: &T) -> Self::SignatureMaterial;

    /// Signs a non-hash input message. For testing only.
    #[cfg(any(test, feature = "fuzzing"))]
    fn sign_arbitrary_message(&self, message: &[u8]) -> Self::SignatureMaterial;

    /// Returns the associated verifying key
    fn verifying_key(&self) -> Self::VerifyingKeyMaterial {
        self.public_key()
    }
}

/// Returns the signing message for the given message.
/// It is used by `SigningKey#sign` function.
pub fn signing_message<T: CryptoHash + Serialize>(message: &T) -> Vec<u8> {
    let mut bytes = <T::Hasher as CryptoHasher>::seed().to_vec();
    bcs::serialize_into(&mut bytes, &message)
        .map_err(|_| CryptoMaterialError::SerializationError)
        .expect("Serialization of signable material should not fail.");
    bytes
}

/// A type for key material that can be publicly shared, and in asymmetric
/// fashion, can be obtained from a [`PrivateKey`][PrivateKey]
/// reference.
/// This convertibility requirement ensures the existence of a
/// deterministic, canonical public key construction from a private key.
pub trait PublicKey: Sized + Clone + Eq + Hash + ValidCryptoMaterial +
    // This unsightly turbofish type parameter is the precise constraint
    // needed to require that there exists an
    //
    // ```
    // impl From<&MyPrivateKeyMaterial> for MyPublicKeyMaterial
    // ```
    //
    // declaration, for any `MyPrivateKeyMaterial`, `MyPublicKeyMaterial`
    // on which we register (respectively) `PublicKey` and `PrivateKey`
    // implementations.
    for<'a> From<&'a <Self as PublicKey>::PrivateKeyMaterial> {
    /// We require public / private types to be coupled, i.e. their
    /// associated type is each other.
    type PrivateKeyMaterial: PrivateKey<PublicKeyMaterial = Self>;
}

/// A type family of public keys that are used for signing.
///
/// This trait has a requirement on a `pub(crate)` marker trait meant to
/// specifically limit its implementations to the present crate.
///
/// It is linked to a type of the Signature family, which carries the
/// verification implementation.
pub trait VerifyingKey:
    PublicKey<PrivateKeyMaterial = <Self as VerifyingKey>::SigningKeyMaterial>
    + ValidCryptoMaterial
    + private::Sealed
{
    /// The associated signing key type for this verifying key.
    type SigningKeyMaterial: SigningKey<VerifyingKeyMaterial = Self>;
    /// The associated signature type for this verifying key.
    type SignatureMaterial: Signature<VerifyingKeyMaterial = Self>;

    /// We provide the striaghtfoward implementation which dispatches to the signature.
    fn verify_struct_signature<T: CryptoHash + Serialize>(
        &self,
        message: &T,
        signature: &Self::SignatureMaterial,
    ) -> Result<()> {
        signature.verify(message, self)
    }

    /// We provide the implementation which dispatches to the signature.
    fn batch_verify<T: CryptoHash + Serialize>(
        message: &T,
        keys_and_signatures: Vec<(Self, Self::SignatureMaterial)>,
    ) -> Result<()> {
        Self::SignatureMaterial::batch_verify(message, keys_and_signatures)
    }
}

/// A type family for signature material that knows which public key type
/// is needed to verify it, and given such a public key, knows how to
/// verify.
///
/// This trait simply requires an association to some type of the
/// [`PublicKey`][PublicKey] family of which we are the `SignatureMaterial`.
///
/// This trait has a requirement on a `pub(crate)` marker trait meant to
/// specifically limit its implementations to the present crate.
///
/// It should be possible to write a generic signature function that
/// checks signature material passed as `&[u8]` and only returns Ok when
/// that material de-serializes to a signature of the expected concrete
/// scheme. This would be done as an extension trait of
/// [`Signature`][Signature].
pub trait Signature:
    for<'a> TryFrom<&'a [u8], Error = CryptoMaterialError>
    + Sized
    + Debug
    + Clone
    + Eq
    + Hash
    + private::Sealed
{
    /// The associated verifying key type for this signature.
    type VerifyingKeyMaterial: VerifyingKey<SignatureMaterial = Self>;
    /// The associated signing key type for this signature
    type SigningKeyMaterial: SigningKey<SignatureMaterial = Self>;

    /// Verification for a struct we unabmiguously know how to serialize and
    /// that we have a domain separation prefix for.
    fn verify<T: CryptoHash + Serialize>(
        &self,
        message: &T,
        public_key: &Self::VerifyingKeyMaterial,
    ) -> Result<()>;

    /// Native verification function.
    fn verify_arbitrary_msg(
        &self,
        message: &[u8],
        public_key: &Self::VerifyingKeyMaterial,
    ) -> Result<()>;

    /// Convert the signature into a byte representation.
    fn to_bytes(&self) -> Vec<u8>;

    /// The implementer can override a batch verification implementation
    /// that by default iterates over each signature. More efficient
    /// implementations exist and should be implemented for many schemes.
    fn batch_verify<T: CryptoHash + Serialize>(
        message: &T,
        keys_and_signatures: Vec<(Self::VerifyingKeyMaterial, Self)>,
    ) -> Result<()> {
        for (key, signature) in keys_and_signatures {
            signature.verify(message, &key)?
        }
        Ok(())
    }
}

/// A type family for schemes which know how to generate key material from
/// a cryptographically-secure [`CryptoRng`][::rand::CryptoRng].
pub trait Uniform {
    /// Generate key material from an RNG. This should generally not be used for production
    /// purposes even with a good source of randomness. When possible use hardware crypto to generate and
    /// store private keys.
    fn generate<R>(rng: &mut R) -> Self
    where
        R: RngCore + CryptoRng;

    /// Generate a random key using the shared TEST_SEED
    fn generate_for_testing() -> Self
    where
        Self: Sized,
    {
        let mut rng: StdRng = SeedableRng::from_seed(crate::test_utils::TEST_SEED);
        Self::generate(&mut rng)
    }
}

/// A type family with a by-convention notion of genesis private key.
pub trait Genesis: PrivateKey {
    /// Produces the genesis private key.
    fn genesis() -> Self;
}

/// A pub(crate) mod hiding a Sealed trait and its implementations, allowing
/// us to make sure implementations are constrained to the crypto crate.
// See https://rust-lang.github.io/api-guidelines/future-proofing.html#sealed-traits-protect-against-downstream-implementations-c-sealed
pub(crate) mod private {
    pub trait Sealed {}

    // Implement for the ed25519, multi-ed25519 signatures
    impl Sealed for crate::ed25519::Ed25519PrivateKey {}
    impl Sealed for crate::ed25519::Ed25519PublicKey {}
    impl Sealed for crate::ed25519::Ed25519Signature {}

    impl Sealed for crate::multi_ed25519::MultiEd25519PrivateKey {}
    impl Sealed for crate::multi_ed25519::MultiEd25519PublicKey {}
    impl Sealed for crate::multi_ed25519::MultiEd25519Signature {}
}
<|endoftext|>


<commit_before>//! Handles the `Enter` key press. At the momently, this only continues
//! comments, but should handle indent some time in the future as well.

use ide_db::base_db::{FilePosition, SourceDatabase};
use ide_db::RootDatabase;
use syntax::{
    algo::find_node_at_offset,
    ast::{self, edit::IndentLevel, AstToken},
    AstNode, SmolStr, SourceFile,
    SyntaxKind::*,
    SyntaxNode, SyntaxToken, TextRange, TextSize, TokenAtOffset,
};

use text_edit::TextEdit;

// Feature: On Enter
//
// rust-analyzer can override kbd:[Enter] key to make it smarter:
//
// - kbd:[Enter] inside triple-slash comments automatically inserts `///`
// - kbd:[Enter] in the middle or after a trailing space in `//` inserts `//`
// - kbd:[Enter] inside `//!` doc comments automatically inserts `//!`
// - kbd:[Enter] after `{` indents contents and closing `}` of single-line block
//
// This action needs to be assigned to shortcut explicitly.
//
// VS Code::
//
// Add the following to `keybindings.json`:
// [source,json]
// ----
// {
//   "key": "Enter",
//   "command": "rust-analyzer.onEnter",
//   "when": "editorTextFocus && !suggestWidgetVisible && editorLangId == rust"
// }
// ----
//
// image::https://user-images.githubusercontent.com/48062697/113065578-04c21800-91b1-11eb-82b8-22b8c481e645.gif[]
pub(crate) fn on_enter(db: &RootDatabase, position: FilePosition) -> Option<TextEdit> {
    let parse = db.parse(position.file_id);
    let file = parse.tree();
    let token = file.syntax().token_at_offset(position.offset).left_biased()?;

    if let Some(comment) = ast::Comment::cast(token.clone()) {
        return on_enter_in_comment(&comment, &file, position.offset);
    }

    if token.kind() == L_CURLY {
        // Typing enter after the `{` of a block expression, where the `}` is on the same line
        if let Some(edit) = find_node_at_offset(file.syntax(), position.offset - TextSize::of('{'))
            .and_then(|block| on_enter_in_block(block, position))
        {
            return Some(edit);
        }
    }

    None
}

fn on_enter_in_comment(
    comment: &ast::Comment,
    file: &ast::SourceFile,
    offset: TextSize,
) -> Option<TextEdit> {
    if comment.kind().shape.is_block() {
        return None;
    }

    let prefix = comment.prefix();
    let comment_range = comment.syntax().text_range();
    if offset < comment_range.start() + TextSize::of(prefix) {
        return None;
    }

    let mut remove_trailing_whitespace = false;
    // Continuing single-line non-doc comments (like this one :) ) is annoying
    if prefix == "//" && comment_range.end() == offset {
        if comment.text().ends_with(' ') {
            cov_mark::hit!(continues_end_of_line_comment_with_space);
            remove_trailing_whitespace = true;
        } else if !followed_by_comment(&comment) {
            return None;
        }
    }

    let indent = node_indent(&file, comment.syntax())?;
    let inserted = format!("\n{}{} $0", indent, prefix);
    let delete = if remove_trailing_whitespace {
        let trimmed_len = comment.text().trim_end().len() as u32;
        let trailing_whitespace_len = comment.text().len() as u32 - trimmed_len;
        TextRange::new(offset - TextSize::from(trailing_whitespace_len), offset)
    } else {
        TextRange::empty(offset)
    };
    let edit = TextEdit::replace(delete, inserted);
    Some(edit)
}

fn on_enter_in_block(block: ast::BlockExpr, position: FilePosition) -> Option<TextEdit> {
    let contents = block_contents(&block)?;

    if block.syntax().text().contains_char('\n') {
        return None;
    }

    let indent = IndentLevel::from_node(block.syntax());
    let mut edit = TextEdit::insert(position.offset, format!("\n{}$0", indent + 1));
    edit.union(TextEdit::insert(contents.text_range().end(), format!("\n{}", indent))).ok()?;
    Some(edit)
}

fn block_contents(block: &ast::BlockExpr) -> Option<SyntaxNode> {
    let mut node = block.tail_expr().map(|e| e.syntax().clone());

    for stmt in block.statements() {
        if node.is_some() {
            // More than 1 node in the block
            return None;
        }

        node = Some(stmt.syntax().clone());
    }

    node
}

fn followed_by_comment(comment: &ast::Comment) -> bool {
    let ws = match comment.syntax().next_token().and_then(ast::Whitespace::cast) {
        Some(it) => it,
        None => return false,
    };
    if ws.spans_multiple_lines() {
        return false;
    }
    ws.syntax().next_token().and_then(ast::Comment::cast).is_some()
}

fn node_indent(file: &SourceFile, token: &SyntaxToken) -> Option<SmolStr> {
    let ws = match file.syntax().token_at_offset(token.text_range().start()) {
        TokenAtOffset::Between(l, r) => {
            assert!(r == *token);
            l
        }
        TokenAtOffset::Single(n) => {
            assert!(n == *token);
            return Some("".into());
        }
        TokenAtOffset::None => unreachable!(),
    };
    if ws.kind() != WHITESPACE {
        return None;
    }
    let text = ws.text();
    let pos = text.rfind('\n').map(|it| it + 1).unwrap_or(0);
    Some(text[pos..].into())
}

#[cfg(test)]
mod tests {
    use stdx::trim_indent;
    use test_utils::assert_eq_text;

    use crate::fixture;

    fn apply_on_enter(before: &str) -> Option<String> {
        let (analysis, position) = fixture::position(&before);
        let result = analysis.on_enter(position).unwrap()?;

        let mut actual = analysis.file_text(position.file_id).unwrap().to_string();
        result.apply(&mut actual);
        Some(actual)
    }

    fn do_check(ra_fixture_before: &str, ra_fixture_after: &str) {
        let ra_fixture_after = &trim_indent(ra_fixture_after);
        let actual = apply_on_enter(ra_fixture_before).unwrap();
        assert_eq_text!(ra_fixture_after, &actual);
    }

    fn do_check_noop(ra_fixture_text: &str) {
        assert!(apply_on_enter(ra_fixture_text).is_none())
    }

    #[test]
    fn continues_doc_comment() {
        do_check(
            r"
/// Some docs$0
fn foo() {
}
",
            r"
/// Some docs
/// $0
fn foo() {
}
",
        );

        do_check(
            r"
impl S {
    /// Some$0 docs.
    fn foo() {}
}
",
            r"
impl S {
    /// Some
    /// $0 docs.
    fn foo() {}
}
",
        );

        do_check(
            r"
///$0 Some docs
fn foo() {
}
",
            r"
///
/// $0 Some docs
fn foo() {
}
",
        );
    }

    #[test]
    fn does_not_continue_before_doc_comment() {
        do_check_noop(r"$0//! docz");
    }

    #[test]
    fn continues_another_doc_comment() {
        do_check(
            r#"
fn main() {
    //! Documentation for$0 on enter
    let x = 1 + 1;
}
"#,
            r#"
fn main() {
    //! Documentation for
    //! $0 on enter
    let x = 1 + 1;
}
"#,
        );
    }

    #[test]
    fn continues_code_comment_in_the_middle_of_line() {
        do_check(
            r"
fn main() {
    // Fix$0 me
    let x = 1 + 1;
}
",
            r"
fn main() {
    // Fix
    // $0 me
    let x = 1 + 1;
}
",
        );
    }

    #[test]
    fn continues_code_comment_in_the_middle_several_lines() {
        do_check(
            r"
fn main() {
    // Fix$0
    // me
    let x = 1 + 1;
}
",
            r"
fn main() {
    // Fix
    // $0
    // me
    let x = 1 + 1;
}
",
        );
    }

    #[test]
    fn does_not_continue_end_of_line_comment() {
        do_check_noop(
            r"
fn main() {
    // Fix me$0
    let x = 1 + 1;
}
",
        );
    }

    #[test]
    fn continues_end_of_line_comment_with_space() {
        cov_mark::check!(continues_end_of_line_comment_with_space);
        do_check(
            r#"
fn main() {
    // Fix me $0
    let x = 1 + 1;
}
"#,
            r#"
fn main() {
    // Fix me
    // $0
    let x = 1 + 1;
}
"#,
        );
    }

    #[test]
    fn trims_all_trailing_whitespace() {
        do_check(
            "
fn main() {
    // Fix me  \t\t   $0
    let x = 1 + 1;
}
",
            "
fn main() {
    // Fix me
    // $0
    let x = 1 + 1;
}
",
        );
    }

    #[test]
    fn indents_fn_body_block() {
        do_check(
            r#"
fn f() {$0()}
        "#,
            r#"
fn f() {
    $0()
}
        "#,
        );
    }

    #[test]
    fn indents_block_expr() {
        do_check(
            r#"
fn f() {
    let x = {$0()};
}
        "#,
            r#"
fn f() {
    let x = {
        $0()
    };
}
        "#,
        );
    }

    #[test]
    fn indents_match_arm() {
        do_check(
            r#"
fn f() {
    match 6 {
        1 => {$0f()},
        _ => (),
    }
}
        "#,
            r#"
fn f() {
    match 6 {
        1 => {
            $0f()
        },
        _ => (),
    }
}
        "#,
        );
    }

    #[test]
    fn indents_block_with_statement() {
        do_check(
            r#"
fn f() {$0a = b}
        "#,
            r#"
fn f() {
    $0a = b
}
        "#,
        );
        do_check(
            r#"
fn f() {$0fn f() {}}
        "#,
            r#"
fn f() {
    $0fn f() {}
}
        "#,
        );
    }

    #[test]
    fn indents_nested_blocks() {
        do_check(
            r#"
fn f() {$0{}}
        "#,
            r#"
fn f() {
    $0{}
}
        "#,
        );
    }

    #[test]
    fn does_not_indent_empty_block() {
        do_check_noop(
            r#"
fn f() {$0}
        "#,
        );
        do_check_noop(
            r#"
fn f() {{$0}}
        "#,
        );
    }

    #[test]
    fn does_not_indent_block_with_too_much_content() {
        do_check_noop(
            r#"
fn f() {$0 a = b; ()}
        "#,
        );
        do_check_noop(
            r#"
fn f() {$0 a = b; a = b; }
        "#,
        );
    }

    #[test]
    fn does_not_indent_multiline_block() {
        do_check_noop(
            r#"
fn f() {$0
}
        "#,
        );
        do_check_noop(
            r#"
fn f() {$0

}
        "#,
        );
    }
}
<commit_msg>Add a cov_mark<commit_after>//! Handles the `Enter` key press. At the momently, this only continues
//! comments, but should handle indent some time in the future as well.

use ide_db::base_db::{FilePosition, SourceDatabase};
use ide_db::RootDatabase;
use syntax::{
    algo::find_node_at_offset,
    ast::{self, edit::IndentLevel, AstToken},
    AstNode, SmolStr, SourceFile,
    SyntaxKind::*,
    SyntaxNode, SyntaxToken, TextRange, TextSize, TokenAtOffset,
};

use text_edit::TextEdit;

// Feature: On Enter
//
// rust-analyzer can override kbd:[Enter] key to make it smarter:
//
// - kbd:[Enter] inside triple-slash comments automatically inserts `///`
// - kbd:[Enter] in the middle or after a trailing space in `//` inserts `//`
// - kbd:[Enter] inside `//!` doc comments automatically inserts `//!`
// - kbd:[Enter] after `{` indents contents and closing `}` of single-line block
//
// This action needs to be assigned to shortcut explicitly.
//
// VS Code::
//
// Add the following to `keybindings.json`:
// [source,json]
// ----
// {
//   "key": "Enter",
//   "command": "rust-analyzer.onEnter",
//   "when": "editorTextFocus && !suggestWidgetVisible && editorLangId == rust"
// }
// ----
//
// image::https://user-images.githubusercontent.com/48062697/113065578-04c21800-91b1-11eb-82b8-22b8c481e645.gif[]
pub(crate) fn on_enter(db: &RootDatabase, position: FilePosition) -> Option<TextEdit> {
    let parse = db.parse(position.file_id);
    let file = parse.tree();
    let token = file.syntax().token_at_offset(position.offset).left_biased()?;

    if let Some(comment) = ast::Comment::cast(token.clone()) {
        return on_enter_in_comment(&comment, &file, position.offset);
    }

    if token.kind() == L_CURLY {
        // Typing enter after the `{` of a block expression, where the `}` is on the same line
        if let Some(edit) = find_node_at_offset(file.syntax(), position.offset - TextSize::of('{'))
            .and_then(|block| on_enter_in_block(block, position))
        {
            cov_mark::hit!(indent_block_contents);
            return Some(edit);
        }
    }

    None
}

fn on_enter_in_comment(
    comment: &ast::Comment,
    file: &ast::SourceFile,
    offset: TextSize,
) -> Option<TextEdit> {
    if comment.kind().shape.is_block() {
        return None;
    }

    let prefix = comment.prefix();
    let comment_range = comment.syntax().text_range();
    if offset < comment_range.start() + TextSize::of(prefix) {
        return None;
    }

    let mut remove_trailing_whitespace = false;
    // Continuing single-line non-doc comments (like this one :) ) is annoying
    if prefix == "//" && comment_range.end() == offset {
        if comment.text().ends_with(' ') {
            cov_mark::hit!(continues_end_of_line_comment_with_space);
            remove_trailing_whitespace = true;
        } else if !followed_by_comment(&comment) {
            return None;
        }
    }

    let indent = node_indent(&file, comment.syntax())?;
    let inserted = format!("\n{}{} $0", indent, prefix);
    let delete = if remove_trailing_whitespace {
        let trimmed_len = comment.text().trim_end().len() as u32;
        let trailing_whitespace_len = comment.text().len() as u32 - trimmed_len;
        TextRange::new(offset - TextSize::from(trailing_whitespace_len), offset)
    } else {
        TextRange::empty(offset)
    };
    let edit = TextEdit::replace(delete, inserted);
    Some(edit)
}

fn on_enter_in_block(block: ast::BlockExpr, position: FilePosition) -> Option<TextEdit> {
    let contents = block_contents(&block)?;

    if block.syntax().text().contains_char('\n') {
        return None;
    }

    let indent = IndentLevel::from_node(block.syntax());
    let mut edit = TextEdit::insert(position.offset, format!("\n{}$0", indent + 1));
    edit.union(TextEdit::insert(contents.text_range().end(), format!("\n{}", indent))).ok()?;
    Some(edit)
}

fn block_contents(block: &ast::BlockExpr) -> Option<SyntaxNode> {
    let mut node = block.tail_expr().map(|e| e.syntax().clone());

    for stmt in block.statements() {
        if node.is_some() {
            // More than 1 node in the block
            return None;
        }

        node = Some(stmt.syntax().clone());
    }

    node
}

fn followed_by_comment(comment: &ast::Comment) -> bool {
    let ws = match comment.syntax().next_token().and_then(ast::Whitespace::cast) {
        Some(it) => it,
        None => return false,
    };
    if ws.spans_multiple_lines() {
        return false;
    }
    ws.syntax().next_token().and_then(ast::Comment::cast).is_some()
}

fn node_indent(file: &SourceFile, token: &SyntaxToken) -> Option<SmolStr> {
    let ws = match file.syntax().token_at_offset(token.text_range().start()) {
        TokenAtOffset::Between(l, r) => {
            assert!(r == *token);
            l
        }
        TokenAtOffset::Single(n) => {
            assert!(n == *token);
            return Some("".into());
        }
        TokenAtOffset::None => unreachable!(),
    };
    if ws.kind() != WHITESPACE {
        return None;
    }
    let text = ws.text();
    let pos = text.rfind('\n').map(|it| it + 1).unwrap_or(0);
    Some(text[pos..].into())
}

#[cfg(test)]
mod tests {
    use stdx::trim_indent;
    use test_utils::assert_eq_text;

    use crate::fixture;

    fn apply_on_enter(before: &str) -> Option<String> {
        let (analysis, position) = fixture::position(&before);
        let result = analysis.on_enter(position).unwrap()?;

        let mut actual = analysis.file_text(position.file_id).unwrap().to_string();
        result.apply(&mut actual);
        Some(actual)
    }

    fn do_check(ra_fixture_before: &str, ra_fixture_after: &str) {
        let ra_fixture_after = &trim_indent(ra_fixture_after);
        let actual = apply_on_enter(ra_fixture_before).unwrap();
        assert_eq_text!(ra_fixture_after, &actual);
    }

    fn do_check_noop(ra_fixture_text: &str) {
        assert!(apply_on_enter(ra_fixture_text).is_none())
    }

    #[test]
    fn continues_doc_comment() {
        do_check(
            r"
/// Some docs$0
fn foo() {
}
",
            r"
/// Some docs
/// $0
fn foo() {
}
",
        );

        do_check(
            r"
impl S {
    /// Some$0 docs.
    fn foo() {}
}
",
            r"
impl S {
    /// Some
    /// $0 docs.
    fn foo() {}
}
",
        );

        do_check(
            r"
///$0 Some docs
fn foo() {
}
",
            r"
///
/// $0 Some docs
fn foo() {
}
",
        );
    }

    #[test]
    fn does_not_continue_before_doc_comment() {
        do_check_noop(r"$0//! docz");
    }

    #[test]
    fn continues_another_doc_comment() {
        do_check(
            r#"
fn main() {
    //! Documentation for$0 on enter
    let x = 1 + 1;
}
"#,
            r#"
fn main() {
    //! Documentation for
    //! $0 on enter
    let x = 1 + 1;
}
"#,
        );
    }

    #[test]
    fn continues_code_comment_in_the_middle_of_line() {
        do_check(
            r"
fn main() {
    // Fix$0 me
    let x = 1 + 1;
}
",
            r"
fn main() {
    // Fix
    // $0 me
    let x = 1 + 1;
}
",
        );
    }

    #[test]
    fn continues_code_comment_in_the_middle_several_lines() {
        do_check(
            r"
fn main() {
    // Fix$0
    // me
    let x = 1 + 1;
}
",
            r"
fn main() {
    // Fix
    // $0
    // me
    let x = 1 + 1;
}
",
        );
    }

    #[test]
    fn does_not_continue_end_of_line_comment() {
        do_check_noop(
            r"
fn main() {
    // Fix me$0
    let x = 1 + 1;
}
",
        );
    }

    #[test]
    fn continues_end_of_line_comment_with_space() {
        cov_mark::check!(continues_end_of_line_comment_with_space);
        do_check(
            r#"
fn main() {
    // Fix me $0
    let x = 1 + 1;
}
"#,
            r#"
fn main() {
    // Fix me
    // $0
    let x = 1 + 1;
}
"#,
        );
    }

    #[test]
    fn trims_all_trailing_whitespace() {
        do_check(
            "
fn main() {
    // Fix me  \t\t   $0
    let x = 1 + 1;
}
",
            "
fn main() {
    // Fix me
    // $0
    let x = 1 + 1;
}
",
        );
    }

    #[test]
    fn indents_fn_body_block() {
        cov_mark::check!(indent_block_contents);
        do_check(
            r#"
fn f() {$0()}
        "#,
            r#"
fn f() {
    $0()
}
        "#,
        );
    }

    #[test]
    fn indents_block_expr() {
        do_check(
            r#"
fn f() {
    let x = {$0()};
}
        "#,
            r#"
fn f() {
    let x = {
        $0()
    };
}
        "#,
        );
    }

    #[test]
    fn indents_match_arm() {
        do_check(
            r#"
fn f() {
    match 6 {
        1 => {$0f()},
        _ => (),
    }
}
        "#,
            r#"
fn f() {
    match 6 {
        1 => {
            $0f()
        },
        _ => (),
    }
}
        "#,
        );
    }

    #[test]
    fn indents_block_with_statement() {
        do_check(
            r#"
fn f() {$0a = b}
        "#,
            r#"
fn f() {
    $0a = b
}
        "#,
        );
        do_check(
            r#"
fn f() {$0fn f() {}}
        "#,
            r#"
fn f() {
    $0fn f() {}
}
        "#,
        );
    }

    #[test]
    fn indents_nested_blocks() {
        do_check(
            r#"
fn f() {$0{}}
        "#,
            r#"
fn f() {
    $0{}
}
        "#,
        );
    }

    #[test]
    fn does_not_indent_empty_block() {
        do_check_noop(
            r#"
fn f() {$0}
        "#,
        );
        do_check_noop(
            r#"
fn f() {{$0}}
        "#,
        );
    }

    #[test]
    fn does_not_indent_block_with_too_much_content() {
        do_check_noop(
            r#"
fn f() {$0 a = b; ()}
        "#,
        );
        do_check_noop(
            r#"
fn f() {$0 a = b; a = b; }
        "#,
        );
    }

    #[test]
    fn does_not_indent_multiline_block() {
        do_check_noop(
            r#"
fn f() {$0
}
        "#,
        );
        do_check_noop(
            r#"
fn f() {$0

}
        "#,
        );
    }
}
<|endoftext|>


<commit_before>extern crate ra_analysis;
extern crate ra_editor;
extern crate ra_syntax;
extern crate relative_path;
extern crate rustc_hash;
extern crate test_utils;

use std::sync::Arc;

use ra_analysis::{
    Analysis, AnalysisHost, CrateGraph, CrateId, FileId, FileResolver, FnDescriptor, JobHandle,
};
use ra_syntax::TextRange;

use relative_path::{RelativePath, RelativePathBuf};
use rustc_hash::FxHashMap;
use test_utils::{assert_eq_dbg, extract_offset};

#[derive(Debug)]
struct FileMap(Vec<(FileId, RelativePathBuf)>);

impl FileMap {
    fn iter<'a>(&'a self) -> impl Iterator<Item = (FileId, &'a RelativePath)> + 'a {
        self.0
            .iter()
            .map(|(id, path)| (*id, path.as_relative_path()))
    }

    fn path(&self, id: FileId) -> &RelativePath {
        self.iter().find(|&(it, _)| it == id).unwrap().1
    }
}

impl FileResolver for FileMap {
    fn file_stem(&self, id: FileId) -> String {
        self.path(id).file_stem().unwrap().to_string()
    }
    fn resolve(&self, id: FileId, rel: &RelativePath) -> Option<FileId> {
        let path = self.path(id).join(rel).normalize();
        let id = self.iter().find(|&(_, p)| path == p)?.0;
        Some(id)
    }
}

fn analysis_host(files: &[(&str, &str)]) -> AnalysisHost {
    let mut host = AnalysisHost::new();
    let mut file_map = Vec::new();
    for (id, &(path, contents)) in files.iter().enumerate() {
        let file_id = FileId((id + 1) as u32);
        assert!(path.starts_with('/'));
        let path = RelativePathBuf::from_path(&path[1..]).unwrap();
        host.change_file(file_id, Some(contents.to_string()));
        file_map.push((file_id, path));
    }
    host.set_file_resolver(Arc::new(FileMap(file_map)));
    host
}

fn analysis(files: &[(&str, &str)]) -> Analysis {
    analysis_host(files).analysis()
}

fn get_signature(text: &str) -> (FnDescriptor, Option<usize>) {
    let (offset, code) = extract_offset(text);
    let code = code.as_str();

    let (_handle, token) = JobHandle::new();
    let snap = analysis(&[("/lib.rs", code)]);

    snap.resolve_callable(FileId(1), offset, &token).unwrap()
}

#[test]
fn test_resolve_module() {
    let snap = analysis(&[("/lib.rs", "mod foo;"), ("/foo.rs", "")]);
    let (_handle, token) = JobHandle::new();
    let symbols = snap.approximately_resolve_symbol(FileId(1), 4.into(), &token);
    assert_eq_dbg(
        r#"[(FileId(2), FileSymbol { name: "foo", node_range: [0; 0), kind: MODULE })]"#,
        &symbols,
    );

    let snap = analysis(&[("/lib.rs", "mod foo;"), ("/foo/mod.rs", "")]);
    let symbols = snap.approximately_resolve_symbol(FileId(1), 4.into(), &token);
    assert_eq_dbg(
        r#"[(FileId(2), FileSymbol { name: "foo", node_range: [0; 0), kind: MODULE })]"#,
        &symbols,
    );
}

#[test]
fn test_unresolved_module_diagnostic() {
    let snap = analysis(&[("/lib.rs", "mod foo;")]);
    let diagnostics = snap.diagnostics(FileId(1));
    assert_eq_dbg(
        r#"[Diagnostic {
            message: "unresolved module",
            range: [4; 7),
            fix: Some(SourceChange {
                label: "create module",
                source_file_edits: [],
                file_system_edits: [CreateFile { anchor: FileId(1), path: "../foo.rs" }],
                cursor_position: None }) }]"#,
        &diagnostics,
    );
}

#[test]
fn test_unresolved_module_diagnostic_no_diag_for_inline_mode() {
    let snap = analysis(&[("/lib.rs", "mod foo {}")]);
    let diagnostics = snap.diagnostics(FileId(1));
    assert_eq_dbg(r#"[]"#, &diagnostics);
}

#[test]
fn test_resolve_parent_module() {
    let snap = analysis(&[("/lib.rs", "mod foo;"), ("/foo.rs", "")]);
    let symbols = snap.parent_module(FileId(2));
    assert_eq_dbg(
        r#"[(FileId(1), FileSymbol { name: "foo", node_range: [0; 8), kind: MODULE })]"#,
        &symbols,
    );
}

#[test]
fn test_resolve_crate_root() {
    let mut host = analysis_host(&[("/lib.rs", "mod foo;"), ("/foo.rs", "")]);
    let snap = host.analysis();
    assert!(snap.crate_for(FileId(2)).is_empty());

    let crate_graph = CrateGraph {
        crate_roots: {
            let mut m = FxHashMap::default();
            m.insert(CrateId(1), FileId(1));
            m
        },
    };
    host.set_crate_graph(crate_graph);
    let snap = host.analysis();

    assert_eq!(snap.crate_for(FileId(2)), vec![CrateId(1)],);
}

#[test]
fn test_fn_signature_two_args_first() {
    let (desc, param) = get_signature(
        r#"fn foo(x: u32, y: u32) -> u32 {x + y}
fn bar() { foo(<|>3, ); }"#,
    );

    assert_eq!(desc.name, "foo".to_string());
    assert_eq!(desc.params, vec!("x".to_string(), "y".to_string()));
    assert_eq!(desc.ret_type, Some("-> u32".into()));
    assert_eq!(param, Some(0));
}

#[test]
fn test_fn_signature_two_args_second() {
    let (desc, param) = get_signature(
        r#"fn foo(x: u32, y: u32) -> u32 {x + y}
fn bar() { foo(3, <|>); }"#,
    );

    assert_eq!(desc.name, "foo".to_string());
    assert_eq!(desc.params, vec!("x".to_string(), "y".to_string()));
    assert_eq!(desc.ret_type, Some("-> u32".into()));
    assert_eq!(param, Some(1));
}

#[test]
fn test_fn_signature_for_impl() {
    let (desc, param) = get_signature(
        r#"struct F; impl F { pub fn new() { F{}} }
fn bar() {let _ : F = F::new(<|>);}"#,
    );

    assert_eq!(desc.name, "new".to_string());
    assert_eq!(desc.params, Vec::<String>::new());
    assert_eq!(desc.ret_type, None);
    assert_eq!(param, None);
}

#[test]
fn test_fn_signature_for_method_self() {
    let (desc, param) = get_signature(
        r#"struct F;
impl F {
    pub fn new() -> F{
        F{}
    }

    pub fn do_it(&self) {}
}

fn bar() {
    let f : F = F::new();
    f.do_it(<|>);
}"#,
    );

    assert_eq!(desc.name, "do_it".to_string());
    assert_eq!(desc.params, vec!["&self".to_string()]);
    assert_eq!(desc.ret_type, None);
    assert_eq!(param, None);
}

#[test]
fn test_fn_signature_for_method_with_arg() {
    let (desc, param) = get_signature(
        r#"struct F;
impl F {
    pub fn new() -> F{
        F{}
    }

    pub fn do_it(&self, x: i32) {}
}

fn bar() {
    let f : F = F::new();
    f.do_it(<|>);
}"#,
    );

    assert_eq!(desc.name, "do_it".to_string());
    assert_eq!(desc.params, vec!["&self".to_string(), "x".to_string()]);
    assert_eq!(desc.ret_type, None);
    assert_eq!(param, Some(1));
}

fn get_all_refs(text: &str) -> Vec<(FileId, TextRange)> {
    let (offset, code) = extract_offset(text);
    let code = code.as_str();

    let (_handle, token) = JobHandle::new();
    let snap = analysis(&[("/lib.rs", code)]);

    snap.find_all_refs(FileId(1), offset, &token)
}

#[test]
fn test_find_all_refs_for_local() {
    let code = r#"
    fn main() {
        let mut i = 1;
        let j = 1;
        i = i<|> + j;

        {
            i = 0;
        }

        i = 5;
    }"#;

    let refs = get_all_refs(code);
    assert_eq!(refs.len(), 5);
}

#[test]
fn test_find_all_refs_for_param_inside() {
    let code = r#"
    fn foo(i : u32) -> u32 {
        i<|>
    }"#;

    let refs = get_all_refs(code);
    assert_eq!(refs.len(), 2);
}<commit_msg>Fix tests<commit_after>extern crate ra_analysis;
extern crate ra_editor;
extern crate ra_syntax;
extern crate relative_path;
extern crate rustc_hash;
extern crate test_utils;

use std::sync::Arc;

use ra_syntax::TextRange;
use relative_path::{RelativePath, RelativePathBuf};
use rustc_hash::FxHashMap;
use test_utils::{assert_eq_dbg, extract_offset};

use ra_analysis::{
    Analysis, AnalysisHost, CrateGraph, CrateId, FileId, FileResolver, FnDescriptor,
};

#[derive(Debug)]
struct FileMap(Vec<(FileId, RelativePathBuf)>);

impl FileMap {
    fn iter<'a>(&'a self) -> impl Iterator<Item = (FileId, &'a RelativePath)> + 'a {
        self.0
            .iter()
            .map(|(id, path)| (*id, path.as_relative_path()))
    }

    fn path(&self, id: FileId) -> &RelativePath {
        self.iter().find(|&(it, _)| it == id).unwrap().1
    }
}

impl FileResolver for FileMap {
    fn file_stem(&self, id: FileId) -> String {
        self.path(id).file_stem().unwrap().to_string()
    }
    fn resolve(&self, id: FileId, rel: &RelativePath) -> Option<FileId> {
        let path = self.path(id).join(rel).normalize();
        let id = self.iter().find(|&(_, p)| path == p)?.0;
        Some(id)
    }
}

fn analysis_host(files: &[(&str, &str)]) -> AnalysisHost {
    let mut host = AnalysisHost::new();
    let mut file_map = Vec::new();
    for (id, &(path, contents)) in files.iter().enumerate() {
        let file_id = FileId((id + 1) as u32);
        assert!(path.starts_with('/'));
        let path = RelativePathBuf::from_path(&path[1..]).unwrap();
        host.change_file(file_id, Some(contents.to_string()));
        file_map.push((file_id, path));
    }
    host.set_file_resolver(Arc::new(FileMap(file_map)));
    host
}

fn analysis(files: &[(&str, &str)]) -> Analysis {
    analysis_host(files).analysis()
}

fn get_signature(text: &str) -> (FnDescriptor, Option<usize>) {
    let (offset, code) = extract_offset(text);
    let code = code.as_str();

    let snap = analysis(&[("/lib.rs", code)]);

    snap.resolve_callable(FileId(1), offset).unwrap().unwrap()
}

#[test]
fn test_resolve_module() {
    let snap = analysis(&[("/lib.rs", "mod foo;"), ("/foo.rs", "")]);
    let symbols = snap.approximately_resolve_symbol(FileId(1), 4.into()).unwrap();
    assert_eq_dbg(
        r#"[(FileId(2), FileSymbol { name: "foo", node_range: [0; 0), kind: MODULE })]"#,
        &symbols,
    );

    let snap = analysis(&[("/lib.rs", "mod foo;"), ("/foo/mod.rs", "")]);
    let symbols = snap.approximately_resolve_symbol(FileId(1), 4.into()).unwrap();
    assert_eq_dbg(
        r#"[(FileId(2), FileSymbol { name: "foo", node_range: [0; 0), kind: MODULE })]"#,
        &symbols,
    );
}

#[test]
fn test_unresolved_module_diagnostic() {
    let snap = analysis(&[("/lib.rs", "mod foo;")]);
    let diagnostics = snap.diagnostics(FileId(1)).unwrap();
    assert_eq_dbg(
        r#"[Diagnostic {
            message: "unresolved module",
            range: [4; 7),
            fix: Some(SourceChange {
                label: "create module",
                source_file_edits: [],
                file_system_edits: [CreateFile { anchor: FileId(1), path: "../foo.rs" }],
                cursor_position: None }) }]"#,
        &diagnostics,
    );
}

#[test]
fn test_unresolved_module_diagnostic_no_diag_for_inline_mode() {
    let snap = analysis(&[("/lib.rs", "mod foo {}")]);
    let diagnostics = snap.diagnostics(FileId(1)).unwrap();
    assert_eq_dbg(r#"[]"#, &diagnostics);
}

#[test]
fn test_resolve_parent_module() {
    let snap = analysis(&[("/lib.rs", "mod foo;"), ("/foo.rs", "")]);
    let symbols = snap.parent_module(FileId(2)).unwrap();
    assert_eq_dbg(
        r#"[(FileId(1), FileSymbol { name: "foo", node_range: [0; 8), kind: MODULE })]"#,
        &symbols,
    );
}

#[test]
fn test_resolve_crate_root() {
    let mut host = analysis_host(&[("/lib.rs", "mod foo;"), ("/foo.rs", "")]);
    let snap = host.analysis();
    assert!(snap.crate_for(FileId(2)).unwrap().is_empty());

    let crate_graph = CrateGraph {
        crate_roots: {
            let mut m = FxHashMap::default();
            m.insert(CrateId(1), FileId(1));
            m
        },
    };
    host.set_crate_graph(crate_graph);
    let snap = host.analysis();

    assert_eq!(snap.crate_for(FileId(2)).unwrap(), vec![CrateId(1)],);
}

#[test]
fn test_fn_signature_two_args_first() {
    let (desc, param) = get_signature(
        r#"fn foo(x: u32, y: u32) -> u32 {x + y}
fn bar() { foo(<|>3, ); }"#,
    );

    assert_eq!(desc.name, "foo".to_string());
    assert_eq!(desc.params, vec!("x".to_string(), "y".to_string()));
    assert_eq!(desc.ret_type, Some("-> u32".into()));
    assert_eq!(param, Some(0));
}

#[test]
fn test_fn_signature_two_args_second() {
    let (desc, param) = get_signature(
        r#"fn foo(x: u32, y: u32) -> u32 {x + y}
fn bar() { foo(3, <|>); }"#,
    );

    assert_eq!(desc.name, "foo".to_string());
    assert_eq!(desc.params, vec!("x".to_string(), "y".to_string()));
    assert_eq!(desc.ret_type, Some("-> u32".into()));
    assert_eq!(param, Some(1));
}

#[test]
fn test_fn_signature_for_impl() {
    let (desc, param) = get_signature(
        r#"struct F; impl F { pub fn new() { F{}} }
fn bar() {let _ : F = F::new(<|>);}"#,
    );

    assert_eq!(desc.name, "new".to_string());
    assert_eq!(desc.params, Vec::<String>::new());
    assert_eq!(desc.ret_type, None);
    assert_eq!(param, None);
}

#[test]
fn test_fn_signature_for_method_self() {
    let (desc, param) = get_signature(
        r#"struct F;
impl F {
    pub fn new() -> F{
        F{}
    }

    pub fn do_it(&self) {}
}

fn bar() {
    let f : F = F::new();
    f.do_it(<|>);
}"#,
    );

    assert_eq!(desc.name, "do_it".to_string());
    assert_eq!(desc.params, vec!["&self".to_string()]);
    assert_eq!(desc.ret_type, None);
    assert_eq!(param, None);
}

#[test]
fn test_fn_signature_for_method_with_arg() {
    let (desc, param) = get_signature(
        r#"struct F;
impl F {
    pub fn new() -> F{
        F{}
    }

    pub fn do_it(&self, x: i32) {}
}

fn bar() {
    let f : F = F::new();
    f.do_it(<|>);
}"#,
    );

    assert_eq!(desc.name, "do_it".to_string());
    assert_eq!(desc.params, vec!["&self".to_string(), "x".to_string()]);
    assert_eq!(desc.ret_type, None);
    assert_eq!(param, Some(1));
}

fn get_all_refs(text: &str) -> Vec<(FileId, TextRange)> {
    let (offset, code) = extract_offset(text);
    let code = code.as_str();

    let snap = analysis(&[("/lib.rs", code)]);

    snap.find_all_refs(FileId(1), offset).unwrap()
}

#[test]
fn test_find_all_refs_for_local() {
    let code = r#"
    fn main() {
        let mut i = 1;
        let j = 1;
        i = i<|> + j;

        {
            i = 0;
        }

        i = 5;
    }"#;

    let refs = get_all_refs(code);
    assert_eq!(refs.len(), 5);
}

#[test]
fn test_find_all_refs_for_param_inside() {
    let code = r#"
    fn foo(i : u32) -> u32 {
        i<|>
    }"#;

    let refs = get_all_refs(code);
    assert_eq!(refs.len(), 2);
}
<|endoftext|>


<commit_before>#![crate_id = "rustenstein3D"]
#![license = "MIT"]
#![crate_type = "bin"]

#![feature(globs)]
#![feature(managed_boxes)]
#![allow(non_camel_case_types)]
#![allow(visible_private_types)]
#![allow(dead_code)]

extern crate native;
extern crate rsfml;

use rsfml::graphics::{RenderWindow, Font};
use rsfml::window::{VideoMode, ContextSettings};
use rsfml::system::Vector2i;
use rsfml::window::Close;

use std::os;
use std::from_str::*;

pub mod FPS;
pub mod event_handler;
pub mod game_mode;
pub mod map;
pub mod mini_map;
pub mod raycasting_engine;
pub mod texture_loader;
pub mod hud;
pub mod animation;
pub mod weapon;
pub mod game;

#[cfg(target_os="macos")]
#[start]
fn start(argc: int, argv: *const *const u8) -> int {
    native::start(argc, argv, main)
}

fn display_help() -> () {
    println!("Arguments availables for rustenstein3D :");
    println!("\t-w [window_width] [window_height] : specify a new size for the window.");
    println!("\t--noground : diseable the ground texturing (improve performance).");
    println!("\t--help : display this help.");
}

fn load_texture() -> texture_loader::TextureLoader {
    let mut texture_loader = texture_loader::TextureLoader::new();
    if texture_loader.load_texture("../resources/ground.tga".to_string()) == false || // 0
       texture_loader.load_texture("../resources/1.tga".to_string()) == false || // 1
       texture_loader.load_texture("../resources/2.tga".to_string()) == false || // 2
       texture_loader.load_texture("../resources/3.tga".to_string()) == false || // 3
       texture_loader.load_texture("../resources/4.tga".to_string()) == false || // 4
       texture_loader.load_texture("../resources/5.tga".to_string()) == false || // 5
       texture_loader.load_texture("../resources/6.tga".to_string()) == false || // 6
       texture_loader.load_texture("../resources/7.tga".to_string()) == false || // 7
       texture_loader.load_texture("../resources/8.tga".to_string()) == false || // 8
       texture_loader.load_texture("../resources/9.tga".to_string()) == false || // 9
       texture_loader.load_texture("../resources/10.tga".to_string()) == false || // 10
       texture_loader.load_texture("../resources/sky.tga".to_string()) == false || // 11
       texture_loader.load_texture("../resources/weapons/gun_1.png".to_string()) == false || // 12
       texture_loader.load_texture("../resources/weapons/gun_2.png".to_string()) == false || // 13
       texture_loader.load_texture("../resources/weapons/gun_3.png".to_string()) == false || // 14
       texture_loader.load_texture("../resources/weapons/gun_4.png".to_string()) == false || // 15
       texture_loader.load_texture("../resources/weapons/gun_5.png".to_string()) == false || // 16
       texture_loader.load_texture("../resources/weapons/gun_6.png".to_string()) == false || // 17
       texture_loader.load_texture("../resources/weapons/gun_shadow.png".to_string()) == false || // 18
       texture_loader.load_texture("../resources/weapons/gun2_1.png".to_string()) == false || // 19
       texture_loader.load_texture("../resources/weapons/gun2_2.png".to_string()) == false || // 20
       texture_loader.load_texture("../resources/weapons/gun2_3.png".to_string()) == false || // 21
       texture_loader.load_texture("../resources/weapons/gun2_4.png".to_string()) == false || // 22
       texture_loader.load_texture("../resources/weapons/gun2_5.png".to_string()) == false || // 23
       texture_loader.load_texture("../resources/weapons/gun2_6.png".to_string()) == false || // 24
       texture_loader.load_texture("../resources/weapons/gun2_shadow.png".to_string()) == false || // 25
       texture_loader.load_texture("../resources/weapons/gun3_1.png".to_string()) == false || // 26
       texture_loader.load_texture("../resources/weapons/gun3_2.png".to_string()) == false || // 27
       texture_loader.load_texture("../resources/weapons/gun3_3.png".to_string()) == false || // 28
       texture_loader.load_texture("../resources/weapons/gun3_4.png".to_string()) == false || // 29
       texture_loader.load_texture("../resources/weapons/gun3_5.png".to_string()) == false || // 30
       texture_loader.load_texture("../resources/weapons/gun3_6.png".to_string()) == false || // 31
       texture_loader.load_texture("../resources/weapons/gun3_shadow.png".to_string()) == false || // 32
       texture_loader.load_texture("../resources/weapons/cut_1.png".to_string()) == false || // 33
       texture_loader.load_texture("../resources/weapons/cut_2.png".to_string()) == false || // 34
       texture_loader.load_texture("../resources/weapons/cut_3.png".to_string()) == false || // 35
       texture_loader.load_texture("../resources/weapons/cut_4.png".to_string()) == false || // 36
       texture_loader.load_texture("../resources/weapons/cut_5.png".to_string()) == false || // 37
       texture_loader.load_texture("../resources/weapons/cut_6.png".to_string()) == false || //38
       texture_loader.load_texture("../resources/weapons/cut_shadow.png".to_string()) == false || // 39
       texture_loader.load_texture("../resources/face1.png".to_string()) == false || //40
       texture_loader.load_texture("../resources/face2.png".to_string()) == false || //41
       texture_loader.load_texture("../resources/face3.png".to_string()) == false { //42
        fail!("Error : Cannot load texture.");
    }
    texture_loader
}

fn main() -> () {
    // Check if a custom width is set.
    let args = os::args();
    let mut width : uint = 768;
    let mut height : uint = 480;
    let mut noground : bool = false;
    let mut i_args = 1;

    while i_args < args.len() {
        match args.get(i_args).as_slice() {
            "--help"       => { display_help(); return; },
            "--noground"   => noground = true,
            "-w"           => {
                if i_args + 2 >= args.len() { fail!("Error missing arguments for -w option."); }
                width = from_str::<uint>(args.get(i_args + 1).as_slice()).expect("Error the first parameter after -w argument is not a width!");
                height = from_str::<uint>(args.get(i_args + 2).as_slice()).expect("Error the second parameter after -w argument is not a width!");
                i_args += 2;
            },
            _              => fail!("Error unknown argument."),
        }
        i_args += 1;
    }

    // Create the render_window.
    let settings = ContextSettings::default();
    let video_mode = VideoMode::new_init(width, height, 32);
    // let video_mode = VideoMode::new_init(512, 384, 32);
    let mut render_window = RenderWindow::new(video_mode,
                                              "Rustenstein3D",
                                              Close,
                                              &settings).expect("Error : Cannot create a render_window!");

    // set the framerate limit to 30 fps.
    render_window.set_framerate_limit(40);

    // hide the cursor.
    render_window.set_mouse_cursor_visible(false);

    // set the mouse positon on the center of the window
    render_window.set_mouse_position(&Vector2i {x : width as i32 / 2,
                                                y : height as i32 / 2});

    // Create the font for the FPS_handler.
    let font = Font::new_from_file("../resources/sansation.ttf")
        .expect("Error : Cannot load font, font resources/sansation.ttf doesn.t exist!");

    // Create the texture loader and load textures
    let texture_loader = load_texture();

    // Create the game_loop and activate the fps handler.
    let mut game_loop = game::GameLoop::new(render_window,
                                            &texture_loader,
                                            noground);
    game_loop.activate_FPS(&font);

    game_loop.run();
}<commit_msg>update to last Rust - crate_id -> crate_name<commit_after>#![crate_name = "rustenstein3D"]
#![license = "MIT"]
#![crate_type = "bin"]

#![feature(globs)]
#![feature(managed_boxes)]
#![allow(non_camel_case_types)]
#![allow(visible_private_types)]
#![allow(dead_code)]

extern crate native;
extern crate rsfml;

use rsfml::graphics::{RenderWindow, Font};
use rsfml::window::{VideoMode, ContextSettings};
use rsfml::system::Vector2i;
use rsfml::window::Close;

use std::os;
use std::from_str::*;

pub mod FPS;
pub mod event_handler;
pub mod game_mode;
pub mod map;
pub mod mini_map;
pub mod raycasting_engine;
pub mod texture_loader;
pub mod hud;
pub mod animation;
pub mod weapon;
pub mod game;

#[cfg(target_os="macos")]
#[start]
fn start(argc: int, argv: *const *const u8) -> int {
    native::start(argc, argv, main)
}

fn display_help() -> () {
    println!("Arguments availables for rustenstein3D :");
    println!("\t-w [window_width] [window_height] : specify a new size for the window.");
    println!("\t--noground : diseable the ground texturing (improve performance).");
    println!("\t--help : display this help.");
}

fn load_texture() -> texture_loader::TextureLoader {
    let mut texture_loader = texture_loader::TextureLoader::new();
    if texture_loader.load_texture("../resources/ground.tga".to_string()) == false || // 0
       texture_loader.load_texture("../resources/1.tga".to_string()) == false || // 1
       texture_loader.load_texture("../resources/2.tga".to_string()) == false || // 2
       texture_loader.load_texture("../resources/3.tga".to_string()) == false || // 3
       texture_loader.load_texture("../resources/4.tga".to_string()) == false || // 4
       texture_loader.load_texture("../resources/5.tga".to_string()) == false || // 5
       texture_loader.load_texture("../resources/6.tga".to_string()) == false || // 6
       texture_loader.load_texture("../resources/7.tga".to_string()) == false || // 7
       texture_loader.load_texture("../resources/8.tga".to_string()) == false || // 8
       texture_loader.load_texture("../resources/9.tga".to_string()) == false || // 9
       texture_loader.load_texture("../resources/10.tga".to_string()) == false || // 10
       texture_loader.load_texture("../resources/sky.tga".to_string()) == false || // 11
       texture_loader.load_texture("../resources/weapons/gun_1.png".to_string()) == false || // 12
       texture_loader.load_texture("../resources/weapons/gun_2.png".to_string()) == false || // 13
       texture_loader.load_texture("../resources/weapons/gun_3.png".to_string()) == false || // 14
       texture_loader.load_texture("../resources/weapons/gun_4.png".to_string()) == false || // 15
       texture_loader.load_texture("../resources/weapons/gun_5.png".to_string()) == false || // 16
       texture_loader.load_texture("../resources/weapons/gun_6.png".to_string()) == false || // 17
       texture_loader.load_texture("../resources/weapons/gun_shadow.png".to_string()) == false || // 18
       texture_loader.load_texture("../resources/weapons/gun2_1.png".to_string()) == false || // 19
       texture_loader.load_texture("../resources/weapons/gun2_2.png".to_string()) == false || // 20
       texture_loader.load_texture("../resources/weapons/gun2_3.png".to_string()) == false || // 21
       texture_loader.load_texture("../resources/weapons/gun2_4.png".to_string()) == false || // 22
       texture_loader.load_texture("../resources/weapons/gun2_5.png".to_string()) == false || // 23
       texture_loader.load_texture("../resources/weapons/gun2_6.png".to_string()) == false || // 24
       texture_loader.load_texture("../resources/weapons/gun2_shadow.png".to_string()) == false || // 25
       texture_loader.load_texture("../resources/weapons/gun3_1.png".to_string()) == false || // 26
       texture_loader.load_texture("../resources/weapons/gun3_2.png".to_string()) == false || // 27
       texture_loader.load_texture("../resources/weapons/gun3_3.png".to_string()) == false || // 28
       texture_loader.load_texture("../resources/weapons/gun3_4.png".to_string()) == false || // 29
       texture_loader.load_texture("../resources/weapons/gun3_5.png".to_string()) == false || // 30
       texture_loader.load_texture("../resources/weapons/gun3_6.png".to_string()) == false || // 31
       texture_loader.load_texture("../resources/weapons/gun3_shadow.png".to_string()) == false || // 32
       texture_loader.load_texture("../resources/weapons/cut_1.png".to_string()) == false || // 33
       texture_loader.load_texture("../resources/weapons/cut_2.png".to_string()) == false || // 34
       texture_loader.load_texture("../resources/weapons/cut_3.png".to_string()) == false || // 35
       texture_loader.load_texture("../resources/weapons/cut_4.png".to_string()) == false || // 36
       texture_loader.load_texture("../resources/weapons/cut_5.png".to_string()) == false || // 37
       texture_loader.load_texture("../resources/weapons/cut_6.png".to_string()) == false || //38
       texture_loader.load_texture("../resources/weapons/cut_shadow.png".to_string()) == false || // 39
       texture_loader.load_texture("../resources/face1.png".to_string()) == false || //40
       texture_loader.load_texture("../resources/face2.png".to_string()) == false || //41
       texture_loader.load_texture("../resources/face3.png".to_string()) == false { //42
        fail!("Error : Cannot load texture.");
    }
    texture_loader
}

fn main() -> () {
    // Check if a custom width is set.
    let args = os::args();
    let mut width : uint = 768;
    let mut height : uint = 480;
    let mut noground : bool = false;
    let mut i_args = 1;

    while i_args < args.len() {
        match args.get(i_args).as_slice() {
            "--help"       => { display_help(); return; },
            "--noground"   => noground = true,
            "-w"           => {
                if i_args + 2 >= args.len() { fail!("Error missing arguments for -w option."); }
                width = from_str::<uint>(args.get(i_args + 1).as_slice()).expect("Error the first parameter after -w argument is not a width!");
                height = from_str::<uint>(args.get(i_args + 2).as_slice()).expect("Error the second parameter after -w argument is not a width!");
                i_args += 2;
            },
            _              => fail!("Error unknown argument."),
        }
        i_args += 1;
    }

    // Create the render_window.
    let settings = ContextSettings::default();
    let video_mode = VideoMode::new_init(width, height, 32);
    // let video_mode = VideoMode::new_init(512, 384, 32);
    let mut render_window = RenderWindow::new(video_mode,
                                              "Rustenstein3D",
                                              Close,
                                              &settings).expect("Error : Cannot create a render_window!");

    // set the framerate limit to 30 fps.
    render_window.set_framerate_limit(40);

    // hide the cursor.
    render_window.set_mouse_cursor_visible(false);

    // set the mouse positon on the center of the window
    render_window.set_mouse_position(&Vector2i {x : width as i32 / 2,
                                                y : height as i32 / 2});

    // Create the font for the FPS_handler.
    let font = Font::new_from_file("../resources/sansation.ttf")
        .expect("Error : Cannot load font, font resources/sansation.ttf doesn.t exist!");

    // Create the texture loader and load textures
    let texture_loader = load_texture();

    // Create the game_loop and activate the fps handler.
    let mut game_loop = game::GameLoop::new(render_window,
                                            &texture_loader,
                                            noground);
    game_loop.activate_FPS(&font);

    game_loop.run();
}<|endoftext|>