<commit_before>// Copyright 2022 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

use std::{
    cmp::min,
    collections::HashMap,
    os::windows::io::RawHandle,
    sync::{Arc, Mutex},
    time::Duration,
};

use winapi::{
    shared::{
        minwindef::{DWORD, FALSE},
        winerror::{ERROR_INVALID_PARAMETER, WAIT_TIMEOUT},
    },
    um::{synchapi::WaitForMultipleObjects, winbase::WAIT_OBJECT_0},
};

use super::{errno_result, Error, Event, EventTrigger, PollToken, Result, TriggeredEvent};
use crate::descriptor::{AsRawDescriptor, Descriptor};
use crate::{error, EventToken, EventType, RawDescriptor, WaitContext};
// MAXIMUM_WAIT_OBJECTS = 64
pub const MAXIMUM_WAIT_OBJECTS: usize = winapi::um::winnt::MAXIMUM_WAIT_OBJECTS as usize;

// TODO(145170451) rizhang: implement round robin if event size is greater than 64

pub trait WaitContextExt {
    /// Removes all handles registered in the WaitContext.
    fn clear(&self) -> Result<()>;
}

impl<T: EventToken> WaitContextExt for WaitContext<T> {
    fn clear(&self) -> Result<()> {
        self.0.clear()
    }
}

struct RegisteredHandles<T: PollToken> {
    triggers: HashMap<Descriptor, T>,
    raw_handles: Vec<Descriptor>,
}

pub struct EventContext<T: PollToken> {
    registered_handles: Arc<Mutex<RegisteredHandles<T>>>,

    // An internally-used event to signify that the list of handles has been modified
    // mid-wait. This is to solve for instances where Thread A has started waiting and
    // Thread B adds an event trigger, which needs to notify Thread A a change has been
    // made.
    handles_modified_event: Event,
}

impl<T: PollToken> EventContext<T> {
    pub fn new() -> Result<EventContext<T>> {
        let new = EventContext {
            registered_handles: Arc::new(Mutex::new(RegisteredHandles {
                triggers: HashMap::new(),
                raw_handles: Vec::new(),
            })),
            handles_modified_event: Event::new().unwrap(),
        };
        // The handles-modified event will be everpresent on the raw_handles to be waited
        // upon to ensure the wait stops and we update it any time the handles list is
        // modified.
        new.registered_handles
            .lock()
            .unwrap()
            .raw_handles
            .push(Descriptor(new.handles_modified_event.as_raw_descriptor()));
        Ok(new)
    }

    /// Creates a new EventContext with the the associated triggers.
    pub fn build_with(triggers: &[EventTrigger<T>]) -> Result<EventContext<T>> {
        let ctx = EventContext::new()?;
        ctx.add_many(triggers)?;
        Ok(ctx)
    }

    /// Adds a trigger to the EventContext.
    pub fn add(&self, trigger: EventTrigger<T>) -> Result<()> {
        self.add_for_event_impl(trigger, EventType::Read)
    }

    /// Adds a trigger to the EventContext.
    pub fn add_many(&self, triggers: &[EventTrigger<T>]) -> Result<()> {
        for trigger in triggers {
            self.add(trigger.clone())?
        }
        Ok(())
    }

    pub fn add_for_event(
        &self,
        descriptor: &dyn AsRawDescriptor,
        event_type: EventType,
        token: T,
    ) -> Result<()> {
        self.add_for_event_impl(EventTrigger::from(descriptor, token), event_type)
    }

    fn add_for_event_impl(&self, trigger: EventTrigger<T>, _event_type: EventType) -> Result<()> {
        let mut registered_handles_locked = self.registered_handles.lock().unwrap();
        if registered_handles_locked
            .triggers
            .contains_key(&Descriptor(trigger.event))
        {
            // If this handle is already added, silently succeed with a noop
            return Ok(());
        }
        registered_handles_locked
            .triggers
            .insert(Descriptor(trigger.event), trigger.token);
        registered_handles_locked
            .raw_handles
            .push(Descriptor(trigger.event));
        // Windows doesn't support watching for specific types of events. Just treat this
        // like a normal add and do nothing with event_type
        self.handles_modified_event.write(1)
    }

    pub fn modify(
        &self,
        descriptor: &dyn AsRawDescriptor,
        _event_type: EventType,
        token: T,
    ) -> Result<()> {
        let trigger = EventTrigger::from(descriptor, token);

        let mut registered_handles_locked = self.registered_handles.lock().unwrap();
        if let std::collections::hash_map::Entry::Occupied(mut e) = registered_handles_locked
            .triggers
            .entry(Descriptor(trigger.event))
        {
            e.insert(trigger.token);
        }
        // Windows doesn't support watching for specific types of events. Ignore the event_type
        // and just modify the token.
        self.handles_modified_event.write(1)
    }

    pub fn delete(&self, event_handle: &dyn AsRawDescriptor) -> Result<()> {
        let mut registered_handles_locked = self.registered_handles.lock().unwrap();
        let result = registered_handles_locked
            .triggers
            .remove(&Descriptor(event_handle.as_raw_descriptor()));
        if result.is_none() {
            // this handle was not registered in the first place. Silently succeed with a noop
            return Ok(());
        }
        let index = registered_handles_locked
            .raw_handles
            .iter()
            .position(|item| item == &Descriptor(event_handle.as_raw_descriptor()))
            .unwrap();
        registered_handles_locked.raw_handles.remove(index);
        self.handles_modified_event.write(1)
    }

    pub fn clear(&self) -> Result<()> {
        let mut registered_handles_locked = self.registered_handles.lock().unwrap();
        registered_handles_locked.triggers.clear();
        registered_handles_locked.raw_handles.clear();

        registered_handles_locked
            .raw_handles
            .push(Descriptor(self.handles_modified_event.as_raw_descriptor()));
        self.handles_modified_event.write(1)
    }

    /// Waits for one or more of the registered triggers to become signaled.
    pub fn wait(&self) -> Result<Vec<TriggeredEvent<T>>> {
        self.wait_timeout(Duration::new(i64::MAX as u64, 0))
    }

    pub fn wait_timeout(&self, timeout: Duration) -> Result<Vec<TriggeredEvent<T>>> {
        let raw_handles_list: Vec<RawHandle> = self
            .registered_handles
            .lock()
            .unwrap()
            .raw_handles
            .clone()
            .into_iter()
            .map(|handle| handle.0)
            .collect();
        if raw_handles_list.len() == 1 {
            // Disallow calls with no handles to wait on. Do not include the handles_modified_event
            // which always populates the list.
            return Err(Error::new(ERROR_INVALID_PARAMETER));
        }
        let result = unsafe {
            WaitForMultipleObjects(
                raw_handles_list.len() as DWORD,
                raw_handles_list.as_ptr(),
                FALSE, // return when one event is signaled
                timeout.as_millis() as DWORD,
            )
        };
        let handles_len = min(MAXIMUM_WAIT_OBJECTS, raw_handles_list.len()) as usize;

        const MAXIMUM_WAIT_OBJECTS_U32: u32 = MAXIMUM_WAIT_OBJECTS as u32;
        match result as u32 {
            WAIT_OBJECT_0..=MAXIMUM_WAIT_OBJECTS_U32 => {
                let mut event_index = (result - WAIT_OBJECT_0) as usize;
                if event_index >= handles_len {
                    // This is not a valid index and should return an error. This case should not be possible
                    // and will likely not return a meaningful system error code, but is still an invalid case.
                    error!("Wait returned index out of range");
                    return errno_result();
                }
                if event_index == 0 {
                    // The handles list has been modified and triggered the wait, try again with the updated
                    // handles list. Note it is possible the list was modified again after the wait which will
                    // trigger the handles_modified_event again, but that will only err towards the safe side
                    // of recursing an extra time.
                    let _ = self.handles_modified_event.read();
                    return self.wait_timeout(timeout);
                }

                let mut events_to_return: Vec<TriggeredEvent<T>> = vec![];
                // Multiple events may be triggered at once, but WaitForMultipleObjects will only return one.
                // Once it returns, loop through the remaining triggers checking each to ensure they haven't
                // also been triggered.
                let mut handles_offset: usize = 0;
                loop {
                    let event_to_return = raw_handles_list[event_index + handles_offset];
                    events_to_return.push(TriggeredEvent {
                        token: T::from_raw_token(
                            self.registered_handles
                                .lock()
                                .unwrap()
                                .triggers
                                .get(&Descriptor(event_to_return))
                                .unwrap()
                                .as_raw_token(),
                        ),
                        // In Windows, events aren't associated with read/writability, so for cross-
                        // compatability, associate with both.
                        is_readable: true,
                        is_writable: true,
                        is_hungup: false,
                    });

                    handles_offset += event_index + 1;
                    if handles_offset >= handles_len {
                        break;
                    }
                    event_index = (unsafe {
                        WaitForMultipleObjects(
                            (raw_handles_list.len() - handles_offset) as DWORD,
                            raw_handles_list[handles_offset..].as_ptr(),
                            FALSE, // return when one event is signaled
                            0,     /* instantaneous timeout */
                        )
                    } - WAIT_OBJECT_0) as usize;

                    if event_index >= (handles_len - handles_offset) {
                        // This indicates a failure condition, as return values greater than the length
                        // of the provided array are reserved for failures.
                        break;
                    }
                }

                Ok(events_to_return)
            }
            WAIT_TIMEOUT => Ok(vec![]),
            // Invalid cases. This is most likely an WAIT_FAILED, but anything not matched by the
            // above is an error case.
            _ => errno_result(),
        }
    }
}

