<commit_before>//! [POST /_matrix/client/r0/search](https://matrix.org/docs/spec/client_server/r0.6.0#post-matrix-client-r0-search)

use std::collections::BTreeMap;

use js_int::{uint, UInt};
use ruma_api::{ruma_api, Outgoing};
use ruma_common::Raw;
use ruma_events::{AnyEvent, AnyStateEvent};
use ruma_identifiers::{EventId, RoomId, UserId};
use serde::{Deserialize, Serialize};

use crate::r0::filter::{IncomingRoomEventFilter, RoomEventFilter};

ruma_api! {
    metadata: {
        description: "Search events.",
        method: POST,
        name: "search",
        path: "/_matrix/client/r0/search",
        rate_limited: true,
        requires_authentication: true,
    }

    #[non_exhaustive]
    request: {
        /// The point to return events from.
        ///
        /// If given, this should be a `next_batch` result from a previous call to this endpoint.
        #[ruma_api(query)]
        pub next_batch: Option<&'a str>,

        /// Describes which categories to search in and their criteria.
        pub search_categories: Categories<'a>,
    }

    #[non_exhaustive]
    response: {
        /// A grouping of search results by category.
        pub search_categories: ResultCategories,
    }

    error: crate::Error
}

impl<'a> Request<'a> {
    /// Creates a new `Request` with the given categories.
    pub fn new(search_categories: Categories<'a>) -> Self {
        Self { next_batch: None, search_categories }
    }
}

impl Response {
    /// Creates a new `Response` with the given search results.
    pub fn new(search_categories: ResultCategories) -> Self {
        Self { search_categories }
    }
}

/// Categories of events that can be searched for.
#[derive(Clone, Debug, Default, Outgoing, Serialize)]
#[non_exhaustive]
pub struct Categories<'a> {
    /// Criteria for searching room events.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub room_events: Option<Criteria<'a>>,
}

impl<'a> Categories<'a> {
    /// Creates an empty `Categories`.
    pub fn new() -> Self {
        Default::default()
    }
}

/// Criteria for searching a category of events.
#[derive(Clone, Copy, Debug, Outgoing, Serialize)]
pub struct Criteria<'a> {
    /// The string to search events for.
    pub search_term: &'a str,

    /// The keys to search for. Defaults to all keys.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub keys: Option<&'a [SearchKeys]>,

    /// A `Filter` to apply to the search.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub filter: Option<RoomEventFilter<'a>>,

    /// The order in which to search for results.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub order_by: Option<OrderBy>,

    /// Configures whether any context for the events returned are included in the response.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub event_context: Option<EventContext>,

    /// Requests the server return the current state for each room returned.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub include_state: Option<bool>,

    /// Requests that the server partitions the result set based on the provided list of keys.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub groupings: Option<Groupings<'a>>,
}

/// Configures whether any context for the events returned are included in the response.
#[derive(Clone, Copy, Debug, Deserialize, Serialize)]
pub struct EventContext {
    /// How many events before the result are returned.
    #[serde(
        default = "default_event_context_limit",
        skip_serializing_if = "is_default_event_context_limit"
    )]
    pub before_limit: UInt,

    /// How many events after the result are returned.
    #[serde(
        default = "default_event_context_limit",
        skip_serializing_if = "is_default_event_context_limit"
    )]
    pub after_limit: UInt,

    /// Requests that the server returns the historic profile information for the users that
    /// sent the events that were returned.
    #[serde(default, skip_serializing_if = "ruma_serde::is_default")]
    pub include_profile: bool,
}

fn default_event_context_limit() -> UInt {
    uint!(5)
}

#[allow(clippy::trivially_copy_pass_by_ref)]
fn is_default_event_context_limit(val: &UInt) -> bool {
    *val == default_event_context_limit()
}

/// Context for search results, if requested.
#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct EventContextResult {
    /// Pagination token for the end of the chunk.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub end: Option<String>,

    /// Events just after the result.
    #[serde(default, skip_serializing_if = "Vec::is_empty")]
    pub events_after: Vec<Raw<AnyEvent>>,

    /// Events just before the result.
    #[serde(default, skip_serializing_if = "Vec::is_empty")]
    pub events_before: Vec<Raw<AnyEvent>>,

    /// The historic profile information of the users that sent the events returned.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub profile_info: Option<BTreeMap<UserId, UserProfile>>,

    /// Pagination token for the start of the chunk.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub start: Option<String>,
}

/// A grouping for partioning the result set.
#[derive(Clone, Copy, Debug, Deserialize, Serialize)]
pub struct Grouping {
    /// The key within events to use for this grouping.
    pub key: Option<GroupingKey>,
}

/// The key within events to use for this grouping.
#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Eq, PartialOrd, Ord, Serialize)]
#[serde(rename_all = "snake_case")]
pub enum GroupingKey {
    /// `room_id`
    RoomId,

    /// `sender`
    Sender,
}

/// Requests that the server partitions the result set based on the provided list of keys.
#[derive(Clone, Copy, Debug, Outgoing, Serialize)]
pub struct Groupings<'a> {
    /// List of groups to request.
    #[serde(default, skip_serializing_if = "<[_]>::is_empty")]
    pub group_by: &'a [Grouping],
}

/// The keys to search for.
#[derive(Clone, Copy, Debug, Deserialize, Serialize)]
pub enum SearchKeys {
    /// content.body
    #[serde(rename = "content.body")]
    ContentBody,

    /// content.name
    #[serde(rename = "content.name")]
    ContentName,

    /// content.topic
    #[serde(rename = "content.topic")]
    ContentTopic,
}

/// The order in which to search for results.
#[derive(Clone, Copy, Debug, Deserialize, Serialize)]
#[serde(rename_all = "snake_case")]
pub enum OrderBy {
    /// Prioritize recent events.
    Recent,

    /// Prioritize events by a numerical ranking of how closely they matched the search
    /// criteria.
    Rank,
}

/// Categories of events that can be searched for.
#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct ResultCategories {
    /// Room event results.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub room_events: Option<ResultRoomEvents>,
}

/// Categories of events that can be searched for.
#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct ResultRoomEvents {
    /// An approximate count of the total number of results found.
    pub count: UInt,

    /// Any groups that were requested.
    pub groups: BTreeMap<GroupingKey, BTreeMap<RoomIdOrUserId, ResultGroup>>,

    /// Token that can be used to get the next batch of results, by passing as the `next_batch`
    /// parameter to the next call. If this field is absent, there are no more results.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub next_batch: Option<String>,

    /// List of results in the requested order.
    pub results: Vec<SearchResult>,

    /// The current state for every room in the results. This is included if the request had the
    /// `include_state` key set with a value of `true`.
    #[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
    pub state: BTreeMap<RoomId, Vec<Raw<AnyStateEvent>>>,

    /// List of words which should be highlighted, useful for stemming which may
    /// change the query terms.
    #[serde(default, skip_serializing_if = "Vec::is_empty")]
    pub highlights: Vec<String>,
}

/// A grouping of results, if requested.
#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct ResultGroup {
    /// Token that can be used to get the next batch of results in the group, by passing as the
    /// `next_batch` parameter to the next call. If this field is absent, there are no more
    /// results in this group.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub next_batch: Option<String>,

    /// Key that can be used to order different groups.
    pub order: UInt,

    /// Which results are in this group.
    pub results: Vec<EventId>,
}

/// A search result.
#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct SearchResult {
    /// Context for result, if requested.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub context: Option<EventContextResult>,

    /// A number that describes how closely this result matches the search. Higher is closer.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub rank: Option<UInt>,

    /// The event that matched.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub result: Option<Raw<AnyEvent>>,
}

/// A user profile.
#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct UserProfile {
    /// The user's avatar URL, if set.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub avatar_url: Option<String>,

    /// The user's display name, if set.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub displayname: Option<String>,
}

/// Represents either a room or user ID for returning grouped search results.
#[derive(Clone, Debug, Deserialize, Eq, Ord, PartialEq, PartialOrd, Serialize)]
pub enum RoomIdOrUserId {
    /// Represents a room ID.
    RoomId(RoomId),

    /// Represents a user ID.
    UserId(UserId),
}
<commit_msg>client-api: Make more types in search_events non-exhaustive<commit_after>//! [POST /_matrix/client/r0/search](https://matrix.org/docs/spec/client_server/r0.6.0#post-matrix-client-r0-search)

use std::collections::BTreeMap;

use js_int::{uint, UInt};
use ruma_api::{ruma_api, Outgoing};
use ruma_common::Raw;
use ruma_events::{AnyEvent, AnyStateEvent};
use ruma_identifiers::{EventId, RoomId, UserId};
use serde::{Deserialize, Serialize};

use crate::r0::filter::{IncomingRoomEventFilter, RoomEventFilter};

ruma_api! {
    metadata: {
        description: "Search events.",
        method: POST,
        name: "search",
        path: "/_matrix/client/r0/search",
        rate_limited: true,
        requires_authentication: true,
    }

    #[non_exhaustive]
    request: {
        /// The point to return events from.
        ///
        /// If given, this should be a `next_batch` result from a previous call to this endpoint.
        #[ruma_api(query)]
        pub next_batch: Option<&'a str>,

        /// Describes which categories to search in and their criteria.
        pub search_categories: Categories<'a>,
    }

    #[non_exhaustive]
    response: {
        /// A grouping of search results by category.
        pub search_categories: ResultCategories,
    }

    error: crate::Error
}

impl<'a> Request<'a> {
    /// Creates a new `Request` with the given categories.
    pub fn new(search_categories: Categories<'a>) -> Self {
        Self { next_batch: None, search_categories }
    }
}

impl Response {
    /// Creates a new `Response` with the given search results.
    pub fn new(search_categories: ResultCategories) -> Self {
        Self { search_categories }
    }
}

/// Categories of events that can be searched for.
#[derive(Clone, Debug, Default, Outgoing, Serialize)]
#[non_exhaustive]
pub struct Categories<'a> {
    /// Criteria for searching room events.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub room_events: Option<Criteria<'a>>,
}

impl<'a> Categories<'a> {
    /// Creates an empty `Categories`.
    pub fn new() -> Self {
        Default::default()
    }
}

/// Criteria for searching a category of events.
#[derive(Clone, Copy, Debug, Outgoing, Serialize)]
#[non_exhaustive]
pub struct Criteria<'a> {
    /// The string to search events for.
    pub search_term: &'a str,

    /// The keys to search for. Defaults to all keys.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub keys: Option<&'a [SearchKeys]>,

    /// A `Filter` to apply to the search.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub filter: Option<RoomEventFilter<'a>>,

    /// The order in which to search for results.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub order_by: Option<OrderBy>,

    /// Configures whether any context for the events returned are included in the response.
    #[serde(default, skip_serializing_if = "EventContext::is_default")]
    pub event_context: EventContext,

    /// Requests the server return the current state for each room returned.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub include_state: Option<bool>,

    /// Requests that the server partitions the result set based on the provided list of keys.
    #[serde(default, skip_serializing_if = "Groupings::is_empty")]
    pub groupings: Groupings<'a>,
}

impl<'a> Criteria<'a> {
    /// Creates a new `Criteria` with the given search term.
    pub fn new(search_term: &'a str) -> Self {
        Self {
            search_term,
            keys: None,
            filter: None,
            order_by: None,
            event_context: Default::default(),
            include_state: None,
            groupings: Default::default(),
        }
    }
}

/// Configures whether any context for the events returned are included in the response.
#[derive(Clone, Copy, Debug, Deserialize, Serialize)]
#[non_exhaustive]
pub struct EventContext {
    /// How many events before the result are returned.
    #[serde(
        default = "default_event_context_limit",
        skip_serializing_if = "is_default_event_context_limit"
    )]
    pub before_limit: UInt,

    /// How many events after the result are returned.
    #[serde(
        default = "default_event_context_limit",
        skip_serializing_if = "is_default_event_context_limit"
    )]
    pub after_limit: UInt,

    /// Requests that the server returns the historic profile information for the users that
    /// sent the events that were returned.
    #[serde(default, skip_serializing_if = "ruma_serde::is_default")]
    pub include_profile: bool,
}

