<commit_before>use std::fs::File;
use std::io::{Read, Write};
use std::mem;
use std::thread;

use keymap;

pub fn keyboard() {
    let mut file = File::open("irq:1").expect("ps2d: failed to open irq:1");
    let mut input = File::open("display:input").expect("ps2d: failed to open display:input");

    let mut lshift = false;
    let mut rshift = false;
    loop {
        let mut irqs = [0; 8];
        if file.read(&mut irqs).expect("ps2d: failed to read irq:1") >= mem::size_of::<usize>() {
            let data: u8;
            unsafe {
                asm!("in al, dx" : "={al}"(data) : "{dx}"(0x60) : : "intel", "volatile");
            }

            file.write(&irqs).expect("ps2d: failed to write irq:1");

            let (scancode, pressed) = if data >= 0x80 {
                (data - 0x80, false)
            } else {
                (data, true)
            };

            if scancode == 0x2A {
                lshift = pressed;
            } else if scancode == 0x36 {
                rshift = pressed;
            } else if pressed {
                let c = keymap::get_char(scancode, lshift || rshift);
                if c != '\0' {
                    print!("{}", c);
                    input.write(&[c as u8]).expect("ps2d: failed to write input");
                }
            }
        } else {
            thread::yield_now();
        }
    }
}
<commit_msg>Do not write ps2d keyboard to serial<commit_after>use std::fs::File;
use std::io::{Read, Write};
use std::mem;
use std::thread;

use keymap;

pub fn keyboard() {
    let mut file = File::open("irq:1").expect("ps2d: failed to open irq:1");
    let mut input = File::open("display:input").expect("ps2d: failed to open display:input");

    let mut lshift = false;
    let mut rshift = false;
    loop {
        let mut irqs = [0; 8];
        if file.read(&mut irqs).expect("ps2d: failed to read irq:1") >= mem::size_of::<usize>() {
            let data: u8;
            unsafe {
                asm!("in al, dx" : "={al}"(data) : "{dx}"(0x60) : : "intel", "volatile");
            }

            file.write(&irqs).expect("ps2d: failed to write irq:1");

            let (scancode, pressed) = if data >= 0x80 {
                (data - 0x80, false)
            } else {
                (data, true)
            };

            if scancode == 0x2A {
                lshift = pressed;
            } else if scancode == 0x36 {
                rshift = pressed;
            } else if pressed {
                let c = keymap::get_char(scancode, lshift || rshift);
                if c != '\0' {
                    input.write(&[c as u8]).expect("ps2d: failed to write input");
                }
            }
        } else {
            thread::yield_now();
        }
    }
}
<|endoftext|>


<commit_before>//! RAII guards for hazards.

use std::{cmp, ops};
use std::sync::atomic;
use {hazard, local};

#[cfg(debug_assertions)]
use std::cell::Cell;
#[cfg(debug_assertions)]
thread_local! {
    /// Number of guards the current thread is creating.
    static CURRENT_CREATING: Cell<usize> = Cell::new(0);
}

/// Assert (in debug mode) that no guards are currently being created in this thread.
///
/// This shall be used when you want to ensure, that a function called within the guard constructor
/// doesn't cause endless looping, due to the blocked hazard.
///
/// In particular, it should be called in functions that could trigger a garbage collection, thus
/// requiring that hazards are eventually unblocked.
pub fn debug_assert_no_create() {
    #[cfg(debug_assertions)]
    CURRENT_CREATING.with(|x| assert_eq!(x.get(), 0));
}

/// A RAII guard protecting from garbage collection.
///
/// This "guards" the held pointer against garbage collection. First when all guards of said
/// pointer is gone (the data is unreachable), it can be collected.
// TODO: Remove this `'static` bound.
#[must_use = "\
    You are getting a `conc::Guard<T>` without using it, which means it is potentially \
    unnecessary overhead. Consider replacing the method with something that doesn't \
    return a guard.\
"]
#[derive(Debug)]
pub struct Guard<T: 'static + ?Sized> {
    /// The inner hazard.
    hazard: hazard::Writer,
    /// The pointer to the protected object.
    pointer: &'static T,
}

impl<T: ?Sized> Guard<T> {
    /// (Failably) create a new guard.
    ///
    /// This has all the same restrictions and properties as `Guard::new()` (please read its
    /// documentation before using), with the exception of being failable.
    ///
    /// This means that the closure can return and error and abort the creation of the guard.
    pub fn try_new<F, E>(ptr: F) -> Result<Guard<T>, E>
    where F: FnOnce() -> Result<&'static T, E> {
        // Increment the number of guards currently being created.
        #[cfg(debug_assertions)]
        CURRENT_CREATING.with(|x| x.set(x.get() + 1));

        // Get a hazard in blocked state.
        let hazard = local::get_hazard();

        // This fence is necessary for ensuring that `hazard` does not get reordered to after `ptr`
        // has run.
        // TODO: Is this fence even necessary?
        atomic::fence(atomic::Ordering::SeqCst);

        // Right here, any garbage collection is blocked, due to the hazard above. This ensures
        // that between the potential read in `ptr` and it being protected by the hazard, there
        // will be no premature free.

        // Evaluate the pointer through the closure.
        let res = ptr();

        // Decrement the number of guards currently being created.
        #[cfg(debug_assertions)]
        CURRENT_CREATING.with(|x| x.set(x.get() - 1));

        match res {
            Ok(ptr) => {
                // Now that we have the pointer, we can protect it by the hazard, unblocking a pending
                // garbage collection if it exists.
                hazard.protect(ptr as *const T as *const u8);

                Ok(Guard {
                    hazard: hazard,
                    pointer: ptr,
                })
            },
            Err(err) => {
                // Set the hazard to free to ensure that the hazard doesn't remain blocking.
                hazard.free();

                Err(err)
            }
        }
    }

    /// Create a new guard.
    ///
    /// Because it must ensure that no garbage collection happens until the pointer is read, it
    /// takes a closure, which is evaluated to the pointer the guard will hold. During the span of
    /// this closure, garbage collection is ensured to not happen, making it safe to read from an
    /// atomic pointer without risking the ABA problem.
    ///
    /// # Important!
    ///
    /// It is very important that this closure does not contain anything which might cause a
    /// garbage collection, as garbage collecting inside this closure will cause the current thread
    /// to be blocked infinitely (because the hazard is blocked) and stop all other threads from
    /// collecting garbage, leading to memory leaks in those.
    pub fn new<F>(ptr: F) -> Guard<T>
    where F: FnOnce() -> &'static T {
        Guard::try_new::<_, ()>(|| Ok(ptr())).unwrap()
    }

    /// Conditionally create a guard.
    ///
    /// This acts `try_new`, but with `Option` instead of `Result`.
    pub fn maybe_new<F>(ptr: F) -> Option<Guard<T>>
    where F: FnOnce() -> Option<&'static T> {
        Guard::try_new(|| ptr().ok_or(())).ok()
    }

    /// Map the pointer to another.
    ///
    /// This allows one to map a pointer to a pointer e.g. to an object referenced by the old. It
    /// is very convenient for creating APIs without the need for creating a wrapper type.
    // TODO: Is this sound?
    pub fn map<U, F>(self, f: F) -> Guard<U>
    where F: FnOnce(&T) -> &U {
        Guard {
            hazard: self.hazard,
            pointer: f(self.pointer),
        }
    }

    /// Get the raw pointer of this guard.
    pub fn as_ptr(&self) -> *const T {
        self.pointer
    }
}

impl<T> cmp::PartialEq for Guard<T> {
    fn eq(&self, other: &Guard<T>) -> bool {
        self.as_ptr() == other.as_ptr()
    }
}

impl<T> cmp::Eq for Guard<T> {}

impl<T> ops::Deref for Guard<T> {
    type Target = T;

    fn deref(&self) -> &T {
        self.pointer
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use std::mem;

    #[test]
    #[should_panic]
    fn panic_during_guard_creation() {
        let _ = Guard::new(|| -> &'static u8 { panic!() });
    }

    #[test]
    fn nested_guard_creation() {
        for _ in 0..100 {
            let _ = Guard::new(|| {
                mem::forget(Guard::new(|| "blah"));
                "blah"
            });
        }
    }

    #[cfg(debug_assertions)]
    #[test]
    #[should_panic]
    fn debug_catch_infinite_blockage() {
        let _ = Guard::new(|| {
            local::export_garbage();
            "blah"
        });
    }
}
<commit_msg>Fix documentation for wrong use of `Guard::new`.<commit_after>//! RAII guards for hazards.

use std::{cmp, ops};
use std::sync::atomic;
use {hazard, local};

#[cfg(debug_assertions)]
use std::cell::Cell;
#[cfg(debug_assertions)]
thread_local! {
    /// Number of guards the current thread is creating.
    static CURRENT_CREATING: Cell<usize> = Cell::new(0);
}

/// Assert (in debug mode) that no guards are currently being created in this thread.
///
/// This shall be used when you want to ensure, that a function called within the guard constructor
/// doesn't cause endless looping, due to the blocked hazard.
///
/// In particular, it should be called in functions that could trigger a garbage collection, thus
/// requiring that hazards are eventually unblocked.
pub fn debug_assert_no_create() {
    #[cfg(debug_assertions)]
    CURRENT_CREATING.with(|x| assert_eq!(x.get(), 0));
}

/// A RAII guard protecting from garbage collection.
///
/// This "guards" the held pointer against garbage collection. First when all guards of said
/// pointer is gone (the data is unreachable), it can be collected.
// TODO: Remove this `'static` bound.
#[must_use = "\
    You are getting a `conc::Guard<T>` without using it, which means it is potentially \
    unnecessary overhead. Consider replacing the method with something that doesn't \
    return a guard.\
"]
#[derive(Debug)]
pub struct Guard<T: 'static + ?Sized> {
    /// The inner hazard.
    hazard: hazard::Writer,
    /// The pointer to the protected object.
    pointer: &'static T,
}

impl<T: ?Sized> Guard<T> {
    /// (Failably) create a new guard.
    ///
    /// This has all the same restrictions and properties as `Guard::new()` (please read its
    /// documentation before using), with the exception of being failable.
    ///
    /// This means that the closure can return and error and abort the creation of the guard.
    pub fn try_new<F, E>(ptr: F) -> Result<Guard<T>, E>
    where F: FnOnce() -> Result<&'static T, E> {
        // Increment the number of guards currently being created.
        #[cfg(debug_assertions)]
        CURRENT_CREATING.with(|x| x.set(x.get() + 1));

        // Get a hazard in blocked state.
        let hazard = local::get_hazard();

        // This fence is necessary for ensuring that `hazard` does not get reordered to after `ptr`
        // has run.
        // TODO: Is this fence even necessary?
        atomic::fence(atomic::Ordering::SeqCst);

        // Right here, any garbage collection is blocked, due to the hazard above. This ensures
        // that between the potential read in `ptr` and it being protected by the hazard, there
        // will be no premature free.

        // Evaluate the pointer through the closure.
        let res = ptr();

        // Decrement the number of guards currently being created.
        #[cfg(debug_assertions)]
        CURRENT_CREATING.with(|x| x.set(x.get() - 1));

        match res {
            Ok(ptr) => {
                // Now that we have the pointer, we can protect it by the hazard, unblocking a pending
                // garbage collection if it exists.
                hazard.protect(ptr as *const T as *const u8);

                Ok(Guard {
                    hazard: hazard,
                    pointer: ptr,
                })
            },
            Err(err) => {
                // Set the hazard to free to ensure that the hazard doesn't remain blocking.
                hazard.free();

                Err(err)
            }
        }
    }