impl<T: PollToken> AsRawDescriptor for EventContext<T> {
    fn as_raw_descriptor(&self) -> RawDescriptor {
        self.handles_modified_event.as_raw_descriptor()
    }
}

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

    #[test]
    #[should_panic]
    fn error_on_empty_context_wait() {
        let ctx: EventContext<u32> = EventContext::new().unwrap();
        let dur = Duration::from_millis(10);
        ctx.wait_timeout(dur).unwrap();
    }
}
<commit_msg>base: windows: use sync::Mutex to avoid unwrap()<commit_after>// Copyright 2022 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

use std::{cmp::min, collections::HashMap, os::windows::io::RawHandle, sync::Arc, time::Duration};

use sync::Mutex;
use winapi::{
    shared::{
        minwindef::{DWORD, FALSE},
        winerror::{ERROR_INVALID_PARAMETER, WAIT_TIMEOUT},
    },
    um::{synchapi::WaitForMultipleObjects, winbase::WAIT_OBJECT_0},
};

use super::{errno_result, Error, Event, EventTrigger, PollToken, Result, TriggeredEvent};
use crate::descriptor::{AsRawDescriptor, Descriptor};
use crate::{error, EventToken, EventType, RawDescriptor, WaitContext};
// MAXIMUM_WAIT_OBJECTS = 64
pub const MAXIMUM_WAIT_OBJECTS: usize = winapi::um::winnt::MAXIMUM_WAIT_OBJECTS as usize;

// TODO(145170451) rizhang: implement round robin if event size is greater than 64

pub trait WaitContextExt {
    /// Removes all handles registered in the WaitContext.
    fn clear(&self) -> Result<()>;
}

impl<T: EventToken> WaitContextExt for WaitContext<T> {
    fn clear(&self) -> Result<()> {
        self.0.clear()
    }
}

struct RegisteredHandles<T: PollToken> {
    triggers: HashMap<Descriptor, T>,
    raw_handles: Vec<Descriptor>,
}

pub struct EventContext<T: PollToken> {
    registered_handles: Arc<Mutex<RegisteredHandles<T>>>,

    // An internally-used event to signify that the list of handles has been modified
    // mid-wait. This is to solve for instances where Thread A has started waiting and
    // Thread B adds an event trigger, which needs to notify Thread A a change has been
    // made.
    handles_modified_event: Event,
}

impl<T: PollToken> EventContext<T> {
    pub fn new() -> Result<EventContext<T>> {
        let new = EventContext {
            registered_handles: Arc::new(Mutex::new(RegisteredHandles {
                triggers: HashMap::new(),
                raw_handles: Vec::new(),
            })),
            handles_modified_event: Event::new().unwrap(),
        };
        // The handles-modified event will be everpresent on the raw_handles to be waited
        // upon to ensure the wait stops and we update it any time the handles list is
        // modified.
        new.registered_handles
            .lock()
            .raw_handles
            .push(Descriptor(new.handles_modified_event.as_raw_descriptor()));
        Ok(new)
    }

    /// Creates a new EventContext with the the associated triggers.
    pub fn build_with(triggers: &[EventTrigger<T>]) -> Result<EventContext<T>> {
        let ctx = EventContext::new()?;
        ctx.add_many(triggers)?;
        Ok(ctx)
    }

    /// Adds a trigger to the EventContext.
    pub fn add(&self, trigger: EventTrigger<T>) -> Result<()> {
        self.add_for_event_impl(trigger, EventType::Read)
    }

    /// Adds a trigger to the EventContext.
    pub fn add_many(&self, triggers: &[EventTrigger<T>]) -> Result<()> {
        for trigger in triggers {
            self.add(trigger.clone())?
        }
        Ok(())
    }

    pub fn add_for_event(
        &self,
        descriptor: &dyn AsRawDescriptor,
        event_type: EventType,
        token: T,
    ) -> Result<()> {
        self.add_for_event_impl(EventTrigger::from(descriptor, token), event_type)
    }

    fn add_for_event_impl(&self, trigger: EventTrigger<T>, _event_type: EventType) -> Result<()> {
        let mut registered_handles_locked = self.registered_handles.lock();
        if registered_handles_locked
            .triggers
            .contains_key(&Descriptor(trigger.event))
        {
            // If this handle is already added, silently succeed with a noop
            return Ok(());
        }
        registered_handles_locked
            .triggers
            .insert(Descriptor(trigger.event), trigger.token);
        registered_handles_locked
            .raw_handles
            .push(Descriptor(trigger.event));
        // Windows doesn't support watching for specific types of events. Just treat this
        // like a normal add and do nothing with event_type
        self.handles_modified_event.write(1)
    }

    pub fn modify(
        &self,
        descriptor: &dyn AsRawDescriptor,
        _event_type: EventType,
        token: T,
    ) -> Result<()> {
        let trigger = EventTrigger::from(descriptor, token);

        let mut registered_handles_locked = self.registered_handles.lock();
        if let std::collections::hash_map::Entry::Occupied(mut e) = registered_handles_locked
            .triggers
            .entry(Descriptor(trigger.event))
        {
            e.insert(trigger.token);
        }
        // Windows doesn't support watching for specific types of events. Ignore the event_type
        // and just modify the token.
        self.handles_modified_event.write(1)
    }

    pub fn delete(&self, event_handle: &dyn AsRawDescriptor) -> Result<()> {
        let mut registered_handles_locked = self.registered_handles.lock();
        let result = registered_handles_locked
            .triggers
            .remove(&Descriptor(event_handle.as_raw_descriptor()));
        if result.is_none() {
            // this handle was not registered in the first place. Silently succeed with a noop
            return Ok(());
        }
        let index = registered_handles_locked
            .raw_handles
            .iter()
            .position(|item| item == &Descriptor(event_handle.as_raw_descriptor()))
            .unwrap();
        registered_handles_locked.raw_handles.remove(index);
        self.handles_modified_event.write(1)
    }

    pub fn clear(&self) -> Result<()> {
        let mut registered_handles_locked = self.registered_handles.lock();
        registered_handles_locked.triggers.clear();
        registered_handles_locked.raw_handles.clear();

        registered_handles_locked
            .raw_handles
            .push(Descriptor(self.handles_modified_event.as_raw_descriptor()));
        self.handles_modified_event.write(1)
    }

    /// Waits for one or more of the registered triggers to become signaled.
    pub fn wait(&self) -> Result<Vec<TriggeredEvent<T>>> {
        self.wait_timeout(Duration::new(i64::MAX as u64, 0))
    }

    pub fn wait_timeout(&self, timeout: Duration) -> Result<Vec<TriggeredEvent<T>>> {
        let raw_handles_list: Vec<RawHandle> = self
            .registered_handles
            .lock()
            .raw_handles
            .clone()
            .into_iter()
            .map(|handle| handle.0)
            .collect();
        if raw_handles_list.len() == 1 {
            // Disallow calls with no handles to wait on. Do not include the handles_modified_event
            // which always populates the list.
            return Err(Error::new(ERROR_INVALID_PARAMETER));
        }
        let result = unsafe {
            WaitForMultipleObjects(
                raw_handles_list.len() as DWORD,
                raw_handles_list.as_ptr(),
                FALSE, // return when one event is signaled
                timeout.as_millis() as DWORD,
            )
        };
        let handles_len = min(MAXIMUM_WAIT_OBJECTS, raw_handles_list.len()) as usize;

        const MAXIMUM_WAIT_OBJECTS_U32: u32 = MAXIMUM_WAIT_OBJECTS as u32;
        match result as u32 {
            WAIT_OBJECT_0..=MAXIMUM_WAIT_OBJECTS_U32 => {
                let mut event_index = (result - WAIT_OBJECT_0) as usize;
                if event_index >= handles_len {
                    // This is not a valid index and should return an error. This case should not be possible
                    // and will likely not return a meaningful system error code, but is still an invalid case.
                    error!("Wait returned index out of range");
                    return errno_result();
                }
                if event_index == 0 {
                    // The handles list has been modified and triggered the wait, try again with the updated
                    // handles list. Note it is possible the list was modified again after the wait which will
                    // trigger the handles_modified_event again, but that will only err towards the safe side
                    // of recursing an extra time.
                    let _ = self.handles_modified_event.read();
                    return self.wait_timeout(timeout);
                }

                let mut events_to_return: Vec<TriggeredEvent<T>> = vec![];
                // Multiple events may be triggered at once, but WaitForMultipleObjects will only return one.
                // Once it returns, loop through the remaining triggers checking each to ensure they haven't
                // also been triggered.
                let mut handles_offset: usize = 0;
                loop {
                    let event_to_return = raw_handles_list[event_index + handles_offset];
                    events_to_return.push(TriggeredEvent {
                        token: T::from_raw_token(
                            self.registered_handles
                                .lock()
                                .triggers
                                .get(&Descriptor(event_to_return))
                                .unwrap()
                                .as_raw_token(),
                        ),
                        // In Windows, events aren't associated with read/writability, so for cross-
                        // compatability, associate with both.
                        is_readable: true,
                        is_writable: true,
                        is_hungup: false,
                    });

                    handles_offset += event_index + 1;
                    if handles_offset >= handles_len {
                        break;
                    }
                    event_index = (unsafe {
                        WaitForMultipleObjects(
                            (raw_handles_list.len() - handles_offset) as DWORD,
                            raw_handles_list[handles_offset..].as_ptr(),
                            FALSE, // return when one event is signaled
                            0,     /* instantaneous timeout */
                        )
                    } - WAIT_OBJECT_0) as usize;

                    if event_index >= (handles_len - handles_offset) {
                        // This indicates a failure condition, as return values greater than the length
                        // of the provided array are reserved for failures.
                        break;
                    }
                }

                Ok(events_to_return)
            }
            WAIT_TIMEOUT => Ok(vec![]),
            // Invalid cases. This is most likely an WAIT_FAILED, but anything not matched by the
            // above is an error case.
            _ => errno_result(),
        }
    }
}