fn default_event_context_limit() -> UInt {
    uint!(5)
}

#[allow(clippy::trivially_copy_pass_by_ref)]
fn is_default_event_context_limit(val: &UInt) -> bool {
    *val == default_event_context_limit()
}

impl EventContext {
    /// Creates an `EventContext` with all-default values.
    pub fn new() -> Self {
        Self {
            before_limit: default_event_context_limit(),
            after_limit: default_event_context_limit(),
            include_profile: false,
        }
    }

    /// Returns whether all fields have their default value.
    pub fn is_default(&self) -> bool {
        self.before_limit == default_event_context_limit()
            && self.after_limit == default_event_context_limit()
            && !self.include_profile
    }
}

impl Default for EventContext {
    fn default() -> Self {
        Self::new()
    }
}

/// Context for search results, if requested.
#[derive(Clone, Debug, Default, Deserialize, Serialize)]
#[non_exhaustive]
pub struct EventContextResult {
    /// Pagination token for the end of the chunk.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub end: Option<String>,

    /// Events just after the result.
    #[serde(default, skip_serializing_if = "Vec::is_empty")]
    pub events_after: Vec<Raw<AnyEvent>>,

    /// Events just before the result.
    #[serde(default, skip_serializing_if = "Vec::is_empty")]
    pub events_before: Vec<Raw<AnyEvent>>,

    /// The historic profile information of the users that sent the events returned.
    #[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
    pub profile_info: BTreeMap<UserId, UserProfile>,

    /// Pagination token for the start of the chunk.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub start: Option<String>,
}

impl EventContextResult {
    /// Creates an empty `EventContextResult`.
    pub fn new() -> Self {
        Default::default()
    }

    /// Returns whether all fields are `None` or an empty list.
    pub fn is_empty(&self) -> bool {
        self.end.is_none()
            && self.events_after.is_empty()
            && self.events_before.is_empty()
            && self.profile_info.is_empty()
            && self.start.is_none()
    }
}

/// A grouping for partioning the result set.
#[derive(Clone, Copy, Default, Debug, Deserialize, Serialize)]
#[non_exhaustive]
pub struct Grouping {
    /// The key within events to use for this grouping.
    pub key: Option<GroupingKey>,
}

impl Grouping {
    /// Creates an empty `Grouping`.
    pub fn new() -> Self {
        Default::default()
    }

    /// Returns whether `key` is `None`.
    pub fn is_empty(&self) -> bool {
        self.key.is_none()
    }
}

/// The key within events to use for this grouping.
#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Eq, PartialOrd, Ord, Serialize)]
#[non_exhaustive]
#[serde(rename_all = "snake_case")]
pub enum GroupingKey {
    /// `room_id`
    RoomId,

    /// `sender`
    Sender,
}

/// Requests that the server partitions the result set based on the provided list of keys.
#[derive(Clone, Copy, Default, Debug, Outgoing, Serialize)]
#[incoming_derive(Default)]
#[non_exhaustive]
pub struct Groupings<'a> {
    /// List of groups to request.
    #[serde(default, skip_serializing_if = "<[_]>::is_empty")]
    pub group_by: &'a [Grouping],
}

impl<'a> Groupings<'a> {
    /// Creates an empty `Groupings`.
    pub fn new() -> Self {
        Default::default()
    }

    /// Returns `true` if all fields are empty.
    pub fn is_empty(&self) -> bool {
        self.group_by.is_empty()
    }
}

/// The keys to search for.
#[derive(Clone, Copy, Debug, Deserialize, Serialize)]
#[non_exhaustive]
pub enum SearchKeys {
    /// content.body
    #[serde(rename = "content.body")]
    ContentBody,

    /// content.name
    #[serde(rename = "content.name")]
    ContentName,

    /// content.topic
    #[serde(rename = "content.topic")]
    ContentTopic,
}

/// The order in which to search for results.
#[derive(Clone, Copy, Debug, Deserialize, Serialize)]
#[non_exhaustive]
#[serde(rename_all = "snake_case")]
pub enum OrderBy {
    /// Prioritize recent events.
    Recent,

    /// Prioritize events by a numerical ranking of how closely they matched the search
    /// criteria.
    Rank,
}

/// Categories of events that can be searched for.
#[derive(Clone, Default, Debug, Deserialize, Serialize)]
#[non_exhaustive]
pub struct ResultCategories {
    /// Room event results.
    #[serde(default, skip_serializing_if = "ResultRoomEvents::is_empty")]
    pub room_events: ResultRoomEvents,
}

impl ResultCategories {
    /// Creates an empty `ResultCategories`.
    pub fn new() -> Self {
        Default::default()
    }
}

/// Categories of events that can be searched for.
#[derive(Clone, Debug, Default, Deserialize, Serialize)]
#[non_exhaustive]
pub struct ResultRoomEvents {
    /// An approximate count of the total number of results found.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub count: Option<UInt>,

    /// Any groups that were requested.
    #[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
    pub groups: BTreeMap<GroupingKey, BTreeMap<RoomIdOrUserId, ResultGroup>>,

    /// Token that can be used to get the next batch of results, by passing as the `next_batch`
    /// parameter to the next call. If this field is absent, there are no more results.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub next_batch: Option<String>,

    /// List of results in the requested order.
    #[serde(default, skip_serializing_if = "Vec::is_empty")]
    pub results: Vec<SearchResult>,

    /// The current state for every room in the results. This is included if the request had the
    /// `include_state` key set with a value of `true`.
    #[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
    pub state: BTreeMap<RoomId, Vec<Raw<AnyStateEvent>>>,

    /// List of words which should be highlighted, useful for stemming which may
    /// change the query terms.
    #[serde(default, skip_serializing_if = "Vec::is_empty")]
    pub highlights: Vec<String>,
}

impl ResultRoomEvents {
    /// Creates an empty `ResultRoomEvents`.
    pub fn new() -> Self {
        Default::default()
    }

    /// Returns `true` if all fields are empty / `None`.
    pub fn is_empty(&self) -> bool {
        self.count.is_none()
            && self.groups.is_empty()
            && self.next_batch.is_none()
            && self.results.is_empty()
            && self.state.is_empty()
            && self.highlights.is_empty()
    }
}

/// A grouping of results, if requested.
#[derive(Clone, Debug, Default, Deserialize, Serialize)]
#[non_exhaustive]
pub struct ResultGroup {
    /// Token that can be used to get the next batch of results in the group, by passing as the
    /// `next_batch` parameter to the next call. If this field is absent, there are no more
    /// results in this group.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub next_batch: Option<String>,

    /// Key that can be used to order different groups.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub order: Option<UInt>,

    /// Which results are in this group.
    #[serde(default, skip_serializing_if = "Vec::is_empty")]
    pub results: Vec<EventId>,
}

impl ResultGroup {
    /// Creates an empty `ResultGroup`.
    pub fn new() -> Self {
        Default::default()
    }

    /// Returns `true` if all fields are empty / `None`.
    pub fn is_empty(&self) -> bool {
        self.next_batch.is_none() && self.order.is_none() && self.results.is_empty()
    }
}

/// A search result.
#[derive(Clone, Debug, Default, Deserialize, Serialize)]
#[non_exhaustive]
pub struct SearchResult {
    /// Context for result, if requested.
    #[serde(skip_serializing_if = "EventContextResult::is_empty")]
    pub context: EventContextResult,

    /// A number that describes how closely this result matches the search. Higher is closer.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub rank: Option<UInt>,

    /// The event that matched.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub result: Option<Raw<AnyEvent>>,
}

impl SearchResult {
    /// Creates an empty `SearchResult`.
    pub fn new() -> Self {
        Default::default()
    }

    /// Returns `true` if all fields are empty / `None`.
    pub fn is_empty(&self) -> bool {
        self.context.is_empty() && self.rank.is_none() && self.result.is_none()
    }
}

/// A user profile.
#[derive(Clone, Debug, Default, Deserialize, Serialize)]
#[non_exhaustive]
pub struct UserProfile {
    /// The user's avatar URL, if set.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub avatar_url: Option<String>,

    /// The user's display name, if set.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub displayname: Option<String>,
}

impl UserProfile {
    /// Creates an empty `UserProfile`.
    pub fn new() -> Self {
        Default::default()
    }

    /// Returns `true` if all fields are `None`.
    pub fn is_empty(&self) -> bool {
        self.avatar_url.is_none() && self.displayname.is_none()
    }
}

/// Represents either a room or user ID for returning grouped search results.
#[derive(Clone, Debug, Deserialize, Eq, Ord, PartialEq, PartialOrd, Serialize)]
pub enum RoomIdOrUserId {
    /// Represents a room ID.
    RoomId(RoomId),

    /// Represents a user ID.
    UserId(UserId),
}
<|endoftext|>


<commit_before>use std::io;
use std::ptr;
use super::ipv4_header;
use super::ipv4_packet::{IPv4Packet, MAX_PACKET_LENGTH};

pub struct IPv4PacketBuffer {
    buf: [u8; MAX_PACKET_LENGTH],
    head: usize,
}

impl IPv4PacketBuffer {
    pub fn new() -> Self {
        Self {
            buf: [0; MAX_PACKET_LENGTH],
            head: 0,
        }
    }

    pub fn read_from<R: io::Read>(&mut self, source: &mut R) -> io::Result<(bool)> {
        let target_slice = &mut self.buf[self.head..];
        let r = source.read(target_slice)?;
        self.head += r;
        Ok(r > 0)
    }

    fn available_packet_length(&self) -> Option<u16> {
        if let Some((version, length)) = ipv4_header::peek_version_length(&self.buf) {
            assert!(version == 4, "Not an IPv4 packet, version={}", version);
            if length <= self.head as u16 {
                // full packet available
                Some(length)
            } else {
                // no full packet available
                None
            }
        } else {
            // no packet
            None
        }
    }