    /// Create a new guard.
    ///
    /// Because it must ensure that no garbage collection happens until the pointer is read, it
    /// takes a closure, which is evaluated to the pointer the guard will hold. During the span of
    /// this closure, garbage collection is ensured to not happen, making it safe to read from an
    /// atomic pointer without risking the ABA problem.
    ///
    /// # Important!
    ///
    /// It is very important that this closure does not contain anything which might cause a
    /// garbage collection, as garbage collecting inside this closure will cause the current thread
    /// to be blocked infinitely (because the hazard is blocked) and stop all other threads from
    /// collecting garbage, leading to memory leaks in those — unless it is compiled in debug mode,
    /// in which case it will likely panic.
    pub fn new<F>(ptr: F) -> Guard<T>
    where F: FnOnce() -> &'static T {
        Guard::try_new::<_, ()>(|| Ok(ptr())).unwrap()
    }

    /// Conditionally create a guard.
    ///
    /// This acts `try_new`, but with `Option` instead of `Result`.
    pub fn maybe_new<F>(ptr: F) -> Option<Guard<T>>
    where F: FnOnce() -> Option<&'static T> {
        Guard::try_new(|| ptr().ok_or(())).ok()
    }

    /// Map the pointer to another.
    ///
    /// This allows one to map a pointer to a pointer e.g. to an object referenced by the old. It
    /// is very convenient for creating APIs without the need for creating a wrapper type.
    // TODO: Is this sound?
    pub fn map<U, F>(self, f: F) -> Guard<U>
    where F: FnOnce(&T) -> &U {
        Guard {
            hazard: self.hazard,
            pointer: f(self.pointer),
        }
    }

    /// Get the raw pointer of this guard.
    pub fn as_ptr(&self) -> *const T {
        self.pointer
    }
}

impl<T> cmp::PartialEq for Guard<T> {
    fn eq(&self, other: &Guard<T>) -> bool {
        self.as_ptr() == other.as_ptr()
    }
}

impl<T> cmp::Eq for Guard<T> {}

impl<T> ops::Deref for Guard<T> {
    type Target = T;