impl<T: PollToken> AsRawDescriptor for EventContext<T> {
    fn as_raw_descriptor(&self) -> RawDescriptor {
        self.handles_modified_event.as_raw_descriptor()
    }
}

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

    #[test]
    #[should_panic]
    fn error_on_empty_context_wait() {
        let ctx: EventContext<u32> = EventContext::new().unwrap();
        let dur = Duration::from_millis(10);
        ctx.wait_timeout(dur).unwrap();
    }
}
<|endoftext|>


<commit_before>/// This modules specifies the input to rust-analyzer. In some sense, this is
/// **the** most important module, because all other fancy stuff is strictly
/// derived from this input.
///
/// Note that neither this module, nor any other part of the analyzer's core do
/// actual IO. See `vfs` and `project_model` in `ra_lsp_server` crate for how
/// actual IO is done and lowered to input.
use std::sync::Arc;

use relative_path::RelativePathBuf;
use rustc_hash::FxHashMap;
use salsa;

use ra_syntax::SmolStr;

/// `FileId` is an integer which uniquely identifies a file. File paths are
/// messy and system-dependent, so most of the code should work directly with
/// `FileId`, without inspecting the path. The mapping between `FileId` and path
/// and `SourceRoot` is constant. File rename is represented as a pair of
/// deletion/creation.
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct FileId(pub u32);

/// Files are grouped into source roots. A source root is a directory on the
/// file systems which is watched for changes. Typically it corresponds to a
/// Cargo package. Source roots *might* be nested: in this case, file belongs to
/// the nearest enclosing source root. Path to files are always relative to a
/// source root, and analyzer does not know the root path of the source root at
/// all. So, a file from one source root can't refere a file in another source
/// root by path.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct SourceRootId(pub u32);

#[derive(Default, Clone, Debug, PartialEq, Eq)]
pub struct SourceRoot {
    pub files: FxHashMap<RelativePathBuf, FileId>,
}

/// `CrateGraph` is a bit of information which turns a set of text files into a
/// number of Rust crates. Each Crate is the `FileId` of it's root module, the
/// set of cfg flags (not yet implemented) and the set of dependencies. Note
/// that, due to cfg's, there might be several crates for a single `FileId`! As
/// in the rust-lang proper, a crate does not have a name. Instead, names are
/// specified on dependency edges. That is, a crate might be known under
/// different names in different dependant crates.
///
/// Note that `CrateGraph` is build-system agnostic: it's a concept of the Rust
/// langauge proper, not a concept of the build system. In practice, we get
/// `CrateGraph` by lowering `cargo metadata` output.
#[derive(Debug, Clone, Default, PartialEq, Eq)]
pub struct CrateGraph {
    arena: FxHashMap<CrateId, CrateData>,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct CrateId(pub u32);

#[derive(Debug, Clone, PartialEq, Eq)]
struct CrateData {
    file_id: FileId,
    dependencies: Vec<Dependency>,
}

impl CrateData {
    fn new(file_id: FileId) -> CrateData {
        CrateData {
            file_id,
            dependencies: Vec::new(),
        }
    }

    fn add_dep(&mut self, name: SmolStr, crate_id: CrateId) {
        self.dependencies.push(Dependency { name, crate_id })
    }
}

#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Dependency {
    pub crate_id: CrateId,
    pub name: SmolStr,
}

impl Dependency {
    pub fn crate_id(&self) -> CrateId {
        self.crate_id
    }
}

impl CrateGraph {
    pub fn add_crate_root(&mut self, file_id: FileId) -> CrateId {
        let crate_id = CrateId(self.arena.len() as u32);
        let prev = self.arena.insert(crate_id, CrateData::new(file_id));
        assert!(prev.is_none());
        crate_id
    }
    pub fn add_dep(&mut self, from: CrateId, name: SmolStr, to: CrateId) {
        if self.dfs_find(from, to) {
           panic!("Cycle dependencies found.")
        }
        self.arena.get_mut(&from).unwrap().add_dep(name, to)
    }
    pub fn crate_root(&self, crate_id: CrateId) -> FileId {
        self.arena[&crate_id].file_id
    }
    pub fn crate_id_for_crate_root(&self, file_id: FileId) -> Option<CrateId> {
        let (&crate_id, _) = self
            .arena
            .iter()
            .find(|(_crate_id, data)| data.file_id == file_id)?;
        Some(crate_id)
    }
    pub fn dependencies<'a>(
        &'a self,
        crate_id: CrateId,
    ) -> impl Iterator<Item=&'a Dependency> + 'a {
        self.arena[&crate_id].dependencies.iter()
    }
    fn dfs_find(&self, target: CrateId, from: CrateId) -> bool {
        for dep in self.dependencies(from) {
            let crate_id = dep.crate_id();
            if crate_id == target {
                return true;
            }
            if self.arena.contains_key(&crate_id) {
                if self.dfs_find(target, crate_id) {
                    return true;
                }
            }
        }
        return false;
    }
}

mod test {
    use super::{CrateGraph, FxHashMap, FileId, SmolStr};
    #[test]
    #[should_panic]
    fn it_should_painc_because_of_cycle_dependencies() {
        let mut graph = CrateGraph {
            arena: FxHashMap::default()
        };
        let crate1 = graph.add_crate_root(FileId(1u32));
        let crate2 = graph.add_crate_root(FileId(2u32));
        let crate3 = graph.add_crate_root(FileId(3u32));
        graph.add_dep(crate1, SmolStr::new("crate2"), crate2);
        graph.add_dep(crate2, SmolStr::new("crate3"), crate3);
        graph.add_dep(crate3, SmolStr::new("crate1"), crate1);
    }

    #[test]
    fn it_works() {
        let mut graph = CrateGraph {
            arena: FxHashMap::default()
        };
        let crate1 = graph.add_crate_root(FileId(1u32));
        let crate2 = graph.add_crate_root(FileId(2u32));
        let crate3 = graph.add_crate_root(FileId(3u32));
        graph.add_dep(crate1, SmolStr::new("crate2"), crate2);
        graph.add_dep(crate2, SmolStr::new("crate3"), crate3);
    }
}

salsa::query_group! {
    pub trait FilesDatabase: salsa::Database {
        /// Text of the file.
        fn file_text(file_id: FileId) -> Arc<String> {
            type FileTextQuery;
            storage input;
        }
        /// Path to a file, relative to the root of its source root.
        fn file_relative_path(file_id: FileId) -> RelativePathBuf {
            type FileRelativePathQuery;
            storage input;
        }
        /// Source root of the file.
        fn file_source_root(file_id: FileId) -> SourceRootId {
            type FileSourceRootQuery;
            storage input;
        }
        /// Contents of the source root.
        fn source_root(id: SourceRootId) -> Arc<SourceRoot> {
            type SourceRootQuery;
            storage input;
        }
        /// The set of "local" (that is, from the current workspace) roots.
        /// Files in local roots are assumed to change frequently.
        fn local_roots() -> Arc<Vec<SourceRootId>> {
            type LocalRootsQuery;
            storage input;
        }
        /// The set of roots for crates.io libraries.
        /// Files in libraries are assumed to never change.
        fn library_roots() -> Arc<Vec<SourceRootId>> {
            type LibraryRootsQuery;
            storage input;
        }
        /// The crate graph.
        fn crate_graph() -> Arc<CrateGraph> {
            type CrateGraphQuery;
            storage input;
        }
    }
}
<commit_msg>add #[cfg(test)]<commit_after>/// This modules specifies the input to rust-analyzer. In some sense, this is
/// **the** most important module, because all other fancy stuff is strictly
/// derived from this input.
///
/// Note that neither this module, nor any other part of the analyzer's core do
/// actual IO. See `vfs` and `project_model` in `ra_lsp_server` crate for how
/// actual IO is done and lowered to input.
use std::sync::Arc;

use relative_path::RelativePathBuf;
use rustc_hash::FxHashMap;
use salsa;

use ra_syntax::SmolStr;

/// `FileId` is an integer which uniquely identifies a file. File paths are
/// messy and system-dependent, so most of the code should work directly with
/// `FileId`, without inspecting the path. The mapping between `FileId` and path
/// and `SourceRoot` is constant. File rename is represented as a pair of
/// deletion/creation.
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct FileId(pub u32);

/// Files are grouped into source roots. A source root is a directory on the
/// file systems which is watched for changes. Typically it corresponds to a
/// Cargo package. Source roots *might* be nested: in this case, file belongs to
/// the nearest enclosing source root. Path to files are always relative to a
/// source root, and analyzer does not know the root path of the source root at
/// all. So, a file from one source root can't refere a file in another source
/// root by path.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct SourceRootId(pub u32);