    pub fn as_ipv4_packet<'a>(&'a mut self) -> Option<IPv4Packet<'a>> {
        let length = self.available_packet_length();
        if let Some(len) = length {
            Some(IPv4Packet::parse(&mut self.buf[..len as usize]))
        } else {
            None
        }
    }

    pub fn next(&mut self) {
        // remove the packet in front of the buffer
        let length = self.available_packet_length()
                .expect("next() called while there was no packet") as usize;
        assert!(self.head >= length);
        self.head -= length;
        if self.head > 0 {
            // some data remaining, move them to the front of the buffer
            unsafe {
                let buf_ptr = self.buf.as_mut_ptr();
                // semantically equivalent to memmove()
                ptr::copy(buf_ptr.offset(length as isize), buf_ptr, length);
            }
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use std::io;
    use byteorder::{BigEndian, WriteBytesExt};
    use ::relay::ipv4_header::Protocol;
    use ::relay::transport_header::TransportHeaderData;

    fn create_packet() -> Vec<u8> {
        let mut raw = Vec::new();
        write_packet_to(&mut raw);
        raw
    }

    fn write_packet_to(raw: &mut Vec<u8>) {
        raw.write_u8(4u8 << 4 | 5).unwrap();
        raw.write_u8(0).unwrap(); // ToS
        raw.write_u16::<BigEndian>(32).unwrap(); // total length 20 + 8 + 4
        raw.write_u32::<BigEndian>(0).unwrap(); // id_flags_fragment_offset
        raw.write_u8(0).unwrap(); // TTL
        raw.write_u8(17).unwrap(); // protocol (UDP)
        raw.write_u16::<BigEndian>(0).unwrap(); // checksum
        raw.write_u32::<BigEndian>(0x12345678).unwrap(); // source address
        raw.write_u32::<BigEndian>(0x42424242).unwrap(); // destination address

        raw.write_u16::<BigEndian>(1234).unwrap(); // source port
        raw.write_u16::<BigEndian>(5678).unwrap(); // destination port
        raw.write_u16::<BigEndian>(12).unwrap(); // length
        raw.write_u16::<BigEndian>(0).unwrap(); // checksum

        raw.write_u32::<BigEndian>(0x11223344).unwrap(); // payload
    }

    fn check_packet_headers(ipv4_packet: &IPv4Packet) {
        let ipv4_header = ipv4_packet.ipv4_header();
        assert_eq!(20, ipv4_header.header_length());
        assert_eq!(32, ipv4_header.total_length());
        assert_eq!(Protocol::UDP, ipv4_header.protocol());
        assert_eq!(0x12345678, ipv4_header.source());
        assert_eq!(0x42424242, ipv4_header.destination());

        if let Some(TransportHeaderData::UDP(ref udp_header)) = *ipv4_packet.transport_header_data() {
            assert_eq!(1234, udp_header.source_port());
            assert_eq!(5678, udp_header.destination_port());
        } else {
            panic!("No UDP transport header");
        }
    }

    #[test]
    fn parse_ipv4_packet_buffer() {
        let raw = create_packet();
        let mut packet_buffer = IPv4PacketBuffer::new();

        let mut cursor = io::Cursor::new(raw);
        packet_buffer.read_from(&mut cursor).unwrap();

        let packet = packet_buffer.as_ipv4_packet().unwrap();
        check_packet_headers(&packet);
    }

    #[test]
    fn parse_fragmented_ipv4_packet_buffer() {
        let raw = create_packet();
        let mut packet_buffer = IPv4PacketBuffer::new();

        let mut cursor = io::Cursor::new(&raw[..14]);
        packet_buffer.read_from(&mut cursor).unwrap();

        assert!(packet_buffer.as_ipv4_packet().is_none());

        let mut cursor = io::Cursor::new(&raw[14..]);
        packet_buffer.read_from(&mut cursor).unwrap();

        let packet = packet_buffer.as_ipv4_packet().unwrap();
        check_packet_headers(&packet);
    }

    fn create_multi_packets() -> Vec<u8> {
        let mut raw = Vec::new();
        for _ in 0..3 {
            write_packet_to(&mut raw);
        }
        raw
    }

    #[test]
    fn parse_multi_packets() {
        let raw = create_multi_packets();
        let mut packet_buffer = IPv4PacketBuffer::new();

        let mut cursor = io::Cursor::new(raw);
        packet_buffer.read_from(&mut cursor).unwrap();

        for _ in 0..3 {
            {
                let packet = packet_buffer.as_ipv4_packet().unwrap();
                check_packet_headers(&packet);
            }
            packet_buffer.next();
        }

        assert!(packet_buffer.as_ipv4_packet().is_none());
    }
}
<commit_msg>Fix call to ptr::copy<commit_after>use std::io;
use std::ptr;
use super::ipv4_header;
use super::ipv4_packet::{IPv4Packet, MAX_PACKET_LENGTH};

pub struct IPv4PacketBuffer {
    buf: [u8; MAX_PACKET_LENGTH],
    head: usize,
}

impl IPv4PacketBuffer {
    pub fn new() -> Self {
        Self {
            buf: [0; MAX_PACKET_LENGTH],
            head: 0,
        }
    }

    pub fn read_from<R: io::Read>(&mut self, source: &mut R) -> io::Result<(bool)> {
        let target_slice = &mut self.buf[self.head..];
        let r = source.read(target_slice)?;
        self.head += r;
        Ok(r > 0)
    }

    fn available_packet_length(&self) -> Option<u16> {
        if let Some((version, length)) = ipv4_header::peek_version_length(&self.buf) {
            assert!(version == 4, "Not an IPv4 packet, version={}", version);
            if length <= self.head as u16 {
                // full packet available
                Some(length)
            } else {
                // no full packet available
                None
            }
        } else {
            // no packet
            None
        }
    }

    pub fn as_ipv4_packet<'a>(&'a mut self) -> Option<IPv4Packet<'a>> {
        let length = self.available_packet_length();
        if let Some(len) = length {
            Some(IPv4Packet::parse(&mut self.buf[..len as usize]))
        } else {
            None
        }
    }

    pub fn next(&mut self) {
        // remove the packet in front of the buffer
        let length = self.available_packet_length()
                .expect("next() called while there was no packet") as usize;
        assert!(self.head >= length);
        self.head -= length;
        if self.head > 0 {
            // some data remaining, move them to the front of the buffer
            unsafe {
                let buf_ptr = self.buf.as_mut_ptr();

                // Before:
                //
                //  consumed                  old_head
                // |        |....................|
                //  <------>
                //   length
                //
                // After:
                //
                //                  new_head (= old_head - length)
                // |....................|
                //                       <------>
                //                        length
                //
                // move from [length..old_head] to [0..new_head]
                //
                // semantically equivalent to memmove()
                ptr::copy(buf_ptr.offset(length as isize), buf_ptr, self.head);
            }
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use std::io;
    use byteorder::{BigEndian, WriteBytesExt};
    use ::relay::ipv4_header::Protocol;
    use ::relay::transport_header::TransportHeaderData;

    fn create_packet() -> Vec<u8> {
        let mut raw = Vec::new();
        write_packet_to(&mut raw);
        raw
    }

    fn write_packet_to(raw: &mut Vec<u8>) {
        raw.write_u8(4u8 << 4 | 5).unwrap();
        raw.write_u8(0).unwrap(); // ToS
        raw.write_u16::<BigEndian>(32).unwrap(); // total length 20 + 8 + 4
        raw.write_u32::<BigEndian>(0).unwrap(); // id_flags_fragment_offset
        raw.write_u8(0).unwrap(); // TTL
        raw.write_u8(17).unwrap(); // protocol (UDP)
        raw.write_u16::<BigEndian>(0).unwrap(); // checksum
        raw.write_u32::<BigEndian>(0x12345678).unwrap(); // source address
        raw.write_u32::<BigEndian>(0x42424242).unwrap(); // destination address

        raw.write_u16::<BigEndian>(1234).unwrap(); // source port
        raw.write_u16::<BigEndian>(5678).unwrap(); // destination port
        raw.write_u16::<BigEndian>(12).unwrap(); // length
        raw.write_u16::<BigEndian>(0).unwrap(); // checksum

        raw.write_u32::<BigEndian>(0x11223344).unwrap(); // payload
    }

    fn check_packet_headers(ipv4_packet: &IPv4Packet) {
        let ipv4_header = ipv4_packet.ipv4_header();
        assert_eq!(20, ipv4_header.header_length());
        assert_eq!(32, ipv4_header.total_length());
        assert_eq!(Protocol::UDP, ipv4_header.protocol());
        assert_eq!(0x12345678, ipv4_header.source());
        assert_eq!(0x42424242, ipv4_header.destination());

        if let Some(TransportHeaderData::UDP(ref udp_header)) = *ipv4_packet.transport_header_data() {
            assert_eq!(1234, udp_header.source_port());
            assert_eq!(5678, udp_header.destination_port());
        } else {
            panic!("No UDP transport header");
        }
    }

    #[test]
    fn parse_ipv4_packet_buffer() {
        let raw = create_packet();
        let mut packet_buffer = IPv4PacketBuffer::new();

        let mut cursor = io::Cursor::new(raw);
        packet_buffer.read_from(&mut cursor).unwrap();

        let packet = packet_buffer.as_ipv4_packet().unwrap();
        check_packet_headers(&packet);
    }

    #[test]
    fn parse_fragmented_ipv4_packet_buffer() {
        let raw = create_packet();
        let mut packet_buffer = IPv4PacketBuffer::new();

        let mut cursor = io::Cursor::new(&raw[..14]);
        packet_buffer.read_from(&mut cursor).unwrap();

        assert!(packet_buffer.as_ipv4_packet().is_none());

        let mut cursor = io::Cursor::new(&raw[14..]);
        packet_buffer.read_from(&mut cursor).unwrap();

        let packet = packet_buffer.as_ipv4_packet().unwrap();
        check_packet_headers(&packet);
    }

    fn create_multi_packets() -> Vec<u8> {
        let mut raw = Vec::new();
        for _ in 0..3 {
            write_packet_to(&mut raw);
        }
        raw
    }

    #[test]
    fn parse_multi_packets() {
        let raw = create_multi_packets();
        let mut packet_buffer = IPv4PacketBuffer::new();

        let mut cursor = io::Cursor::new(raw);
        packet_buffer.read_from(&mut cursor).unwrap();

        for _ in 0..3 {
            {
                let packet = packet_buffer.as_ipv4_packet().unwrap();
                check_packet_headers(&packet);
            }
            packet_buffer.next();
        }

        assert!(packet_buffer.as_ipv4_packet().is_none());
    }
}
<|endoftext|>


<commit_before>use crate::config::MovableTypesHookOutput;
use crate::cpp_data::CppPath;
use crate::cpp_data::CppTypeDeclarationKind;
use crate::cpp_type::CppPointerLikeTypeKind;
use crate::cpp_type::CppType;
use crate::database::CppItemData;
use crate::processor::ProcessorData;
use log::{info, trace};
use ritual_common::errors::Result;
use std::collections::HashMap;

pub fn set_allocation_places(data: &mut ProcessorData<'_>) -> Result<()> {
    if let Some(hook) = data.config.movable_types_hook() {
        for type1 in data
            .current_database
            .cpp_items_mut()
            .iter_mut()
            .filter_map(|item| item.cpp_data.as_type_mut())
        {
            if let CppTypeDeclarationKind::Class { is_movable, .. } = &mut type1.kind {
                *is_movable = hook(&type1.path)? == MovableTypesHookOutput::Movable;
            }
        }
    }

    Ok(())
}

/// Detects the preferred type allocation place for each type based on
/// API of all known methods. Doesn't actually change the data,
/// only suggests stack allocated types for manual configuration.
pub fn suggest_allocation_places(data: &mut ProcessorData<'_>) -> Result<()> {
    const MAX_ITEMS: usize = 10;

    #[derive(Default, Debug)]
    struct TypeStats {
        virtual_functions: Vec<String>,
        pointer_encounters: Vec<String>,
        non_pointer_encounters: Vec<String>,
    };
    fn check_type(
        cpp_type: &CppType,
        is_behind_pointer: bool,
        data_map: &mut HashMap<CppPath, TypeStats>,
        item_text: &str,
    ) {
        match cpp_type {
            CppType::Class(path) => {
                let good_path = path.deinstantiate();
                if let Some(stats) = data_map.get_mut(&good_path) {
                    if is_behind_pointer {
                        if stats.pointer_encounters.len() < MAX_ITEMS {
                            stats.pointer_encounters.push(item_text.to_string());
                        }
                    } else if stats.non_pointer_encounters.len() < MAX_ITEMS {
                        stats.non_pointer_encounters.push(item_text.to_string());
                    }
                }

                if let Some(args) = &path.last().template_arguments {
                    for arg in args {
                        check_type(arg, false, data_map, item_text);
                    }
                }
            }
            CppType::PointerLike { kind, target, .. } => {
                check_type(
                    target,
                    *kind == CppPointerLikeTypeKind::Pointer,
                    data_map,
                    item_text,
                );
            }
            _ => {}
        }
    }

    let mut data_map = HashMap::new();

    for item in data.current_database.cpp_items() {
        if !item.source.is_parser() || item.source_ffi_item.is_some() {
            continue;
        }
        if let CppItemData::Type(type1) = &item.cpp_data {
            if !type1.kind.is_class() {
                continue;
            }
            if let Some(hook) = data.config.movable_types_hook() {
                if hook(&type1.path)? != MovableTypesHookOutput::Unknown {
                    continue;
                }
            }
            let good_path = type1.path.deinstantiate();
            data_map.insert(good_path, Default::default());
        }
    }

    for item in data.current_database.cpp_items() {
        if !item.source.is_parser() || item.source_ffi_item.is_some() {
            continue;
        }
        if let CppItemData::Function(function) = &item.cpp_data {
            if function.is_private() {
                continue;
            }
            let item_text = function.short_text();
            for t in &function.arguments {
                check_type(&t.argument_type, false, &mut data_map, &item_text);
            }
            check_type(&function.return_type, false, &mut data_map, &item_text);
            if function.is_virtual() {
                let type1 = function.class_type()?;
                let good_path = type1.deinstantiate();
                if let Some(stats) = data_map.get_mut(&good_path) {
                    if stats.virtual_functions.len() < MAX_ITEMS {
                        stats.virtual_functions.push(item_text);
                    }
                }
            }
        }
    }

    for (name, stats) in &data_map {
        trace!("type = {}; stats = {:?}", name.to_cpp_pseudo_code(), stats);
    }

    for (path, stats) in data_map {
        let suggestion = if stats.virtual_functions.is_empty() {
            if stats.pointer_encounters.is_empty() {
                if stats.non_pointer_encounters.len() == MAX_ITEMS {
                    "movable (no pointers, no virtual functions)"
                } else {
                    "probably movable (no pointers, no virtual functions, but too few items)"
                }
            } else if stats.pointer_encounters.len() < 5
                && stats.non_pointer_encounters.len() == MAX_ITEMS
            {
                "probably movable (few pointers)"
            } else if stats.pointer_encounters.len() == MAX_ITEMS {
                "immovable (many pointers)"
            } else {
                "unknown (too few items)"
            }
        } else {
            "immovable (has virtual functions)"
        };
        info!("{:?} is {}", path.to_templateless_string(), suggestion);
        info!("path = {}", path.to_cpp_pseudo_code());
        info!("* virtual_functions ({}):", stats.virtual_functions.len());
        for item in &stats.virtual_functions {
            info!("* * {}", item);
        }
        info!("* pointer_encounters ({}):", stats.pointer_encounters.len());
        for item in &stats.pointer_encounters {
            info!("* * {}", item);
        }
        info!(
            "* non_pointer_encounters ({}):",
            stats.non_pointer_encounters.len()
        );
        for item in &stats.non_pointer_encounters {
            info!("* * {}", item);
        }
    }

    Ok(())
}
<commit_msg>split suggest_allocation_places()<commit_after>use crate::config::MovableTypesHookOutput;
use crate::cpp_data::CppPath;
use crate::cpp_data::CppTypeDeclarationKind;
use crate::cpp_type::CppPointerLikeTypeKind;
use crate::cpp_type::CppType;
use crate::database::CppItemData;
use crate::processor::ProcessorData;
use log::{info, trace};
use ritual_common::errors::Result;
use std::collections::HashMap;

pub fn set_allocation_places(data: &mut ProcessorData<'_>) -> Result<()> {
    if let Some(hook) = data.config.movable_types_hook() {
        for type1 in data
            .current_database
            .cpp_items_mut()
            .iter_mut()
            .filter_map(|item| item.cpp_data.as_type_mut())
        {
            if let CppTypeDeclarationKind::Class { is_movable, .. } = &mut type1.kind {
                *is_movable = hook(&type1.path)? == MovableTypesHookOutput::Movable;
            }
        }
    }

    Ok(())
}

#[derive(Default, Debug)]
struct TypeStats {
    virtual_functions: Vec<String>,
    pointer_encounters: Vec<String>,
    non_pointer_encounters: Vec<String>,
}

fn log_results(data_map: &HashMap<CppPath, TypeStats>) {
    for (name, stats) in data_map {
        trace!("type = {}; stats = {:?}", name.to_cpp_pseudo_code(), stats);
    }

    for (path, stats) in data_map {
        let suggestion = if stats.virtual_functions.is_empty() {
            if stats.pointer_encounters.is_empty() {
                if stats.non_pointer_encounters.len() == MAX_ITEMS {
                    "movable (no pointers, no virtual functions)"
                } else {
                    "probably movable (no pointers, no virtual functions, but too few items)"
                }
            } else if stats.pointer_encounters.len() < 5
                && stats.non_pointer_encounters.len() == MAX_ITEMS
            {
                "probably movable (few pointers)"
            } else if stats.pointer_encounters.len() == MAX_ITEMS {
                "immovable (many pointers)"
            } else {
                "unknown (too few items)"
            }
        } else {
            "immovable (has virtual functions)"
        };
        info!("{:?} is {}", path.to_templateless_string(), suggestion);
        info!("path = {}", path.to_cpp_pseudo_code());
        info!("* virtual_functions ({}):", stats.virtual_functions.len());
        for item in &stats.virtual_functions {
            info!("* * {}", item);
        }
        info!("* pointer_encounters ({}):", stats.pointer_encounters.len());
        for item in &stats.pointer_encounters {
            info!("* * {}", item);
        }
        info!(
            "* non_pointer_encounters ({}):",
            stats.non_pointer_encounters.len()
        );
        for item in &stats.non_pointer_encounters {
            info!("* * {}", item);
        }
    }
}

fn check_type(
    cpp_type: &CppType,
    is_behind_pointer: bool,
    data_map: &mut HashMap<CppPath, TypeStats>,
    item_text: &str,
) {
    match cpp_type {
        CppType::Class(path) => {
            let good_path = path.deinstantiate();
            if let Some(stats) = data_map.get_mut(&good_path) {
                if is_behind_pointer {
                    if stats.pointer_encounters.len() < MAX_ITEMS {
                        stats.pointer_encounters.push(item_text.to_string());
                    }
                } else if stats.non_pointer_encounters.len() < MAX_ITEMS {
                    stats.non_pointer_encounters.push(item_text.to_string());
                }
            }

            if let Some(args) = &path.last().template_arguments {
                for arg in args {
                    check_type(arg, false, data_map, item_text);
                }
            }
        }
        CppType::PointerLike { kind, target, .. } => {
            check_type(
                target,
                *kind == CppPointerLikeTypeKind::Pointer,
                data_map,
                item_text,
            );
        }
        _ => {}
    }
}

const MAX_ITEMS: usize = 10;

/// Detects the preferred type allocation place for each type based on
/// API of all known methods. Doesn't actually change the data,
/// only suggests stack allocated types for manual configuration.
pub fn suggest_allocation_places(data: &mut ProcessorData<'_>) -> Result<()> {
    let mut data_map = HashMap::new();

    for item in data.current_database.cpp_items() {
        if !item.source.is_parser() || item.source_ffi_item.is_some() {
            continue;
        }
        if let CppItemData::Type(type1) = &item.cpp_data {
            if !type1.kind.is_class() {
                continue;
            }
            if let Some(hook) = data.config.movable_types_hook() {
                if hook(&type1.path)? != MovableTypesHookOutput::Unknown {
                    continue;
                }
            }
            let good_path = type1.path.deinstantiate();
            data_map.insert(good_path, Default::default());
        }
    }

    for item in data.current_database.cpp_items() {
        if !item.source.is_parser() || item.source_ffi_item.is_some() {
            continue;
        }
        if let CppItemData::Function(function) = &item.cpp_data {
            if function.is_private() {
                continue;
            }
            let item_text = function.short_text();
            for t in &function.arguments {
                check_type(&t.argument_type, false, &mut data_map, &item_text);
            }
            check_type(&function.return_type, false, &mut data_map, &item_text);
            if function.is_virtual() {
                let type1 = function.class_type()?;
                let good_path = type1.deinstantiate();
                if let Some(stats) = data_map.get_mut(&good_path) {
                    if stats.virtual_functions.len() < MAX_ITEMS {
                        stats.virtual_functions.push(item_text);
                    }
                }
            }
        }
    }

    log_results(&data_map);

    Ok(())
}
<|endoftext|>


<commit_before>//! Owned and borrowed OS handles.

#![unstable(feature = "io_safety", issue = "87074")]

use super::raw::{AsRawHandle, FromRawHandle, IntoRawHandle, RawHandle};
use crate::convert::TryFrom;
use crate::ffi::c_void;
use crate::fmt;
use crate::fs;
use crate::marker::PhantomData;
use crate::mem::forget;
use crate::ptr::NonNull;
use crate::sys::c;
use crate::sys_common::{AsInner, FromInner, IntoInner};

/// A borrowed handle.
///
/// This has a lifetime parameter to tie it to the lifetime of something that
/// owns the handle.
///
/// This uses `repr(transparent)` and has the representation of a host handle,
/// so it can be used in FFI in places where a handle is passed as an argument,
/// it is not captured or consumed, and it is never null.
///
/// Note that it *may* have the value `INVALID_HANDLE_VALUE`. See [here] for
/// the full story.
///
/// [here]: https://devblogs.microsoft.com/oldnewthing/20040302-00/?p=40443
#[derive(Copy, Clone)]
#[repr(transparent)]
#[unstable(feature = "io_safety", issue = "87074")]
pub struct BorrowedHandle<'handle> {
    raw: NonNull<c_void>,
    _phantom: PhantomData<&'handle OwnedHandle>,
}