    fn deref(&self) -> &T {
        self.pointer
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use std::mem;

    #[test]
    #[should_panic]
    fn panic_during_guard_creation() {
        let _ = Guard::new(|| -> &'static u8 { panic!() });
    }

    #[test]
    fn nested_guard_creation() {
        for _ in 0..100 {
            let _ = Guard::new(|| {
                mem::forget(Guard::new(|| "blah"));
                "blah"
            });
        }
    }

    #[cfg(debug_assertions)]
    #[test]
    #[should_panic]
    fn debug_catch_infinite_blockage() {
        let _ = Guard::new(|| {
            local::export_garbage();
            "blah"
        });
    }
}
<|endoftext|>


<commit_before>//! The thread-local state.

use std::{mem, thread};
use std::cell::RefCell;
use {global, hazard, guard};
use garbage::Garbage;

thread_local! {
    /// The state of this thread.
    static STATE: RefCell<State> = RefCell::new(State::default());
}

/// Add new garbage to be deleted.
///
/// This garbage is pushed to a thread-local queue. When enough garbage is accumulated in the
/// thread, it is exported to the global state.
pub fn add_garbage(garbage: Garbage) {
    // Since this function can trigger a GC, it must not be called inside a guard constructor.
    guard::debug_assert_no_create();

    debug_assert!(!garbage.ptr().is_null(), "Garbage is a null pointer. If this is intentional, \
        consider running the destructor directly instead.");

    if STATE.state() == thread::LocalKeyState::Destroyed {
        // The state was deinitialized, so we must rely on the global state for queueing garbage.
        global::export_garbage(vec![garbage]);
    } else {
        // Add the garbage.
        if STATE.with(|s| s.borrow_mut().add_garbage(garbage)) {
            // The local state exported garbage to the global state, hence we must tick in order to
            // ensure that the garbage is periodically collected.
            global::tick();
        }
    }
}

/// Get a blocked hazard.
///
/// If possible, this will simply pop one of the thread-local cache of hazards. Otherwise, one must
/// be registered in the global state.
///
/// # Fence
///
/// This does not fence, and you must thus be careful with updating the value afterwards, as
/// reordering can happen, meaning that the hazard has not been blocked yet.
pub fn get_hazard() -> hazard::Writer {
    if STATE.state() == thread::LocalKeyState::Destroyed {
        // The state was deinitialized, so we must rely on the global state for creating new
        // hazards.
        global::create_hazard()
    } else {
        STATE.with(|s| s.borrow_mut().get_hazard())
    }
}

/// Free a hazard.
///
/// This frees a hazard to the thread-local cache of hazards.
///
/// It is important that the hazard is **not** in blocked state, as such thing can cause infinite
/// looping.
///
/// # Panics
///
/// This might panic in debug mode if the hazard given is in blocked state.
pub fn free_hazard(hazard: hazard::Writer) {
    // Since this function can trigger a GC, it must not be called inside a guard constructor.
    guard::debug_assert_no_create();

    debug_assert!(!hazard.is_blocked(), "Freeing a blocked hazards. See docs.");

    if STATE.state() == thread::LocalKeyState::Destroyed {
        // Since the state was deinitialized, we cannot store it for later reuse, so we are forced
        // to simply kill the hazard.
        hazard.kill();
    } else {
        STATE.with(|s| s.borrow_mut().free_hazard(hazard));
    }
}

/// Export the garbage of this thread to the global state.
///
/// This is useful for propagating accumulated garbage such that it can be destroyed by the next
/// garbage collection.
pub fn export_garbage() {
    // Since this function can trigger a GC, it must not be called inside a guard constructor.
    guard::debug_assert_no_create();

    // We can only export when the TLS variable isn't destroyed. Otherwise, there would be nothing
    // to export!
    if STATE.state() != thread::LocalKeyState::Destroyed {
        STATE.with(|s| s.borrow_mut().export_garbage());
        // We tick after the state is no longer reserved, as the tick could potentially call
        // destructor that access the TLS variable.
        global::tick();
    }
}

/// A thread-local state.
#[derive(Default)]
struct State {
    /// The cached garbage waiting to be exported to the global state.
    garbage: Vec<Garbage>,
    /// The cache of currently available hazards.
    ///
    /// We maintain this cache to avoid the performance hit of creating new hazards.
    ///
    /// The hazards in this vector are not necessarily in state "free". Only when a sufficient
    /// amount of available hazards has accumulated, they will be set to free. This means that we
    /// don't have to reset the state of a hazard after usage, giving a quite significant speed-up.
    available_hazards: Vec<hazard::Writer>,
    /// The hazards in the cache before this index are free.
    ///
    /// This number keeps track what hazards in `self.available_hazard` are set to state "free".
    /// Before this index, every hazard must be set to "free".
    ///
    /// It is useful for knowing when to free the hazards to allow garbage collection.
    available_hazards_free_before: usize,
}

impl State {
    /// Get the number of hazards in the cache which are not in state "free".
    fn non_free_hazards(&self) -> usize {
        self.available_hazards.len() - self.available_hazards_free_before
    }

    /// See `get_hazard()`.
    fn get_hazard(&mut self) -> hazard::Writer {
        // Check if there is hazards in the cache.
        if let Some(hazard) = self.available_hazards.pop() {
            // There is; we don't need to create a new hazard.

            // Since the hazard popped from the cache is not blocked, we must block the hazard to
            // satisfy the requirements of this function.
            hazard.block();
            hazard
        } else {
            // There is not; we must create a new hazard.
            global::create_hazard()
        }
    }

    /// See `free_hazard()`.
    fn free_hazard(&mut self, hazard: hazard::Writer) {
        /// The maximal amount of hazards before cleaning up.
        ///
        /// With "cleaning up" we mean setting the state of the hazards to "free" in order to allow
        /// garbage collection of the object it is currently protecting.
        const MAX_NON_FREE_HAZARDS: usize = 128;

        // Push the given hazard to the cache.
        self.available_hazards.push(hazard);

        // Check if we exceeded the limit.
        if self.non_free_hazards() > MAX_NON_FREE_HAZARDS {
            // We did; we must now set the non-free hazards to "free".
            for i in &self.available_hazards[self.available_hazards_free_before..] {
                i.set(hazard::State::Free);
            }

            // Update the counter such that we mark the new hazards set to "free".
            self.available_hazards_free_before = self.available_hazards.len();
        }
    }

    /// Queues garbage to destroy.
    ///
    /// Eventually the added garbage will be exported to the global state through
    /// `global::add_garbage()`.
    ///
    /// See `add_garbage` for more information.
    ///
    /// When this happens (i.e. the global state gets the garbage), it returns `true`. Otherwise,
    /// it returns `false`.
    fn add_garbage(&mut self, garbage: Garbage) -> bool {
        /// The maximal amount of garbage before exportation to the global state.
        const MAX_GARBAGE: usize = 128;

        // Push the garbage to the cache of garbage.
        self.garbage.push(garbage);

        // Export the garbage if it exceeds the limit.
        // TODO: use memory instead of items as a metric.
        if self.garbage.len() > MAX_GARBAGE {
            self.export_garbage();
            true
        } else { false }
    }

    /// See `export_garbage()` for more information.
    fn export_garbage(&mut self) {
        // Clear the vector and export the garbage.
        global::export_garbage(mem::replace(&mut self.garbage, Vec::new()));
    }
}

impl Drop for State {
    fn drop(&mut self) {
        // The thread is exiting, thus we must export the garbage to the global state to avoid
        // memory leaks. It is very important that this does indeed not tick, as causing garbage
        // collection means accessing RNG state, a TLS variable, which cannot be done when, we are
        // here, after it has deinitialized.
        self.export_garbage();

        // Clear every hazard to "dead" state.
        for hazard in self.available_hazards.drain(..) {
            hazard.kill();
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use garbage;
    use std::{mem, ptr};

    #[cfg(debug_assertions)]
    #[should_panic]
    #[test]
    fn debug_free_blocked() {
        let (writer, reader) = hazard::create();
        mem::forget(reader);

        free_hazard(writer);
    }

    #[cfg(debug_assertions)]
    #[should_panic]
    #[test]
    fn debug_add_null_garbage() {
        add_garbage(unsafe { garbage::Garbage::new_box(ptr::null::<u8>()) });
    }
}
<commit_msg>Test that the destructor actually runs in `conc::local`.<commit_after>//! The thread-local state.

use std::{mem, thread};
use std::cell::RefCell;
use {global, hazard, guard};
use garbage::Garbage;

thread_local! {
    /// The state of this thread.
    static STATE: RefCell<State> = RefCell::new(State::default());
}

/// Add new garbage to be deleted.
///
/// This garbage is pushed to a thread-local queue. When enough garbage is accumulated in the
/// thread, it is exported to the global state.
pub fn add_garbage(garbage: Garbage) {
    // Since this function can trigger a GC, it must not be called inside a guard constructor.
    guard::debug_assert_no_create();

    debug_assert!(!garbage.ptr().is_null(), "Garbage is a null pointer. If this is intentional, \
        consider running the destructor directly instead.");

    if STATE.state() == thread::LocalKeyState::Destroyed {
        // The state was deinitialized, so we must rely on the global state for queueing garbage.
        global::export_garbage(vec![garbage]);
    } else {
        // Add the garbage.
        if STATE.with(|s| s.borrow_mut().add_garbage(garbage)) {
            // The local state exported garbage to the global state, hence we must tick in order to
            // ensure that the garbage is periodically collected.
            global::tick();
        }
    }
}

/// Get a blocked hazard.
///
/// If possible, this will simply pop one of the thread-local cache of hazards. Otherwise, one must
/// be registered in the global state.
///
/// # Fence
///
/// This does not fence, and you must thus be careful with updating the value afterwards, as
/// reordering can happen, meaning that the hazard has not been blocked yet.
pub fn get_hazard() -> hazard::Writer {
    if STATE.state() == thread::LocalKeyState::Destroyed {
        // The state was deinitialized, so we must rely on the global state for creating new
        // hazards.
        global::create_hazard()
    } else {
        STATE.with(|s| s.borrow_mut().get_hazard())
    }
}

/// Free a hazard.
///
/// This frees a hazard to the thread-local cache of hazards.
///
/// It is important that the hazard is **not** in blocked state, as such thing can cause infinite
/// looping.
///
/// # Panics
///
/// This might panic in debug mode if the hazard given is in blocked state.
pub fn free_hazard(hazard: hazard::Writer) {
    // Since this function can trigger a GC, it must not be called inside a guard constructor.
    guard::debug_assert_no_create();

    debug_assert!(!hazard.is_blocked(), "Freeing a blocked hazards. See docs.");

    if STATE.state() == thread::LocalKeyState::Destroyed {
        // Since the state was deinitialized, we cannot store it for later reuse, so we are forced
        // to simply kill the hazard.
        hazard.kill();
    } else {
        STATE.with(|s| s.borrow_mut().free_hazard(hazard));
    }
}

/// Export the garbage of this thread to the global state.
///
/// This is useful for propagating accumulated garbage such that it can be destroyed by the next
/// garbage collection.
pub fn export_garbage() {
    // Since this function can trigger a GC, it must not be called inside a guard constructor.
    guard::debug_assert_no_create();

    // We can only export when the TLS variable isn't destroyed. Otherwise, there would be nothing
    // to export!
    if STATE.state() != thread::LocalKeyState::Destroyed {
        STATE.with(|s| s.borrow_mut().export_garbage());
        // We tick after the state is no longer reserved, as the tick could potentially call
        // destructor that access the TLS variable.
        global::tick();
    }
}

/// A thread-local state.
#[derive(Default)]
struct State {
    /// The cached garbage waiting to be exported to the global state.
    garbage: Vec<Garbage>,
    /// The cache of currently available hazards.
    ///
    /// We maintain this cache to avoid the performance hit of creating new hazards.
    ///
    /// The hazards in this vector are not necessarily in state "free". Only when a sufficient
    /// amount of available hazards has accumulated, they will be set to free. This means that we
    /// don't have to reset the state of a hazard after usage, giving a quite significant speed-up.
    available_hazards: Vec<hazard::Writer>,
    /// The hazards in the cache before this index are free.
    ///
    /// This number keeps track what hazards in `self.available_hazard` are set to state "free".
    /// Before this index, every hazard must be set to "free".
    ///
    /// It is useful for knowing when to free the hazards to allow garbage collection.
    available_hazards_free_before: usize,
}

impl State {
    /// Get the number of hazards in the cache which are not in state "free".
    fn non_free_hazards(&self) -> usize {
        self.available_hazards.len() - self.available_hazards_free_before
    }

    /// See `get_hazard()`.
    fn get_hazard(&mut self) -> hazard::Writer {
        // Check if there is hazards in the cache.
        if let Some(hazard) = self.available_hazards.pop() {
            // There is; we don't need to create a new hazard.

            // Since the hazard popped from the cache is not blocked, we must block the hazard to
            // satisfy the requirements of this function.
            hazard.block();
            hazard
        } else {
            // There is not; we must create a new hazard.
            global::create_hazard()
        }
    }

    /// See `free_hazard()`.
    fn free_hazard(&mut self, hazard: hazard::Writer) {
        /// The maximal amount of hazards before cleaning up.
        ///
        /// With "cleaning up" we mean setting the state of the hazards to "free" in order to allow
        /// garbage collection of the object it is currently protecting.
        const MAX_NON_FREE_HAZARDS: usize = 128;

        // Push the given hazard to the cache.
        self.available_hazards.push(hazard);

        // Check if we exceeded the limit.
        if self.non_free_hazards() > MAX_NON_FREE_HAZARDS {
            // We did; we must now set the non-free hazards to "free".
            for i in &self.available_hazards[self.available_hazards_free_before..] {
                i.set(hazard::State::Free);
            }

            // Update the counter such that we mark the new hazards set to "free".
            self.available_hazards_free_before = self.available_hazards.len();
        }
    }

    /// Queues garbage to destroy.
    ///
    /// Eventually the added garbage will be exported to the global state through
    /// `global::add_garbage()`.
    ///
    /// See `add_garbage` for more information.
    ///
    /// When this happens (i.e. the global state gets the garbage), it returns `true`. Otherwise,
    /// it returns `false`.
    fn add_garbage(&mut self, garbage: Garbage) -> bool {
        /// The maximal amount of garbage before exportation to the global state.
        const MAX_GARBAGE: usize = 128;

        // Push the garbage to the cache of garbage.
        self.garbage.push(garbage);

        // Export the garbage if it exceeds the limit.
        // TODO: use memory instead of items as a metric.
        if self.garbage.len() > MAX_GARBAGE {
            self.export_garbage();
            true
        } else { false }
    }

    /// See `export_garbage()` for more information.
    fn export_garbage(&mut self) {
        // Clear the vector and export the garbage.
        global::export_garbage(mem::replace(&mut self.garbage, Vec::new()));
    }
}

impl Drop for State {
    fn drop(&mut self) {
        // The thread is exiting, thus we must export the garbage to the global state to avoid
        // memory leaks. It is very important that this does indeed not tick, as causing garbage
        // collection means accessing RNG state, a TLS variable, which cannot be done when, we are
        // here, after it has deinitialized.
        self.export_garbage();

        // Clear every hazard to "dead" state.
        for hazard in self.available_hazards.drain(..) {
            hazard.kill();
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use garbage::Garbage;
    use hazard;
    use std::{mem, ptr};

    #[test]
    fn dtor_runs() {
        fn dtor(x: *const u8) {
            unsafe {
                *(x as *mut u8) = 1;
            }
        }

        for _ in 0..1000 {
            let b = Box::new(0);
            let h = get_hazard();
            h.set(hazard::State::Protect(&*b));
            add_garbage(Garbage::new(&*b, dtor));
            ::gc();
            assert_eq!(*b, 0);
            ::gc();
            h.set(hazard::State::Free);
            ::gc();
            assert_eq!(*b, 1);
        }
    }

    #[cfg(debug_assertions)]
    #[should_panic]
    #[test]
    fn debug_free_blocked() {
        let (writer, reader) = hazard::create();
        mem::forget(reader);

        free_hazard(writer);
    }

    #[cfg(debug_assertions)]
    #[should_panic]
    #[test]
    fn debug_add_null_garbage() {
        add_garbage(unsafe { Garbage::new_box(ptr::null::<u8>()) });
    }
}
<|endoftext|>


<commit_before>#![cfg(feature = "serde")]

#[macro_use]
extern crate enum_map;
#[macro_use]
extern crate serde;
extern crate serde_json;

use enum_map::EnumMap;

#[derive(Debug, EnumMap, PartialEq, Deserialize, Serialize)]
enum Example {
    A,
    B,
}

const JSON: &str = r#"{"A":5,"B":10}"#;

#[test]
fn json_serialization() {
    let map = enum_map! { Example::A => 5, Example::B => 10 };
    assert_eq!(serde_json::to_string(&map).unwrap(), String::from(JSON));
}

#[test]
fn json_deserialization() {
    let example: EnumMap<Example, i32> = serde_json::from_str(JSON).unwrap();
    assert_eq!(example, enum_map! { Example::A => 5, Example::B => 10 });
}
<commit_msg>Test invalid deserialization<commit_after>#![cfg(feature = "serde")]

#[macro_use]
extern crate enum_map;
#[macro_use]
extern crate serde;
extern crate serde_json;

use enum_map::EnumMap;

#[derive(Debug, EnumMap, PartialEq, Deserialize, Serialize)]
enum Example {
    A,
    B,
}

const JSON: &str = r#"{"A":5,"B":10}"#;

#[test]
fn json_serialization() {
    let map = enum_map! { Example::A => 5, Example::B => 10 };
    assert_eq!(serde_json::to_string(&map).unwrap(), String::from(JSON));
}

#[test]
fn json_deserialization() {
    let example: EnumMap<Example, i32> = serde_json::from_str(JSON).unwrap();
    assert_eq!(example, enum_map! { Example::A => 5, Example::B => 10 });
}

#[test]
fn json_invalid_deserialization() {
    let example: Result<EnumMap<Example, i32>, _> = serde_json::from_str(r"{}");
    assert!(example.is_err());
}<|endoftext|>


<commit_before>// TODO: These tests are in this crate as a workaround for testing treeflection_derive
// while tests folder does not work with macros 1.1 https://github.com/rust-lang/rust/issues/37480
// alternatively we could move the other tests into this crate

#![feature(proc_macro)]

extern crate treeflection;
#[macro_use] extern crate treeflection_derive;
#[macro_use] extern crate matches;

use treeflection::{Node, NodeRunner, NodeToken};

#[derive(Node)]
struct Parent {
    pub foo: String,
    pub bar: u32,
    pub baz: bool,
    pub child: Child,
    private: i64,
}

#[derive(Node)]
struct Child {
    pub qux: i32,
}

impl Parent {
    fn new() -> Parent {
        Parent {
            foo: String::from("hiya"),
            bar: 42,
            baz: true,
            child: Child {
                qux: -13,
            },
            private: 1337,
        }
    }
}

#[test]
fn get_struct() {
    let runner = NodeRunner { tokens: vec!(NodeToken::Get) };
    assert_eq!(Parent::new().node_step(runner), String::from("This is a struct"));
}

#[test]
fn copy_struct() {
    let runner = NodeRunner { tokens: vec!(NodeToken::CopyFrom) };
    assert_eq!(Parent::new().node_step(runner), String::from("Parent cannot 'CopyFrom'"));
}

#[test]
fn no_property() {
    let runner = NodeRunner { tokens: vec!(
        NodeToken::Get,
        NodeToken::ChainProperty(String::from("notfoo")),
    )};
    assert_eq!(Parent::new().node_step(runner), String::from("Parent does not have a property 'notfoo'"));
}

#[test]
fn private_property() {
    let runner = NodeRunner { tokens: vec!(
        NodeToken::Get,
        NodeToken::ChainProperty(String::from("private")),
    )};
    assert_eq!(Parent::new().node_step(runner), String::from("Parent does not have a property 'private'"));
}

#[test]
fn string_property() {
    let runner = NodeRunner { tokens: vec!(
        NodeToken::Get,
        NodeToken::ChainProperty(String::from("foo")),
    )};
    assert_eq!(Parent::new().node_step(runner), String::from("hiya"));
}

#[test]
fn uint_property() {
    let runner = NodeRunner { tokens: vec!(
        NodeToken::Get,
        NodeToken::ChainProperty(String::from("bar")),
    )};
    assert_eq!(Parent::new().node_step(runner), String::from("42"));
}

#[test]
fn bool_property() {
    let runner = NodeRunner { tokens: vec!(
        NodeToken::Get,
        NodeToken::ChainProperty(String::from("baz")),
    )};
    assert_eq!(Parent::new().node_step(runner), String::from("true"));
}

#[test]
fn int_child_property() {
    let runner = NodeRunner { tokens: vec!(
        NodeToken::Get,
        NodeToken::ChainProperty(String::from("qux")),
        NodeToken::ChainProperty(String::from("child")),
    )};
    assert_eq!(Parent::new().node_step(runner), "-13");
}

#[derive(Node)]
enum SomeEnum {
    Foo,
    Bar,
    //Baz {x: f32, y: f32},
    Qux (u8),
    Quux (i64, String, bool),
}

//impl Node for SomeEnum {
//    fn node_step ( & mut self , mut runner : NodeRunner ) -> String {
//        match runner . step ( ) {
//            NodeToken :: Get => {
//                match self {
//                    &mut SomeEnum :: Foo => String::from("Foo") ,
//                    &mut SomeEnum :: Bar => String::from("Bar") ,
//                    &mut SomeEnum :: Qux(ref v1) => format!("Qux({})", v1) ,
//                    &mut SomeEnum :: Quux(ref v1, ref v2, ref v3) => format!("Quux({}, {}, {})", v1, v2, v3) ,
//                }
//            }
//            NodeToken :: Set ( value ) => {
//                match value.as_ref() {
//                    "Foo" => { *self = SomeEnum :: Foo; String::from("") } ,
//                    "Bar" => { *self = SomeEnum :: Bar; String::from("") } ,
//                    value_miss => { format!("{} is not a valid value for {}", value_miss, "SomeEnum") },
//                }
//            }
//            action => { format ! ( "{} cannot '{:?}'" , "SomeEnum" , action ) }
//        }
//    }
//}

#[test]
fn get_unit_enum() {
    let mut some_enum = SomeEnum::Foo;
    let runner = NodeRunner { tokens: vec!(NodeToken::Get) };
    assert_eq!(some_enum.node_step(runner), "Foo");

    let mut some_enum = SomeEnum::Bar;
    let runner = NodeRunner { tokens: vec!(NodeToken::Get) };
    assert_eq!(some_enum.node_step(runner), "Bar");
}

#[test]
fn set_unit_enum() {
    let mut some_enum = SomeEnum::Bar;
    let runner = NodeRunner { tokens: vec!( NodeToken::Set(String::from("Foo")) )};
    some_enum.node_step(runner);
    assert!(matches!(some_enum, SomeEnum::Foo));

    let mut some_enum = SomeEnum::Bar;
    let runner = NodeRunner { tokens: vec!( NodeToken::Set(String::from("Bar")) )};
    some_enum.node_step(runner);
    assert!(matches!(some_enum, SomeEnum::Bar));

    let mut some_enum = SomeEnum::Foo;
    let runner = NodeRunner { tokens: vec!( NodeToken::Set(String::from("Bar")) )};
    some_enum.node_step(runner);
    assert!(matches!(some_enum, SomeEnum::Bar));

    let mut some_enum = SomeEnum::Foo;
    let runner = NodeRunner { tokens: vec!( NodeToken::Set(String::from("Foo")) )};
    some_enum.node_step(runner);
    assert!(matches!(some_enum, SomeEnum::Foo));

    let mut some_enum = SomeEnum::Foo;
    let runner = NodeRunner { tokens: vec!( NodeToken::Set(String::from("Aether")) )};
    assert_eq!(some_enum.node_step(runner), "Aether is not a valid value for SomeEnum");
    assert!(matches!(some_enum, SomeEnum::Foo));
}

#[test]
fn get_tuple_enum() {
    let mut some_enum = SomeEnum::Qux(42);
    let runner = NodeRunner { tokens: vec!(NodeToken::Get) };
    assert_eq!(some_enum.node_step(runner), "Qux(42)");

    let mut some_enum = SomeEnum::Quux(-1337, String::from("YOYOYO"), true);
    let runner = NodeRunner { tokens: vec!(NodeToken::Get) };
    assert_eq!(some_enum.node_step(runner), "Quux(-1337, YOYOYO, true)");
}

#[test]
fn set_tuple_enum() {
}

#[test]
fn get_struct_enum() {
}

#[test]
fn set_struct_enum() {
}

#[test]
fn copy_enum() {
    let mut some_enum = SomeEnum::Foo;
    let runner = NodeRunner { tokens: vec!(NodeToken::CopyFrom) };
    assert_eq!(some_enum.node_step(runner), String::from("SomeEnum cannot 'CopyFrom'"));
}
<commit_msg>Add tuple enum setter test<commit_after>// TODO: These tests are in this crate as a workaround for testing treeflection_derive
// while tests folder does not work with macros 1.1 https://github.com/rust-lang/rust/issues/37480
// alternatively we could move the other tests into this crate

#![feature(proc_macro)]

extern crate treeflection;
#[macro_use] extern crate treeflection_derive;
#[macro_use] extern crate matches;

use treeflection::{Node, NodeRunner, NodeToken};

#[derive(Node)]
struct Parent {
    pub foo: String,
    pub bar: u32,
    pub baz: bool,
    pub child: Child,
    private: i64,
}

#[derive(Node)]
struct Child {
    pub qux: i32,
}

impl Parent {
    fn new() -> Parent {
        Parent {
            foo: String::from("hiya"),
            bar: 42,
            baz: true,
            child: Child {
                qux: -13,
            },
            private: 1337,
        }
    }
}

#[test]
fn get_struct() {
    let runner = NodeRunner { tokens: vec!(NodeToken::Get) };
    assert_eq!(Parent::new().node_step(runner), String::from("This is a struct"));
}

#[test]
fn copy_struct() {
    let runner = NodeRunner { tokens: vec!(NodeToken::CopyFrom) };
    assert_eq!(Parent::new().node_step(runner), String::from("Parent cannot 'CopyFrom'"));
}

#[test]
fn no_property() {
    let runner = NodeRunner { tokens: vec!(
        NodeToken::Get,
        NodeToken::ChainProperty(String::from("notfoo")),
    )};
    assert_eq!(Parent::new().node_step(runner), String::from("Parent does not have a property 'notfoo'"));
}

#[test]
fn private_property() {
    let runner = NodeRunner { tokens: vec!(
        NodeToken::Get,
        NodeToken::ChainProperty(String::from("private")),
    )};
    assert_eq!(Parent::new().node_step(runner), String::from("Parent does not have a property 'private'"));
}

#[test]
fn string_property() {
    let runner = NodeRunner { tokens: vec!(
        NodeToken::Get,
        NodeToken::ChainProperty(String::from("foo")),
    )};
    assert_eq!(Parent::new().node_step(runner), String::from("hiya"));
}

#[test]
fn uint_property() {
    let runner = NodeRunner { tokens: vec!(
        NodeToken::Get,
        NodeToken::ChainProperty(String::from("bar")),
    )};
    assert_eq!(Parent::new().node_step(runner), String::from("42"));
}

#[test]
fn bool_property() {
    let runner = NodeRunner { tokens: vec!(
        NodeToken::Get,
        NodeToken::ChainProperty(String::from("baz")),
    )};
    assert_eq!(Parent::new().node_step(runner), String::from("true"));
}

#[test]
fn int_child_property() {
    let runner = NodeRunner { tokens: vec!(
        NodeToken::Get,
        NodeToken::ChainProperty(String::from("qux")),
        NodeToken::ChainProperty(String::from("child")),
    )};
    assert_eq!(Parent::new().node_step(runner), "-13");
}

#[derive(Node)]
enum SomeEnum {
    Foo,
    Bar,
    //Baz {x: f32, y: f32},
    Qux (u8),
    Quux (i64, String, bool),
}

//impl Node for SomeEnum {
//    fn node_step ( & mut self , mut runner : NodeRunner ) -> String {
//        match runner . step ( ) {
//            NodeToken :: Get => {
//                match self {
//                    &mut SomeEnum :: Foo => String::from("Foo") ,
//                    &mut SomeEnum :: Bar => String::from("Bar") ,
//                    &mut SomeEnum :: Qux(ref v1) => format!("Qux({})", v1) ,
//                    &mut SomeEnum :: Quux(ref v1, ref v2, ref v3) => format!("Quux({}, {}, {})", v1, v2, v3) ,
//                }
//            }
//            NodeToken :: Set ( value ) => {
//                // TODO: Handle value not in correct format
//                let mut tokens: Vec<String> = vec!();
//                let enum_ident = match value.find('(') {
//                    Some(split_index) => {
//                        let inner_ident = &value[..split_index];
//                        let tuple = &value[split_index+1..value.len()-1];
//                        for element in tuple.split(',') {
//                            tokens.push(element.trim().to_string());
//                        }
//                        inner_ident
//                    }
//                    None => {
//                        value.as_str()
//                    }
//                };
//                match enum_ident {
//                    "Foo" => { *self = SomeEnum :: Foo; String :: from("") } ,
//                    "Bar" => { *self = SomeEnum :: Bar; String :: from("") } ,
//                    "Qux" => {
//                        let v0 = tokens[0].parse::<u8>().unwrap();
//                        *self = SomeEnum :: Qux (v0);
//                        String :: from("")
//                    } ,
//                    "Quux" => {
//                        let v0 = tokens[0].parse::<i64>().unwrap();
//                        let v1 = tokens[1].clone();
//                        let v2 = tokens[2].parse::<bool>().unwrap();
//                        *self = SomeEnum :: Quux (v0, v1, v2);
//                        String :: from("")
//                    } ,
//                    value_miss => { format!("{} is not a valid value for {}", value_miss, "SomeEnum") },
//                }
//            }
//            action => { format ! ( "{} cannot '{:?}'" , "SomeEnum" , action ) }
//        }
//    }
//}

#[test]
fn get_unit_enum() {
    let mut some_enum = SomeEnum::Foo;
    let runner = NodeRunner { tokens: vec!(NodeToken::Get) };
    assert_eq!(some_enum.node_step(runner), "Foo");

    let mut some_enum = SomeEnum::Bar;
    let runner = NodeRunner { tokens: vec!(NodeToken::Get) };
    assert_eq!(some_enum.node_step(runner), "Bar");
}

#[test]
fn set_unit_enum() {
    let mut some_enum = SomeEnum::Bar;
    let runner = NodeRunner { tokens: vec!( NodeToken::Set(String::from("Foo")) )};
    some_enum.node_step(runner);
    assert!(matches!(some_enum, SomeEnum::Foo));

    let mut some_enum = SomeEnum::Bar;
    let runner = NodeRunner { tokens: vec!( NodeToken::Set(String::from("Bar")) )};
    some_enum.node_step(runner);
    assert!(matches!(some_enum, SomeEnum::Bar));

    let mut some_enum = SomeEnum::Foo;
    let runner = NodeRunner { tokens: vec!( NodeToken::Set(String::from("Bar")) )};
    some_enum.node_step(runner);
    assert!(matches!(some_enum, SomeEnum::Bar));

    let mut some_enum = SomeEnum::Foo;
    let runner = NodeRunner { tokens: vec!( NodeToken::Set(String::from("Foo")) )};
    some_enum.node_step(runner);
    assert!(matches!(some_enum, SomeEnum::Foo));

    let mut some_enum = SomeEnum::Foo;
    let runner = NodeRunner { tokens: vec!( NodeToken::Set(String::from("Aether")) )};
    assert_eq!(some_enum.node_step(runner), "Aether is not a valid value for SomeEnum");
    assert!(matches!(some_enum, SomeEnum::Foo));
}

#[test]
fn get_tuple_enum() {
    let mut some_enum = SomeEnum::Qux(42);
    let runner = NodeRunner { tokens: vec!(NodeToken::Get) };
    assert_eq!(some_enum.node_step(runner), "Qux(42)");

    let mut some_enum = SomeEnum::Quux(-1337, String::from("YOYOYO"), true);
    let runner = NodeRunner { tokens: vec!(NodeToken::Get) };
    assert_eq!(some_enum.node_step(runner), "Quux(-1337, YOYOYO, true)");
}

#[test]
fn set_tuple_enum() {
    let mut some_enum = SomeEnum::Foo;
    let runner = NodeRunner { tokens: vec!(NodeToken::Set(String::from("Qux(13)"))) };
    some_enum.node_step(runner);
    assert!(matches!(some_enum, SomeEnum::Qux(13)));

    let mut some_enum = SomeEnum::Bar;
    let runner = NodeRunner { tokens: vec!(NodeToken::Set(String::from("Quux(-42, SomeString, true)"))) };
    some_enum.node_step(runner);
    assert!(matches!(some_enum, SomeEnum::Quux(-42, SomeString, true)));
}

#[test]
fn get_struct_enum() {
}

#[test]
fn set_struct_enum() {
}

#[test]
fn copy_enum() {
    let mut some_enum = SomeEnum::Foo;
    let runner = NodeRunner { tokens: vec!(NodeToken::CopyFrom) };
    assert_eq!(some_enum.node_step(runner), String::from("SomeEnum cannot 'CopyFrom'"));
}
<|endoftext|>


<commit_before>//! `ra_assits` crate provides a bunch of code assists, aslo known as code
//! actions (in LSP) or intentions (in IntelliJ).
//!
//! An assist is a micro-refactoring, which is automatically activated in
//! certain context. For example, if the cursor is over `,`, a "swap `,`" assist
//! becomes available.

mod assist_ctx;

use ra_text_edit::TextEdit;
use ra_syntax::{TextUnit, SyntaxNode, Direction};
use ra_db::FileRange;
use hir::db::HirDatabase;

pub(crate) use crate::assist_ctx::{AssistCtx, Assist};

#[derive(Debug)]
pub struct AssistLabel {
    /// Short description of the assist, as shown in the UI.
    pub label: String,
}

pub struct AssistAction {
    pub edit: TextEdit,
    pub cursor_position: Option<TextUnit>,
}

/// Return all the assists applicable at the given position.
///
/// Assists are returned in the "unresolved" state, that is only labels are
/// returned, without actual edits.
pub fn applicable_assists<H>(db: &H, range: FileRange) -> Vec<AssistLabel>
where
    H: HirDatabase + 'static,
{
    AssistCtx::with_ctx(db, range, false, |ctx| {
        all_assists()
            .iter()
            .filter_map(|f| f(ctx.clone()))
            .map(|a| match a {
                Assist::Unresolved(label) => label,
                Assist::Resolved(..) => unreachable!(),
            })
            .collect()
    })
}

/// Return all the assists applicable at the given position.
///
/// Assists are returned in the "resolved" state, that is with edit fully
/// computed.
pub fn assists<H>(db: &H, range: FileRange) -> Vec<(AssistLabel, AssistAction)>
where
    H: HirDatabase + 'static,
{
    AssistCtx::with_ctx(db, range, false, |ctx| {
        all_assists()
            .iter()
            .filter_map(|f| f(ctx.clone()))
            .map(|a| match a {
                Assist::Resolved(label, action) => (label, action),
                Assist::Unresolved(..) => unreachable!(),
            })
            .collect()
    })
}

mod add_derive;
mod add_impl;
mod flip_comma;
mod change_visibility;
mod fill_match_arms;
mod introduce_variable;
mod replace_if_let_with_match;
mod split_import;
fn all_assists<DB: HirDatabase>() -> &'static [fn(AssistCtx<DB>) -> Option<Assist>] {
    &[
        add_derive::add_derive,
        add_impl::add_impl,
        change_visibility::change_visibility,
        fill_match_arms::fill_match_arms,
        flip_comma::flip_comma,
        introduce_variable::introduce_variable,
        replace_if_let_with_match::replace_if_let_with_match,
        split_import::split_import,
    ]
}

fn non_trivia_sibling(node: &SyntaxNode, direction: Direction) -> Option<&SyntaxNode> {
    node.siblings(direction)
        .skip(1)
        .find(|node| !node.kind().is_trivia())
}

#[cfg(test)]
mod helpers {
    use hir::mock::MockDatabase;
    use ra_syntax::TextRange;
    use ra_db::FileRange;
    use test_utils::{extract_offset, assert_eq_text, add_cursor, extract_range};

    use crate::{AssistCtx, Assist};

    pub(crate) fn check_assist(
        assist: fn(AssistCtx<MockDatabase>) -> Option<Assist>,
        before: &str,
        after: &str,
    ) {
        let (before_cursor_pos, before) = extract_offset(before);
        let (db, _source_root, file_id) = MockDatabase::with_single_file(&before);
        let frange = FileRange {
            file_id,
            range: TextRange::offset_len(before_cursor_pos, 0.into()),
        };
        let assist =
            AssistCtx::with_ctx(&db, frange, true, assist).expect("code action is not applicable");
        let action = match assist {
            Assist::Unresolved(_) => unreachable!(),
            Assist::Resolved(_, it) => it,
        };

        let actual = action.edit.apply(&before);
        let actual_cursor_pos = match action.cursor_position {
            None => action
                .edit
                .apply_to_offset(before_cursor_pos)
                .expect("cursor position is affected by the edit"),
            Some(off) => off,
        };
        let actual = add_cursor(&actual, actual_cursor_pos);
        assert_eq_text!(after, &actual);
    }

    pub(crate) fn check_assist_range(
        assist: fn(AssistCtx<MockDatabase>) -> Option<Assist>,
        before: &str,
        after: &str,
    ) {
        let (range, before) = extract_range(before);
        let (db, _source_root, file_id) = MockDatabase::with_single_file(&before);
        let frange = FileRange { file_id, range };
        let assist =
            AssistCtx::with_ctx(&db, frange, true, assist).expect("code action is not applicable");
        let action = match assist {
            Assist::Unresolved(_) => unreachable!(),
            Assist::Resolved(_, it) => it,
        };

        let mut actual = action.edit.apply(&before);
        if let Some(pos) = action.cursor_position {
            actual = add_cursor(&actual, pos);
        }
        assert_eq_text!(after, &actual);
    }

    pub(crate) fn check_assist_not_applicable(
        assist: fn(AssistCtx<MockDatabase>) -> Option<Assist>,
        before: &str,
    ) {
        let (before_cursor_pos, before) = extract_offset(before);
        let (db, _source_root, file_id) = MockDatabase::with_single_file(&before);
        let frange = FileRange {
            file_id,
            range: TextRange::offset_len(before_cursor_pos, 0.into()),
        };
        let assist = AssistCtx::with_ctx(&db, frange, true, assist);
        assert!(assist.is_none());
    }

}
<commit_msg>assists: compute edit<commit_after>//! `ra_assits` crate provides a bunch of code assists, aslo known as code
//! actions (in LSP) or intentions (in IntelliJ).
//!
//! An assist is a micro-refactoring, which is automatically activated in
//! certain context. For example, if the cursor is over `,`, a "swap `,`" assist
//! becomes available.

mod assist_ctx;

use ra_text_edit::TextEdit;
use ra_syntax::{TextUnit, SyntaxNode, Direction};
use ra_db::FileRange;
use hir::db::HirDatabase;

pub(crate) use crate::assist_ctx::{AssistCtx, Assist};

#[derive(Debug)]
pub struct AssistLabel {
    /// Short description of the assist, as shown in the UI.
    pub label: String,
}

pub struct AssistAction {
    pub edit: TextEdit,
    pub cursor_position: Option<TextUnit>,
}

/// Return all the assists applicable at the given position.
///
/// Assists are returned in the "unresolved" state, that is only labels are
/// returned, without actual edits.
pub fn applicable_assists<H>(db: &H, range: FileRange) -> Vec<AssistLabel>
where
    H: HirDatabase + 'static,
{
    AssistCtx::with_ctx(db, range, false, |ctx| {
        all_assists()
            .iter()
            .filter_map(|f| f(ctx.clone()))
            .map(|a| match a {
                Assist::Unresolved(label) => label,
                Assist::Resolved(..) => unreachable!(),
            })
            .collect()
    })
}

/// Return all the assists applicable at the given position.
///
/// Assists are returned in the "resolved" state, that is with edit fully
/// computed.
pub fn assists<H>(db: &H, range: FileRange) -> Vec<(AssistLabel, AssistAction)>
where
    H: HirDatabase + 'static,
{
    AssistCtx::with_ctx(db, range, true, |ctx| {
        all_assists()
            .iter()
            .filter_map(|f| f(ctx.clone()))
            .map(|a| match a {
                Assist::Resolved(label, action) => (label, action),
                Assist::Unresolved(..) => unreachable!(),
            })
            .collect()
    })
}

mod add_derive;
mod add_impl;
mod flip_comma;
mod change_visibility;
mod fill_match_arms;
mod introduce_variable;
mod replace_if_let_with_match;
mod split_import;
fn all_assists<DB: HirDatabase>() -> &'static [fn(AssistCtx<DB>) -> Option<Assist>] {
    &[
        add_derive::add_derive,
        add_impl::add_impl,
        change_visibility::change_visibility,
        fill_match_arms::fill_match_arms,
        flip_comma::flip_comma,
        introduce_variable::introduce_variable,
        replace_if_let_with_match::replace_if_let_with_match,
        split_import::split_import,
    ]
}

fn non_trivia_sibling(node: &SyntaxNode, direction: Direction) -> Option<&SyntaxNode> {
    node.siblings(direction)
        .skip(1)
        .find(|node| !node.kind().is_trivia())
}

#[cfg(test)]
mod helpers {
    use hir::mock::MockDatabase;
    use ra_syntax::TextRange;
    use ra_db::FileRange;
    use test_utils::{extract_offset, assert_eq_text, add_cursor, extract_range};

    use crate::{AssistCtx, Assist};

    pub(crate) fn check_assist(
        assist: fn(AssistCtx<MockDatabase>) -> Option<Assist>,
        before: &str,
        after: &str,
    ) {
        let (before_cursor_pos, before) = extract_offset(before);
        let (db, _source_root, file_id) = MockDatabase::with_single_file(&before);
        let frange = FileRange {
            file_id,
            range: TextRange::offset_len(before_cursor_pos, 0.into()),
        };
        let assist =
            AssistCtx::with_ctx(&db, frange, true, assist).expect("code action is not applicable");
        let action = match assist {
            Assist::Unresolved(_) => unreachable!(),
            Assist::Resolved(_, it) => it,
        };

        let actual = action.edit.apply(&before);
        let actual_cursor_pos = match action.cursor_position {
            None => action
                .edit
                .apply_to_offset(before_cursor_pos)
                .expect("cursor position is affected by the edit"),
            Some(off) => off,
        };
        let actual = add_cursor(&actual, actual_cursor_pos);
        assert_eq_text!(after, &actual);
    }

    pub(crate) fn check_assist_range(
        assist: fn(AssistCtx<MockDatabase>) -> Option<Assist>,
        before: &str,
        after: &str,
    ) {
        let (range, before) = extract_range(before);
        let (db, _source_root, file_id) = MockDatabase::with_single_file(&before);
        let frange = FileRange { file_id, range };
        let assist =
            AssistCtx::with_ctx(&db, frange, true, assist).expect("code action is not applicable");
        let action = match assist {
            Assist::Unresolved(_) => unreachable!(),
            Assist::Resolved(_, it) => it,
        };

        let mut actual = action.edit.apply(&before);
        if let Some(pos) = action.cursor_position {
            actual = add_cursor(&actual, pos);
        }
        assert_eq_text!(after, &actual);
    }

    pub(crate) fn check_assist_not_applicable(
        assist: fn(AssistCtx<MockDatabase>) -> Option<Assist>,
        before: &str,
    ) {
        let (before_cursor_pos, before) = extract_offset(before);
        let (db, _source_root, file_id) = MockDatabase::with_single_file(&before);
        let frange = FileRange {
            file_id,
            range: TextRange::offset_len(before_cursor_pos, 0.into()),
        };
        let assist = AssistCtx::with_ctx(&db, frange, true, assist);
        assert!(assist.is_none());
    }

}
<|endoftext|>


<commit_before><commit_msg>Auto merge of #1378 - RalfJung:workspace-error, r=RalfJung<commit_after><|endoftext|>


<commit_before>//! Fully type-check project and print various stats, like the number of type
//! errors.

use std::{
    path::Path,
    time::{Instant, SystemTime, UNIX_EPOCH},
};

use hir::{
    db::{AstDatabase, DefDatabase, HirDatabase},
    original_range, AssocItem, Crate, HasSource, HirDisplay, ModuleDef,
};
use hir_def::FunctionId;
use hir_ty::{Ty, TypeWalk};
use itertools::Itertools;
use oorandom::Rand32;
use ra_db::{
    salsa::{self, ParallelDatabase},
    SourceDatabaseExt,
};
use ra_syntax::AstNode;
use rayon::prelude::*;
use rustc_hash::FxHashSet;
use stdx::format_to;

use crate::{
    cli::{
        load_cargo::load_cargo, progress_report::ProgressReport, report_metric, Result, Verbosity,
    },
    print_memory_usage,
};

/// Need to wrap Snapshot to provide `Clone` impl for `map_with`
struct Snap<DB>(DB);
impl<DB: ParallelDatabase> Clone for Snap<salsa::Snapshot<DB>> {
    fn clone(&self) -> Snap<salsa::Snapshot<DB>> {
        Snap(self.0.snapshot())
    }
}

pub fn analysis_stats(
    verbosity: Verbosity,
    memory_usage: bool,
    path: &Path,
    only: Option<&str>,
    with_deps: bool,
    randomize: bool,
    parallel: bool,
    load_output_dirs: bool,
    with_proc_macro: bool,
) -> Result<()> {
    let mut rng = {
        let seed = SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_millis() as u64;
        Rand32::new(seed)
    };

    let db_load_time = Instant::now();
    let (host, vfs) = load_cargo(path, load_output_dirs, with_proc_macro)?;
    let db = host.raw_database();
    eprintln!("Database loaded {:?}", db_load_time.elapsed());
    let analysis_time = Instant::now();
    let mut num_crates = 0;
    let mut visited_modules = FxHashSet::default();
    let mut visit_queue = Vec::new();

    let mut krates = Crate::all(db);
    if randomize {
        shuffle(&mut rng, &mut krates);
    }
    for krate in krates {
        let module = krate.root_module(db).expect("crate without root module");
        let file_id = module.definition_source(db).file_id;
        let file_id = file_id.original_file(db);
        let source_root = db.file_source_root(file_id);
        let source_root = db.source_root(source_root);
        if !source_root.is_library || with_deps {
            num_crates += 1;
            visit_queue.push(module);
        }
    }

    if randomize {
        shuffle(&mut rng, &mut visit_queue);
    }

    eprintln!("Crates in this dir: {}", num_crates);
    let mut num_decls = 0;
    let mut funcs = Vec::new();
    while let Some(module) = visit_queue.pop() {
        if visited_modules.insert(module) {
            visit_queue.extend(module.children(db));

            for decl in module.declarations(db) {
                num_decls += 1;
                if let ModuleDef::Function(f) = decl {
                    funcs.push(f);
                }
            }

            for impl_def in module.impl_defs(db) {
                for item in impl_def.items(db) {
                    num_decls += 1;
                    if let AssocItem::Function(f) = item {
                        funcs.push(f);
                    }
                }
            }
        }
    }
    eprintln!("Total modules found: {}", visited_modules.len());
    eprintln!("Total declarations: {}", num_decls);
    eprintln!("Total functions: {}", funcs.len());
    let item_collection_memory = ra_prof::memory_usage();
    eprintln!(
        "Item Collection: {:?}, {}",
        analysis_time.elapsed(),
        item_collection_memory.allocated
    );

    if randomize {
        shuffle(&mut rng, &mut funcs);
    }

    let mut bar = match verbosity {
        Verbosity::Quiet | Verbosity::Spammy => ProgressReport::hidden(),
        _ => ProgressReport::new(funcs.len() as u64),
    };

    if parallel {
        let inference_time = Instant::now();
        let snap = Snap(db.snapshot());
        funcs
            .par_iter()
            .map_with(snap, |snap, &f| {
                let f_id = FunctionId::from(f);
                snap.0.body(f_id.into());
                snap.0.infer(f_id.into());
            })
            .count();
        eprintln!(
            "Parallel Inference: {:?}, {}",
            inference_time.elapsed(),
            ra_prof::memory_usage().allocated
        );
    }

    let inference_time = Instant::now();
    bar.tick();
    let mut num_exprs = 0;
    let mut num_exprs_unknown = 0;
    let mut num_exprs_partially_unknown = 0;
    let mut num_type_mismatches = 0;
    for f in funcs {
        let name = f.name(db);
        let full_name = f
            .module(db)
            .path_to_root(db)
            .into_iter()
            .rev()
            .filter_map(|it| it.name(db))
            .chain(Some(f.name(db)))
            .join("::");
        if let Some(only_name) = only {
            if name.to_string() != only_name && full_name != only_name {
                continue;
            }
        }
        let mut msg = format!("processing: {}", full_name);
        if verbosity.is_verbose() {
            let src = f.source(db);
            let original_file = src.file_id.original_file(db);
            let path = vfs.file_path(original_file);
            let syntax_range = src.value.syntax().text_range();
            format_to!(msg, " ({} {:?})", path, syntax_range);
        }
        if verbosity.is_spammy() {
            bar.println(msg.to_string());
        }
        bar.set_message(&msg);
        let f_id = FunctionId::from(f);
        let body = db.body(f_id.into());
        let inference_result = db.infer(f_id.into());
        let (previous_exprs, previous_unknown, previous_partially_unknown) =
            (num_exprs, num_exprs_unknown, num_exprs_partially_unknown);
        for (expr_id, _) in body.exprs.iter() {
            let ty = &inference_result[expr_id];
            num_exprs += 1;
            if let Ty::Unknown = ty {
                num_exprs_unknown += 1;
            } else {
                let mut is_partially_unknown = false;
                ty.walk(&mut |ty| {
                    if let Ty::Unknown = ty {
                        is_partially_unknown = true;
                    }
                });
                if is_partially_unknown {
                    num_exprs_partially_unknown += 1;
                }
            }
            if only.is_some() && verbosity.is_spammy() {
                // in super-verbose mode for just one function, we print every single expression
                let (_, sm) = db.body_with_source_map(f_id.into());
                let src = sm.expr_syntax(expr_id);
                if let Ok(src) = src {
                    let node = {
                        let root = db.parse_or_expand(src.file_id).unwrap();
                        src.value.to_node(&root)
                    };
                    let original_file = src.file_id.original_file(db);
                    let line_index = host.analysis().file_line_index(original_file).unwrap();
                    let text_range = node.syntax().text_range();
                    let (start, end) = (
                        line_index.line_col(text_range.start()),
                        line_index.line_col(text_range.end()),
                    );
                    bar.println(format!(
                        "{}:{}-{}:{}: {}",
                        start.line + 1,
                        start.col_utf16,
                        end.line + 1,
                        end.col_utf16,
                        ty.display(db)
                    ));
                } else {
                    bar.println(format!("unknown location: {}", ty.display(db)));
                }
            }
            if let Some(mismatch) = inference_result.type_mismatch_for_expr(expr_id) {
                num_type_mismatches += 1;
                if verbosity.is_verbose() {
                    let (_, sm) = db.body_with_source_map(f_id.into());
                    let src = sm.expr_syntax(expr_id);
                    if let Ok(src) = src {
                        // FIXME: it might be nice to have a function (on Analysis?) that goes from Source<T> -> (LineCol, LineCol) directly
                        // But also, we should just turn the type mismatches into diagnostics and provide these
                        let root = db.parse_or_expand(src.file_id).unwrap();
                        let node = src.map(|e| e.to_node(&root).syntax().clone());
                        let original_range = original_range(db, node.as_ref());
                        let path = vfs.file_path(original_range.file_id);
                        let line_index =
                            host.analysis().file_line_index(original_range.file_id).unwrap();
                        let text_range = original_range.range;
                        let (start, end) = (
                            line_index.line_col(text_range.start()),
                            line_index.line_col(text_range.end()),
                        );
                        bar.println(format!(
                            "{} {}:{}-{}:{}: Expected {}, got {}",
                            path,
                            start.line + 1,
                            start.col_utf16,
                            end.line + 1,
                            end.col_utf16,
                            mismatch.expected.display(db),
                            mismatch.actual.display(db)
                        ));
                    } else {
                        bar.println(format!(
                            "{}: Expected {}, got {}",
                            name,
                            mismatch.expected.display(db),
                            mismatch.actual.display(db)
                        ));
                    }
                }
            }
        }
        if verbosity.is_spammy() {
            bar.println(format!(
                "In {}: {} exprs, {} unknown, {} partial",
                full_name,
                num_exprs - previous_exprs,
                num_exprs_unknown - previous_unknown,
                num_exprs_partially_unknown - previous_partially_unknown
            ));
        }
        bar.inc(1);
    }
    bar.finish_and_clear();
    eprintln!("Total expressions: {}", num_exprs);
    eprintln!(
        "Expressions of unknown type: {} ({}%)",
        num_exprs_unknown,
        if num_exprs > 0 { num_exprs_unknown * 100 / num_exprs } else { 100 }
    );
    report_metric("unknown type", num_exprs_unknown, "#");

    eprintln!(
        "Expressions of partially unknown type: {} ({}%)",
        num_exprs_partially_unknown,
        if num_exprs > 0 { num_exprs_partially_unknown * 100 / num_exprs } else { 100 }
    );

    eprintln!("Type mismatches: {}", num_type_mismatches);
    report_metric("type mismatches", num_type_mismatches, "#");

    let inference_time = inference_time.elapsed();
    let total_memory = ra_prof::memory_usage();
    eprintln!(
        "Inference: {:?}, {}",
        inference_time,
        total_memory.allocated - item_collection_memory.allocated
    );

    let analysis_time = analysis_time.elapsed();
    eprintln!("Total: {:?}, {}", analysis_time, total_memory);
    report_metric("total time", analysis_time.as_millis() as u64, "ms");
    report_metric("total memory", total_memory.allocated.megabytes() as u64, "MB");

    if memory_usage {
        print_memory_usage(host, vfs);
    }

    Ok(())
}

fn shuffle<T>(rng: &mut Rand32, slice: &mut [T]) {
    for i in (1..slice.len()).rev() {
        let idx = rng.rand_range(0..i as u32) as usize;
        slice.swap(idx, i - 1)
    }
}
<commit_msg>Update crates/rust-analyzer/src/cli/analysis_stats.rs<commit_after>//! Fully type-check project and print various stats, like the number of type
//! errors.

use std::{
    path::Path,
    time::{Instant, SystemTime, UNIX_EPOCH},
};

use hir::{
    db::{AstDatabase, DefDatabase, HirDatabase},
    original_range, AssocItem, Crate, HasSource, HirDisplay, ModuleDef,
};
use hir_def::FunctionId;
use hir_ty::{Ty, TypeWalk};
use itertools::Itertools;
use oorandom::Rand32;
use ra_db::{
    salsa::{self, ParallelDatabase},
    SourceDatabaseExt,
};
use ra_syntax::AstNode;
use rayon::prelude::*;
use rustc_hash::FxHashSet;
use stdx::format_to;

use crate::{
    cli::{
        load_cargo::load_cargo, progress_report::ProgressReport, report_metric, Result, Verbosity,
    },
    print_memory_usage,
};

/// Need to wrap Snapshot to provide `Clone` impl for `map_with`
struct Snap<DB>(DB);
impl<DB: ParallelDatabase> Clone for Snap<salsa::Snapshot<DB>> {
    fn clone(&self) -> Snap<salsa::Snapshot<DB>> {
        Snap(self.0.snapshot())
    }
}

pub fn analysis_stats(
    verbosity: Verbosity,
    memory_usage: bool,
    path: &Path,
    only: Option<&str>,
    with_deps: bool,
    randomize: bool,
    parallel: bool,
    load_output_dirs: bool,
    with_proc_macro: bool,
) -> Result<()> {
    let mut rng = {
        let seed = SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_millis() as u64;
        Rand32::new(seed)
    };

    let db_load_time = Instant::now();
    let (host, vfs) = load_cargo(path, load_output_dirs, with_proc_macro)?;
    let db = host.raw_database();
    eprintln!("Database loaded {:?}", db_load_time.elapsed());
    let analysis_time = Instant::now();
    let mut num_crates = 0;
    let mut visited_modules = FxHashSet::default();
    let mut visit_queue = Vec::new();

    let mut krates = Crate::all(db);
    if randomize {
        shuffle(&mut rng, &mut krates);
    }
    for krate in krates {
        let module = krate.root_module(db).expect("crate without root module");
        let file_id = module.definition_source(db).file_id;
        let file_id = file_id.original_file(db);
        let source_root = db.file_source_root(file_id);
        let source_root = db.source_root(source_root);
        if !source_root.is_library || with_deps {
            num_crates += 1;
            visit_queue.push(module);
        }
    }

    if randomize {
        shuffle(&mut rng, &mut visit_queue);
    }

    eprintln!("Crates in this dir: {}", num_crates);
    let mut num_decls = 0;
    let mut funcs = Vec::new();
    while let Some(module) = visit_queue.pop() {
        if visited_modules.insert(module) {
            visit_queue.extend(module.children(db));

            for decl in module.declarations(db) {
                num_decls += 1;
                if let ModuleDef::Function(f) = decl {
                    funcs.push(f);
                }
            }

            for impl_def in module.impl_defs(db) {
                for item in impl_def.items(db) {
                    num_decls += 1;
                    if let AssocItem::Function(f) = item {
                        funcs.push(f);
                    }
                }
            }
        }
    }
    eprintln!("Total modules found: {}", visited_modules.len());
    eprintln!("Total declarations: {}", num_decls);
    eprintln!("Total functions: {}", funcs.len());
    let item_collection_memory = ra_prof::memory_usage();
    eprintln!(
        "Item Collection: {:?}, {}",
        analysis_time.elapsed(),
        item_collection_memory.allocated
    );

    if randomize {
        shuffle(&mut rng, &mut funcs);
    }

    let mut bar = match verbosity {
        Verbosity::Quiet | Verbosity::Spammy => ProgressReport::hidden(),
        _ => ProgressReport::new(funcs.len() as u64),
    };

    if parallel {
        let inference_time = Instant::now();
        let snap = Snap(db.snapshot());
        funcs
            .par_iter()
            .map_with(snap, |snap, &f| {
                let f_id = FunctionId::from(f);
                snap.0.body(f_id.into());
                snap.0.infer(f_id.into());
            })
            .count();
        eprintln!(
            "Parallel Inference: {:?}, {}",
            inference_time.elapsed(),
            ra_prof::memory_usage().allocated
        );
    }

    let inference_time = Instant::now();
    bar.tick();
    let mut num_exprs = 0;
    let mut num_exprs_unknown = 0;
    let mut num_exprs_partially_unknown = 0;
    let mut num_type_mismatches = 0;
    for f in funcs {
        let name = f.name(db);
        let full_name = f
            .module(db)
            .path_to_root(db)
            .into_iter()
            .rev()
            .filter_map(|it| it.name(db))
            .chain(Some(f.name(db)))
            .join("::");
        if let Some(only_name) = only {
            if name.to_string() != only_name && full_name != only_name {
                continue;
            }
        }
        let mut msg = format!("processing: {}", full_name);
        if verbosity.is_verbose() {
            let src = f.source(db);
            let original_file = src.file_id.original_file(db);
            let path = vfs.file_path(original_file);
            let syntax_range = src.value.syntax().text_range();
            format_to!(msg, " ({} {:?})", path, syntax_range);
        }
        if verbosity.is_spammy() {
            bar.println(msg.to_string());
        }
        bar.set_message(&msg);
        let f_id = FunctionId::from(f);
        let body = db.body(f_id.into());
        let inference_result = db.infer(f_id.into());
        let (previous_exprs, previous_unknown, previous_partially_unknown) =
            (num_exprs, num_exprs_unknown, num_exprs_partially_unknown);
        for (expr_id, _) in body.exprs.iter() {
            let ty = &inference_result[expr_id];
            num_exprs += 1;
            if let Ty::Unknown = ty {
                num_exprs_unknown += 1;
            } else {
                let mut is_partially_unknown = false;
                ty.walk(&mut |ty| {
                    if let Ty::Unknown = ty {
                        is_partially_unknown = true;
                    }
                });
                if is_partially_unknown {
                    num_exprs_partially_unknown += 1;
                }
            }
            if only.is_some() && verbosity.is_spammy() {
                // in super-verbose mode for just one function, we print every single expression
                let (_, sm) = db.body_with_source_map(f_id.into());
                let src = sm.expr_syntax(expr_id);
                if let Ok(src) = src {
                    let node = {
                        let root = db.parse_or_expand(src.file_id).unwrap();
                        src.value.to_node(&root)
                    };
                    let original_file = src.file_id.original_file(db);
                    let line_index = host.analysis().file_line_index(original_file).unwrap();
                    let text_range = node.syntax().text_range();
                    let (start, end) = (
                        line_index.line_col(text_range.start()),
                        line_index.line_col(text_range.end()),
                    );
                    bar.println(format!(
                        "{}:{}-{}:{}: {}",
                        start.line + 1,
                        start.col_utf16,
                        end.line + 1,
                        end.col_utf16,
                        ty.display(db)
                    ));
                } else {
                    bar.println(format!("unknown location: {}", ty.display(db)));
                }
            }
            if let Some(mismatch) = inference_result.type_mismatch_for_expr(expr_id) {
                num_type_mismatches += 1;
                if verbosity.is_verbose() {
                    let (_, sm) = db.body_with_source_map(f_id.into());
                    let src = sm.expr_syntax(expr_id);
                    if let Ok(src) = src {
                        // FIXME: it might be nice to have a function (on Analysis?) that goes from Source<T> -> (LineCol, LineCol) directly
                        // But also, we should just turn the type mismatches into diagnostics and provide these
                        let root = db.parse_or_expand(src.file_id).unwrap();
                        let node = src.map(|e| e.to_node(&root).syntax().clone());
                        let original_range = original_range(db, node.as_ref());
                        let path = vfs.file_path(original_range.file_id);
                        let line_index =
                            host.analysis().file_line_index(original_range.file_id).unwrap();
                        let text_range = original_range.range;
                        let (start, end) = (
                            line_index.line_col(text_range.start()),
                            line_index.line_col(text_range.end()),
                        );
                        bar.println(format!(
                            "{} {}:{}-{}:{}: Expected {}, got {}",
                            path,
                            start.line + 1,
                            start.col_utf16,
                            end.line + 1,
                            end.col_utf16,
                            mismatch.expected.display(db),
                            mismatch.actual.display(db)
                        ));
                    } else {
                        bar.println(format!(
                            "{}: Expected {}, got {}",
                            name,
                            mismatch.expected.display(db),
                            mismatch.actual.display(db)
                        ));
                    }
                }
            }
        }
        if verbosity.is_spammy() {
            bar.println(format!(
                "In {}: {} exprs, {} unknown, {} partial",
                full_name,
                num_exprs - previous_exprs,
                num_exprs_unknown - previous_unknown,
                num_exprs_partially_unknown - previous_partially_unknown
            ));
        }
        bar.inc(1);
    }
    bar.finish_and_clear();
    eprintln!("Total expressions: {}", num_exprs);
    eprintln!(
        "Expressions of unknown type: {} ({}%)",
        num_exprs_unknown,
        if num_exprs > 0 { num_exprs_unknown * 100 / num_exprs } else { 100 }
    );
    report_metric("unknown type", num_exprs_unknown, "#");

    eprintln!(
        "Expressions of partially unknown type: {} ({}%)",
        num_exprs_partially_unknown,
        if num_exprs > 0 { num_exprs_partially_unknown * 100 / num_exprs } else { 100 }
    );

    eprintln!("Type mismatches: {}", num_type_mismatches);
    report_metric("type mismatches", num_type_mismatches, "#");

    let inference_time = inference_time.elapsed();
    let total_memory = ra_prof::memory_usage();
    eprintln!(
        "Inference: {:?}, {}",
        inference_time,
        total_memory.allocated - item_collection_memory.allocated
    );

    let analysis_time = analysis_time.elapsed();
    eprintln!("Total: {:?}, {}", analysis_time, total_memory);
    report_metric("total time", analysis_time.as_millis() as u64, "ms");
    report_metric("total memory", total_memory.allocated.megabytes() as u64, "MB");

    if memory_usage {
        print_memory_usage(host, vfs);
    }

    Ok(())
}

fn shuffle<T>(rng: &mut Rand32, slice: &mut [T]) {
    for i in (1..slice.len()).rev() {
        let idx = rng.rand_range(0..i as u32) as usize;
        slice.swap(idx, i)
    }
}
<|endoftext|>


<commit_before>// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0

//! External, remote flash abstraction.
//!
//! This crate provides the [`Flash`] (and related) traits, which represent
//! *abstract flash devices*. An abstract flash device is a region of memory
//! that can be transactionally read or written. Such a "device" can range
//! from a simple Rust slice to a remote SPI flash device (or even a subregion of
//! it!).
//!
//! [`Flash`]: trait.Flash.html

use zerocopy::AsBytes;
use zerocopy::FromBytes;

#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};

/// Provides access to a flash-like storage device.
///
/// This trait provides abstract operations on a device, as if it were a
/// block of random-access memory. It is the implementation's responsibility
/// to implement these operations efficiently with respect to the underlying
/// device.
///
/// The `Flash` trait comes implemented for `[u8]`.
pub trait Flash {
    /// The error type returned by transactions with this `Flash`.
    type Error: Sized;

    /// Gets the size, in bytes, of this device.
    fn size(&self) -> Result<u32, Self::Error>;

    /// Attempt to read `slice` into `out`.
    ///
    /// If `out` is smaller than `slice.len`, only the first `out.len()` bytes
    /// will be read.
    fn read(
        &self,
        slice: FlashSlice,
        out: &mut [u8],
    ) -> Result<(), Self::Error>;
}

/// An abstract pointer into a [`Flash`] type.
///
/// A `FlashPtr` needs to be used in conjunction with a [`Flash`]
/// implementation to be read from or written to.
///
/// [`Flash`]: trait.Flash.html
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, AsBytes, FromBytes)]
#[repr(transparent)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct FlashPtr {
    /// The abstract address of this pointer.
    pub address: u32,
}

impl FlashPtr {
    /// Convenience method for creating a `FlashPtr` without having to use
    /// a struct literal.
    pub const fn new(address: u32) -> Self {
        Self { address }
    }
}

/// An abstrace slice into a [`Flash`] type.
///
/// Much like a [`FlashPtr`], a `FlashSlice` needs to be interpreted with
/// respect to a [`Flash`] implementation.
///
/// [`Flash`]: trait.Flash.html
/// [`FlashPtr`]: trait.FlashPtr.html
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, AsBytes, FromBytes)]
#[repr(C)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct FlashSlice {
    /// The base pointer for this slice.
    #[cfg_attr(feature = "serde", serde(flatten))]
    pub ptr: FlashPtr,
    /// The length of the slice, in bytes.
    pub len: u32,
}