#[derive(Default, Clone, Debug, PartialEq, Eq)]
pub struct SourceRoot {
    pub files: FxHashMap<RelativePathBuf, FileId>,
}

/// `CrateGraph` is a bit of information which turns a set of text files into a
/// number of Rust crates. Each Crate is the `FileId` of it's root module, the
/// set of cfg flags (not yet implemented) and the set of dependencies. Note
/// that, due to cfg's, there might be several crates for a single `FileId`! As
/// in the rust-lang proper, a crate does not have a name. Instead, names are
/// specified on dependency edges. That is, a crate might be known under
/// different names in different dependant crates.
///
/// Note that `CrateGraph` is build-system agnostic: it's a concept of the Rust
/// langauge proper, not a concept of the build system. In practice, we get
/// `CrateGraph` by lowering `cargo metadata` output.
#[derive(Debug, Clone, Default, PartialEq, Eq)]
pub struct CrateGraph {
    arena: FxHashMap<CrateId, CrateData>,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct CrateId(pub u32);

#[derive(Debug, Clone, PartialEq, Eq)]
struct CrateData {
    file_id: FileId,
    dependencies: Vec<Dependency>,
}

impl CrateData {
    fn new(file_id: FileId) -> CrateData {
        CrateData {
            file_id,
            dependencies: Vec::new(),
        }
    }

    fn add_dep(&mut self, name: SmolStr, crate_id: CrateId) {
        self.dependencies.push(Dependency { name, crate_id })
    }
}

#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Dependency {
    pub crate_id: CrateId,
    pub name: SmolStr,
}

impl Dependency {
    pub fn crate_id(&self) -> CrateId {
        self.crate_id
    }
}

impl CrateGraph {
    pub fn add_crate_root(&mut self, file_id: FileId) -> CrateId {
        let crate_id = CrateId(self.arena.len() as u32);
        let prev = self.arena.insert(crate_id, CrateData::new(file_id));
        assert!(prev.is_none());
        crate_id
    }
    pub fn add_dep(&mut self, from: CrateId, name: SmolStr, to: CrateId) {
        if self.dfs_find(from, to) {
           panic!("Cycle dependencies found.")
        }
        self.arena.get_mut(&from).unwrap().add_dep(name, to)
    }
    pub fn crate_root(&self, crate_id: CrateId) -> FileId {
        self.arena[&crate_id].file_id
    }
    pub fn crate_id_for_crate_root(&self, file_id: FileId) -> Option<CrateId> {
        let (&crate_id, _) = self
            .arena
            .iter()
            .find(|(_crate_id, data)| data.file_id == file_id)?;
        Some(crate_id)
    }
    pub fn dependencies<'a>(
        &'a self,
        crate_id: CrateId,
    ) -> impl Iterator<Item=&'a Dependency> + 'a {
        self.arena[&crate_id].dependencies.iter()
    }
    fn dfs_find(&self, target: CrateId, from: CrateId) -> bool {
        for dep in self.dependencies(from) {
            let crate_id = dep.crate_id();
            if crate_id == target {
                return true;
            }
            if self.arena.contains_key(&crate_id) {
                if self.dfs_find(target, crate_id) {
                    return true;
                }
            }
        }
        return false;
    }
}

#[cfg(test)]
mod tests {
    use super::{CrateGraph, FxHashMap, FileId, SmolStr};
    #[test]
    #[should_panic]
    fn it_should_painc_because_of_cycle_dependencies() {
        let mut graph = CrateGraph {
            arena: FxHashMap::default()
        };
        let crate1 = graph.add_crate_root(FileId(1u32));
        let crate2 = graph.add_crate_root(FileId(2u32));
        let crate3 = graph.add_crate_root(FileId(3u32));
        graph.add_dep(crate1, SmolStr::new("crate2"), crate2);
        graph.add_dep(crate2, SmolStr::new("crate3"), crate3);
        graph.add_dep(crate3, SmolStr::new("crate1"), crate1);
    }

    #[test]
    fn it_works() {
        let mut graph = CrateGraph {
            arena: FxHashMap::default()
        };
        let crate1 = graph.add_crate_root(FileId(1u32));
        let crate2 = graph.add_crate_root(FileId(2u32));
        let crate3 = graph.add_crate_root(FileId(3u32));
        graph.add_dep(crate1, SmolStr::new("crate2"), crate2);
        graph.add_dep(crate2, SmolStr::new("crate3"), crate3);
    }
}

salsa::query_group! {
    pub trait FilesDatabase: salsa::Database {
        /// Text of the file.
        fn file_text(file_id: FileId) -> Arc<String> {
            type FileTextQuery;
            storage input;
        }
        /// Path to a file, relative to the root of its source root.
        fn file_relative_path(file_id: FileId) -> RelativePathBuf {
            type FileRelativePathQuery;
            storage input;
        }
        /// Source root of the file.
        fn file_source_root(file_id: FileId) -> SourceRootId {
            type FileSourceRootQuery;
            storage input;
        }
        /// Contents of the source root.
        fn source_root(id: SourceRootId) -> Arc<SourceRoot> {
            type SourceRootQuery;
            storage input;
        }
        /// The set of "local" (that is, from the current workspace) roots.
        /// Files in local roots are assumed to change frequently.
        fn local_roots() -> Arc<Vec<SourceRootId>> {
            type LocalRootsQuery;
            storage input;
        }
        /// The set of roots for crates.io libraries.
        /// Files in libraries are assumed to never change.
        fn library_roots() -> Arc<Vec<SourceRootId>> {
            type LibraryRootsQuery;
            storage input;
        }
        /// The crate graph.
        fn crate_graph() -> Arc<CrateGraph> {
            type CrateGraphQuery;
            storage input;
        }
    }
}
<|endoftext|>


<commit_before>use derive_more::{Add, Div, From, Mul, Sub};
use std::f64::consts::{PI, TAU};

/// The OrbTk way to handle angles
#[derive(Add, Sub, Copy, From, Clone, Debug, PartialEq, Mul, Div)]
pub struct Angle(f64);

impl Angle {
    // TODO: Make this const fns, when the floating point operations on const fns become stable
    pub fn from_radians(radians: f64) -> Angle {
        Angle(radians)
    }

    pub fn from_degrees(degrees: f64) -> Angle {
        Angle(degrees * PI / 180.0)
    }

    /// Takes a number between 0.0 and 1.0 where 0.0 represents 0 degrees and 1.0 360 degrees
    pub fn from_turn(turn: f64) -> Angle {
        Angle(turn * TAU)
    }

    pub fn to_radians(&self) -> f64 {
        self.0
    }

    pub fn to_degrees(&self) -> f64 {
        self.0 * 180.0 / PI
    }

    /// Gives a number between 0.0 and 1.0 where 0.0 represents 0 degrees and 1.0 360 degrees
    pub fn to_turn(&self) -> f64 {
        self.0 / TAU
    }

    /// Creates a `Angle` with a value of 0.0
    pub fn zero() -> Angle {
        Angle(0.0)
    }
}

impl Default for Angle {
    fn default() -> Self {
        Self::zero()
    }
}
<commit_msg>define TAU for older rust versions<commit_after>use derive_more::{Add, Div, From, Mul, Sub};
use std::f64::consts::PI;

pub const TAU: f64 = 6.283_185_307_179_586_f64;

/// The OrbTk way to handle angles
#[derive(Add, Sub, Copy, From, Clone, Debug, PartialEq, Mul, Div)]
pub struct Angle(f64);

impl Angle {
    // TODO: Make this const fns, when the floating point operations on const fns become stable
    pub fn from_radians(radians: f64) -> Angle {
        Angle(radians)
    }

    pub fn from_degrees(degrees: f64) -> Angle {
        Angle(degrees * PI / 180.0)
    }

    /// Takes a number between 0.0 and 1.0 where 0.0 represents 0 degrees and 1.0 360 degrees
    pub fn from_turn(turn: f64) -> Angle {
        Angle(turn * TAU)
    }

    pub fn to_radians(&self) -> f64 {
        self.0
    }

    pub fn to_degrees(&self) -> f64 {
        self.0 * 180.0 / PI
    }

    /// Gives a number between 0.0 and 1.0 where 0.0 represents 0 degrees and 1.0 360 degrees
    pub fn to_turn(&self) -> f64 {
        self.0 / TAU
    }

    /// Creates a `Angle` with a value of 0.0
    pub fn zero() -> Angle {
        Angle(0.0)
    }
}

impl Default for Angle {
    fn default() -> Self {
        Self::zero()
    }
}
<|endoftext|>


<commit_before>// Copyright 2015 Gomez Guillaume
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//   http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use std::fs;

pub fn loop_over_files<T>(path: &str, data: &mut T, func: &Fn(&str, &mut T)) {
    match fs::read_dir(path) {
        Ok(it) => {
            for entry in it {
                check_path_type(entry.unwrap().path().to_str().unwrap(), data, func);
            }
        }
        Err(e) => {
            println!("Error while trying to iterate over {}: {}", path, e);
        }
    }
}

fn check_path_type<T>(path: &str, data: &mut T, func: &Fn(&str, &mut T)) {
    match fs::metadata(path) {
        Ok(m) => {
            if m.is_dir() {
                if path == ".." || path == "." {
                    return;
                }
                loop_over_files(path, data, func);
            } else {
                if path == "./comments.cmts" || !path.ends_with(".rs") {
                    return;
                }
                println!("-> {}", path);
                func(path, data);
            }
        }
        Err(e) => {
            println!("An error occurred on '{}': {}", path, e);
        }
    }
}