/// An owned handle.
///
/// This closes the handle on drop.
///
/// This uses `repr(transparent)` and has the representation of a host handle,
/// so it can be used in FFI in places where a handle is passed as a consumed
/// argument or returned as an owned value, and is never null.
///
/// Note that it *may* have the value `INVALID_HANDLE_VALUE`. See [here] for
/// the full story. For APIs like `CreateFileW` which report errors with
/// `INVALID_HANDLE_VALUE` instead of null, use [`OptionFileHandle`] instead
/// of `Option<OwnedHandle>`.
///
/// [here]: https://devblogs.microsoft.com/oldnewthing/20040302-00/?p=40443
#[repr(transparent)]
#[unstable(feature = "io_safety", issue = "87074")]
pub struct OwnedHandle {
    raw: NonNull<c_void>,
}

/// Similar to `Option<OwnedHandle>`, but intended for use in FFI interfaces
/// where `INVALID_HANDLE_VALUE` is used as the sentry value, and null values
/// are not used at all, such as in the return value of `CreateFileW`.
///
/// If this holds an owned handle, it closes the handle on drop.
///
/// This uses `repr(transparent)` and has the representation of a host handle,
/// so it can be used in FFI in places where a non-null handle is passed as a
/// consumed argument or returned as an owned value, or it is
/// `INVALID_HANDLE_VALUE` indicating an error or an otherwise absent value.
///
#[repr(transparent)]
#[unstable(feature = "io_safety", issue = "87074")]
pub struct OptionFileHandle {
    raw: RawHandle,
}

// The Windows [`HANDLE`] type may be transferred across and shared between
// thread boundaries (despite containing a `*mut void`, which in general isn't
// `Send` or `Sync`).
//
// [`HANDLE`]: std::os::windows::raw::HANDLE
unsafe impl Send for OwnedHandle {}
unsafe impl Send for OptionFileHandle {}
unsafe impl Send for BorrowedHandle<'_> {}
unsafe impl Sync for OwnedHandle {}
unsafe impl Sync for OptionFileHandle {}
unsafe impl Sync for BorrowedHandle<'_> {}

impl BorrowedHandle<'_> {
    /// Return a `BorrowedHandle` holding the given raw handle.
    ///
    /// # Safety
    ///
    /// The resource pointed to by `raw` must remain open for the duration of
    /// the returned `BorrowedHandle`, and it must not be null.
    #[inline]
    #[unstable(feature = "io_safety", issue = "87074")]
    pub unsafe fn borrow_raw_handle(raw: RawHandle) -> Self {
        assert!(!raw.is_null());
        Self { raw: NonNull::new_unchecked(raw), _phantom: PhantomData }
    }
}

impl OptionFileHandle {
    /// Return an empty `OptionFileHandle` with no resource.
    #[inline]
    #[unstable(feature = "io_safety", issue = "87074")]
    pub const fn none() -> Self {
        Self { raw: c::INVALID_HANDLE_VALUE }
    }
}

impl TryFrom<OptionFileHandle> for OwnedHandle {
    type Error = ();

    #[inline]
    fn try_from(option: OptionFileHandle) -> Result<Self, ()> {
        let raw = option.raw;
        forget(option);
        if let Some(non_null) = NonNull::new(raw) {
            if non_null.as_ptr() != c::INVALID_HANDLE_VALUE {
                Ok(Self { raw: non_null })
            } else {
                Err(())
            }
        } else {
            // In theory, we ought to be able to assume that the pointer here
            // is never null, change `option.raw` to `NonNull`, and obviate the
            // the panic path here. Unfortunately, Win32 documentation doesn't
            // explicitly guarantee this anywhere.
            //
            // APIs like [`CreateFileW`] itself have `HANDLE` arguments where a
            // null handle indicates an absent value, which wouldn't work if
            // null were a valid handle value, so it seems very unlikely that
            // it could ever return null. But who knows?
            //
            // [`CreateFileW`]: https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilew
            panic!("An `OptionFileHandle` was null!");
        }
    }
}