impl FlashSlice {
    /// Convenience method for creating a `FlashSlice` without having to use
    /// a struct literal
    pub const fn new(ptr: u32, len: u32) -> Self {
        Self {
            ptr: FlashPtr::new(ptr),
            len,
        }
    }
}

/// An unspecified out-of-bounds error.
#[derive(Copy, Clone, Debug)]
pub struct OutOfBounds;

impl Flash for [u8] {
    type Error = OutOfBounds;

    fn size(&self) -> Result<u32, Self::Error> {
        Ok(self.len() as u32)
    }

    fn read(
        &self,
        slice: FlashSlice,
        out: &mut [u8],
    ) -> Result<(), Self::Error> {
        let start = slice.ptr.address as usize;
        let end = start + slice.len as usize;
        if start >= self.len() || end > self.len() {
            return Err(OutOfBounds);
        }
        out.copy_from_slice(&self[start..end]);
        Ok(())
    }
}
<commit_msg>[hardware] Add Flash::program() and FlashZero<commit_after>// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0

//! External, remote flash abstraction.
//!
//! This crate provides the [`Flash`] (and related) traits, which represent
//! *abstract flash devices*. An abstract flash device is a region of memory
//! that can be transactionally read or written. Such a "device" can range
//! from a simple Rust slice to a remote SPI flash device (or even a subregion of
//! it!).
//!
//! [`Flash`]: trait.Flash.html