pub fn join(s: &[String], join_part: &str) -> String {
    let mut ret = String::new();
    let mut it = 0;

    while it < s.len() {
        ret.push_str(&s[it]);
        it += 1;
        if it < s.len() {
            ret.push_str(join_part);
        }
    }
    ret
}<commit_msg>Fixes #1<commit_after>// Copyright 2015 Gomez Guillaume
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//   http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use std::fs;

pub fn loop_over_files<T>(path: &str, data: &mut T, func: &Fn(&str, &mut T)) {
    match fs::read_dir(path) {
        Ok(it) => {
            let mut entries = vec!();

            for entry in it {
                let path = entry.unwrap().path().to_str().unwrap().to_owned();

                entries.push(path.clone());
            }
            entries.sort();
            for entry in entries {
                check_path_type(&entry, data, func);
            }
        }
        Err(e) => {
            println!("Error while trying to iterate over {}: {}", path, e);
        }
    }
}

fn check_path_type<T>(path: &str, data: &mut T, func: &Fn(&str, &mut T)) {
    match fs::metadata(path) {
        Ok(m) => {
            if m.is_dir() {
                if path == ".." || path == "." {
                    return;
                }
                loop_over_files(path, data, func);
            } else {
                if path == "./comments.cmts" || !path.ends_with(".rs") {
                    return;
                }
                println!("-> {}", path);
                func(path, data);
            }
        }
        Err(e) => {
            println!("An error occurred on '{}': {}", path, e);
        }
    }
}

pub fn join(s: &[String], join_part: &str) -> String {
    let mut ret = String::new();
    let mut it = 0;

    while it < s.len() {
        ret.push_str(&s[it]);
        it += 1;
        if it < s.len() {
            ret.push_str(join_part);
        }
    }
    ret
}<|endoftext|>


<commit_before>use std::fs::{File, OpenOptions};
use std::io;
use std::io::prelude::*;
use std::io::ErrorKind;
use std::path::{Path, PathBuf};

pub fn create(pass: Option<&str>, lint_name: Option<&str>, category: Option<&str>) -> Result<(), io::Error> {
    let pass = pass.expect("`pass` argument is validated by clap");
    let lint_name = lint_name.expect("`name` argument is validated by clap");
    let category = category.expect("`category` argument is validated by clap");

    match open_files(lint_name) {
        Ok((mut test_file, mut lint_file)) => {
            let (pass_type, pass_import, context_import) = match pass {
                "early" => ("EarlyLintPass", "use syntax::ast::*;", "EarlyContext"),
                "late" => ("LateLintPass", "use rustc_hir::*;", "LateContext"),
                _ => {
                    unreachable!("`pass_type` should only ever be `early` or `late`!");
                },
            };

            let camel_case_name = to_camel_case(lint_name);

            if let Err(e) = test_file.write_all(get_test_file_contents(lint_name).as_bytes()) {
                return Err(io::Error::new(
                    ErrorKind::Other,
                    format!("Could not write to test file: {}", e),
                ));
            };

            if let Err(e) = lint_file.write_all(
                get_lint_file_contents(
                    pass_type,
                    lint_name,
                    &camel_case_name,
                    category,
                    pass_import,
                    context_import,
                )
                .as_bytes(),
            ) {
                return Err(io::Error::new(
                    ErrorKind::Other,
                    format!("Could not write to lint file: {}", e),
                ));
            }
            Ok(())
        },
        Err(e) => Err(io::Error::new(
            ErrorKind::Other,
            format!("Unable to create lint: {}", e),
        )),
    }
}

fn open_files(lint_name: &str) -> Result<(File, File), io::Error> {
    let project_root = project_root()?;

    let test_file_path = project_root.join("tests").join("ui").join(format!("{}.rs", lint_name));
    let lint_file_path = project_root
        .join("clippy_lints")
        .join("src")
        .join(format!("{}.rs", lint_name));

    if Path::new(&test_file_path).exists() {
        return Err(io::Error::new(
            ErrorKind::AlreadyExists,
            format!("test file {:?} already exists", test_file_path),
        ));
    }
    if Path::new(&lint_file_path).exists() {
        return Err(io::Error::new(
            ErrorKind::AlreadyExists,
            format!("lint file {:?} already exists", lint_file_path),
        ));
    }

    let test_file = OpenOptions::new().write(true).create_new(true).open(test_file_path)?;
    let lint_file = OpenOptions::new().write(true).create_new(true).open(lint_file_path)?;

    Ok((test_file, lint_file))
}

fn project_root() -> Result<PathBuf, io::Error> {
    let current_dir = std::env::current_dir()?;
    for path in current_dir.ancestors() {
        let result = std::fs::read_to_string(path.join("Cargo.toml"));
        if let Err(err) = &result {
            if err.kind() == io::ErrorKind::NotFound {
                continue;
            }
        }

        let content = result?;
        if content.contains("[package]\nname = \"clippy\"") {
            return Ok(path.to_path_buf());
        }
    }
    Err(io::Error::new(ErrorKind::Other, "Unable to find project root"))
}

fn to_camel_case(name: &str) -> String {
    name.split('_')
        .map(|s| {
            if s.is_empty() {
                String::from("")
            } else {
                [&s[0..1].to_uppercase(), &s[1..]].concat()
            }
        })
        .collect()
}

fn get_test_file_contents(lint_name: &str) -> String {
    format!(
        "#![warn(clippy::{})]

fn main() {{
    // test code goes here
}}
",
        lint_name
    )
}

fn get_lint_file_contents(
    pass_type: &str,
    lint_name: &str,
    camel_case_name: &str,
    category: &str,
    pass_import: &str,
    context_import: &str,
) -> String {
    format!(
        "use rustc::lint::{{LintArray, LintPass, {type}, {context_import}}};
use rustc_session::{{declare_lint_pass, declare_tool_lint}};
{pass_import}

declare_clippy_lint! {{
    /// **What it does:**
    ///
    /// **Why is this bad?**
    ///
    /// **Known problems:** None.
    ///
    /// **Example:**
    ///
    /// ```rust
    /// // example code
    /// ```
    pub {name_upper},
    {category},
    \"default lint description\"
}}

declare_lint_pass!({name_camel} => [{name_upper}]);

impl {type} for {name_camel} {{}}
",
        type=pass_type,
        name_upper=lint_name.to_uppercase(),
        name_camel=camel_case_name,
        category=category,
        pass_import=pass_import,
        context_import=context_import
    )
}

#[test]
fn test_camel_case() {
    let s = "a_lint";
    let s2 = to_camel_case(s);
    assert_eq!(s2, "ALint");

    let name = "a_really_long_new_lint";
    let name2 = to_camel_case(name);
    assert_eq!(name2, "AReallyLongNewLint");

    let name3 = "lint__name";
    let name4 = to_camel_case(name3);
    assert_eq!(name4, "LintName");
}
<commit_msg>Auto merge of #5055 - krishna-veerareddy:fix-new-lint-generator-import, r=llogiq<commit_after>use std::fs::{File, OpenOptions};
use std::io;
use std::io::prelude::*;
use std::io::ErrorKind;
use std::path::{Path, PathBuf};

pub fn create(pass: Option<&str>, lint_name: Option<&str>, category: Option<&str>) -> Result<(), io::Error> {
    let pass = pass.expect("`pass` argument is validated by clap");
    let lint_name = lint_name.expect("`name` argument is validated by clap");
    let category = category.expect("`category` argument is validated by clap");

    match open_files(lint_name) {
        Ok((mut test_file, mut lint_file)) => {
            let (pass_type, pass_lifetimes, pass_import, context_import) = match pass {
                "early" => ("EarlyLintPass", "", "use syntax::ast::*;", "EarlyContext"),
                "late" => ("LateLintPass", "<'_, '_>", "use rustc_hir::*;", "LateContext"),
                _ => {
                    unreachable!("`pass_type` should only ever be `early` or `late`!");
                },
            };

            let camel_case_name = to_camel_case(lint_name);

            if let Err(e) = test_file.write_all(get_test_file_contents(lint_name).as_bytes()) {
                return Err(io::Error::new(
                    ErrorKind::Other,
                    format!("Could not write to test file: {}", e),
                ));
            };

            if let Err(e) = lint_file.write_all(
                get_lint_file_contents(
                    pass_type,
                    pass_lifetimes,
                    lint_name,
                    &camel_case_name,
                    category,
                    pass_import,
                    context_import,
                )
                .as_bytes(),
            ) {
                return Err(io::Error::new(
                    ErrorKind::Other,
                    format!("Could not write to lint file: {}", e),
                ));
            }
            Ok(())
        },
        Err(e) => Err(io::Error::new(
            ErrorKind::Other,
            format!("Unable to create lint: {}", e),
        )),
    }
}