impl From<OwnedHandle> for OptionFileHandle {
    #[inline]
    fn from(owned: OwnedHandle) -> Self {
        let raw = owned.raw;
        forget(owned);
        Self { raw: raw.as_ptr() }
    }
}

impl AsRawHandle for BorrowedHandle<'_> {
    #[inline]
    fn as_raw_handle(&self) -> RawHandle {
        self.raw.as_ptr()
    }
}

impl AsRawHandle for OwnedHandle {
    #[inline]
    fn as_raw_handle(&self) -> RawHandle {
        self.raw.as_ptr()
    }
}

impl IntoRawHandle for OwnedHandle {
    #[inline]
    fn into_raw_handle(self) -> RawHandle {
        let raw = self.raw.as_ptr();
        forget(self);
        raw
    }
}

impl FromRawHandle for OwnedHandle {
    /// Constructs a new instance of `Self` from the given raw handle.
    ///
    /// # Safety
    ///
    /// The resource pointed to by `raw` must be open and suitable for assuming
    /// ownership. The resource must not require any cleanup other than `CloseHandle`.
    #[inline]
    unsafe fn from_raw_handle(raw: RawHandle) -> Self {
        assert!(!raw.is_null());
        Self { raw: NonNull::new_unchecked(raw) }
    }
}

impl FromRawHandle for OptionFileHandle {
    /// Constructs a new instance of `Self` from the given raw handle.
    ///
    /// # Safety
    ///
    /// The resource pointed to by `raw` must be either open and otherwise
    /// unowned, or equal to `INVALID_HANDLE_VALUE``. Note that not all Windows
    /// APIs use `INVALID_HANDLE_VALUE` for errors; see [here] for the full
    /// story.
    ///
    /// [here]: https://devblogs.microsoft.com/oldnewthing/20040302-00/?p=40443
    #[inline]
    unsafe fn from_raw_handle(raw: RawHandle) -> Self {
        assert!(!raw.is_null());
        Self { raw }
    }
}

impl Drop for OwnedHandle {
    #[inline]
    fn drop(&mut self) {
        unsafe {
            let _ = c::CloseHandle(self.raw.as_ptr());
        }
    }
}

impl Drop for OptionFileHandle {
    #[inline]
    fn drop(&mut self) {
        unsafe {
            let _ = c::CloseHandle(self.raw);
        }
    }
}

impl fmt::Debug for BorrowedHandle<'_> {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.debug_struct("BorrowedHandle").field("handle", &self.raw).finish()
    }
}

impl fmt::Debug for OwnedHandle {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.debug_struct("OwnedHandle").field("handle", &self.raw).finish()
    }
}

impl fmt::Debug for OptionFileHandle {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.debug_struct("OptionFileHandle").field("handle", &self.raw).finish()
    }
}

/// A trait to borrow the handle from an underlying object.
#[unstable(feature = "io_safety", issue = "87074")]
pub trait AsHandle {
    /// Borrows the handle.
    ///
    /// # Example
    ///
    /// ```rust,no_run
    /// use std::fs::File;
    /// # use std::io;
    /// use std::os::windows::{AsHandle, BorrowedHandle};
    ///
    /// let mut f = File::open("foo.txt")?;
    /// let borrowed_handle: BorrowedHandle<'_> = f.as_handle();
    /// # Ok::<(), io::Error>(())
    /// ```
    fn as_handle(&self) -> BorrowedHandle<'_>;
}

impl AsHandle for BorrowedHandle<'_> {
    #[inline]
    fn as_handle(&self) -> BorrowedHandle<'_> {
        *self
    }
}

impl AsHandle for OwnedHandle {
    #[inline]
    fn as_handle(&self) -> BorrowedHandle<'_> {
        unsafe { BorrowedHandle::borrow_raw_handle(self.as_raw_handle()) }
    }
}

impl AsHandle for fs::File {
    #[inline]
    fn as_handle(&self) -> BorrowedHandle<'_> {
        self.as_inner().as_handle()
    }
}

impl From<fs::File> for OwnedHandle {
    #[inline]
    fn from(file: fs::File) -> OwnedHandle {
        file.into_inner().into_inner().into_inner().into()
    }
}

impl From<OwnedHandle> for fs::File {
    #[inline]
    fn from(owned: OwnedHandle) -> Self {
        Self::from_inner(FromInner::from_inner(FromInner::from_inner(owned)))
    }
}

impl AsHandle for crate::io::Stdin {
    #[inline]
    fn as_handle(&self) -> BorrowedHandle<'_> {
        unsafe { BorrowedHandle::borrow_raw_handle(self.as_raw_handle()) }
    }
}

impl<'a> AsHandle for crate::io::StdinLock<'a> {
    #[inline]
    fn as_handle(&self) -> BorrowedHandle<'_> {
        unsafe { BorrowedHandle::borrow_raw_handle(self.as_raw_handle()) }
    }
}

impl AsHandle for crate::io::Stdout {
    #[inline]
    fn as_handle(&self) -> BorrowedHandle<'_> {
        unsafe { BorrowedHandle::borrow_raw_handle(self.as_raw_handle()) }
    }
}

impl<'a> AsHandle for crate::io::StdoutLock<'a> {
    #[inline]
    fn as_handle(&self) -> BorrowedHandle<'_> {
        unsafe { BorrowedHandle::borrow_raw_handle(self.as_raw_handle()) }
    }
}

impl AsHandle for crate::io::Stderr {
    #[inline]
    fn as_handle(&self) -> BorrowedHandle<'_> {
        unsafe { BorrowedHandle::borrow_raw_handle(self.as_raw_handle()) }
    }
}

impl<'a> AsHandle for crate::io::StderrLock<'a> {
    #[inline]
    fn as_handle(&self) -> BorrowedHandle<'_> {
        unsafe { BorrowedHandle::borrow_raw_handle(self.as_raw_handle()) }
    }
}

impl AsHandle for crate::process::ChildStdin {
    #[inline]
    fn as_handle(&self) -> BorrowedHandle<'_> {
        unsafe { BorrowedHandle::borrow_raw_handle(self.as_raw_handle()) }
    }
}

impl From<crate::process::ChildStdin> for OwnedHandle {
    #[inline]
    fn from(child_stdin: crate::process::ChildStdin) -> OwnedHandle {
        unsafe { OwnedHandle::from_raw_handle(child_stdin.into_raw_handle()) }
    }
}

impl AsHandle for crate::process::ChildStdout {
    #[inline]
    fn as_handle(&self) -> BorrowedHandle<'_> {
        unsafe { BorrowedHandle::borrow_raw_handle(self.as_raw_handle()) }
    }
}

impl From<crate::process::ChildStdout> for OwnedHandle {
    #[inline]
    fn from(child_stdout: crate::process::ChildStdout) -> OwnedHandle {
        unsafe { OwnedHandle::from_raw_handle(child_stdout.into_raw_handle()) }
    }
}

impl AsHandle for crate::process::ChildStderr {
    #[inline]
    fn as_handle(&self) -> BorrowedHandle<'_> {
        unsafe { BorrowedHandle::borrow_raw_handle(self.as_raw_handle()) }
    }
}

impl From<crate::process::ChildStderr> for OwnedHandle {
    #[inline]
    fn from(child_stderr: crate::process::ChildStderr) -> OwnedHandle {
        unsafe { OwnedHandle::from_raw_handle(child_stderr.into_raw_handle()) }
    }
}

impl<T> AsHandle for crate::thread::JoinHandle<T> {
    #[inline]
    fn as_handle(&self) -> BorrowedHandle<'_> {
        unsafe { BorrowedHandle::borrow_raw_handle(self.as_raw_handle()) }
    }
}

impl<T> From<crate::thread::JoinHandle<T>> for OwnedHandle {
    #[inline]
    fn from(join_handle: crate::thread::JoinHandle<T>) -> OwnedHandle {
        join_handle.into_inner().into_handle().into_inner()
    }
}
<commit_msg>Add a comment about `OptionFileHandle`.<commit_after>//! Owned and borrowed OS handles.

#![unstable(feature = "io_safety", issue = "87074")]

use super::raw::{AsRawHandle, FromRawHandle, IntoRawHandle, RawHandle};
use crate::convert::TryFrom;
use crate::ffi::c_void;
use crate::fmt;
use crate::fs;
use crate::marker::PhantomData;
use crate::mem::forget;
use crate::ptr::NonNull;
use crate::sys::c;
use crate::sys_common::{AsInner, FromInner, IntoInner};

/// A borrowed handle.
///
/// This has a lifetime parameter to tie it to the lifetime of something that
/// owns the handle.
///
/// This uses `repr(transparent)` and has the representation of a host handle,
/// so it can be used in FFI in places where a handle is passed as an argument,
/// it is not captured or consumed, and it is never null.
///
/// Note that it *may* have the value `INVALID_HANDLE_VALUE`. See [here] for
/// the full story.
///
/// [here]: https://devblogs.microsoft.com/oldnewthing/20040302-00/?p=40443
#[derive(Copy, Clone)]
#[repr(transparent)]
#[unstable(feature = "io_safety", issue = "87074")]
pub struct BorrowedHandle<'handle> {
    raw: NonNull<c_void>,
    _phantom: PhantomData<&'handle OwnedHandle>,
}

/// An owned handle.
///
/// This closes the handle on drop.
///
/// This uses `repr(transparent)` and has the representation of a host handle,
/// so it can be used in FFI in places where a handle is passed as a consumed
/// argument or returned as an owned value, and is never null.
///
/// Note that it *may* have the value `INVALID_HANDLE_VALUE`. See [here] for
/// the full story. For APIs like `CreateFileW` which report errors with
/// `INVALID_HANDLE_VALUE` instead of null, use [`OptionFileHandle`] instead
/// of `Option<OwnedHandle>`.
///
/// [here]: https://devblogs.microsoft.com/oldnewthing/20040302-00/?p=40443
#[repr(transparent)]
#[unstable(feature = "io_safety", issue = "87074")]
pub struct OwnedHandle {
    raw: NonNull<c_void>,
}

/// Similar to `Option<OwnedHandle>`, but intended for use in FFI interfaces
/// where `INVALID_HANDLE_VALUE` is used as the sentry value, and null values
/// are not used at all, such as in the return value of `CreateFileW`.
///
/// The main thing you can do with an `OptionFileHandle` is to convert it into
/// an `OwnedHandle` using its [`TryFrom`] implementation, and this conversion
/// takes care of the check for `INVALID_HANDLE_VALUE`.
///
/// If this holds an owned handle, it closes the handle on drop.
///
/// This uses `repr(transparent)` and has the representation of a host handle,
/// so it can be used in FFI in places where a non-null handle is passed as a
/// consumed argument or returned as an owned value, or it is
/// `INVALID_HANDLE_VALUE` indicating an error or an otherwise absent value.
///
#[repr(transparent)]
#[unstable(feature = "io_safety", issue = "87074")]
pub struct OptionFileHandle {
    raw: RawHandle,
}

// The Windows [`HANDLE`] type may be transferred across and shared between
// thread boundaries (despite containing a `*mut void`, which in general isn't
// `Send` or `Sync`).
//
// [`HANDLE`]: std::os::windows::raw::HANDLE
unsafe impl Send for OwnedHandle {}
unsafe impl Send for OptionFileHandle {}
unsafe impl Send for BorrowedHandle<'_> {}
unsafe impl Sync for OwnedHandle {}
unsafe impl Sync for OptionFileHandle {}
unsafe impl Sync for BorrowedHandle<'_> {}

impl BorrowedHandle<'_> {
    /// Return a `BorrowedHandle` holding the given raw handle.
    ///
    /// # Safety
    ///
    /// The resource pointed to by `raw` must remain open for the duration of
    /// the returned `BorrowedHandle`, and it must not be null.
    #[inline]
    #[unstable(feature = "io_safety", issue = "87074")]
    pub unsafe fn borrow_raw_handle(raw: RawHandle) -> Self {
        assert!(!raw.is_null());
        Self { raw: NonNull::new_unchecked(raw), _phantom: PhantomData }
    }
}

impl OptionFileHandle {
    /// Return an empty `OptionFileHandle` with no resource.
    #[inline]
    #[unstable(feature = "io_safety", issue = "87074")]
    pub const fn none() -> Self {
        Self { raw: c::INVALID_HANDLE_VALUE }
    }
}