use core::convert::TryInto;

use static_assertions::assert_obj_safe;

use zerocopy::AsBytes;
use zerocopy::FromBytes;

#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};

use crate::io;
use crate::mem::Arena;

/// A [`Flash`] error.
///
/// All of these errors are non-retryable; a [`Flash`] implementation should
/// block until the operation succeeds.
///
/// [`Flash`]: trait.Flash.html
#[derive(Copy, Clone, Debug)]
pub enum Error {
    /// Indicates that an operation failed because the requested
    /// operation was outside of the device's address space.
    OutOfRange,

    /// Indicates that the device is locked in some manner and cannot
    /// be affected by the operation.
    Locked,

    /// Indicates that an internal invariant was violated, such as running out
    /// of memory.
    Internal,

    /// Indicates that an unspecified error occured.
    Unspecified,
}

/// Provides access to a flash-like storage device.
///
/// This trait provides abstract operations on a device, as if it were a
/// block of random-access memory. It is the implementation's responsibility
/// to implement these operations efficiently with respect to the underlying
/// device.
///
/// The `Flash` trait comes implemented for `[u8]`.
pub trait Flash {
    /// Returns the size, in bytes, of this device.
    fn size(&self) -> Result<u32, Error>;

    /// Attempts to read `out.len()` bytes starting at `offset`.
    fn read(&self, offset: FlashPtr, out: &mut [u8]) -> Result<(), Error>;