fn open_files(lint_name: &str) -> Result<(File, File), io::Error> {
    let project_root = project_root()?;

    let test_file_path = project_root.join("tests").join("ui").join(format!("{}.rs", lint_name));
    let lint_file_path = project_root
        .join("clippy_lints")
        .join("src")
        .join(format!("{}.rs", lint_name));

    if Path::new(&test_file_path).exists() {
        return Err(io::Error::new(
            ErrorKind::AlreadyExists,
            format!("test file {:?} already exists", test_file_path),
        ));
    }
    if Path::new(&lint_file_path).exists() {
        return Err(io::Error::new(
            ErrorKind::AlreadyExists,
            format!("lint file {:?} already exists", lint_file_path),
        ));
    }

    let test_file = OpenOptions::new().write(true).create_new(true).open(test_file_path)?;
    let lint_file = OpenOptions::new().write(true).create_new(true).open(lint_file_path)?;

    Ok((test_file, lint_file))
}

fn project_root() -> Result<PathBuf, io::Error> {
    let current_dir = std::env::current_dir()?;
    for path in current_dir.ancestors() {
        let result = std::fs::read_to_string(path.join("Cargo.toml"));
        if let Err(err) = &result {
            if err.kind() == io::ErrorKind::NotFound {
                continue;
            }
        }

        let content = result?;
        if content.contains("[package]\nname = \"clippy\"") {
            return Ok(path.to_path_buf());
        }
    }
    Err(io::Error::new(ErrorKind::Other, "Unable to find project root"))
}

fn to_camel_case(name: &str) -> String {
    name.split('_')
        .map(|s| {
            if s.is_empty() {
                String::from("")
            } else {
                [&s[0..1].to_uppercase(), &s[1..]].concat()
            }
        })
        .collect()
}

fn get_test_file_contents(lint_name: &str) -> String {
    format!(
        "#![warn(clippy::{})]

fn main() {{
    // test code goes here
}}
",
        lint_name
    )
}

fn get_lint_file_contents(
    pass_type: &str,
    pass_lifetimes: &str,
    lint_name: &str,
    camel_case_name: &str,
    category: &str,
    pass_import: &str,
    context_import: &str,
) -> String {
    format!(
        "use rustc_lint::{{LintArray, LintPass, {type}, {context_import}}};
use rustc_session::{{declare_lint_pass, declare_tool_lint}};
{pass_import}

declare_clippy_lint! {{
    /// **What it does:**
    ///
    /// **Why is this bad?**
    ///
    /// **Known problems:** None.
    ///
    /// **Example:**
    ///
    /// ```rust
    /// // example code
    /// ```
    pub {name_upper},
    {category},
    \"default lint description\"
}}

declare_lint_pass!({name_camel} => [{name_upper}]);

impl {type}{lifetimes} for {name_camel} {{}}
",
        type=pass_type,
        lifetimes=pass_lifetimes,
        name_upper=lint_name.to_uppercase(),
        name_camel=camel_case_name,
        category=category,
        pass_import=pass_import,
        context_import=context_import
    )
}

#[test]
fn test_camel_case() {
    let s = "a_lint";
    let s2 = to_camel_case(s);
    assert_eq!(s2, "ALint");

    let name = "a_really_long_new_lint";
    let name2 = to_camel_case(name);
    assert_eq!(name2, "AReallyLongNewLint");

    let name3 = "lint__name";
    let name4 = to_camel_case(name3);
    assert_eq!(name4, "LintName");
}
<|endoftext|>


<commit_before>#![allow(clippy::filter_map)]

use std::fs;
use std::fs::File;
use std::io::prelude::*;
use std::path::PathBuf;

// This module takes an absolute path to a rustc repo and alters the dependencies to point towards
// the respective rustc subcrates instead of using extern crate xyz.
// This allows rust analyzer to analyze rustc internals and show proper information inside clippy
// code. See https://github.com/rust-analyzer/rust-analyzer/issues/3517 and https://github.com/rust-lang/rust-clippy/issues/5514 for details

pub fn run(rustc_path: Option<&str>) {
    // we can unwrap here because the arg is required here
    let rustc_path = PathBuf::from(rustc_path.unwrap());
    assert!(rustc_path.is_dir(), "path is not a directory");
    let rustc_source_basedir = rustc_path.join("src");
    assert!(
        rustc_source_basedir.is_dir(),
        "are you sure the path leads to a rustc repo?"
    );

    let clippy_root_manifest = fs::read_to_string("Cargo.toml").expect("failed to read ./Cargo.toml");
    let clippy_root_lib_rs = fs::read_to_string("src/driver.rs").expect("failed to read ./src/driver.rs");
    inject_deps_into_manifest(
        &rustc_source_basedir,
        "Cargo.toml",
        &clippy_root_manifest,
        &clippy_root_lib_rs,
    )
    .expect("Failed to inject deps into ./Cargo.toml");

    let clippy_lints_manifest =
        fs::read_to_string("clippy_lints/Cargo.toml").expect("failed to read ./clippy_lints/Cargo.toml");
    let clippy_lints_lib_rs =
        fs::read_to_string("clippy_lints/src/lib.rs").expect("failed to read ./clippy_lints/src/lib.rs");
    inject_deps_into_manifest(
        &rustc_source_basedir,
        "clippy_lints/Cargo.toml",
        &clippy_lints_manifest,
        &clippy_lints_lib_rs,
    )
    .expect("Failed to inject deps into ./clippy_lints/Cargo.toml");
}

fn inject_deps_into_manifest(
    rustc_source_dir: &PathBuf,
    manifest_path: &str,
    cargo_toml: &str,
    lib_rs: &str,
) -> std::io::Result<()> {
    let extern_crates = lib_rs
        .lines()
        // get the deps
        .filter(|line| line.starts_with("extern crate"))
        // we have something like "extern crate foo;", we only care about the "foo"
        //              ↓          ↓
        // extern crate rustc_middle;
        .map(|s| &s[13..(s.len() - 1)]);

    let new_deps = extern_crates.map(|dep| {
        // format the dependencies that are going to be put inside the Cargo.toml
        format!(
            "{dep} = {{ path = \"{source_path}/lib{dep}\" }}\n",
            dep = dep,
            source_path = rustc_source_dir.display()
        )
    });

    // format a new [dependencies]-block with the new deps we need to inject
    let mut all_deps = String::from("[target.'cfg(NOT_A_PLATFORM)'.dependencies]\n");
    new_deps.for_each(|dep_line| {
        all_deps.push_str(&dep_line);
    });
    all_deps.push_str("\n[dependencies]\n");

    // replace "[dependencies]" with
    // [dependencies]
    // dep1 = { path = ... }
    // dep2 = { path = ... }
    // etc
    let new_manifest = cargo_toml.replacen("[dependencies]\n", &all_deps, 1);

    // println!("{}", new_manifest);
    let mut file = File::create(manifest_path)?;
    file.write_all(new_manifest.as_bytes())?;

    println!("Dependency paths injected: {}", manifest_path);

    Ok(())
}
<commit_msg>update cargo dev ra-setup to changed rustc source paths<commit_after>#![allow(clippy::filter_map)]

use std::fs;
use std::fs::File;
use std::io::prelude::*;
use std::path::PathBuf;

// This module takes an absolute path to a rustc repo and alters the dependencies to point towards
// the respective rustc subcrates instead of using extern crate xyz.
// This allows rust analyzer to analyze rustc internals and show proper information inside clippy
// code. See https://github.com/rust-analyzer/rust-analyzer/issues/3517 and https://github.com/rust-lang/rust-clippy/issues/5514 for details

pub fn run(rustc_path: Option<&str>) {
    // we can unwrap here because the arg is required here
    let rustc_path = PathBuf::from(rustc_path.unwrap());
    assert!(rustc_path.is_dir(), "path is not a directory");
    let rustc_source_basedir = rustc_path.join("compiler");
    assert!(
        rustc_source_basedir.is_dir(),
        "are you sure the path leads to a rustc repo?"
    );

    let clippy_root_manifest = fs::read_to_string("Cargo.toml").expect("failed to read ./Cargo.toml");
    let clippy_root_lib_rs = fs::read_to_string("src/driver.rs").expect("failed to read ./src/driver.rs");
    inject_deps_into_manifest(
        &rustc_source_basedir,
        "Cargo.toml",
        &clippy_root_manifest,
        &clippy_root_lib_rs,
    )
    .expect("Failed to inject deps into ./Cargo.toml");

    let clippy_lints_manifest =
        fs::read_to_string("clippy_lints/Cargo.toml").expect("failed to read ./clippy_lints/Cargo.toml");
    let clippy_lints_lib_rs =
        fs::read_to_string("clippy_lints/src/lib.rs").expect("failed to read ./clippy_lints/src/lib.rs");
    inject_deps_into_manifest(
        &rustc_source_basedir,
        "clippy_lints/Cargo.toml",
        &clippy_lints_manifest,
        &clippy_lints_lib_rs,
    )
    .expect("Failed to inject deps into ./clippy_lints/Cargo.toml");
}

fn inject_deps_into_manifest(
    rustc_source_dir: &PathBuf,
    manifest_path: &str,
    cargo_toml: &str,
    lib_rs: &str,
) -> std::io::Result<()> {
    let extern_crates = lib_rs
        .lines()
        // get the deps
        .filter(|line| line.starts_with("extern crate"))
        // we have something like "extern crate foo;", we only care about the "foo"
        //              ↓          ↓
        // extern crate rustc_middle;
        .map(|s| &s[13..(s.len() - 1)]);

    let new_deps = extern_crates.map(|dep| {
        // format the dependencies that are going to be put inside the Cargo.toml
        format!(
            "{dep} = {{ path = \"{source_path}/{dep}\" }}\n",
            dep = dep,
            source_path = rustc_source_dir.display()
        )
    });

    // format a new [dependencies]-block with the new deps we need to inject
    let mut all_deps = String::from("[target.'cfg(NOT_A_PLATFORM)'.dependencies]\n");
    new_deps.for_each(|dep_line| {
        all_deps.push_str(&dep_line);
    });
    all_deps.push_str("\n[dependencies]\n");

    // replace "[dependencies]" with
    // [dependencies]
    // dep1 = { path = ... }
    // dep2 = { path = ... }
    // etc
    let new_manifest = cargo_toml.replacen("[dependencies]\n", &all_deps, 1);

    // println!("{}", new_manifest);
    let mut file = File::create(manifest_path)?;
    file.write_all(new_manifest.as_bytes())?;

    println!("Dependency paths injected: {}", manifest_path);

    Ok(())
}
<|endoftext|>