impl TryFrom<OptionFileHandle> for OwnedHandle {
    type Error = ();

    #[inline]
    fn try_from(option: OptionFileHandle) -> Result<Self, ()> {
        let raw = option.raw;
        forget(option);
        if let Some(non_null) = NonNull::new(raw) {
            if non_null.as_ptr() != c::INVALID_HANDLE_VALUE {
                Ok(Self { raw: non_null })
            } else {
                Err(())
            }
        } else {
            // In theory, we ought to be able to assume that the pointer here
            // is never null, change `option.raw` to `NonNull`, and obviate the
            // the panic path here. Unfortunately, Win32 documentation doesn't
            // explicitly guarantee this anywhere.
            //
            // APIs like [`CreateFileW`] itself have `HANDLE` arguments where a
            // null handle indicates an absent value, which wouldn't work if
            // null were a valid handle value, so it seems very unlikely that
            // it could ever return null. But who knows?
            //
            // [`CreateFileW`]: https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilew
            panic!("An `OptionFileHandle` was null!");
        }
    }
}

impl From<OwnedHandle> for OptionFileHandle {
    #[inline]
    fn from(owned: OwnedHandle) -> Self {
        let raw = owned.raw;
        forget(owned);
        Self { raw: raw.as_ptr() }
    }
}

impl AsRawHandle for BorrowedHandle<'_> {
    #[inline]
    fn as_raw_handle(&self) -> RawHandle {
        self.raw.as_ptr()
    }
}

impl AsRawHandle for OwnedHandle {
    #[inline]
    fn as_raw_handle(&self) -> RawHandle {
        self.raw.as_ptr()
    }
}

impl IntoRawHandle for OwnedHandle {
    #[inline]
    fn into_raw_handle(self) -> RawHandle {
        let raw = self.raw.as_ptr();
        forget(self);
        raw
    }
}

impl FromRawHandle for OwnedHandle {
    /// Constructs a new instance of `Self` from the given raw handle.
    ///
    /// # Safety
    ///
    /// The resource pointed to by `raw` must be open and suitable for assuming
    /// ownership. The resource must not require any cleanup other than `CloseHandle`.
    #[inline]
    unsafe fn from_raw_handle(raw: RawHandle) -> Self {
        assert!(!raw.is_null());
        Self { raw: NonNull::new_unchecked(raw) }
    }
}

impl FromRawHandle for OptionFileHandle {
    /// Constructs a new instance of `Self` from the given raw handle.
    ///
    /// # Safety
    ///
    /// The resource pointed to by `raw` must be either open and otherwise
    /// unowned, or equal to `INVALID_HANDLE_VALUE``. Note that not all Windows
    /// APIs use `INVALID_HANDLE_VALUE` for errors; see [here] for the full
    /// story.
    ///
    /// [here]: https://devblogs.microsoft.com/oldnewthing/20040302-00/?p=40443
    #[inline]
    unsafe fn from_raw_handle(raw: RawHandle) -> Self {
        assert!(!raw.is_null());
        Self { raw }
    }
}

impl Drop for OwnedHandle {
    #[inline]
    fn drop(&mut self) {
        unsafe {
            let _ = c::CloseHandle(self.raw.as_ptr());
        }
    }
}

impl Drop for OptionFileHandle {
    #[inline]
    fn drop(&mut self) {
        unsafe {
            let _ = c::CloseHandle(self.raw);
        }
    }
}

impl fmt::Debug for BorrowedHandle<'_> {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.debug_struct("BorrowedHandle").field("handle", &self.raw).finish()
    }
}

impl fmt::Debug for OwnedHandle {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.debug_struct("OwnedHandle").field("handle", &self.raw).finish()
    }
}

impl fmt::Debug for OptionFileHandle {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.debug_struct("OptionFileHandle").field("handle", &self.raw).finish()
    }
}

/// A trait to borrow the handle from an underlying object.
#[unstable(feature = "io_safety", issue = "87074")]
pub trait AsHandle {
    /// Borrows the handle.
    ///
    /// # Example
    ///
    /// ```rust,no_run
    /// use std::fs::File;
    /// # use std::io;
    /// use std::os::windows::{AsHandle, BorrowedHandle};
    ///
    /// let mut f = File::open("foo.txt")?;
    /// let borrowed_handle: BorrowedHandle<'_> = f.as_handle();
    /// # Ok::<(), io::Error>(())
    /// ```
    fn as_handle(&self) -> BorrowedHandle<'_>;
}

impl AsHandle for BorrowedHandle<'_> {
    #[inline]
    fn as_handle(&self) -> BorrowedHandle<'_> {
        *self
    }
}

impl AsHandle for OwnedHandle {
    #[inline]
    fn as_handle(&self) -> BorrowedHandle<'_> {
        unsafe { BorrowedHandle::borrow_raw_handle(self.as_raw_handle()) }
    }
}

impl AsHandle for fs::File {
    #[inline]
    fn as_handle(&self) -> BorrowedHandle<'_> {
        self.as_inner().as_handle()
    }
}

impl From<fs::File> for OwnedHandle {
    #[inline]
    fn from(file: fs::File) -> OwnedHandle {
        file.into_inner().into_inner().into_inner().into()
    }
}

impl From<OwnedHandle> for fs::File {
    #[inline]
    fn from(owned: OwnedHandle) -> Self {
        Self::from_inner(FromInner::from_inner(FromInner::from_inner(owned)))
    }
}

impl AsHandle for crate::io::Stdin {
    #[inline]
    fn as_handle(&self) -> BorrowedHandle<'_> {
        unsafe { BorrowedHandle::borrow_raw_handle(self.as_raw_handle()) }
    }
}

impl<'a> AsHandle for crate::io::StdinLock<'a> {
    #[inline]
    fn as_handle(&self) -> BorrowedHandle<'_> {
        unsafe { BorrowedHandle::borrow_raw_handle(self.as_raw_handle()) }
    }
}

impl AsHandle for crate::io::Stdout {
    #[inline]
    fn as_handle(&self) -> BorrowedHandle<'_> {
        unsafe { BorrowedHandle::borrow_raw_handle(self.as_raw_handle()) }
    }
}

impl<'a> AsHandle for crate::io::StdoutLock<'a> {
    #[inline]
    fn as_handle(&self) -> BorrowedHandle<'_> {
        unsafe { BorrowedHandle::borrow_raw_handle(self.as_raw_handle()) }
    }
}

impl AsHandle for crate::io::Stderr {
    #[inline]
    fn as_handle(&self) -> BorrowedHandle<'_> {
        unsafe { BorrowedHandle::borrow_raw_handle(self.as_raw_handle()) }
    }
}

impl<'a> AsHandle for crate::io::StderrLock<'a> {
    #[inline]
    fn as_handle(&self) -> BorrowedHandle<'_> {
        unsafe { BorrowedHandle::borrow_raw_handle(self.as_raw_handle()) }
    }
}

impl AsHandle for crate::process::ChildStdin {
    #[inline]
    fn as_handle(&self) -> BorrowedHandle<'_> {
        unsafe { BorrowedHandle::borrow_raw_handle(self.as_raw_handle()) }
    }
}

impl From<crate::process::ChildStdin> for OwnedHandle {
    #[inline]
    fn from(child_stdin: crate::process::ChildStdin) -> OwnedHandle {
        unsafe { OwnedHandle::from_raw_handle(child_stdin.into_raw_handle()) }
    }
}

impl AsHandle for crate::process::ChildStdout {
    #[inline]
    fn as_handle(&self) -> BorrowedHandle<'_> {
        unsafe { BorrowedHandle::borrow_raw_handle(self.as_raw_handle()) }
    }
}

impl From<crate::process::ChildStdout> for OwnedHandle {
    #[inline]
    fn from(child_stdout: crate::process::ChildStdout) -> OwnedHandle {
        unsafe { OwnedHandle::from_raw_handle(child_stdout.into_raw_handle()) }
    }
}

impl AsHandle for crate::process::ChildStderr {
    #[inline]
    fn as_handle(&self) -> BorrowedHandle<'_> {
        unsafe { BorrowedHandle::borrow_raw_handle(self.as_raw_handle()) }
    }
}

impl From<crate::process::ChildStderr> for OwnedHandle {
    #[inline]
    fn from(child_stderr: crate::process::ChildStderr) -> OwnedHandle {
        unsafe { OwnedHandle::from_raw_handle(child_stderr.into_raw_handle()) }
    }
}

impl<T> AsHandle for crate::thread::JoinHandle<T> {
    #[inline]
    fn as_handle(&self) -> BorrowedHandle<'_> {
        unsafe { BorrowedHandle::borrow_raw_handle(self.as_raw_handle()) }
    }
}

impl<T> From<crate::thread::JoinHandle<T>> for OwnedHandle {
    #[inline]
    fn from(join_handle: crate::thread::JoinHandle<T>) -> OwnedHandle {
        join_handle.into_inner().into_handle().into_inner()
    }
}
<|endoftext|>


<commit_before>#![feature(slice_concat_ext)]

extern crate compiletest_rs as compiletest;
extern crate colored;

use colored::*;

use std::slice::SliceConcatExt;
use std::path::{PathBuf, Path};
use std::io::Write;

macro_rules! eprintln {
    ($($arg:tt)*) => {
        let stderr = std::io::stderr();
        writeln!(stderr.lock(), $($arg)*).unwrap();
    }
}

fn miri_path() -> PathBuf {
    if rustc_test_suite().is_some() {
        PathBuf::from(option_env!("MIRI_PATH").unwrap())
    } else {
        PathBuf::from(concat!("target/", env!("PROFILE"), "/miri"))
    }
}

fn rustc_test_suite() -> Option<PathBuf> {
    option_env!("RUSTC_TEST_SUITE").map(PathBuf::from)
}

fn rustc_lib_path() -> PathBuf {
    option_env!("RUSTC_LIB_PATH").unwrap().into()
}

fn have_fullmir() -> bool {
    // We assume we have full MIR when MIRI_SYSROOT is set or when we are in rustc
    std::env::var("MIRI_SYSROOT").is_ok() || rustc_test_suite().is_some()
}

fn compile_fail(sysroot: &Path, path: &str, target: &str, host: &str, need_fullmir: bool) {
    if need_fullmir && !have_fullmir() {
        eprintln!("{}", format!(
            "## Skipping compile-fail tests in {} against miri for target {} due to missing mir",
            path,
            target
        ).yellow().bold());
        return;
    }

    eprintln!("{}", format!(
        "## Running compile-fail tests in {} against miri for target {}",
        path,
        target
    ).green().bold());
    let mut config = compiletest::Config::default().tempdir();
    config.mode = "compile-fail".parse().expect("Invalid mode");
    config.rustc_path = miri_path();
    let mut flags = Vec::new();
    if rustc_test_suite().is_some() {
        config.run_lib_path = rustc_lib_path();
        config.compile_lib_path = rustc_lib_path();
    }
    flags.push(format!("--sysroot {}", sysroot.display()));
    flags.push("-Dwarnings -Dunused".to_owned()); // overwrite the -Aunused in compiletest-rs
    config.src_base = PathBuf::from(path.to_string());
    flags.push("-Zmir-emit-validate=1".to_owned());
    flags.push("-Zmiri-disable-validation".to_owned());
    config.target_rustcflags = Some(flags.join(" "));
    config.target = target.to_owned();
    config.host = host.to_owned();
    compiletest::run_tests(&config);
}