    /// Attempts to write `out.len()` bytes starting at `offset`.
    ///
    /// Note that this function is not guaranteed to succeed (and be
    /// reflected in the return value of `read`) until `flush()` is called.
    /// This is to permit a `Flash` implementation to buffer writes before
    /// sending them out.
    ///
    /// Implementations are, as an optimization, permitted to assume that
    /// writes will be serial and localized, so as to minimize clearing
    /// operations on flash hardware.
    fn program(&mut self, offset: FlashPtr, buf: &[u8]) -> Result<(), Error>;

    /// Flushes any pending `program()` operations.
    fn flush(&mut self) -> Result<(), Error> {
        Ok(())
    }
}
assert_obj_safe!(Flash);

impl<F: Flash> Flash for &mut F {
    #[inline]
    fn size(&self) -> Result<u32, Error> {
        F::size(self)
    }

    #[inline]
    fn read(&self, offset: FlashPtr, out: &mut [u8]) -> Result<(), Error> {
        F::read(self, offset, out)
    }

    #[inline]
    fn program(&mut self, offset: FlashPtr, buf: &[u8]) -> Result<(), Error> {
        F::program(self, offset, buf)
    }

    #[inline]
    fn flush(&mut self) -> Result<(), Error> {
        F::flush(self)
    }
}