<commit_before>/* diosix hypervisor code for handling hardware interrupts and software exceptions
 *
 * (c) Chris Williams, 2019-2021.
 *
 * See LICENSE for usage and copying.
 */

use super::scheduler;
use super::capsule;
use super::pcore;
use super::hardware;

/* platform-specific code must implement all this */
use platform;
use platform::irq::{IRQContext, IRQType, IRQCause, IRQSeverity, IRQ};
use platform::cpu::PrivilegeMode;
use platform::instructions::{self, EmulationResult};
use platform::syscalls;
use platform::timer;

/* hypervisor_irq_handler
   entry point for hardware interrupts and software exceptions, collectively known as IRQs.
   call down into platform-specific handlers
   => context = platform-specific context of the IRQ, which may be modified depending
   on the IRQ raised.
*/
#[no_mangle]
pub extern "C" fn hypervisor_irq_handler(mut context: IRQContext)
{
    /* if dispatch() returns an IRQ context then we need to handle it here
    at the high level. if it returns None, the platform-specific code handled it.
    note: the platform library should take care of hardware specfic things like
    catching illegal instructions that can be fixed up and handled transparently */
    if let Some(irq) = platform::irq::dispatch(context)
    {
        match irq.irq_type
        {
            IRQType::Exception => exception(irq, &mut context),
            IRQType::Interrupt => interrupt(irq, &mut context),
        };
    }
}

/* handle software exception */
fn exception(irq: IRQ, context: &mut IRQContext)
{
    match (irq.severity, irq.privilege_mode, irq.cause)
    {
        /* catch illegal instructions we may be able to emulate */
        (_, PrivilegeMode::Supervisor, IRQCause::IllegalInstruction) =>
        {
            match instructions::emulate(irq.privilege_mode, context)
            {
                EmulationResult::Success => (), /* nothing more to do, return */
                EmulationResult::Yield =>
                {
                    /* instruction was some kind of sleep or pause operation.
                    try to find something else to run in the meantime */
                    scheduler::ping();
                },

                /* if we can't handle the instruction,
                kill the capsule and force a context switch.
                TODO: is killing the whole capsule a little extreme? */
                _ => fatal_exception(&irq)
            }
        },

        /* catch environment calls from supervisor mode */
        (_, PrivilegeMode::Supervisor, IRQCause::SupervisorEnvironmentCall) =>
        {
            /* determine what we need to do from the platform code's decoding */
            if let Some(action) = syscalls::handler(context)
            {
                match action
                {
                    syscalls::Action::Terminate => if let Err(_e) = capsule::destroy_current()
                    {
                        hvalert!("BUG: Failed to terminate currently running capsule ({:?})", _e);
                        syscalls::failed(context, syscalls::ActionResult::Failed);
                    }
                    else
                    {
                        /* find something else to run, this virtual core is dead */
                        scheduler::ping();
                    },

                    syscalls::Action::Restart => if let Err(_e) = capsule::restart_current()
                    {
                        hvalert!("BUG: Failed to restart currently running capsule ({:?})", _e);
                        syscalls::failed(context, syscalls::ActionResult::Failed);
                    }
                    else
                    {
                        /* find something else to run, this virtual core is being replaced */
                        scheduler::ping();
                    },

                    syscalls::Action::TimerIRQAt(target) =>
                    {
                        /* mark this virtual core as awaiting a timer IRQ and
                        schedule a timer interrupt in anticipation */
                        pcore::PhysicalCore::set_virtualcore_timer_target(Some(target));
                        hardware::scheduler_timer_at(target);
                    },

                    /* stash debug output character into capsule's buffer */
                    syscalls::Action::OutputChar(character) => if let Some(capsule_id) = pcore::PhysicalCore::get_capsule_id()
                    {
                        /* FIXME: improve this. don't allow linux to flood us with ^@ for some reason during boot */
                        if character != '^' && character != '@'
                        {
                            if let Err(_e) = capsule::debug_write(capsule_id, character)
                            {
                                hvdebug!("Couldn't buffer debug byte {} from capsule {}: {:?}", character, capsule_id, _e);
                                syscalls::failed(context, syscalls::ActionResult::Failed);
                            }
                        }
                    },

                    /* get a character from the user. TODO: improve the buffering of this input */
                    syscalls::Action::InputChar =>
                    {
                        match hardware::read_debug_char()
                        {
                            Some(c) =>
                            {
                                hvprint!("{}", c);
                                syscalls::result(context, c as usize);
                            },
                            None => syscalls::result(context, usize::MAX) /* -1 == nothing to read */
                        }
                    },

                    _ => if let Some(c) = pcore::PhysicalCore::get_capsule_id()
                    {
                        hvalert!("Capsule {}: Unhandled syscall: {:x?} at 0x{:x}", c, action, irq.pc);
                    }
                    else
                    {
                        hvdebug!("Unhandled syscall: {:x?} at 0x{:x} in unknown capsule", action, irq.pc);
                    }
                }
            }
        },

        /* catch everything else, halting if fatal */
        (severity, privilege, cause) =>
        {
            /* if an unhandled fatal exception reaches us here from the supervisor or user mode,
            kill the capsule. if the hypervisor can't handle its own fatal exception, give up */
            match privilege
            {
                PrivilegeMode::Supervisor | PrivilegeMode::User => if severity == IRQSeverity::Fatal
                {
                    /* TODO: is it wise to blow away the whole capsule for a user exception?
                    the supervisor should really catch its user-level faults */
                    fatal_exception(&irq);
                },
                PrivilegeMode::Machine =>
                {
                    if severity == IRQSeverity::Fatal
                    {
                        hvalert!("Halting physical CPU core for {:?} at 0x{:x}, stack 0x{:x} integrity {:?}",
                            cause, irq.pc, irq.sp, pcore::PhysicalCore::integrity_check());
                        debughousekeeper!(); // flush the debug output
                        loop {}
                    }
                }
            }
        }
    }
}

/* handle hardware interrupt */
fn interrupt(irq: IRQ, _: &mut IRQContext)
{
    match irq.cause
    {
        IRQCause::MachineTimer =>
        {
            /* make a scheduling decision and raise any supervior-level timer IRQs*/
            scheduler::ping();
            check_supervisor_timer_irq();
        },
        _ => hvdebug!("Unhandled hardware interrupt: {:?}", irq.cause)
    }

    /* clear the interrupt condition */
    platform::irq::acknowledge(irq);
}

/* is the virtual core we're about to run awaiting a timer IRQ?
if so, and if its timer target value has been passed, generate a pending timer IRQ */
fn check_supervisor_timer_irq()
{
    if let Some(target) = pcore::PhysicalCore::get_virtualcore_timer_target()
    {
        match (hardware::scheduler_get_timer_now(), hardware::scheduler_get_timer_frequency())
        {
            (Some(time), Some(freq)) =>
            {
                let current = time.to_exact(freq);
                if current >= target.to_exact(freq)
                {
                    /* create a pending timer IRQ for the supervisor kernel and clear the target */
                    timer::trigger_supervisor_irq();
                    pcore::PhysicalCore::set_virtualcore_timer_target(None);
                }
            },
            (_, _) => ()
        }
    }
}

/* kill the running capsule, alert the user, and then find something else to run.
   if the capsule is important enough to auto-restart-on-crash, try to revive it */
fn fatal_exception(irq: &IRQ)
{
    hvalert!("Terminating running capsule {} for {:?} at 0x{:x}, stack 0x{:x}",
        match pcore::PhysicalCore::this().get_virtualcore_id()
        {
            Some(id) => format!("{}.{}", id.capsuleid, id.vcoreid),
            None => format!("[unknown!]")
        }, irq.cause, irq.pc, irq.sp);

    let mut terminate = false; // when true, destroy the current capsule
    let mut reschedule = false; // when true, we must find another vcore to run

    match capsule::is_current_autorestart()
    {
        Some(true) =>
        {
            hvalert!("Restarting capsule due to auto-restart-on-crash flag");
            if let Err(err) = capsule::restart_current()
            {
                hvalert!("Can't restart capsule ({:?}), letting it die instead", err);
                terminate = true;
            }
            else
            {
                /* the current vcore is no longer running due to restart */
                reschedule = true;
            }
        },
        Some(false) => terminate = true,
        None =>
        {
            hvalert!("BUG: fatal_exception() can't find the running capsule to kill");
            return;
        },
    }

    if terminate == true
    {
        match capsule::destroy_current()
        {
            Err(e) => hvalert!("BUG: Failed to kill running capsule ({:?})", e),
            _ =>
            {
                hvdebug!("Terminated running capsule");

                /* the current vcore is no longer running due to restart */
                reschedule = true;
            }
        }
    }

    if reschedule == true
    {
        /* force a context switch to find another virtual core to run
        because this virtual core no longer exists */
        scheduler::ping();
    }
}<commit_msg>Redirect character IO to capsule services<commit_after>/* diosix hypervisor code for handling hardware interrupts and software exceptions
 *
 * (c) Chris Williams, 2019-2021.
 *
 * See LICENSE for usage and copying.
 */