fn miri_pass(sysroot: &Path, path: &str, target: &str, host: &str, need_fullmir: bool, opt: bool) {
    if need_fullmir && !have_fullmir() {
        eprintln!("{}", format!(
            "## Skipping run-pass tests in {} against miri for target {} due to missing mir",
            path,
            target
        ).yellow().bold());
        return;
    }

    let opt_str = if opt { " with optimizations" } else { "" };
    eprintln!("{}", format!(
        "## Running run-pass tests in {} against miri for target {}{}",
        path,
        target,
        opt_str
    ).green().bold());
    let mut config = compiletest::Config::default().tempdir();
    config.mode = "ui".parse().expect("Invalid mode");
    config.src_base = PathBuf::from(path);
    config.target = target.to_owned();
    config.host = host.to_owned();
    config.rustc_path = miri_path();
    if rustc_test_suite().is_some() {
        config.run_lib_path = rustc_lib_path();
        config.compile_lib_path = rustc_lib_path();
    }
    let mut flags = Vec::new();
    flags.push(format!("--sysroot {}", sysroot.display()));
    flags.push("-Dwarnings -Dunused".to_owned()); // overwrite the -Aunused in compiletest-rs
    if have_fullmir() {
        flags.push("-Zmiri-start-fn".to_owned());
    }
    if opt {
        flags.push("-Zmir-opt-level=3".to_owned());
    } else {
        flags.push("-Zmir-opt-level=0".to_owned());
        // For now, only validate without optimizations.  Inlining breaks validation.
        flags.push("-Zmir-emit-validate=1".to_owned());
    }
    config.target_rustcflags = Some(flags.join(" "));
    compiletest::run_tests(&config);
}

fn is_target_dir<P: Into<PathBuf>>(path: P) -> bool {
    let mut path = path.into();
    path.push("lib");
    path.metadata().map(|m| m.is_dir()).unwrap_or(false)
}

fn for_all_targets<F: FnMut(String)>(sysroot: &Path, mut f: F) {
    let target_dir = sysroot.join("lib").join("rustlib");
    for entry in std::fs::read_dir(target_dir).expect("invalid sysroot") {
        let entry = entry.unwrap();
        if !is_target_dir(entry.path()) {
            continue;
        }
        let target = entry.file_name().into_string().unwrap();
        f(target);
    }
}

fn get_sysroot() -> PathBuf {
    let sysroot = std::env::var("MIRI_SYSROOT").unwrap_or_else(|_| {
        let sysroot = std::process::Command::new("rustc")
            .arg("--print")
            .arg("sysroot")
            .output()
            .expect("rustc not found")
            .stdout;
        String::from_utf8(sysroot).expect("sysroot is not utf8")
    });
    PathBuf::from(sysroot.trim())
}

fn get_host() -> String {
    let rustc = rustc_test_suite().unwrap_or(PathBuf::from("rustc"));
    println!("using rustc at {}", rustc.display());
    let host = std::process::Command::new(rustc)
        .arg("-vV")
        .output()
        .expect("rustc not found for -vV")
        .stdout;
    let host = std::str::from_utf8(&host).expect("sysroot is not utf8");
    let host = host.split("\nhost: ").nth(1).expect(
        "no host: part in rustc -vV",
    );
    let host = host.split('\n').next().expect("no \n after host");
    String::from(host)
}

fn run_pass_miri(opt: bool) {
    let sysroot = get_sysroot();
    let host = get_host();

    for_all_targets(&sysroot, |target| {
        miri_pass(&sysroot, "tests/run-pass", &target, &host, false, opt);
    });
    miri_pass(&sysroot, "tests/run-pass-fullmir", &host, &host, true, opt);
}

fn compile_fail_miri() {
    let sysroot = get_sysroot();
    let host = get_host();

    // FIXME: run tests for other targets, too
    compile_fail(&sysroot, "tests/compile-fail", &host, &host, false);
    compile_fail(&sysroot, "tests/compile-fail-fullmir", &host, &host, true);
}

#[test]
fn test() {
    // We put everything into a single test to avoid the parallelism `cargo test`
    // introduces.  We still get parallelism within our tests because `compiletest`
    // uses `libtest` which runs jobs in parallel.

    run_pass_miri(false);
    // FIXME: Disabled for now, as the optimizer is pretty broken and crashes...
    // See https://github.com/rust-lang/rust/issues/50411
    //run_pass_miri(true);

    compile_fail_miri();
}
<commit_msg>do not validate start-fn code<commit_after>#![feature(slice_concat_ext)]

extern crate compiletest_rs as compiletest;
extern crate colored;

use colored::*;

use std::slice::SliceConcatExt;
use std::path::{PathBuf, Path};
use std::io::Write;

macro_rules! eprintln {
    ($($arg:tt)*) => {
        let stderr = std::io::stderr();
        writeln!(stderr.lock(), $($arg)*).unwrap();
    }
}

fn miri_path() -> PathBuf {
    if rustc_test_suite().is_some() {
        PathBuf::from(option_env!("MIRI_PATH").unwrap())
    } else {
        PathBuf::from(concat!("target/", env!("PROFILE"), "/miri"))
    }
}

fn rustc_test_suite() -> Option<PathBuf> {
    option_env!("RUSTC_TEST_SUITE").map(PathBuf::from)
}

fn rustc_lib_path() -> PathBuf {
    option_env!("RUSTC_LIB_PATH").unwrap().into()
}

fn have_fullmir() -> bool {
    // We assume we have full MIR when MIRI_SYSROOT is set or when we are in rustc
    std::env::var("MIRI_SYSROOT").is_ok() || rustc_test_suite().is_some()
}

fn compile_fail(sysroot: &Path, path: &str, target: &str, host: &str, need_fullmir: bool) {
    if need_fullmir && !have_fullmir() {
        eprintln!("{}", format!(
            "## Skipping compile-fail tests in {} against miri for target {} due to missing mir",
            path,
            target
        ).yellow().bold());
        return;
    }

    eprintln!("{}", format!(
        "## Running compile-fail tests in {} against miri for target {}",
        path,
        target
    ).green().bold());
    let mut config = compiletest::Config::default().tempdir();
    config.mode = "compile-fail".parse().expect("Invalid mode");
    config.rustc_path = miri_path();
    let mut flags = Vec::new();
    if rustc_test_suite().is_some() {
        config.run_lib_path = rustc_lib_path();
        config.compile_lib_path = rustc_lib_path();
    }
    flags.push(format!("--sysroot {}", sysroot.display()));
    flags.push("-Dwarnings -Dunused".to_owned()); // overwrite the -Aunused in compiletest-rs
    config.src_base = PathBuf::from(path.to_string());
    flags.push("-Zmir-emit-validate=1".to_owned());
    flags.push("-Zmiri-disable-validation".to_owned());
    config.target_rustcflags = Some(flags.join(" "));
    config.target = target.to_owned();
    config.host = host.to_owned();
    compiletest::run_tests(&config);
}

fn miri_pass(sysroot: &Path, path: &str, target: &str, host: &str, need_fullmir: bool, opt: bool) {
    if need_fullmir && !have_fullmir() {
        eprintln!("{}", format!(
            "## Skipping run-pass tests in {} against miri for target {} due to missing mir",
            path,
            target
        ).yellow().bold());
        return;
    }

    let opt_str = if opt { " with optimizations" } else { "" };
    eprintln!("{}", format!(
        "## Running run-pass tests in {} against miri for target {}{}",
        path,
        target,
        opt_str
    ).green().bold());
    let mut config = compiletest::Config::default().tempdir();
    config.mode = "ui".parse().expect("Invalid mode");
    config.src_base = PathBuf::from(path);
    config.target = target.to_owned();
    config.host = host.to_owned();
    config.rustc_path = miri_path();
    if rustc_test_suite().is_some() {
        config.run_lib_path = rustc_lib_path();
        config.compile_lib_path = rustc_lib_path();
    }
    let mut flags = Vec::new();
    flags.push(format!("--sysroot {}", sysroot.display()));
    flags.push("-Dwarnings -Dunused".to_owned()); // overwrite the -Aunused in compiletest-rs
    if have_fullmir() {
        flags.push("-Zmiri-start-fn".to_owned());
        // start-fn uses ptr::read, and so fails validation
        flags.push("-Zmiri-disable-validation".to_owned());
    }
    if opt {
        flags.push("-Zmir-opt-level=3".to_owned());
    } else {
        flags.push("-Zmir-opt-level=0".to_owned());
        // For now, only validate without optimizations.  Inlining breaks validation.
        flags.push("-Zmir-emit-validate=1".to_owned());
    }
    config.target_rustcflags = Some(flags.join(" "));
    compiletest::run_tests(&config);
}

fn is_target_dir<P: Into<PathBuf>>(path: P) -> bool {
    let mut path = path.into();
    path.push("lib");
    path.metadata().map(|m| m.is_dir()).unwrap_or(false)
}

fn for_all_targets<F: FnMut(String)>(sysroot: &Path, mut f: F) {
    let target_dir = sysroot.join("lib").join("rustlib");
    for entry in std::fs::read_dir(target_dir).expect("invalid sysroot") {
        let entry = entry.unwrap();
        if !is_target_dir(entry.path()) {
            continue;
        }
        let target = entry.file_name().into_string().unwrap();
        f(target);
    }
}

fn get_sysroot() -> PathBuf {
    let sysroot = std::env::var("MIRI_SYSROOT").unwrap_or_else(|_| {
        let sysroot = std::process::Command::new("rustc")
            .arg("--print")
            .arg("sysroot")
            .output()
            .expect("rustc not found")
            .stdout;
        String::from_utf8(sysroot).expect("sysroot is not utf8")
    });
    PathBuf::from(sysroot.trim())
}

fn get_host() -> String {
    let rustc = rustc_test_suite().unwrap_or(PathBuf::from("rustc"));
    println!("using rustc at {}", rustc.display());
    let host = std::process::Command::new(rustc)
        .arg("-vV")
        .output()
        .expect("rustc not found for -vV")
        .stdout;
    let host = std::str::from_utf8(&host).expect("sysroot is not utf8");
    let host = host.split("\nhost: ").nth(1).expect(
        "no host: part in rustc -vV",
    );
    let host = host.split('\n').next().expect("no \n after host");
    String::from(host)
}

fn run_pass_miri(opt: bool) {
    let sysroot = get_sysroot();
    let host = get_host();

    for_all_targets(&sysroot, |target| {
        miri_pass(&sysroot, "tests/run-pass", &target, &host, false, opt);
    });
    miri_pass(&sysroot, "tests/run-pass-fullmir", &host, &host, true, opt);
}

fn compile_fail_miri() {
    let sysroot = get_sysroot();
    let host = get_host();

    // FIXME: run tests for other targets, too
    compile_fail(&sysroot, "tests/compile-fail", &host, &host, false);
    compile_fail(&sysroot, "tests/compile-fail-fullmir", &host, &host, true);
}

#[test]
fn test() {
    // We put everything into a single test to avoid the parallelism `cargo test`
    // introduces.  We still get parallelism within our tests because `compiletest`
    // uses `libtest` which runs jobs in parallel.

    run_pass_miri(false);
    // FIXME: Disabled for now, as the optimizer is pretty broken and crashes...
    // See https://github.com/rust-lang/rust/issues/50411
    //run_pass_miri(true);

    compile_fail_miri();
}
<|endoftext|>


<commit_before>#![feature(slice_concat_ext)]

extern crate compiletest_rs as compiletest;
extern crate colored;

use colored::*;

use std::slice::SliceConcatExt;
use std::path::{PathBuf, Path};
use std::io::Write;

macro_rules! eprintln {
    ($($arg:tt)*) => {
        let stderr = std::io::stderr();
        writeln!(stderr.lock(), $($arg)*).unwrap();
    }
}

fn miri_path() -> PathBuf {
    if rustc_test_suite().is_some() {
        PathBuf::from(option_env!("MIRI_PATH").unwrap())
    } else {
        PathBuf::from(concat!("target/", env!("PROFILE"), "/miri"))
    }
}

fn rustc_test_suite() -> Option<PathBuf> {
    option_env!("RUSTC_TEST_SUITE").map(PathBuf::from)
}

fn rustc_lib_path() -> PathBuf {
    option_env!("RUSTC_LIB_PATH").unwrap().into()
}

fn have_fullmir() -> bool {
    // We assume we have full MIR when MIRI_SYSROOT is set or when we are in rustc
    std::env::var("MIRI_SYSROOT").is_ok() || rustc_test_suite().is_some()
}