/// A [`Flash`] type that allows for zero-copy reads.
///
/// This trait allows implementations that can support a zero-copy read,
/// perhaps due to their inherently-buffering nature, to do so directly.
///
/// Normal [`Flash`] implementations can be made to support zero-copy
/// reads by combining them with an [`Arena`]; see [`ArenaFlash`].
///
/// `[u8]` implements this trait with no overhead.
///
/// [`Flash`]: trait.Flash.html
/// [`ArenaFlash]: struct.ArenaFlash.html
pub trait FlashZero: Flash {
    /// Attempts to zero-copy read the given region out of this device.
    fn read_zerocopy(&self, slice: FlashSlice) -> Result<&[u8], Error>;

    /// Hints to the implementation that it can release any buffered contents
    /// it is currently holding.
    ///
    /// See [`Arena::reset()`].
    ///
    /// [`Arena::reset()`]: ../../mem/trait.Arena.html#tymethod.reset
    fn reset(&mut self) -> Result<(), Error> {
        Ok(())
    }
}
assert_obj_safe!(FlashZero);

impl<F: FlashZero> FlashZero for &mut F {
    #[inline]
    fn read_zerocopy(&self, slice: FlashSlice) -> Result<&[u8], Error> {
        F::read_zerocopy(self, slice)
    }