use super::scheduler;
use super::capsule;
use super::pcore;
use super::hardware;
use super::error::Cause;

/* platform-specific code must implement all this */
use platform;
use platform::irq::{IRQContext, IRQType, IRQCause, IRQSeverity, IRQ};
use platform::cpu::PrivilegeMode;
use platform::instructions::{self, EmulationResult};
use platform::syscalls;
use platform::timer;

/* hypervisor_irq_handler
   entry point for hardware interrupts and software exceptions, collectively known as IRQs.
   call down into platform-specific handlers
   => context = platform-specific context of the IRQ, which may be modified depending
   on the IRQ raised.
*/
#[no_mangle]
pub extern "C" fn hypervisor_irq_handler(mut context: IRQContext)
{
    /* if dispatch() returns an IRQ context then we need to handle it here
    at the high level. if it returns None, the platform-specific code handled it.
    note: the platform library should take care of hardware specfic things like
    catching illegal instructions that can be fixed up and handled transparently */
    if let Some(irq) = platform::irq::dispatch(context)
    {
        match irq.irq_type
        {
            IRQType::Exception => exception(irq, &mut context),
            IRQType::Interrupt => interrupt(irq, &mut context),
        };
    }
}

/* handle software exception */
fn exception(irq: IRQ, context: &mut IRQContext)
{
    match (irq.severity, irq.privilege_mode, irq.cause)
    {
        /* catch illegal instructions we may be able to emulate */
        (_, PrivilegeMode::Supervisor, IRQCause::IllegalInstruction) =>
        {
            match instructions::emulate(irq.privilege_mode, context)
            {
                EmulationResult::Success => (), /* nothing more to do, return */
                EmulationResult::Yield =>
                {
                    /* instruction was some kind of sleep or pause operation.
                    try to find something else to run in the meantime */
                    scheduler::ping();
                },

                /* if we can't handle the instruction,
                kill the capsule and force a context switch.
                TODO: is killing the whole capsule a little extreme? */
                _ => fatal_exception(&irq)
            }
        },

        /* catch environment calls from supervisor mode */
        (_, PrivilegeMode::Supervisor, IRQCause::SupervisorEnvironmentCall) =>
        {
            /* determine what we need to do from the platform code's decoding */
            if let Some(action) = syscalls::handler(context)
            {
                match action
                {
                    syscalls::Action::Terminate => if let Err(_e) = capsule::destroy_current()
                    {
                        hvalert!("BUG: Failed to terminate currently running capsule ({:?})", _e);
                        syscalls::failed(context, syscalls::ActionResult::Failed);
                    }
                    else
                    {
                        /* find something else to run, this virtual core is dead */
                        scheduler::ping();
                    },

                    syscalls::Action::Restart => if let Err(_e) = capsule::restart_current()
                    {
                        hvalert!("BUG: Failed to restart currently running capsule ({:?})", _e);
                        syscalls::failed(context, syscalls::ActionResult::Failed);
                    }
                    else
                    {
                        /* find something else to run, this virtual core is being replaced */
                        scheduler::ping();
                    },

                    syscalls::Action::TimerIRQAt(target) =>
                    {
                        /* mark this virtual core as awaiting a timer IRQ and
                        schedule a timer interrupt in anticipation */
                        pcore::PhysicalCore::set_virtualcore_timer_target(Some(target));
                        hardware::scheduler_timer_at(target);
                    },

                    /* output a character to the user from this capsule */
                    syscalls::Action::OutputChar(character) => if let Some(capsule_id) = pcore::PhysicalCore::get_capsule_id()
                    {
                        /* FIXME: improve this. don't allow the linux kernel to flood us with ^@
                           for some reason during boot when waiting for the network to reply with DHCP information */
                        if character != '^' && character != '@'
                        {
                            if let Err(_e) = capsule::putc(capsule_id, character)
                            {
                                hvdebug!("Couldn't buffer console byte {} from capsule {}: {:?}", character, capsule_id, _e);
                                syscalls::failed(context, syscalls::ActionResult::Failed);
                            }
                        }
                    },

                    /* get a character from the user for this capsule */
                    syscalls::Action::InputChar => if let Some(capsule_id) = pcore::PhysicalCore::get_capsule_id()
                    {
                        match capsule::getc(capsule_id)
                        {
                            Ok(c) => syscalls::result(context, c as usize),
                            Err(Cause::CapsuleStdinEmpty) => syscalls::result(context, usize::MAX), /* -1 == nothing to read */
                            Err(_e) =>
                            {
                                hvdebug!("Couldn't read console buffer from capsule {}: {:?}", capsule_id, _e);
                                syscalls::failed(context, syscalls::ActionResult::Failed);
                            }
                        }
                    },

                    _ => if let Some(c) = pcore::PhysicalCore::get_capsule_id()
                    {
                        hvalert!("Capsule {}: Unhandled syscall: {:x?} at 0x{:x}", c, action, irq.pc);
                    }
                    else
                    {
                        hvdebug!("Unhandled syscall: {:x?} at 0x{:x} in unknown capsule", action, irq.pc);
                    }
                }
            }
        },

        /* catch everything else, halting if fatal */
        (severity, privilege, cause) =>
        {
            /* if an unhandled fatal exception reaches us here from the supervisor or user mode,
            kill the capsule. if the hypervisor can't handle its own fatal exception, give up */
            match privilege
            {
                PrivilegeMode::Supervisor | PrivilegeMode::User => if severity == IRQSeverity::Fatal
                {
                    /* TODO: is it wise to blow away the whole capsule for a user exception?
                    the supervisor should really catch its user-level faults */
                    fatal_exception(&irq);
                },
                PrivilegeMode::Machine =>
                {
                    if severity == IRQSeverity::Fatal
                    {
                        hvalert!("Halting physical CPU core for {:?} at 0x{:x}, stack 0x{:x} integrity {:?}",
                            cause, irq.pc, irq.sp, pcore::PhysicalCore::integrity_check());
                        debughousekeeper!(); // flush the debug output
                        loop {}
                    }
                }
            }
        }
    }
}

/* handle hardware interrupt */
fn interrupt(irq: IRQ, _: &mut IRQContext)
{
    match irq.cause
    {
        IRQCause::MachineTimer =>
        {
            /* make a scheduling decision and raise any supervior-level timer IRQs*/
            scheduler::ping();
            check_supervisor_timer_irq();
        },
        _ => hvdebug!("Unhandled hardware interrupt: {:?}", irq.cause)
    }

    /* clear the interrupt condition */
    platform::irq::acknowledge(irq);
}

/* is the virtual core we're about to run awaiting a timer IRQ?
if so, and if its timer target value has been passed, generate a pending timer IRQ */
fn check_supervisor_timer_irq()
{
    if let Some(target) = pcore::PhysicalCore::get_virtualcore_timer_target()
    {
        match (hardware::scheduler_get_timer_now(), hardware::scheduler_get_timer_frequency())
        {
            (Some(time), Some(freq)) =>
            {
                let current = time.to_exact(freq);
                if current >= target.to_exact(freq)
                {
                    /* create a pending timer IRQ for the supervisor kernel and clear the target */
                    timer::trigger_supervisor_irq();
                    pcore::PhysicalCore::set_virtualcore_timer_target(None);
                }
            },
            (_, _) => ()
        }
    }
}

/* kill the running capsule, alert the user, and then find something else to run.
   if the capsule is important enough to auto-restart-on-crash, try to revive it */
fn fatal_exception(irq: &IRQ)
{
    hvalert!("Terminating running capsule {} for {:?} at 0x{:x}, stack 0x{:x}",
        match pcore::PhysicalCore::this().get_virtualcore_id()
        {
            Some(id) => format!("{}.{}", id.capsuleid, id.vcoreid),
            None => format!("[unknown!]")
        }, irq.cause, irq.pc, irq.sp);

    let mut terminate = false; // when true, destroy the current capsule
    let mut reschedule = false; // when true, we must find another vcore to run

    match capsule::is_current_autorestart()
    {
        Some(true) =>
        {
            hvalert!("Restarting capsule due to auto-restart-on-crash flag");
            if let Err(err) = capsule::restart_current()
            {
                hvalert!("Can't restart capsule ({:?}), letting it die instead", err);
                terminate = true;
            }
            else
            {
                /* the current vcore is no longer running due to restart */
                reschedule = true;
            }
        },
        Some(false) => terminate = true,
        None =>
        {
            hvalert!("BUG: fatal_exception() can't find the running capsule to kill");
            return;
        },
    }

    if terminate == true
    {
        match capsule::destroy_current()
        {
            Err(e) => hvalert!("BUG: Failed to kill running capsule ({:?})", e),
            _ =>
            {
                hvdebug!("Terminated running capsule");

                /* the current vcore is no longer running due to restart */
                reschedule = true;
            }
        }
    }

    if reschedule == true
    {
        /* force a context switch to find another virtual core to run
        because this virtual core no longer exists */
        scheduler::ping();
    }
}<|endoftext|>