fn compile_fail(sysroot: &Path, path: &str, target: &str, host: &str, need_fullmir: bool) {
    if need_fullmir && !have_fullmir() {
        eprintln!("{}", format!(
            "## Skipping compile-fail tests in {} against miri for target {} due to missing mir",
            path,
            target
        ).yellow().bold());
        return;
    }

    eprintln!("{}", format!(
        "## Running compile-fail tests in {} against miri for target {}",
        path,
        target
    ).green().bold());
    let mut config = compiletest::Config::default().tempdir();
    config.mode = "compile-fail".parse().expect("Invalid mode");
    config.rustc_path = miri_path();
    let mut flags = Vec::new();
    if rustc_test_suite().is_some() {
        config.run_lib_path = rustc_lib_path();
        config.compile_lib_path = rustc_lib_path();
    }
    flags.push(format!("--sysroot {}", sysroot.display()));
    flags.push("-Dwarnings -Dunused".to_owned()); // overwrite the -Aunused in compiletest-rs
    config.src_base = PathBuf::from(path.to_string());
    flags.push("-Zmir-emit-validate=1".to_owned());
    config.target_rustcflags = Some(flags.join(" "));
    config.target = target.to_owned();
    config.host = host.to_owned();
    compiletest::run_tests(&config);
}

fn miri_pass(sysroot: &Path, path: &str, target: &str, host: &str, need_fullmir: bool, opt: bool) {
    if need_fullmir && !have_fullmir() {
        eprintln!("{}", format!(
            "## Skipping run-pass tests in {} against miri for target {} due to missing mir",
            path,
            target
        ).yellow().bold());
        return;
    }

    let opt_str = if opt { " with optimizations" } else { "" };
    eprintln!("{}", format!(
        "## Running run-pass tests in {} against miri for target {}{}",
        path,
        target,
        opt_str
    ).green().bold());
    let mut config = compiletest::Config::default().tempdir();
    config.mode = "ui".parse().expect("Invalid mode");
    config.src_base = PathBuf::from(path);
    config.target = target.to_owned();
    config.host = host.to_owned();
    config.rustc_path = miri_path();
    if rustc_test_suite().is_some() {
        config.run_lib_path = rustc_lib_path();
        config.compile_lib_path = rustc_lib_path();
    }
    let mut flags = Vec::new();
    flags.push(format!("--sysroot {}", sysroot.display()));
    flags.push("-Dwarnings -Dunused".to_owned()); // overwrite the -Aunused in compiletest-rs
    if have_fullmir() {
        flags.push("-Zmiri-start-fn".to_owned());
    }
    if opt {
        flags.push("-Zmir-opt-level=3".to_owned());
    } else {
        flags.push("-Zmir-opt-level=0".to_owned());
        // For now, only validate without optimizations.  Inlining breaks validation.
        flags.push("-Zmir-emit-validate=1".to_owned());
    }
    config.target_rustcflags = Some(flags.join(" "));
    compiletest::run_tests(&config);
}

fn is_target_dir<P: Into<PathBuf>>(path: P) -> bool {
    let mut path = path.into();
    path.push("lib");
    path.metadata().map(|m| m.is_dir()).unwrap_or(false)
}

fn for_all_targets<F: FnMut(String)>(sysroot: &Path, mut f: F) {
    let target_dir = sysroot.join("lib").join("rustlib");
    for entry in std::fs::read_dir(target_dir).expect("invalid sysroot") {
        let entry = entry.unwrap();
        if !is_target_dir(entry.path()) {
            continue;
        }
        let target = entry.file_name().into_string().unwrap();
        f(target);
    }
}

fn get_sysroot() -> PathBuf {
    let sysroot = std::env::var("MIRI_SYSROOT").unwrap_or_else(|_| {
        let sysroot = std::process::Command::new("rustc")
            .arg("--print")
            .arg("sysroot")
            .output()
            .expect("rustc not found")
            .stdout;
        String::from_utf8(sysroot).expect("sysroot is not utf8")
    });
    PathBuf::from(sysroot.trim())
}

fn get_host() -> String {
    let rustc = rustc_test_suite().unwrap_or(PathBuf::from("rustc"));
    println!("using rustc at {}", rustc.display());
    let host = std::process::Command::new(rustc)
        .arg("-vV")
        .output()
        .expect("rustc not found for -vV")
        .stdout;
    let host = std::str::from_utf8(&host).expect("sysroot is not utf8");
    let host = host.split("\nhost: ").nth(1).expect(
        "no host: part in rustc -vV",
    );
    let host = host.split('\n').next().expect("no \n after host");
    String::from(host)
}

fn run_pass_miri(opt: bool) {
    let sysroot = get_sysroot();
    let host = get_host();

    for_all_targets(&sysroot, |target| {
        miri_pass(&sysroot, "tests/run-pass", &target, &host, false, opt);
    });
    miri_pass(&sysroot, "tests/run-pass-fullmir", &host, &host, true, opt);
}

fn compile_fail_miri() {
    let sysroot = get_sysroot();
    let host = get_host();

    // FIXME: run tests for other targets, too
    compile_fail(&sysroot, "tests/compile-fail", &host, &host, false);
    compile_fail(&sysroot, "tests/compile-fail-fullmir", &host, &host, true);
}

#[test]
fn test() {
    // We put everything into a single test to avoid the parallelism `cargo test`
    // introduces.  We still get parallelism within our tests because `compiletest`
    // uses `libtest` which runs jobs in parallel.

    run_pass_miri(false);
    // FIXME: Disabled for now, as the optimizer is pretty broken and crashes...
    // See https://github.com/rust-lang/rust/issues/50411
    //run_pass_miri(true);

    compile_fail_miri();
}
<commit_msg>test with opt levels 0 and 1<commit_after>#![feature(slice_concat_ext)]

extern crate compiletest_rs as compiletest;
extern crate colored;

use colored::*;

use std::slice::SliceConcatExt;
use std::path::{PathBuf, Path};
use std::io::Write;

macro_rules! eprintln {
    ($($arg:tt)*) => {
        let stderr = std::io::stderr();
        writeln!(stderr.lock(), $($arg)*).unwrap();
    }
}

fn miri_path() -> PathBuf {
    if rustc_test_suite().is_some() {
        PathBuf::from(option_env!("MIRI_PATH").unwrap())
    } else {
        PathBuf::from(concat!("target/", env!("PROFILE"), "/miri"))
    }
}

fn rustc_test_suite() -> Option<PathBuf> {
    option_env!("RUSTC_TEST_SUITE").map(PathBuf::from)
}

fn rustc_lib_path() -> PathBuf {
    option_env!("RUSTC_LIB_PATH").unwrap().into()
}

fn have_fullmir() -> bool {
    // We assume we have full MIR when MIRI_SYSROOT is set or when we are in rustc
    std::env::var("MIRI_SYSROOT").is_ok() || rustc_test_suite().is_some()
}

fn compile_fail(sysroot: &Path, path: &str, target: &str, host: &str, need_fullmir: bool) {
    if need_fullmir && !have_fullmir() {
        eprintln!("{}", format!(
            "## Skipping compile-fail tests in {} against miri for target {} due to missing mir",
            path,
            target
        ).yellow().bold());
        return;
    }

    eprintln!("{}", format!(
        "## Running compile-fail tests in {} against miri for target {}",
        path,
        target
    ).green().bold());
    let mut config = compiletest::Config::default().tempdir();
    config.mode = "compile-fail".parse().expect("Invalid mode");
    config.rustc_path = miri_path();
    let mut flags = Vec::new();
    if rustc_test_suite().is_some() {
        config.run_lib_path = rustc_lib_path();
        config.compile_lib_path = rustc_lib_path();
    }
    flags.push(format!("--sysroot {}", sysroot.display()));
    flags.push("-Dwarnings -Dunused".to_owned()); // overwrite the -Aunused in compiletest-rs
    config.src_base = PathBuf::from(path.to_string());
    flags.push("-Zmir-emit-validate=1".to_owned());
    config.target_rustcflags = Some(flags.join(" "));
    config.target = target.to_owned();
    config.host = host.to_owned();
    compiletest::run_tests(&config);
}

fn miri_pass(sysroot: &Path, path: &str, target: &str, host: &str, need_fullmir: bool, opt: bool) {
    if need_fullmir && !have_fullmir() {
        eprintln!("{}", format!(
            "## Skipping run-pass tests in {} against miri for target {} due to missing mir",
            path,
            target
        ).yellow().bold());
        return;
    }

    let opt_str = if opt { " with optimizations" } else { "" };
    eprintln!("{}", format!(
        "## Running run-pass tests in {} against miri for target {}{}",
        path,
        target,
        opt_str
    ).green().bold());
    let mut config = compiletest::Config::default().tempdir();
    config.mode = "ui".parse().expect("Invalid mode");
    config.src_base = PathBuf::from(path);
    config.target = target.to_owned();
    config.host = host.to_owned();
    config.rustc_path = miri_path();
    if rustc_test_suite().is_some() {
        config.run_lib_path = rustc_lib_path();
        config.compile_lib_path = rustc_lib_path();
    }
    let mut flags = Vec::new();
    flags.push(format!("--sysroot {}", sysroot.display()));
    flags.push("-Dwarnings -Dunused".to_owned()); // overwrite the -Aunused in compiletest-rs
    if have_fullmir() {
        flags.push("-Zmiri-start-fn".to_owned());
    }
    if opt {
        // FIXME: Using level 1 (instead of 3) for now, as the optimizer is pretty broken
        // and crashes...
        // Level 0 and 1 are not the same, so this still gives us *some* coverage.
        // See https://github.com/rust-lang/rust/issues/50411
        flags.push("-Zmir-opt-level=1".to_owned());
    } else {
        flags.push("-Zmir-opt-level=0".to_owned());
        // For now, only validate without optimizations.  Inlining breaks validation.
        flags.push("-Zmir-emit-validate=1".to_owned());
    }
    config.target_rustcflags = Some(flags.join(" "));
    compiletest::run_tests(&config);
}

fn is_target_dir<P: Into<PathBuf>>(path: P) -> bool {
    let mut path = path.into();
    path.push("lib");
    path.metadata().map(|m| m.is_dir()).unwrap_or(false)
}

fn for_all_targets<F: FnMut(String)>(sysroot: &Path, mut f: F) {
    let target_dir = sysroot.join("lib").join("rustlib");
    for entry in std::fs::read_dir(target_dir).expect("invalid sysroot") {
        let entry = entry.unwrap();
        if !is_target_dir(entry.path()) {
            continue;
        }
        let target = entry.file_name().into_string().unwrap();
        f(target);
    }
}

fn get_sysroot() -> PathBuf {
    let sysroot = std::env::var("MIRI_SYSROOT").unwrap_or_else(|_| {
        let sysroot = std::process::Command::new("rustc")
            .arg("--print")
            .arg("sysroot")
            .output()
            .expect("rustc not found")
            .stdout;
        String::from_utf8(sysroot).expect("sysroot is not utf8")
    });
    PathBuf::from(sysroot.trim())
}

fn get_host() -> String {
    let rustc = rustc_test_suite().unwrap_or(PathBuf::from("rustc"));
    println!("using rustc at {}", rustc.display());
    let host = std::process::Command::new(rustc)
        .arg("-vV")
        .output()
        .expect("rustc not found for -vV")
        .stdout;
    let host = std::str::from_utf8(&host).expect("sysroot is not utf8");
    let host = host.split("\nhost: ").nth(1).expect(
        "no host: part in rustc -vV",
    );
    let host = host.split('\n').next().expect("no \n after host");
    String::from(host)
}

fn run_pass_miri(opt: bool) {
    let sysroot = get_sysroot();
    let host = get_host();

    for_all_targets(&sysroot, |target| {
        miri_pass(&sysroot, "tests/run-pass", &target, &host, false, opt);
    });
    miri_pass(&sysroot, "tests/run-pass-fullmir", &host, &host, true, opt);
}

fn compile_fail_miri() {
    let sysroot = get_sysroot();
    let host = get_host();

    // FIXME: run tests for other targets, too
    compile_fail(&sysroot, "tests/compile-fail", &host, &host, false);
    compile_fail(&sysroot, "tests/compile-fail-fullmir", &host, &host, true);
}

#[test]
fn test() {
    // We put everything into a single test to avoid the parallelism `cargo test`
    // introduces.  We still get parallelism within our tests because `compiletest`
    // uses `libtest` which runs jobs in parallel.

    run_pass_miri(false);
    run_pass_miri(true);

    compile_fail_miri();
}
<|endoftext|>