    #[inline]
    fn reset(&mut self) -> Result<(), Error> {
        F::reset(self)
    }
}

/// Shim for supporting "zero-copy" reads on a [`Flash`] via an [`Arena`].
///
/// This type wraps a [`Flash`] type, plus an [`Arena`], and implements
/// zero-copy reads by first allocating a buffer on the arena and then
/// using that allocation to perform a read.
///
/// [`Flash`]: trait.Flash.html
/// [`Arena`]: ../../mem/trait.Arena.html
pub struct ArenaFlash<F, A>(pub F, pub A);

impl<F: Flash, A> Flash for ArenaFlash<F, A> {
    #[inline]
    fn size(&self) -> Result<u32, Error> {
        self.0.size()
    }

    #[inline]
    fn read(&self, offset: FlashPtr, out: &mut [u8]) -> Result<(), Error> {
        self.0.read(offset, out)
    }

    #[inline]
    fn program(&mut self, offset: FlashPtr, buf: &[u8]) -> Result<(), Error> {
        self.0.program(offset, buf)
    }

    #[inline]
    fn flush(&mut self) -> Result<(), Error> {
        self.0.flush()
    }
}

impl<F: Flash, A: Arena> FlashZero for ArenaFlash<F, A> {
    fn read_zerocopy(&self, slice: FlashSlice) -> Result<&[u8], Error> {
        let Self(flash, arena) = self;
        let buf = arena
            .alloc(slice.len as usize)
            .map_err(|_| Error::Internal)?;
        flash.read(slice.ptr, buf)?;
        Ok(buf)
    }

    #[inline]
    fn reset(&mut self) -> Result<(), Error> {
        Ok(self.1.reset())
    }
}

/// A [`Read`]/[`Write`] implementation for operating on a [`Flash`] serially.
///
/// [`Read`]: ../../io/read/trait.Read.html
/// [`Write`]: ../../io/write/trait.Write.html
/// [`Flash`]: trait.Flash.html
pub struct FlashIo<F> {
    flash: F,
    cursor: u32,
    len: u32,
}

impl<F: Flash> FlashIo<F> {
    /// Creates a new `FlashIo`, reading/writing from the beginning of `flash`.
    pub fn new(flash: F) -> Result<Self, Error> {
        let len = flash.size()?;
        Ok(Self {
            flash,
            cursor: 0,
            len,
        })
    }

    /// Skips the cursor `bytes` bytes forward.
    ///
    /// This operation always succeeds, but attempting to read past the end of
    /// flash will always result in an error.
    pub fn skip(&mut self, bytes: usize) {
        self.cursor = self.cursor.saturating_add(bytes as u32);
    }
}

impl<F: Flash> io::Read for FlashIo<F> {
    fn read_bytes(&mut self, out: &mut [u8]) -> Result<(), io::Error> {
        self.flash
            .read(FlashPtr::new(self.cursor), out)
            .map_err(|_| io::Error::Internal)?;
        self.cursor += out.len() as u32;
        Ok(())
    }

    fn remaining_data(&self) -> usize {
        self.len.saturating_sub(self.cursor) as usize
    }
}

impl<F: Flash> io::Write for FlashIo<F> {
    fn write_bytes(&mut self, buf: &[u8]) -> Result<(), io::Error> {
        self.flash
            .program(FlashPtr::new(self.cursor), buf)
            .map_err(|_| io::Error::Internal)?;
        self.cursor += buf.len() as u32;
        Ok(())
    }
}

/// An abstract pointer into a [`Flash`] type.
///
/// A `FlashPtr` needs to be used in conjunction with a [`Flash`]
/// implementation to be read from or written to.
///
/// [`Flash`]: trait.Flash.html
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, AsBytes, FromBytes)]
#[repr(transparent)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct FlashPtr {
    /// The abstract address of this pointer.
    pub address: u32,
}

impl FlashPtr {
    /// Convenience method for creating a `FlashPtr` without having to use
    /// a struct literal.
    pub const fn new(address: u32) -> Self {
        Self { address }
    }
}

/// An abstrace slice into a [`Flash`] type.
///
/// Much like a [`FlashPtr`], a `FlashSlice` needs to be interpreted with
/// respect to a [`Flash`] implementation.
///
/// [`Flash`]: trait.Flash.html
/// [`FlashPtr`]: trait.FlashPtr.html
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, AsBytes, FromBytes)]
#[repr(C)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct FlashSlice {
    /// The base pointer for this slice.
    #[cfg_attr(feature = "serde", serde(flatten))]
    pub ptr: FlashPtr,
    /// The length of the slice, in bytes.
    pub len: u32,
}

impl FlashSlice {
    /// Convenience method for creating a `FlashSlice` without having to use
    /// a struct literal
    pub const fn new(ptr: u32, len: u32) -> Self {
        Self {
            ptr: FlashPtr::new(ptr),
            len,
        }
    }
}

/// An unspecified out-of-bounds error.
#[derive(Copy, Clone, Debug)]
pub struct OutOfBounds;

impl Flash for [u8] {
    fn size(&self) -> Result<u32, Error> {
        self.len().try_into().map_err(|_| Error::Unspecified)
    }

    fn read(&self, offset: FlashPtr, out: &mut [u8]) -> Result<(), Error> {
        let start = offset.address as usize;
        let end = start.checked_add(out.len()).ok_or(Error::OutOfRange)?;
        if end >= self.len() {
            return Err(Error::OutOfRange);
        }

        out.copy_from_slice(&self[start..end]);
        Ok(())
    }

    fn program(&mut self, offset: FlashPtr, buf: &[u8]) -> Result<(), Error> {
        let start = offset.address as usize;
        let end = start.checked_add(buf.len()).ok_or(Error::OutOfRange)?;
        if end >= self.len() {
            return Err(Error::OutOfRange);
        }

        self[start..end].copy_from_slice(buf);
        Ok(())
    }
}

impl FlashZero for [u8] {
    fn read_zerocopy(&self, offset: FlashSlice) -> Result<&[u8], Error> {
        let start = offset.ptr.address as usize;
        let end = start
            .checked_add(offset.len as usize)
            .ok_or(Error::OutOfRange)?;
        if end >= self.len() {
            return Err(Error::OutOfRange);
        }

        Ok(&self[start..end])
    }
}
<|endoftext|>