<commit_before>use std::cmp;

use vec::Vec2;
use view::{View,DimensionRequest,SizeRequest};
use div::*;
use printer::Printer;

/// A simple view showing a fixed text
pub struct TextView {
    content: String,
}

fn strip_last_newline(content: &str) -> &str {
    if !content.is_empty() && content.chars().last().unwrap() == '\n' {
        &content[..content.len() - 1]
    } else {
        content
    }
}

/// Returns the number of lines required to display the given text with the
/// specified maximum line width.
fn get_line_span(line: &str, max_width: usize) -> usize {
    let mut lines = 1;
    let mut length = 0;
    for l in line.split(" ").map(|word| word.len()) {
        length += l;
        if length >= max_width {
            length = l;
            lines += 1;
        }
        length += 1;
    }
    lines
}

impl TextView {
    /// Creates a new TextView with the given content.
    pub fn new(content: &str) -> Self {
        let content = strip_last_newline(content);
        TextView {
            content: content.to_string(),
        }
    }

    /// Replace the text in this view.
    pub fn set_content(&mut self, content: &str) {
        let content = strip_last_newline(content);
        self.content = content.to_string();
    }

    /// Returns the current text in this view.
    pub fn get_content(&self) -> &str {
        &self.content
    }

    /// Returns the number of lines required to display the content
    /// with the given width.
    fn get_num_lines(&self, max_width: usize) -> usize {
        self.content.split("\n")
            .map(|line| get_line_span(line, max_width))
            .fold(0, |sum, x| sum + x)
    }

    fn get_num_cols(&self, max_height: usize) -> usize {
        (div_up_usize(self.content.len(), max_height)..self.content.len())
            .find(|w| self.get_num_lines(*w) <= max_height)
            .unwrap()
    }

    fn get_ideal_size(&self) -> Vec2 {
        let mut max_width = 0;
        let mut height = 0;

        for line in self.content.split("\n") {
            height += 1;
            max_width = cmp::max(max_width, line.len() as u32);
        }

        Vec2::new(max_width, height)
    }
}

struct LinesIterator<'a> {
    line: &'a str,
    start: usize,
    width: usize,
}

impl <'a> LinesIterator<'a> {
    fn new(line: &'a str, width: usize) -> Self {
        if line.len() == 0 {
            LinesIterator {
                line: " ",
                width: width,
                start: 0,
            }
        } else {
            LinesIterator {
                line: line,
                width: width,
                start: 0,
            }
        }
    }
}

impl <'a> Iterator for LinesIterator<'a> {

    type Item = &'a str;

    fn next(&mut self) -> Option<&'a str> {
        if self.start >= self.line.len() {
            None
        } else if self.start + self.width >= self.line.len() {
            let start = self.start;
            self.start = self.line.len();
            Some(&self.line[start..])
        } else {
            let start = self.start;
            let (end,skip_space) = match self.line[start..start+self.width].rfind(" ") {
                // Hard break
                None => (start + self.width, false),
                Some(i) => (start+i, true),
            };
            self.start = end;
            if skip_space {
                self.start += 1;
            }
            Some(&self.line[start..end])
        }
    }
}

impl View for TextView {
    fn draw(&mut self, printer: &Printer, _: bool) {
        // We don't have a focused view

        let lines = self.content.split("\n")
            .flat_map(|line| LinesIterator::new(line, printer.size.x as usize));
        for (i, line) in lines.enumerate() {
            printer.print((0,i), line);
        }

    }

    fn get_min_size(&self, size: SizeRequest) -> Vec2 {
        match (size.w,size.h) {
            (DimensionRequest::Unknown, DimensionRequest::Unknown) => Vec2::new(self.content.len() as u32, 1),
            (DimensionRequest::Fixed(w),_) => {
                let h = self.get_num_lines(w as usize) as u32;
                Vec2::new(w, h)
            },
            (_,DimensionRequest::Fixed(h)) => {
                let w = self.get_num_cols(h as usize) as u32;
                Vec2::new(w, h)
            },
            (DimensionRequest::AtMost(w),_) => {
                let ideal = self.get_ideal_size();

                if w >= ideal.x {
                    ideal
                } else {
                    let h = self.get_num_lines(w as usize) as u32;
                    Vec2::new(w, h)
                }
            },
            _ => unreachable!(),
        }
    }
}
<commit_msg>New TextView layout computation<commit_after>use std::cmp;

use vec::Vec2;
use view::{View,DimensionRequest,SizeRequest};
use div::*;
use printer::Printer;

/// A simple view showing a fixed text
pub struct TextView {
    content: String,
    rows: Vec<Row>,
    start_line: usize,
}

struct Row {
    start: usize,
    end: usize,
}

fn strip_last_newline(content: &str) -> &str {
    if !content.is_empty() && content.chars().last().unwrap() == '\n' {
        &content[..content.len() - 1]
    } else {
        content
    }
}

/// Returns the number of lines required to display the given text with the
/// specified maximum line width.
fn get_line_span(line: &str, max_width: usize) -> usize {
    let mut lines = 1;
    let mut length = 0;
    for l in line.split(" ").map(|word| word.len()) {
        length += l;
        if length >= max_width {
            length = l;
            lines += 1;
        }
        length += 1;
    }
    lines
}

impl TextView {
    /// Creates a new TextView with the given content.
    pub fn new(content: &str) -> Self {
        let content = strip_last_newline(content);
        TextView {
            content: content.to_string(),
            start_line: 0,
            rows: Vec::new(),
        }
    }

    /// Replace the text in this view.
    pub fn set_content(&mut self, content: &str) {
        let content = strip_last_newline(content);
        self.content = content.to_string();
    }

    /// Returns the current text in this view.
    pub fn get_content(&self) -> &str {
        &self.content
    }

    /// Returns the number of lines required to display the content
    /// with the given width.
    fn get_num_lines(&self, max_width: usize) -> usize {
        self.content.split("\n")
            .map(|line| get_line_span(line, max_width))
            .fold(0, |sum, x| sum + x)
    }

    fn get_num_cols(&self, max_height: usize) -> usize {
        (div_up_usize(self.content.len(), max_height)..self.content.len())
            .find(|w| self.get_num_lines(*w) <= max_height)
            .unwrap()
    }

    fn get_ideal_size(&self) -> Vec2 {
        let mut max_width = 0;
        let mut height = 0;

        for line in self.content.split("\n") {
            height += 1;
            max_width = cmp::max(max_width, line.len() as u32);
        }

        Vec2::new(max_width, height)
    }
}

struct LinesIterator<'a> {
    content: &'a str,
    start: usize,
    width: usize,
}

impl <'a> LinesIterator<'a> {
    fn new(content: &'a str, width: usize) -> Self {
        LinesIterator {
            content: content,
            width: width,
            start: 0,
        }
    }
}

impl <'a> Iterator for LinesIterator<'a> {

    type Item = Row;

    fn next(&mut self) -> Option<Row> {
        if self.start >= self.content.len() {
            // This is the end
            return None;
        }

        let start = self.start;
        let content = &self.content[self.start..];

        if let Some(next) = content.find("\n") {
            if next < self.width {
                self.start += next+1;
                return Some(Row {
                    start: start,
                    end: next + start,
                });
            }
        }

        if content.len() <= self.width {
            self.start += content.len();
            return Some(Row{
                start: start,
                end: start + content.len(),
            });
        }

        if let Some(i) = content[..self.width].rfind(" ") {
            self.start += i+1;
            return Some(Row {
                start: start,
                end: i + start,
            });
        }

        self.start += self.width;
        return Some(Row {
            start: start,
            end: start + self.width,
        });
    }
}

impl View for TextView {
    fn draw(&mut self, printer: &Printer, _: bool) {
        // We don't have a focused view
        for (i,line) in self.rows.iter().skip(self.start_line).map(|row| &self.content[row.start..row.end]).enumerate() {
            printer.print((0,i), line);
        }
    }

    fn get_min_size(&self, size: SizeRequest) -> Vec2 {
        match (size.w,size.h) {
            // If we have no directive, ask for a single big line.
            // TODO: what if the text has newlines??
            (DimensionRequest::Unknown, DimensionRequest::Unknown) => Vec2::new(self.content.len() as u32, 1),
            (DimensionRequest::Fixed(w),_) => {
                let h = self.get_num_lines(w as usize) as u32;
                Vec2::new(w, h)
            },
            (_,DimensionRequest::Fixed(h)) => {
                let w = self.get_num_cols(h as usize) as u32;
                Vec2::new(w, h)
            },
            (DimensionRequest::AtMost(w),_) => {
                // Don't _force_ the max width, but take it if we have to.
                let ideal = self.get_ideal_size();

                if w >= ideal.x {
                    ideal
                } else {
                    let h = self.get_num_lines(w as usize) as u32;
                    Vec2::new(w, h)
                }
            },
            _ => unreachable!(),
        }
    }

    fn layout(&mut self, size: Vec2) {
        // Compute the text rows.

        self.rows = LinesIterator::new(&self.content, size.x as usize).collect();
    }
}
<|endoftext|>


<commit_before>//
// Sysinfo
//
// Copyright (c) 2018 Guillaume Gomez
//

use sys::component::{self, Component};
use sys::disk::Disk;
use sys::processor::*;

use std::collections::HashMap;
use std::mem::{size_of, zeroed};

use DiskExt;
use Pid;
use ProcessExt;
use SystemExt;

use windows::network::{self, NetworkData};
use windows::processor::CounterValue;
use windows::tools::*;
use windows::process::{
    Process, compute_cpu_usage, get_handle, get_parent_process_id, update_proc_info,
};

use winapi::shared::minwindef::{DWORD, FALSE};
use winapi::shared::winerror::ERROR_SUCCESS;
use winapi::um::minwinbase::STILL_ACTIVE;
use winapi::um::pdh::PdhEnumObjectItemsW;
use winapi::um::processthreadsapi::GetExitCodeProcess;
use winapi::um::psapi::K32EnumProcesses;
use winapi::um::sysinfoapi::{
    GlobalMemoryStatusEx, MEMORYSTATUSEX,
};
use winapi::um::winnt::HANDLE;

/// Struct containing system's information.
pub struct System {
    process_list: HashMap<usize, Process>,
    mem_total: u64,
    mem_free: u64,
    swap_total: u64,
    swap_free: u64,
    processors: Vec<Processor>,
    temperatures: Vec<Component>,
    disks: Vec<Disk>,
    query: Option<Query>,
    network: NetworkData,
    uptime: u64,
}

impl System {
    fn clear_procs(&mut self) {
        if self.processors.len() > 0 {
            let mut to_delete = Vec::new();

            for (pid, proc_) in self.process_list.iter_mut() {
                if !is_proc_running(get_handle(proc_)) {
                    to_delete.push(*pid);
                } else {
                    compute_cpu_usage(proc_, self.processors.len() as u64 - 1);
                }
            }
            for pid in to_delete {
                self.process_list.remove(&pid);
            }
        }
    }
}

impl SystemExt for System {
    #[allow(non_snake_case)]
    fn new() -> System {
        let mut s = System {
            process_list: HashMap::new(),
            mem_total: 0,
            mem_free: 0,
            swap_total: 0,
            swap_free: 0,
            processors: init_processors(),
            temperatures: component::get_components(),
            disks: unsafe { get_disks() },
            query: Query::new(),
            network: network::new(),
            uptime: get_uptime(),
        };
        if let Some(ref mut query) = s.query {
            let x = unsafe { load_symbols() };
            let processor_trans = get_translation(&"Processor".to_owned(), &x).expect("translation failed");
            let idle_time_trans = get_translation(&"% Idle Time".to_owned(), &x).expect("translation failed");
            let proc_time_trans = get_translation(&"% Processor Time".to_owned(), &x).expect("translation failed");
            add_counter(format!("\\{}(_Total)\\{}", processor_trans, proc_time_trans),
                        query,
                        get_key_used(&mut s.processors[0]),
                        "tot_0".to_owned(),
                        CounterValue::Float(0.));
            add_counter(format!("\\{}(_Total)\\{}", processor_trans, idle_time_trans),
                        query,
                        get_key_idle(&mut s.processors[0]),
                        "tot_1".to_owned(),
                        CounterValue::Float(0.));
            for (pos, proc_) in s.processors.iter_mut().skip(1).enumerate() {
                add_counter(format!("\\{}({})\\{}", processor_trans, pos, proc_time_trans),
                            query,
                            get_key_used(proc_),
                            format!("{}_0", pos),
                            CounterValue::Float(0.));
                add_counter(format!("\\{}({})\\{}", processor_trans, pos, idle_time_trans),
                            query,
                            get_key_idle(proc_),
                            format!("{}_1", pos),
                            CounterValue::Float(0.));
            }

            let network_trans = get_translation(&"Network Interface".to_owned(), &x).expect("translation failed");
            let network_in_trans = get_translation(&"Bytes Received/Sec".to_owned(), &x).expect("translation failed");
            let network_out_trans = get_translation(&"Bytes Sent/sec".to_owned(), &x).expect("translation failed");

            const PERF_DETAIL_WIZARD: DWORD = 400;
            const PDH_MORE_DATA: DWORD = 0x800007D2;

            let mut network_trans_utf16: Vec<u16> = network_trans.encode_utf16().collect();
            network_trans_utf16.push(0);
            let mut dwCounterListSize: DWORD = 0;
            let mut dwInstanceListSize: DWORD = 0;
            let status = unsafe {
                PdhEnumObjectItemsW(::std::ptr::null(),
                                    ::std::ptr::null(),
                                    network_trans_utf16.as_ptr(),
                                    ::std::ptr::null_mut(),
                                    &mut dwCounterListSize,
                                    ::std::ptr::null_mut(),
                                    &mut dwInstanceListSize,
                                    PERF_DETAIL_WIZARD,
                                    0)
            };
            if status != PDH_MORE_DATA as i32 {
                panic!("got invalid status: {:x}", status);
            }
            let mut pwsCounterListBuffer: Vec<u16> = Vec::with_capacity(dwCounterListSize as usize);
            let mut pwsInstanceListBuffer: Vec<u16> = Vec::with_capacity(dwInstanceListSize as usize);
            unsafe {
                pwsCounterListBuffer.set_len(dwCounterListSize as usize);
                pwsInstanceListBuffer.set_len(dwInstanceListSize as usize);
            }
            let status = unsafe {
                PdhEnumObjectItemsW(::std::ptr::null(),
                                    ::std::ptr::null(),
                                    network_trans_utf16.as_ptr(),
                                    pwsCounterListBuffer.as_mut_ptr(),
                                    &mut dwCounterListSize,
                                    pwsInstanceListBuffer.as_mut_ptr(),
                                    &mut dwInstanceListSize,
                                    PERF_DETAIL_WIZARD,
                                    0)
            };
            if status != ERROR_SUCCESS as i32 {
                panic!("got invalid status: {:x}", status);
            }

            for (pos, x) in pwsInstanceListBuffer.split(|x| *x == 0)
                                                 .filter(|x| x.len() > 0)
                                                 .enumerate() {
                let net_interface = String::from_utf16(x).expect("invalid utf16");
                let mut key_in = None;
                add_counter(format!("\\{}({})\\{}",
                                    network_trans, net_interface, network_in_trans),
                            query,
                            &mut key_in,
                            format!("net{}_in", pos),
                            CounterValue::Integer(0));
                if key_in.is_some() {
                    network::get_keys_in(&mut s.network).push(key_in.unwrap());
                }
                let mut key_out = None;
                add_counter(format!("\\{}({})\\{}",
                                    network_trans, net_interface, network_out_trans),
                            query,
                            &mut key_out,
                            format!("net{}_out", pos),
                            CounterValue::Integer(0));
                if key_out.is_some() {
                    network::get_keys_out(&mut s.network).push(key_out.unwrap());
                }
            }
            query.start();
        }
        s.refresh_all();
        s
    }

    fn refresh_system(&mut self) {
        unsafe {
            let mut mem_info: MEMORYSTATUSEX = zeroed();
            mem_info.dwLength = size_of::<MEMORYSTATUSEX>() as u32;
            GlobalMemoryStatusEx(&mut mem_info);
            self.mem_total = auto_cast!(mem_info.ullTotalPhys, u64);
            self.mem_free = auto_cast!(mem_info.ullAvailPhys, u64);
            //self.swap_total = auto_cast!(mem_info.ullTotalPageFile - mem_info.ullTotalPhys, u64);
            //self.swap_free = auto_cast!(mem_info.ullAvailPageFile, u64);
        }
        self.uptime = get_uptime();
        if let Some(ref mut query) = self.query {
            for p in self.processors.iter_mut() {
                let mut idle_time = None;
                if let &mut Some(ref key_idle) = get_key_idle(p) {
                    idle_time = Some(query.get(&key_idle.unique_id).expect("key disappeared"));
                }
                if let Some(idle_time) = idle_time {
                    set_cpu_usage(p, 1. - idle_time);
                }
            }
        }
    }

    fn refresh_network(&mut self) {
        network::refresh(&mut self.network, &self.query);
    }

    fn refresh_process(&mut self, pid: Pid) -> bool {
        if refresh_existing_process(self, pid, true) == false {
            self.process_list.remove(&pid);
            false
        } else {
            true
        }
    }

    fn refresh_processes(&mut self) {
        let mut process_ids: [DWORD; 1024] = [0; 1024];
        let mut cb_needed = 0;

        unsafe {
            let size = ::std::mem::size_of::<DWORD>() * process_ids.len();
            if K32EnumProcesses(process_ids.as_mut_ptr(),
                                size as DWORD,
                                &mut cb_needed) == 0 {
                return
            }
            let nb_processes = cb_needed / ::std::mem::size_of::<DWORD>() as DWORD;

            for i in 0..nb_processes as usize {
                let pid = process_ids[i] as Pid;
                if refresh_existing_process(self, pid, false) == true {
                    continue
                }
                let mut p = Process::new(pid, get_parent_process_id(pid), 0);
                update_proc_info(&mut p);
                self.process_list.insert(pid, p);
            }
        }
        self.clear_procs();
    }

    fn refresh_disks(&mut self) {
        for disk in &mut self.disks {
            disk.update();
        }
    }

    fn refresh_disk_list(&mut self) {
        self.disks = unsafe { get_disks() };
    }

    fn get_process_list(&self) -> &HashMap<Pid, Process> {
        &self.process_list
    }

    fn get_process(&self, pid: Pid) -> Option<&Process> {
        self.process_list.get(&(pid as usize))
    }

    fn get_processor_list(&self) -> &[Processor] {
        &self.processors[..]
    }

    fn get_total_memory(&self) -> u64 {
        self.mem_total >> 10
    }

    fn get_free_memory(&self) -> u64 {
        self.mem_free >> 10
    }

    fn get_used_memory(&self) -> u64 {
        (self.mem_total - self.mem_free) >> 10
    }

    fn get_total_swap(&self) -> u64 {
        self.swap_total >> 10
    }

    fn get_free_swap(&self) -> u64 {
        self.swap_free >> 10
    }

    fn get_used_swap(&self) -> u64 {
        (self.swap_total - self.swap_free) >> 10
    }

    fn get_components_list(&self) -> &[Component] {
        &self.temperatures[..]
    }

    fn get_disks(&self) -> &[Disk] {
        &self.disks[..]
    }

    fn get_network(&self) -> &NetworkData {
        &self.network
    }

    fn get_uptime(&self) -> u64 {
        self.uptime
    }
}

fn is_proc_running(handle: HANDLE) -> bool {
    let mut exit_code = 0;
    let ret = unsafe { GetExitCodeProcess(handle, &mut exit_code) };
    !(ret == FALSE || exit_code != STILL_ACTIVE)
}

fn refresh_existing_process(s: &mut System, pid: Pid, compute_cpu: bool) -> bool {
    if let Some(ref mut entry) = s.process_list.get_mut(&(pid as usize)) {
        if !is_proc_running(get_handle(entry)) {
            return false;
        }
        update_proc_info(entry);
        if compute_cpu {
            compute_cpu_usage(entry, s.processors.len() as u64 - 1);
        }
        true
    } else {
        false
    }
}
<commit_msg>Fix windows translation failures<commit_after>//
// Sysinfo
//
// Copyright (c) 2018 Guillaume Gomez
//

use sys::component::{self, Component};
use sys::disk::Disk;
use sys::processor::*;

use std::collections::HashMap;
use std::mem::{size_of, zeroed};

use DiskExt;
use Pid;
use ProcessExt;
use SystemExt;

use windows::network::{self, NetworkData};
use windows::processor::CounterValue;
use windows::tools::*;
use windows::process::{
    Process, compute_cpu_usage, get_handle, get_parent_process_id, update_proc_info,
};

use winapi::shared::minwindef::{DWORD, FALSE};
use winapi::shared::winerror::ERROR_SUCCESS;
use winapi::um::minwinbase::STILL_ACTIVE;
use winapi::um::pdh::PdhEnumObjectItemsW;
use winapi::um::processthreadsapi::GetExitCodeProcess;
use winapi::um::psapi::K32EnumProcesses;
use winapi::um::sysinfoapi::{
    GlobalMemoryStatusEx, MEMORYSTATUSEX,
};
use winapi::um::winnt::HANDLE;

/// Struct containing system's information.
pub struct System {
    process_list: HashMap<usize, Process>,
    mem_total: u64,
    mem_free: u64,
    swap_total: u64,
    swap_free: u64,
    processors: Vec<Processor>,
    temperatures: Vec<Component>,
    disks: Vec<Disk>,
    query: Option<Query>,
    network: NetworkData,
    uptime: u64,
}

impl System {
    fn clear_procs(&mut self) {
        if self.processors.len() > 0 {
            let mut to_delete = Vec::new();

            for (pid, proc_) in self.process_list.iter_mut() {
                if !is_proc_running(get_handle(proc_)) {
                    to_delete.push(*pid);
                } else {
                    compute_cpu_usage(proc_, self.processors.len() as u64 - 1);
                }
            }
            for pid in to_delete {
                self.process_list.remove(&pid);
            }
        }
    }
}

impl SystemExt for System {
    #[allow(non_snake_case)]
    fn new() -> System {
        let mut s = System {
            process_list: HashMap::new(),
            mem_total: 0,
            mem_free: 0,
            swap_total: 0,
            swap_free: 0,
            processors: init_processors(),
            temperatures: component::get_components(),
            disks: unsafe { get_disks() },
            query: Query::new(),
            network: network::new(),
            uptime: get_uptime(),
        };
        // TODO: in case a translation fails, it might be nice to log it somewhere...
        if let Some(ref mut query) = s.query {
            let x = unsafe { load_symbols() };
            if let Some(processor_trans) = get_translation(&"Processor".to_owned(), &x) {
                let idle_time_trans = get_translation(&"% Idle Time".to_owned(), &x);
                let proc_time_trans = get_translation(&"% Processor Time".to_owned(), &x);
                if let Some(ref proc_time_trans) = proc_time_trans {
                    add_counter(format!("\\{}(_Total)\\{}", processor_trans, proc_time_trans),
                                query,
                                get_key_used(&mut s.processors[0]),
                                "tot_0".to_owned(),
                                CounterValue::Float(0.));
                }
                if let Some(ref idle_time_trans) = idle_time_trans {
                    add_counter(format!("\\{}(_Total)\\{}", processor_trans, idle_time_trans),
                                query,
                                get_key_idle(&mut s.processors[0]),
                                "tot_1".to_owned(),
                                CounterValue::Float(0.));
                }
                for (pos, proc_) in s.processors.iter_mut().skip(1).enumerate() {
                    if let Some(ref proc_time_trans) = proc_time_trans {
                        add_counter(format!("\\{}({})\\{}", processor_trans, pos, proc_time_trans),
                                    query,
                                    get_key_used(proc_),
                                    format!("{}_0", pos),
                                    CounterValue::Float(0.));
                    }
                    if let Some(ref idle_time_trans) = idle_time_trans {
                        add_counter(format!("\\{}({})\\{}", processor_trans, pos, idle_time_trans),
                                    query,
                                    get_key_idle(proc_),
                                    format!("{}_1", pos),
                                    CounterValue::Float(0.));
                    }
                }
            }

            if let Some(network_trans) = get_translation(&"Network Interface".to_owned(), &x) {
                let network_in_trans = get_translation(&"Bytes Received/Sec".to_owned(), &x);
                let network_out_trans = get_translation(&"Bytes Sent/sec".to_owned(), &x);

                const PERF_DETAIL_WIZARD: DWORD = 400;
                const PDH_MORE_DATA: DWORD = 0x800007D2;

                let mut network_trans_utf16: Vec<u16> = network_trans.encode_utf16().collect();
                network_trans_utf16.push(0);
                let mut dwCounterListSize: DWORD = 0;
                let mut dwInstanceListSize: DWORD = 0;
                let status = unsafe {
                    PdhEnumObjectItemsW(::std::ptr::null(),
                                        ::std::ptr::null(),
                                        network_trans_utf16.as_ptr(),
                                        ::std::ptr::null_mut(),
                                        &mut dwCounterListSize,
                                        ::std::ptr::null_mut(),
                                        &mut dwInstanceListSize,
                                        PERF_DETAIL_WIZARD,
                                        0)
                };
                if status != PDH_MORE_DATA as i32 {
                    panic!("got invalid status: {:x}", status);
                }
                let mut pwsCounterListBuffer: Vec<u16> = Vec::with_capacity(dwCounterListSize as usize);
                let mut pwsInstanceListBuffer: Vec<u16> = Vec::with_capacity(dwInstanceListSize as usize);
                unsafe {
                    pwsCounterListBuffer.set_len(dwCounterListSize as usize);
                    pwsInstanceListBuffer.set_len(dwInstanceListSize as usize);
                }
                let status = unsafe {
                    PdhEnumObjectItemsW(::std::ptr::null(),
                                        ::std::ptr::null(),
                                        network_trans_utf16.as_ptr(),
                                        pwsCounterListBuffer.as_mut_ptr(),
                                        &mut dwCounterListSize,
                                        pwsInstanceListBuffer.as_mut_ptr(),
                                        &mut dwInstanceListSize,
                                        PERF_DETAIL_WIZARD,
                                        0)
                };
                if status != ERROR_SUCCESS as i32 {
                    panic!("got invalid status: {:x}", status);
                }

                for (pos, x) in pwsInstanceListBuffer.split(|x| *x == 0)
                                                     .filter(|x| x.len() > 0)
                                                     .enumerate() {
                    let net_interface = String::from_utf16(x).expect("invalid utf16");
                    if let Some(ref network_in_trans) = network_in_trans {
                        let mut key_in = None;
                        add_counter(format!("\\{}({})\\{}",
                                            network_trans, net_interface, network_in_trans),
                                    query,
                                    &mut key_in,
                                    format!("net{}_in", pos),
                                    CounterValue::Integer(0));
                        if key_in.is_some() {
                            network::get_keys_in(&mut s.network).push(key_in.unwrap());
                        }
                    }
                    if let Some(ref network_out_trans) = network_out_trans {
                        let mut key_out = None;
                        add_counter(format!("\\{}({})\\{}",
                                            network_trans, net_interface, network_out_trans),
                                    query,
                                    &mut key_out,
                                    format!("net{}_out", pos),
                                    CounterValue::Integer(0));
                        if key_out.is_some() {
                            network::get_keys_out(&mut s.network).push(key_out.unwrap());
                        }
                    }
                }
            }
            query.start();
        }
        s.refresh_all();
        s
    }

    fn refresh_system(&mut self) {
        unsafe {
            let mut mem_info: MEMORYSTATUSEX = zeroed();
            mem_info.dwLength = size_of::<MEMORYSTATUSEX>() as u32;
            GlobalMemoryStatusEx(&mut mem_info);
            self.mem_total = auto_cast!(mem_info.ullTotalPhys, u64);
            self.mem_free = auto_cast!(mem_info.ullAvailPhys, u64);
            //self.swap_total = auto_cast!(mem_info.ullTotalPageFile - mem_info.ullTotalPhys, u64);
            //self.swap_free = auto_cast!(mem_info.ullAvailPageFile, u64);
        }
        self.uptime = get_uptime();
        if let Some(ref mut query) = self.query {
            for p in self.processors.iter_mut() {
                let mut idle_time = None;
                if let &mut Some(ref key_idle) = get_key_idle(p) {
                    idle_time = Some(query.get(&key_idle.unique_id).expect("key disappeared"));
                }
                if let Some(idle_time) = idle_time {
                    set_cpu_usage(p, 1. - idle_time);
                }
            }
        }
    }

    fn refresh_network(&mut self) {
        network::refresh(&mut self.network, &self.query);
    }

    fn refresh_process(&mut self, pid: Pid) -> bool {
        if refresh_existing_process(self, pid, true) == false {
            self.process_list.remove(&pid);
            false
        } else {
            true
        }
    }

    fn refresh_processes(&mut self) {
        let mut process_ids: [DWORD; 1024] = [0; 1024];
        let mut cb_needed = 0;

        unsafe {
            let size = ::std::mem::size_of::<DWORD>() * process_ids.len();
            if K32EnumProcesses(process_ids.as_mut_ptr(),
                                size as DWORD,
                                &mut cb_needed) == 0 {
                return
            }
            let nb_processes = cb_needed / ::std::mem::size_of::<DWORD>() as DWORD;

            for i in 0..nb_processes as usize {
                let pid = process_ids[i] as Pid;
                if refresh_existing_process(self, pid, false) == true {
                    continue
                }
                let mut p = Process::new(pid, get_parent_process_id(pid), 0);
                update_proc_info(&mut p);
                self.process_list.insert(pid, p);
            }
        }
        self.clear_procs();
    }

    fn refresh_disks(&mut self) {
        for disk in &mut self.disks {
            disk.update();
        }
    }

    fn refresh_disk_list(&mut self) {
        self.disks = unsafe { get_disks() };
    }

    fn get_process_list(&self) -> &HashMap<Pid, Process> {
        &self.process_list
    }

    fn get_process(&self, pid: Pid) -> Option<&Process> {
        self.process_list.get(&(pid as usize))
    }

    fn get_processor_list(&self) -> &[Processor] {
        &self.processors[..]
    }

    fn get_total_memory(&self) -> u64 {
        self.mem_total >> 10
    }

    fn get_free_memory(&self) -> u64 {
        self.mem_free >> 10
    }

    fn get_used_memory(&self) -> u64 {
        (self.mem_total - self.mem_free) >> 10
    }

    fn get_total_swap(&self) -> u64 {
        self.swap_total >> 10
    }

    fn get_free_swap(&self) -> u64 {
        self.swap_free >> 10
    }

    fn get_used_swap(&self) -> u64 {
        (self.swap_total - self.swap_free) >> 10
    }

    fn get_components_list(&self) -> &[Component] {
        &self.temperatures[..]
    }

    fn get_disks(&self) -> &[Disk] {
        &self.disks[..]
    }

    fn get_network(&self) -> &NetworkData {
        &self.network
    }

    fn get_uptime(&self) -> u64 {
        self.uptime
    }
}

fn is_proc_running(handle: HANDLE) -> bool {
    let mut exit_code = 0;
    let ret = unsafe { GetExitCodeProcess(handle, &mut exit_code) };
    !(ret == FALSE || exit_code != STILL_ACTIVE)
}

fn refresh_existing_process(s: &mut System, pid: Pid, compute_cpu: bool) -> bool {
    if let Some(ref mut entry) = s.process_list.get_mut(&(pid as usize)) {
        if !is_proc_running(get_handle(entry)) {
            return false;
        }
        update_proc_info(entry);
        if compute_cpu {
            compute_cpu_usage(entry, s.processors.len() as u64 - 1);
        }
        true
    } else {
        false
    }
}
<|endoftext|>


<commit_before>// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

// @has intra_links/index.html
// @has - '//a/@href' '../intra_links/struct.ThisType.html'
// @has - '//a/@href' '../intra_links/enum.ThisEnum.html'
// @has - '//a/@href' '../intra_links/trait.ThisTrait.html'
// @has - '//a/@href' '../intra_links/type.ThisAlias.html'
// @has - '//a/@href' '../intra_links/union.ThisUnion.html'
// @has - '//a/@href' '../intra_links/fn.this_function.html'
// @has - '//a/@href' '../intra_links/constant.THIS_CONST.html'
// @has - '//a/@href' '../intra_links/static.THIS_STATIC.html'
//! In this crate we would like to link to:
//!
//! * [`ThisType`](struct ::ThisType)
//! * [`ThisEnum`](enum ::ThisEnum)
//! * [`ThisTrait`](trait ::ThisTrait)
//! * [`ThisAlias`](type ::ThisAlias)
//! * [`ThisUnion`](union ::ThisUnion)
//! * [`this_function`](::this_function())
//! * [`THIS_CONST`](const ::THIS_CONST)
//! * [`THIS_STATIC`](static ::THIS_STATIC)

pub struct ThisType;
pub enum ThisEnum { ThisVariant, }
pub trait ThisTrait {}
pub type ThisAlias = Result<(), ()>;
pub union ThisUnion { this_field: usize, }

pub fn this_function() {}
pub const THIS_CONST: usize = 5usize;
pub static THIS_STATIC: usize = 5usize;
<commit_msg>add a macro to the intra-links test<commit_after>// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

// @has intra_links/index.html
// @has - '//a/@href' '../intra_links/struct.ThisType.html'
// @has - '//a/@href' '../intra_links/enum.ThisEnum.html'
// @has - '//a/@href' '../intra_links/trait.ThisTrait.html'
// @has - '//a/@href' '../intra_links/type.ThisAlias.html'
// @has - '//a/@href' '../intra_links/union.ThisUnion.html'
// @has - '//a/@href' '../intra_links/fn.this_function.html'
// @has - '//a/@href' '../intra_links/constant.THIS_CONST.html'
// @has - '//a/@href' '../intra_links/static.THIS_STATIC.html'
// @has - '//a/@href' '../intra_links/macro.this_macro.html'
//! In this crate we would like to link to:
//!
//! * [`ThisType`](ThisType)
//! * [`ThisEnum`](ThisEnum)
//! * [`ThisTrait`](ThisTrait)
//! * [`ThisAlias`](ThisAlias)
//! * [`ThisUnion`](ThisUnion)
//! * [`this_function`](this_function)
//! * [`THIS_CONST`](THIS_CONST)
//! * [`THIS_STATIC`](THIS_STATIC)
//! * [`this_macro`](this_macro!)

#[macro_export]
macro_rules! this_macro {
    () => {};
}

pub struct ThisType;
pub enum ThisEnum { ThisVariant, }
pub trait ThisTrait {}
pub type ThisAlias = Result<(), ()>;
pub union ThisUnion { this_field: usize, }

pub fn this_function() {}
pub const THIS_CONST: usize = 5usize;
pub static THIS_STATIC: usize = 5usize;
<|endoftext|>


<commit_before>use hyper;
use rustc_serialize::{Encodable, Decodable};
use super::error::Error;
use super::serde;

pub struct Client {
    http_client: hyper::Client,
    server_uri: String,
}

impl Client {
    pub fn new(server_uri: &str) -> Client {
        Client {
            http_client: hyper::Client::new(),
            server_uri: server_uri.to_owned(),
        }
    }

    pub fn request<T: Decodable>(&self, request: Request) -> ClientResult<T> {
        let mut body = Vec::<u8>::new();
        request.encoder.write_request(&request.name, &mut body)?;

        let body = String::from_utf8(body)?;
        let res = self.http_client
            .post(&self.server_uri)
            .body(&body)
            .send()?;

        let mut res = serde::Decoder::new_response(res)?;

        Ok(T::decode(&mut res.pop()
            .ok_or(Error::Decoding(serde::value::DecodeError::UnsupportedDataFormat))?)?)
    }
}

pub struct Request {
    name: String,
    encoder: serde::Encoder,
}

impl Request {
    pub fn new(function_name: &str) -> Request {
        Request {
            name: String::from(function_name),
            encoder: serde::Encoder::new(),
        }
    }

    pub fn add<T: Encodable>(&mut self, parameter: &T) -> Result<(), serde::encoder::Error> {
        parameter.encode(&mut self.encoder)
    }
}

pub type ClientResult<T> = Result<T, Error>;
<commit_msg>Rewrite String conversion<commit_after>use hyper;
use rustc_serialize::{Encodable, Decodable};
use super::error::Error;
use super::serde;

pub struct Client {
    http_client: hyper::Client,
    server_uri: String,
}

impl Client {
    pub fn new(server_uri: &str) -> Client {
        Client {
            http_client: hyper::Client::new(),
            server_uri: String::from(server_uri),
        }
    }

    pub fn request<T: Decodable>(&self, request: Request) -> ClientResult<T> {
        let mut body = Vec::<u8>::new();
        request.encoder.write_request(&request.name, &mut body)?;

        let body = String::from_utf8(body)?;
        let res = self.http_client
            .post(&self.server_uri)
            .body(&body)
            .send()?;

        let mut res = serde::Decoder::new_response(res)?;

        Ok(T::decode(&mut res.pop()
            .ok_or(Error::Decoding(serde::value::DecodeError::UnsupportedDataFormat))?)?)
    }
}

pub struct Request {
    name: String,
    encoder: serde::Encoder,
}

impl Request {
    pub fn new(function_name: &str) -> Request {
        Request {
            name: String::from(function_name),
            encoder: serde::Encoder::new(),
        }
    }

    pub fn add<T: Encodable>(&mut self, parameter: &T) -> Result<(), serde::encoder::Error> {
        parameter.encode(&mut self.encoder)
    }
}

pub type ClientResult<T> = Result<T, Error>;
<|endoftext|>


<commit_before>use crate::syntax::atom::Atom::{self, *};
use crate::syntax::report::Errors;
use crate::syntax::types::TrivialReason;
use crate::syntax::{
    error, ident, Api, Enum, ExternFn, ExternType, Impl, Lang, Receiver, Ref, Slice, Struct, Ty1,
    Type, Types,
};
use proc_macro2::{Delimiter, Group, Ident, TokenStream};
use quote::{quote, ToTokens};
use std::fmt::Display;

pub(crate) struct Check<'a> {
    apis: &'a [Api],
    types: &'a Types<'a>,
    errors: &'a mut Errors,
}

pub(crate) fn typecheck(cx: &mut Errors, apis: &[Api], types: &Types) {
    do_typecheck(&mut Check {
        apis,
        types,
        errors: cx,
    });
}

fn do_typecheck(cx: &mut Check) {
    ident::check_all(cx, cx.apis);

    for ty in cx.types {
        match ty {
            Type::Ident(ident) => check_type_ident(cx, &ident.rust),
            Type::RustBox(ptr) => check_type_box(cx, ptr),
            Type::RustVec(ty) => check_type_rust_vec(cx, ty),
            Type::UniquePtr(ptr) => check_type_unique_ptr(cx, ptr),
            Type::CxxVector(ptr) => check_type_cxx_vector(cx, ptr),
            Type::Ref(ty) => check_type_ref(cx, ty),
            Type::Slice(ty) => check_type_slice(cx, ty),
            Type::Str(_) | Type::Fn(_) | Type::Void(_) | Type::SliceRefU8(_) => {}
        }
    }

    for api in cx.apis {
        match api {
            Api::Struct(strct) => check_api_struct(cx, strct),
            Api::Enum(enm) => check_api_enum(cx, enm),
            Api::CxxType(ety) | Api::RustType(ety) => check_api_type(cx, ety),
            Api::CxxFunction(efn) | Api::RustFunction(efn) => check_api_fn(cx, efn),
            Api::Impl(imp) => check_api_impl(cx, imp),
            Api::Include(_) | Api::TypeAlias(_) => {}
        }
    }
}

impl Check<'_> {
    pub(crate) fn error(&mut self, sp: impl ToTokens, msg: impl Display) {
        self.errors.error(sp, msg);
    }
}

fn check_type_ident(cx: &mut Check, ident: &Ident) {
    if Atom::from(ident).is_none()
        && !cx.types.structs.contains_key(ident)
        && !cx.types.enums.contains_key(ident)
        && !cx.types.cxx.contains(ident)
        && !cx.types.rust.contains(ident)
    {
        let msg = format!("unsupported type: {}", ident);
        cx.error(ident, &msg);
    }
}

fn check_type_box(cx: &mut Check, ptr: &Ty1) {
    if let Type::Ident(ident) = &ptr.inner {
        if cx.types.cxx.contains(&ident.rust)
            && !cx.types.structs.contains_key(&ident.rust)
            && !cx.types.enums.contains_key(&ident.rust)
        {
            cx.error(ptr, error::BOX_CXX_TYPE.msg);
        }

        if Atom::from(&ident.rust).is_none() {
            return;
        }
    }

    cx.error(ptr, "unsupported target type of Box");
}

fn check_type_rust_vec(cx: &mut Check, ty: &Ty1) {
    if let Type::Ident(ident) = &ty.inner {
        if cx.types.cxx.contains(&ident.rust)
            && !cx.types.structs.contains_key(&ident.rust)
            && !cx.types.enums.contains_key(&ident.rust)
        {
            cx.error(ty, "Rust Vec containing C++ type is not supported yet");
            return;
        }

        match Atom::from(&ident.rust) {
            None | Some(U8) | Some(U16) | Some(U32) | Some(U64) | Some(Usize) | Some(I8)
            | Some(I16) | Some(I32) | Some(I64) | Some(Isize) | Some(F32) | Some(F64)
            | Some(RustString) => return,
            Some(Bool) => { /* todo */ }
            Some(CxxString) => {}
        }
    }

    cx.error(ty, "unsupported element type of Vec");
}

fn check_type_unique_ptr(cx: &mut Check, ptr: &Ty1) {
    if let Type::Ident(ident) = &ptr.inner {
        if cx.types.rust.contains(&ident.rust) {
            cx.error(ptr, "unique_ptr of a Rust type is not supported yet");
        }

        match Atom::from(&ident.rust) {
            None | Some(CxxString) => return,
            _ => {}
        }
    } else if let Type::CxxVector(_) = &ptr.inner {
        return;
    }

    cx.error(ptr, "unsupported unique_ptr target type");
}

fn check_type_cxx_vector(cx: &mut Check, ptr: &Ty1) {
    if let Type::Ident(ident) = &ptr.inner {
        if cx.types.rust.contains(&ident.rust) {
            cx.error(
                ptr,
                "C++ vector containing a Rust type is not supported yet",
            );
        }

        match Atom::from(&ident.rust) {
            None | Some(U8) | Some(U16) | Some(U32) | Some(U64) | Some(Usize) | Some(I8)
            | Some(I16) | Some(I32) | Some(I64) | Some(Isize) | Some(F32) | Some(F64)
            | Some(CxxString) => return,
            Some(Bool) | Some(RustString) => {}
        }
    }

    cx.error(ptr, "unsupported vector target type");
}

fn check_type_ref(cx: &mut Check, ty: &Ref) {
    if ty.lifetime.is_some() {
        cx.error(ty, "references with explicit lifetimes are not supported");
    }

    match ty.inner {
        Type::Fn(_) | Type::Void(_) => {}
        Type::Ref(_) => {
            cx.error(ty, "C++ does not allow references to references");
            return;
        }
        _ => return,
    }

    cx.error(ty, "unsupported reference type");
}

fn check_type_slice(cx: &mut Check, ty: &Slice) {
    cx.error(ty, "only &[u8] is supported so far, not other slice types");
}

fn check_api_struct(cx: &mut Check, strct: &Struct) {
    let name = &strct.name;
    check_reserved_name(cx, &name.rust);

    if strct.fields.is_empty() {
        let span = span_for_struct_error(strct);
        cx.error(span, "structs without any fields are not supported");
    }

    if cx.types.cxx.contains(&name.rust) {
        if let Some(ety) = cx.types.untrusted.get(&name.rust) {
            let msg = "extern shared struct must be declared in an `unsafe extern` block";
            cx.error(ety, msg);
        }
    }

    for field in &strct.fields {
        if is_unsized(cx, &field.ty) {
            let desc = describe(cx, &field.ty);
            let msg = format!("using {} by value is not supported", desc);
            cx.error(field, msg);
        }
        if let Type::Fn(_) = field.ty {
            cx.error(
                field,
                "function pointers in a struct field are not implemented yet",
            );
        }
    }
}

fn check_api_enum(cx: &mut Check, enm: &Enum) {
    check_reserved_name(cx, &enm.name.rust);

    if enm.variants.is_empty() {
        let span = span_for_enum_error(enm);
        cx.error(span, "enums without any variants are not supported");
    }
}

fn check_api_type(cx: &mut Check, ety: &ExternType) {
    check_reserved_name(cx, &ety.name.rust);

    if let Some(reason) = cx.types.required_trivial.get(&ety.name.rust) {
        let what = match reason {
            TrivialReason::StructField(strct) => format!("a field of `{}`", strct.name.rust),
            TrivialReason::FunctionArgument(efn) => format!("an argument of `{}`", efn.name.rust),
            TrivialReason::FunctionReturn(efn) => format!("a return value of `{}`", efn.name.rust),
        };
        let msg = format!(
            "needs a cxx::ExternType impl in order to be used as {}",
            what,
        );
        cx.error(ety, msg);
    }
}

fn check_api_fn(cx: &mut Check, efn: &ExternFn) {
    if let Some(receiver) = &efn.receiver {
        let ref span = span_for_receiver_error(receiver);

        if receiver.ty.is_self() {
            let mutability = match receiver.mutability {
                Some(_) => "mut ",
                None => "",
            };
            let msg = format!(
                "unnamed receiver type is only allowed if the surrounding \
                 extern block contains exactly one extern type; \
                 use `self: &{mutability}TheType`",
                mutability = mutability,
            );
            cx.error(span, msg);
        } else if !cx.types.structs.contains_key(&receiver.ty.rust)
            && !cx.types.cxx.contains(&receiver.ty.rust)
            && !cx.types.rust.contains(&receiver.ty.rust)
        {
            cx.error(span, "unrecognized receiver type");
        }

        if receiver.lifetime.is_some() {
            cx.error(span, "references with explicit lifetimes are not supported");
        }
    }

    for arg in &efn.args {
        if is_unsized(cx, &arg.ty) {
            let desc = describe(cx, &arg.ty);
            let msg = format!("passing {} by value is not supported", desc);
            cx.error(arg, msg);
        }
        if let Type::Fn(_) = arg.ty {
            if efn.lang == Lang::Rust {
                cx.error(
                    arg,
                    "passing a function pointer from C++ to Rust is not implemented yet",
                );
            }
        }
    }

    if let Some(ty) = &efn.ret {
        if is_unsized(cx, ty) {
            let desc = describe(cx, ty);
            let msg = format!("returning {} by value is not supported", desc);
            cx.error(ty, msg);
        }
        if let Type::Fn(_) = ty {
            cx.error(ty, "returning a function pointer is not implemented yet");
        }
    }

    if efn.lang == Lang::Cxx {
        check_mut_return_restriction(cx, efn);
    }

    check_multiple_arg_lifetimes(cx, efn);
}

fn check_api_impl(cx: &mut Check, imp: &Impl) {
    if let Type::UniquePtr(ty) | Type::CxxVector(ty) = &imp.ty {
        if let Type::Ident(inner) = &ty.inner {
            if Atom::from(&inner.rust).is_none() {
                return;
            }
        }
    }

    cx.error(imp, "unsupported Self type of explicit impl");
}

fn check_mut_return_restriction(cx: &mut Check, efn: &ExternFn) {
    match &efn.ret {
        Some(Type::Ref(ty)) if ty.mutability.is_some() => {}
        _ => return,
    }

    for arg in &efn.args {
        if let Type::Ref(ty) = &arg.ty {
            if ty.mutability.is_some() {
                return;
            }
        }
    }

    cx.error(
        efn,
        "&mut return type is not allowed unless there is a &mut argument",
    );
}

fn check_multiple_arg_lifetimes(cx: &mut Check, efn: &ExternFn) {
    match &efn.ret {
        Some(Type::Ref(_)) => {}
        _ => return,
    }

    let mut reference_args = 0;
    for arg in &efn.args {
        if let Type::Ref(_) = &arg.ty {
            reference_args += 1;
        }
    }

    if efn.receiver.is_some() {
        reference_args += 1;
    }

    if reference_args != 1 {
        cx.error(
            efn,
            "functions that return a reference must take exactly one input reference",
        );
    }
}

fn check_reserved_name(cx: &mut Check, ident: &Ident) {
    if ident == "Box"
        || ident == "UniquePtr"
        || ident == "Vec"
        || ident == "CxxVector"
        || Atom::from(ident).is_some()
    {
        cx.error(ident, "reserved name");
    }
}

fn is_unsized(cx: &mut Check, ty: &Type) -> bool {
    let ident = match ty {
        Type::Ident(ident) => &ident.rust,
        Type::CxxVector(_) | Type::Slice(_) | Type::Void(_) => return true,
        _ => return false,
    };
    ident == CxxString
        || cx.types.cxx.contains(ident)
            && !cx.types.structs.contains_key(ident)
            && !cx.types.enums.contains_key(ident)
            && !(cx.types.aliases.contains_key(ident)
                && cx.types.required_trivial.contains_key(ident))
        || cx.types.rust.contains(ident)
}

fn span_for_struct_error(strct: &Struct) -> TokenStream {
    let struct_token = strct.struct_token;
    let mut brace_token = Group::new(Delimiter::Brace, TokenStream::new());
    brace_token.set_span(strct.brace_token.span);
    quote!(#struct_token #brace_token)
}

fn span_for_enum_error(enm: &Enum) -> TokenStream {
    let enum_token = enm.enum_token;
    let mut brace_token = Group::new(Delimiter::Brace, TokenStream::new());
    brace_token.set_span(enm.brace_token.span);
    quote!(#enum_token #brace_token)
}

fn span_for_receiver_error(receiver: &Receiver) -> TokenStream {
    let ampersand = receiver.ampersand;
    let lifetime = &receiver.lifetime;
    let mutability = receiver.mutability;
    if receiver.shorthand {
        let var = receiver.var;
        quote!(#ampersand #lifetime #mutability #var)
    } else {
        let ty = &receiver.ty;
        quote!(#ampersand #lifetime #mutability #ty)
    }
}

fn describe(cx: &mut Check, ty: &Type) -> String {
    match ty {
        Type::Ident(ident) => {
            if cx.types.structs.contains_key(&ident.rust) {
                "struct".to_owned()
            } else if cx.types.enums.contains_key(&ident.rust) {
                "enum".to_owned()
            } else if cx.types.aliases.contains_key(&ident.rust) {
                "C++ type".to_owned()
            } else if cx.types.cxx.contains(&ident.rust) {
                "opaque C++ type".to_owned()
            } else if cx.types.rust.contains(&ident.rust) {
                "opaque Rust type".to_owned()
            } else if Atom::from(&ident.rust) == Some(CxxString) {
                "C++ string".to_owned()
            } else {
                ident.rust.to_string()
            }
        }
        Type::RustBox(_) => "Box".to_owned(),
        Type::RustVec(_) => "Vec".to_owned(),
        Type::UniquePtr(_) => "unique_ptr".to_owned(),
        Type::Ref(_) => "reference".to_owned(),
        Type::Str(_) => "&str".to_owned(),
        Type::CxxVector(_) => "C++ vector".to_owned(),
        Type::Slice(_) => "slice".to_owned(),
        Type::SliceRefU8(_) => "&[u8]".to_owned(),
        Type::Fn(_) => "function pointer".to_owned(),
        Type::Void(_) => "()".to_owned(),
    }
}
<commit_msg>Reject fallible function pointer which doesn't work yet<commit_after>use crate::syntax::atom::Atom::{self, *};
use crate::syntax::report::Errors;
use crate::syntax::types::TrivialReason;
use crate::syntax::{
    error, ident, Api, Enum, ExternFn, ExternType, Impl, Lang, Receiver, Ref, Signature, Slice,
    Struct, Ty1, Type, Types,
};
use proc_macro2::{Delimiter, Group, Ident, TokenStream};
use quote::{quote, ToTokens};
use std::fmt::Display;

pub(crate) struct Check<'a> {
    apis: &'a [Api],
    types: &'a Types<'a>,
    errors: &'a mut Errors,
}

pub(crate) fn typecheck(cx: &mut Errors, apis: &[Api], types: &Types) {
    do_typecheck(&mut Check {
        apis,
        types,
        errors: cx,
    });
}

fn do_typecheck(cx: &mut Check) {
    ident::check_all(cx, cx.apis);

    for ty in cx.types {
        match ty {
            Type::Ident(ident) => check_type_ident(cx, &ident.rust),
            Type::RustBox(ptr) => check_type_box(cx, ptr),
            Type::RustVec(ty) => check_type_rust_vec(cx, ty),
            Type::UniquePtr(ptr) => check_type_unique_ptr(cx, ptr),
            Type::CxxVector(ptr) => check_type_cxx_vector(cx, ptr),
            Type::Ref(ty) => check_type_ref(cx, ty),
            Type::Slice(ty) => check_type_slice(cx, ty),
            Type::Fn(ty) => check_type_fn(cx, ty),
            Type::Str(_) | Type::Void(_) | Type::SliceRefU8(_) => {}
        }
    }

    for api in cx.apis {
        match api {
            Api::Struct(strct) => check_api_struct(cx, strct),
            Api::Enum(enm) => check_api_enum(cx, enm),
            Api::CxxType(ety) | Api::RustType(ety) => check_api_type(cx, ety),
            Api::CxxFunction(efn) | Api::RustFunction(efn) => check_api_fn(cx, efn),
            Api::Impl(imp) => check_api_impl(cx, imp),
            Api::Include(_) | Api::TypeAlias(_) => {}
        }
    }
}

impl Check<'_> {
    pub(crate) fn error(&mut self, sp: impl ToTokens, msg: impl Display) {
        self.errors.error(sp, msg);
    }
}

fn check_type_ident(cx: &mut Check, ident: &Ident) {
    if Atom::from(ident).is_none()
        && !cx.types.structs.contains_key(ident)
        && !cx.types.enums.contains_key(ident)
        && !cx.types.cxx.contains(ident)
        && !cx.types.rust.contains(ident)
    {
        let msg = format!("unsupported type: {}", ident);
        cx.error(ident, &msg);
    }
}

fn check_type_box(cx: &mut Check, ptr: &Ty1) {
    if let Type::Ident(ident) = &ptr.inner {
        if cx.types.cxx.contains(&ident.rust)
            && !cx.types.structs.contains_key(&ident.rust)
            && !cx.types.enums.contains_key(&ident.rust)
        {
            cx.error(ptr, error::BOX_CXX_TYPE.msg);
        }

        if Atom::from(&ident.rust).is_none() {
            return;
        }
    }

    cx.error(ptr, "unsupported target type of Box");
}

fn check_type_rust_vec(cx: &mut Check, ty: &Ty1) {
    if let Type::Ident(ident) = &ty.inner {
        if cx.types.cxx.contains(&ident.rust)
            && !cx.types.structs.contains_key(&ident.rust)
            && !cx.types.enums.contains_key(&ident.rust)
        {
            cx.error(ty, "Rust Vec containing C++ type is not supported yet");
            return;
        }

        match Atom::from(&ident.rust) {
            None | Some(U8) | Some(U16) | Some(U32) | Some(U64) | Some(Usize) | Some(I8)
            | Some(I16) | Some(I32) | Some(I64) | Some(Isize) | Some(F32) | Some(F64)
            | Some(RustString) => return,
            Some(Bool) => { /* todo */ }
            Some(CxxString) => {}
        }
    }

    cx.error(ty, "unsupported element type of Vec");
}

fn check_type_unique_ptr(cx: &mut Check, ptr: &Ty1) {
    if let Type::Ident(ident) = &ptr.inner {
        if cx.types.rust.contains(&ident.rust) {
            cx.error(ptr, "unique_ptr of a Rust type is not supported yet");
        }

        match Atom::from(&ident.rust) {
            None | Some(CxxString) => return,
            _ => {}
        }
    } else if let Type::CxxVector(_) = &ptr.inner {
        return;
    }

    cx.error(ptr, "unsupported unique_ptr target type");
}

fn check_type_cxx_vector(cx: &mut Check, ptr: &Ty1) {
    if let Type::Ident(ident) = &ptr.inner {
        if cx.types.rust.contains(&ident.rust) {
            cx.error(
                ptr,
                "C++ vector containing a Rust type is not supported yet",
            );
        }

        match Atom::from(&ident.rust) {
            None | Some(U8) | Some(U16) | Some(U32) | Some(U64) | Some(Usize) | Some(I8)
            | Some(I16) | Some(I32) | Some(I64) | Some(Isize) | Some(F32) | Some(F64)
            | Some(CxxString) => return,
            Some(Bool) | Some(RustString) => {}
        }
    }

    cx.error(ptr, "unsupported vector target type");
}

fn check_type_ref(cx: &mut Check, ty: &Ref) {
    if ty.lifetime.is_some() {
        cx.error(ty, "references with explicit lifetimes are not supported");
    }

    match ty.inner {
        Type::Fn(_) | Type::Void(_) => {}
        Type::Ref(_) => {
            cx.error(ty, "C++ does not allow references to references");
            return;
        }
        _ => return,
    }

    cx.error(ty, "unsupported reference type");
}

fn check_type_slice(cx: &mut Check, ty: &Slice) {
    cx.error(ty, "only &[u8] is supported so far, not other slice types");
}

fn check_type_fn(cx: &mut Check, ty: &Signature) {
    if ty.throws {
        cx.error(ty, "function pointer returning Result is not supported yet");
    }
}

fn check_api_struct(cx: &mut Check, strct: &Struct) {
    let name = &strct.name;
    check_reserved_name(cx, &name.rust);

    if strct.fields.is_empty() {
        let span = span_for_struct_error(strct);
        cx.error(span, "structs without any fields are not supported");
    }

    if cx.types.cxx.contains(&name.rust) {
        if let Some(ety) = cx.types.untrusted.get(&name.rust) {
            let msg = "extern shared struct must be declared in an `unsafe extern` block";
            cx.error(ety, msg);
        }
    }

    for field in &strct.fields {
        if is_unsized(cx, &field.ty) {
            let desc = describe(cx, &field.ty);
            let msg = format!("using {} by value is not supported", desc);
            cx.error(field, msg);
        }
        if let Type::Fn(_) = field.ty {
            cx.error(
                field,
                "function pointers in a struct field are not implemented yet",
            );
        }
    }
}

fn check_api_enum(cx: &mut Check, enm: &Enum) {
    check_reserved_name(cx, &enm.name.rust);

    if enm.variants.is_empty() {
        let span = span_for_enum_error(enm);
        cx.error(span, "enums without any variants are not supported");
    }
}

fn check_api_type(cx: &mut Check, ety: &ExternType) {
    check_reserved_name(cx, &ety.name.rust);

    if let Some(reason) = cx.types.required_trivial.get(&ety.name.rust) {
        let what = match reason {
            TrivialReason::StructField(strct) => format!("a field of `{}`", strct.name.rust),
            TrivialReason::FunctionArgument(efn) => format!("an argument of `{}`", efn.name.rust),
            TrivialReason::FunctionReturn(efn) => format!("a return value of `{}`", efn.name.rust),
        };
        let msg = format!(
            "needs a cxx::ExternType impl in order to be used as {}",
            what,
        );
        cx.error(ety, msg);
    }
}

fn check_api_fn(cx: &mut Check, efn: &ExternFn) {
    if let Some(receiver) = &efn.receiver {
        let ref span = span_for_receiver_error(receiver);

        if receiver.ty.is_self() {
            let mutability = match receiver.mutability {
                Some(_) => "mut ",
                None => "",
            };
            let msg = format!(
                "unnamed receiver type is only allowed if the surrounding \
                 extern block contains exactly one extern type; \
                 use `self: &{mutability}TheType`",
                mutability = mutability,
            );
            cx.error(span, msg);
        } else if !cx.types.structs.contains_key(&receiver.ty.rust)
            && !cx.types.cxx.contains(&receiver.ty.rust)
            && !cx.types.rust.contains(&receiver.ty.rust)
        {
            cx.error(span, "unrecognized receiver type");
        }

        if receiver.lifetime.is_some() {
            cx.error(span, "references with explicit lifetimes are not supported");
        }
    }

    for arg in &efn.args {
        if is_unsized(cx, &arg.ty) {
            let desc = describe(cx, &arg.ty);
            let msg = format!("passing {} by value is not supported", desc);
            cx.error(arg, msg);
        }
        if let Type::Fn(_) = arg.ty {
            if efn.lang == Lang::Rust {
                cx.error(
                    arg,
                    "passing a function pointer from C++ to Rust is not implemented yet",
                );
            }
        }
    }

    if let Some(ty) = &efn.ret {
        if is_unsized(cx, ty) {
            let desc = describe(cx, ty);
            let msg = format!("returning {} by value is not supported", desc);
            cx.error(ty, msg);
        }
        if let Type::Fn(_) = ty {
            cx.error(ty, "returning a function pointer is not implemented yet");
        }
    }

    if efn.lang == Lang::Cxx {
        check_mut_return_restriction(cx, efn);
    }

    check_multiple_arg_lifetimes(cx, efn);
}

fn check_api_impl(cx: &mut Check, imp: &Impl) {
    if let Type::UniquePtr(ty) | Type::CxxVector(ty) = &imp.ty {
        if let Type::Ident(inner) = &ty.inner {
            if Atom::from(&inner.rust).is_none() {
                return;
            }
        }
    }

    cx.error(imp, "unsupported Self type of explicit impl");
}

fn check_mut_return_restriction(cx: &mut Check, efn: &ExternFn) {
    match &efn.ret {
        Some(Type::Ref(ty)) if ty.mutability.is_some() => {}
        _ => return,
    }

    for arg in &efn.args {
        if let Type::Ref(ty) = &arg.ty {
            if ty.mutability.is_some() {
                return;
            }
        }
    }

    cx.error(
        efn,
        "&mut return type is not allowed unless there is a &mut argument",
    );
}

fn check_multiple_arg_lifetimes(cx: &mut Check, efn: &ExternFn) {
    match &efn.ret {
        Some(Type::Ref(_)) => {}
        _ => return,
    }

    let mut reference_args = 0;
    for arg in &efn.args {
        if let Type::Ref(_) = &arg.ty {
            reference_args += 1;
        }
    }

    if efn.receiver.is_some() {
        reference_args += 1;
    }

    if reference_args != 1 {
        cx.error(
            efn,
            "functions that return a reference must take exactly one input reference",
        );
    }
}

fn check_reserved_name(cx: &mut Check, ident: &Ident) {
    if ident == "Box"
        || ident == "UniquePtr"
        || ident == "Vec"
        || ident == "CxxVector"
        || Atom::from(ident).is_some()
    {
        cx.error(ident, "reserved name");
    }
}

fn is_unsized(cx: &mut Check, ty: &Type) -> bool {
    let ident = match ty {
        Type::Ident(ident) => &ident.rust,
        Type::CxxVector(_) | Type::Slice(_) | Type::Void(_) => return true,
        _ => return false,
    };
    ident == CxxString
        || cx.types.cxx.contains(ident)
            && !cx.types.structs.contains_key(ident)
            && !cx.types.enums.contains_key(ident)
            && !(cx.types.aliases.contains_key(ident)
                && cx.types.required_trivial.contains_key(ident))
        || cx.types.rust.contains(ident)
}

fn span_for_struct_error(strct: &Struct) -> TokenStream {
    let struct_token = strct.struct_token;
    let mut brace_token = Group::new(Delimiter::Brace, TokenStream::new());
    brace_token.set_span(strct.brace_token.span);
    quote!(#struct_token #brace_token)
}

fn span_for_enum_error(enm: &Enum) -> TokenStream {
    let enum_token = enm.enum_token;
    let mut brace_token = Group::new(Delimiter::Brace, TokenStream::new());
    brace_token.set_span(enm.brace_token.span);
    quote!(#enum_token #brace_token)
}

fn span_for_receiver_error(receiver: &Receiver) -> TokenStream {
    let ampersand = receiver.ampersand;
    let lifetime = &receiver.lifetime;
    let mutability = receiver.mutability;
    if receiver.shorthand {
        let var = receiver.var;
        quote!(#ampersand #lifetime #mutability #var)
    } else {
        let ty = &receiver.ty;
        quote!(#ampersand #lifetime #mutability #ty)
    }
}

fn describe(cx: &mut Check, ty: &Type) -> String {
    match ty {
        Type::Ident(ident) => {
            if cx.types.structs.contains_key(&ident.rust) {
                "struct".to_owned()
            } else if cx.types.enums.contains_key(&ident.rust) {
                "enum".to_owned()
            } else if cx.types.aliases.contains_key(&ident.rust) {
                "C++ type".to_owned()
            } else if cx.types.cxx.contains(&ident.rust) {
                "opaque C++ type".to_owned()
            } else if cx.types.rust.contains(&ident.rust) {
                "opaque Rust type".to_owned()
            } else if Atom::from(&ident.rust) == Some(CxxString) {
                "C++ string".to_owned()
            } else {
                ident.rust.to_string()
            }
        }
        Type::RustBox(_) => "Box".to_owned(),
        Type::RustVec(_) => "Vec".to_owned(),
        Type::UniquePtr(_) => "unique_ptr".to_owned(),
        Type::Ref(_) => "reference".to_owned(),
        Type::Str(_) => "&str".to_owned(),
        Type::CxxVector(_) => "C++ vector".to_owned(),
        Type::Slice(_) => "slice".to_owned(),
        Type::SliceRefU8(_) => "&[u8]".to_owned(),
        Type::Fn(_) => "function pointer".to_owned(),
        Type::Void(_) => "()".to_owned(),
    }
}
<|endoftext|>


<commit_before>use crate::syntax::atom::Atom::{self, *};
use crate::syntax::report::Errors;
use crate::syntax::types::TrivialReason;
use crate::syntax::{
    error, ident, Api, Enum, ExternFn, ExternType, Impl, Lang, Receiver, Ref, Slice, Struct, Ty1,
    Type, Types,
};
use proc_macro2::{Delimiter, Group, Ident, TokenStream};
use quote::{quote, ToTokens};
use std::fmt::Display;

pub(crate) struct Check<'a> {
    apis: &'a [Api],
    types: &'a Types<'a>,
    errors: &'a mut Errors,
}

pub(crate) fn typecheck(cx: &mut Errors, apis: &[Api], types: &Types) {
    do_typecheck(&mut Check {
        apis,
        types,
        errors: cx,
    });
}

fn do_typecheck(cx: &mut Check) {
    ident::check_all(cx, cx.apis);

    for ty in cx.types {
        match ty {
            Type::Ident(ident) => check_type_ident(cx, &ident.rust),
            Type::RustBox(ptr) => check_type_box(cx, ptr),
            Type::RustVec(ty) => check_type_rust_vec(cx, ty),
            Type::UniquePtr(ptr) => check_type_unique_ptr(cx, ptr),
            Type::CxxVector(ptr) => check_type_cxx_vector(cx, ptr),
            Type::Ref(ty) => check_type_ref(cx, ty),
            Type::Slice(ty) => check_type_slice(cx, ty),
            _ => {}
        }
    }

    for api in cx.apis {
        match api {
            Api::Struct(strct) => check_api_struct(cx, strct),
            Api::Enum(enm) => check_api_enum(cx, enm),
            Api::CxxType(ety) | Api::RustType(ety) => check_api_type(cx, ety),
            Api::CxxFunction(efn) | Api::RustFunction(efn) => check_api_fn(cx, efn),
            Api::Impl(imp) => check_api_impl(cx, imp),
            _ => {}
        }
    }
}

impl Check<'_> {
    pub(crate) fn error(&mut self, sp: impl ToTokens, msg: impl Display) {
        self.errors.error(sp, msg);
    }
}

fn check_type_ident(cx: &mut Check, ident: &Ident) {
    if Atom::from(ident).is_none()
        && !cx.types.structs.contains_key(ident)
        && !cx.types.enums.contains_key(ident)
        && !cx.types.cxx.contains(ident)
        && !cx.types.rust.contains(ident)
    {
        let msg = format!("unsupported type: {}", ident);
        cx.error(ident, &msg);
    }
}

fn check_type_box(cx: &mut Check, ptr: &Ty1) {
    if let Type::Ident(ident) = &ptr.inner {
        if cx.types.cxx.contains(&ident.rust)
            && !cx.types.structs.contains_key(&ident.rust)
            && !cx.types.enums.contains_key(&ident.rust)
        {
            cx.error(ptr, error::BOX_CXX_TYPE.msg);
        }

        if Atom::from(&ident.rust).is_none() {
            return;
        }
    }

    cx.error(ptr, "unsupported target type of Box");
}

fn check_type_rust_vec(cx: &mut Check, ty: &Ty1) {
    if let Type::Ident(ident) = &ty.inner {
        if cx.types.cxx.contains(&ident.rust)
            && !cx.types.structs.contains_key(&ident.rust)
            && !cx.types.enums.contains_key(&ident.rust)
        {
            cx.error(ty, "Rust Vec containing C++ type is not supported yet");
            return;
        }

        match Atom::from(&ident.rust) {
            None | Some(U8) | Some(U16) | Some(U32) | Some(U64) | Some(Usize) | Some(I8)
            | Some(I16) | Some(I32) | Some(I64) | Some(Isize) | Some(F32) | Some(F64)
            | Some(RustString) => return,
            Some(Bool) => { /* todo */ }
            Some(CxxString) => {}
        }
    }

    cx.error(ty, "unsupported element type of Vec");
}

fn check_type_unique_ptr(cx: &mut Check, ptr: &Ty1) {
    if let Type::Ident(ident) = &ptr.inner {
        if cx.types.rust.contains(&ident.rust) {
            cx.error(ptr, "unique_ptr of a Rust type is not supported yet");
        }

        match Atom::from(&ident.rust) {
            None | Some(CxxString) => return,
            _ => {}
        }
    } else if let Type::CxxVector(_) = &ptr.inner {
        return;
    }

    cx.error(ptr, "unsupported unique_ptr target type");
}

fn check_type_cxx_vector(cx: &mut Check, ptr: &Ty1) {
    if let Type::Ident(ident) = &ptr.inner {
        if cx.types.rust.contains(&ident.rust) {
            cx.error(
                ptr,
                "C++ vector containing a Rust type is not supported yet",
            );
        }

        match Atom::from(&ident.rust) {
            None | Some(U8) | Some(U16) | Some(U32) | Some(U64) | Some(Usize) | Some(I8)
            | Some(I16) | Some(I32) | Some(I64) | Some(Isize) | Some(F32) | Some(F64)
            | Some(CxxString) => return,
            Some(Bool) | Some(RustString) => {}
        }
    }

    cx.error(ptr, "unsupported vector target type");
}

fn check_type_ref(cx: &mut Check, ty: &Ref) {
    if ty.lifetime.is_some() {
        cx.error(ty, "references with explicit lifetimes are not supported");
    }

    match ty.inner {
        Type::Fn(_) | Type::Void(_) => {}
        Type::Ref(_) => {
            cx.error(ty, "C++ does not allow references to references");
            return;
        }
        _ => return,
    }

    cx.error(ty, "unsupported reference type");
}

fn check_type_slice(cx: &mut Check, ty: &Slice) {
    cx.error(ty, "only &[u8] is supported so far, not other slice types");
}

fn check_api_struct(cx: &mut Check, strct: &Struct) {
    let name = &strct.name;
    check_reserved_name(cx, &name.rust);

    if strct.fields.is_empty() {
        let span = span_for_struct_error(strct);
        cx.error(span, "structs without any fields are not supported");
    }

    if cx.types.cxx.contains(&name.rust) {
        if let Some(ety) = cx.types.untrusted.get(&name.rust) {
            let msg = "extern shared struct must be declared in an `unsafe extern` block";
            cx.error(ety, msg);
        }
    }

    for field in &strct.fields {
        if is_unsized(cx, &field.ty) {
            let desc = describe(cx, &field.ty);
            let msg = format!("using {} by value is not supported", desc);
            cx.error(field, msg);
        }
        if let Type::Fn(_) = field.ty {
            cx.error(
                field,
                "function pointers in a struct field are not implemented yet",
            );
        }
    }
}

fn check_api_enum(cx: &mut Check, enm: &Enum) {
    check_reserved_name(cx, &enm.name.rust);

    if enm.variants.is_empty() {
        let span = span_for_enum_error(enm);
        cx.error(span, "enums without any variants are not supported");
    }
}

fn check_api_type(cx: &mut Check, ety: &ExternType) {
    check_reserved_name(cx, &ety.name.rust);

    if let Some(reason) = cx.types.required_trivial.get(&ety.name.rust) {
        let what = match reason {
            TrivialReason::StructField(strct) => format!("a field of `{}`", strct.name.rust),
            TrivialReason::FunctionArgument(efn) => format!("an argument of `{}`", efn.name.rust),
            TrivialReason::FunctionReturn(efn) => format!("a return value of `{}`", efn.name.rust),
        };
        let msg = format!(
            "needs a cxx::ExternType impl in order to be used as {}",
            what,
        );
        cx.error(ety, msg);
    }
}

fn check_api_fn(cx: &mut Check, efn: &ExternFn) {
    if let Some(receiver) = &efn.receiver {
        let ref span = span_for_receiver_error(receiver);

        if receiver.ty.is_self() {
            let mutability = match receiver.mutability {
                Some(_) => "mut ",
                None => "",
            };
            let msg = format!(
                "unnamed receiver type is only allowed if the surrounding \
                 extern block contains exactly one extern type; \
                 use `self: &{mutability}TheType`",
                mutability = mutability,
            );
            cx.error(span, msg);
        } else if !cx.types.structs.contains_key(&receiver.ty.rust)
            && !cx.types.cxx.contains(&receiver.ty.rust)
            && !cx.types.rust.contains(&receiver.ty.rust)
        {
            cx.error(span, "unrecognized receiver type");
        }

        if receiver.lifetime.is_some() {
            cx.error(span, "references with explicit lifetimes are not supported");
        }
    }

    for arg in &efn.args {
        if is_unsized(cx, &arg.ty) {
            let desc = describe(cx, &arg.ty);
            let msg = format!("passing {} by value is not supported", desc);
            cx.error(arg, msg);
        }
        if let Type::Fn(_) = arg.ty {
            if efn.lang == Lang::Rust {
                cx.error(
                    arg,
                    "passing a function pointer from C++ to Rust is not implemented yet",
                );
            }
        }
    }

    if let Some(ty) = &efn.ret {
        if is_unsized(cx, ty) {
            let desc = describe(cx, ty);
            let msg = format!("returning {} by value is not supported", desc);
            cx.error(ty, msg);
        }
        if let Type::Fn(_) = ty {
            cx.error(ty, "returning a function pointer is not implemented yet");
        }
    }

    if efn.lang == Lang::Cxx {
        check_mut_return_restriction(cx, efn);
    }

    check_multiple_arg_lifetimes(cx, efn);
}

fn check_api_impl(cx: &mut Check, imp: &Impl) {
    if let Type::UniquePtr(ty) | Type::CxxVector(ty) = &imp.ty {
        if let Type::Ident(inner) = &ty.inner {
            if Atom::from(&inner.rust).is_none() {
                return;
            }
        }
    }

    cx.error(imp, "unsupported Self type of explicit impl");
}

fn check_mut_return_restriction(cx: &mut Check, efn: &ExternFn) {
    match &efn.ret {
        Some(Type::Ref(ty)) if ty.mutability.is_some() => {}
        _ => return,
    }

    for arg in &efn.args {
        if let Type::Ref(ty) = &arg.ty {
            if ty.mutability.is_some() {
                return;
            }
        }
    }

    cx.error(
        efn,
        "&mut return type is not allowed unless there is a &mut argument",
    );
}

fn check_multiple_arg_lifetimes(cx: &mut Check, efn: &ExternFn) {
    match &efn.ret {
        Some(Type::Ref(_)) => {}
        _ => return,
    }

    let mut reference_args = 0;
    for arg in &efn.args {
        if let Type::Ref(_) = &arg.ty {
            reference_args += 1;
        }
    }

    if efn.receiver.is_some() {
        reference_args += 1;
    }

    if reference_args != 1 {
        cx.error(
            efn,
            "functions that return a reference must take exactly one input reference",
        );
    }
}

fn check_reserved_name(cx: &mut Check, ident: &Ident) {
    if ident == "Box"
        || ident == "UniquePtr"
        || ident == "Vec"
        || ident == "CxxVector"
        || Atom::from(ident).is_some()
    {
        cx.error(ident, "reserved name");
    }
}

fn is_unsized(cx: &mut Check, ty: &Type) -> bool {
    let ident = match ty {
        Type::Ident(ident) => &ident.rust,
        Type::CxxVector(_) | Type::Slice(_) | Type::Void(_) => return true,
        _ => return false,
    };
    ident == CxxString
        || cx.types.cxx.contains(ident)
            && !cx.types.structs.contains_key(ident)
            && !cx.types.enums.contains_key(ident)
            && !(cx.types.aliases.contains_key(ident)
                && cx.types.required_trivial.contains_key(ident))
        || cx.types.rust.contains(ident)
}

fn span_for_struct_error(strct: &Struct) -> TokenStream {
    let struct_token = strct.struct_token;
    let mut brace_token = Group::new(Delimiter::Brace, TokenStream::new());
    brace_token.set_span(strct.brace_token.span);
    quote!(#struct_token #brace_token)
}

fn span_for_enum_error(enm: &Enum) -> TokenStream {
    let enum_token = enm.enum_token;
    let mut brace_token = Group::new(Delimiter::Brace, TokenStream::new());
    brace_token.set_span(enm.brace_token.span);
    quote!(#enum_token #brace_token)
}

fn span_for_receiver_error(receiver: &Receiver) -> TokenStream {
    let ampersand = receiver.ampersand;
    let lifetime = &receiver.lifetime;
    let mutability = receiver.mutability;
    if receiver.shorthand {
        let var = receiver.var;
        quote!(#ampersand #lifetime #mutability #var)
    } else {
        let ty = &receiver.ty;
        quote!(#ampersand #lifetime #mutability #ty)
    }
}

fn describe(cx: &mut Check, ty: &Type) -> String {
    match ty {
        Type::Ident(ident) => {
            if cx.types.structs.contains_key(&ident.rust) {
                "struct".to_owned()
            } else if cx.types.enums.contains_key(&ident.rust) {
                "enum".to_owned()
            } else if cx.types.aliases.contains_key(&ident.rust) {
                "C++ type".to_owned()
            } else if cx.types.cxx.contains(&ident.rust) {
                "opaque C++ type".to_owned()
            } else if cx.types.rust.contains(&ident.rust) {
                "opaque Rust type".to_owned()
            } else if Atom::from(&ident.rust) == Some(CxxString) {
                "C++ string".to_owned()
            } else {
                ident.rust.to_string()
            }
        }
        Type::RustBox(_) => "Box".to_owned(),
        Type::RustVec(_) => "Vec".to_owned(),
        Type::UniquePtr(_) => "unique_ptr".to_owned(),
        Type::Ref(_) => "reference".to_owned(),
        Type::Str(_) => "&str".to_owned(),
        Type::CxxVector(_) => "C++ vector".to_owned(),
        Type::Slice(_) => "slice".to_owned(),
        Type::SliceRefU8(_) => "&[u8]".to_owned(),
        Type::Fn(_) => "function pointer".to_owned(),
        Type::Void(_) => "()".to_owned(),
    }
}
<commit_msg>Exhaustive matching of types and apis in typechecker<commit_after>use crate::syntax::atom::Atom::{self, *};
use crate::syntax::report::Errors;
use crate::syntax::types::TrivialReason;
use crate::syntax::{
    error, ident, Api, Enum, ExternFn, ExternType, Impl, Lang, Receiver, Ref, Slice, Struct, Ty1,
    Type, Types,
};
use proc_macro2::{Delimiter, Group, Ident, TokenStream};
use quote::{quote, ToTokens};
use std::fmt::Display;

pub(crate) struct Check<'a> {
    apis: &'a [Api],
    types: &'a Types<'a>,
    errors: &'a mut Errors,
}

pub(crate) fn typecheck(cx: &mut Errors, apis: &[Api], types: &Types) {
    do_typecheck(&mut Check {
        apis,
        types,
        errors: cx,
    });
}

fn do_typecheck(cx: &mut Check) {
    ident::check_all(cx, cx.apis);

    for ty in cx.types {
        match ty {
            Type::Ident(ident) => check_type_ident(cx, &ident.rust),
            Type::RustBox(ptr) => check_type_box(cx, ptr),
            Type::RustVec(ty) => check_type_rust_vec(cx, ty),
            Type::UniquePtr(ptr) => check_type_unique_ptr(cx, ptr),
            Type::CxxVector(ptr) => check_type_cxx_vector(cx, ptr),
            Type::Ref(ty) => check_type_ref(cx, ty),
            Type::Slice(ty) => check_type_slice(cx, ty),
            Type::Str(_) | Type::Fn(_) | Type::Void(_) | Type::SliceRefU8(_) => {}
        }
    }

    for api in cx.apis {
        match api {
            Api::Struct(strct) => check_api_struct(cx, strct),
            Api::Enum(enm) => check_api_enum(cx, enm),
            Api::CxxType(ety) | Api::RustType(ety) => check_api_type(cx, ety),
            Api::CxxFunction(efn) | Api::RustFunction(efn) => check_api_fn(cx, efn),
            Api::Impl(imp) => check_api_impl(cx, imp),
            Api::Include(_) | Api::TypeAlias(_) => {}
        }
    }
}

impl Check<'_> {
    pub(crate) fn error(&mut self, sp: impl ToTokens, msg: impl Display) {
        self.errors.error(sp, msg);
    }
}

fn check_type_ident(cx: &mut Check, ident: &Ident) {
    if Atom::from(ident).is_none()
        && !cx.types.structs.contains_key(ident)
        && !cx.types.enums.contains_key(ident)
        && !cx.types.cxx.contains(ident)
        && !cx.types.rust.contains(ident)
    {
        let msg = format!("unsupported type: {}", ident);
        cx.error(ident, &msg);
    }
}

fn check_type_box(cx: &mut Check, ptr: &Ty1) {
    if let Type::Ident(ident) = &ptr.inner {
        if cx.types.cxx.contains(&ident.rust)
            && !cx.types.structs.contains_key(&ident.rust)
            && !cx.types.enums.contains_key(&ident.rust)
        {
            cx.error(ptr, error::BOX_CXX_TYPE.msg);
        }

        if Atom::from(&ident.rust).is_none() {
            return;
        }
    }

    cx.error(ptr, "unsupported target type of Box");
}

fn check_type_rust_vec(cx: &mut Check, ty: &Ty1) {
    if let Type::Ident(ident) = &ty.inner {
        if cx.types.cxx.contains(&ident.rust)
            && !cx.types.structs.contains_key(&ident.rust)
            && !cx.types.enums.contains_key(&ident.rust)
        {
            cx.error(ty, "Rust Vec containing C++ type is not supported yet");
            return;
        }

        match Atom::from(&ident.rust) {
            None | Some(U8) | Some(U16) | Some(U32) | Some(U64) | Some(Usize) | Some(I8)
            | Some(I16) | Some(I32) | Some(I64) | Some(Isize) | Some(F32) | Some(F64)
            | Some(RustString) => return,
            Some(Bool) => { /* todo */ }
            Some(CxxString) => {}
        }
    }

    cx.error(ty, "unsupported element type of Vec");
}

fn check_type_unique_ptr(cx: &mut Check, ptr: &Ty1) {
    if let Type::Ident(ident) = &ptr.inner {
        if cx.types.rust.contains(&ident.rust) {
            cx.error(ptr, "unique_ptr of a Rust type is not supported yet");
        }

        match Atom::from(&ident.rust) {
            None | Some(CxxString) => return,
            _ => {}
        }
    } else if let Type::CxxVector(_) = &ptr.inner {
        return;
    }

    cx.error(ptr, "unsupported unique_ptr target type");
}

fn check_type_cxx_vector(cx: &mut Check, ptr: &Ty1) {
    if let Type::Ident(ident) = &ptr.inner {
        if cx.types.rust.contains(&ident.rust) {
            cx.error(
                ptr,
                "C++ vector containing a Rust type is not supported yet",
            );
        }

        match Atom::from(&ident.rust) {
            None | Some(U8) | Some(U16) | Some(U32) | Some(U64) | Some(Usize) | Some(I8)
            | Some(I16) | Some(I32) | Some(I64) | Some(Isize) | Some(F32) | Some(F64)
            | Some(CxxString) => return,
            Some(Bool) | Some(RustString) => {}
        }
    }

    cx.error(ptr, "unsupported vector target type");
}

fn check_type_ref(cx: &mut Check, ty: &Ref) {
    if ty.lifetime.is_some() {
        cx.error(ty, "references with explicit lifetimes are not supported");
    }

    match ty.inner {
        Type::Fn(_) | Type::Void(_) => {}
        Type::Ref(_) => {
            cx.error(ty, "C++ does not allow references to references");
            return;
        }
        _ => return,
    }

    cx.error(ty, "unsupported reference type");
}

fn check_type_slice(cx: &mut Check, ty: &Slice) {
    cx.error(ty, "only &[u8] is supported so far, not other slice types");
}

fn check_api_struct(cx: &mut Check, strct: &Struct) {
    let name = &strct.name;
    check_reserved_name(cx, &name.rust);

    if strct.fields.is_empty() {
        let span = span_for_struct_error(strct);
        cx.error(span, "structs without any fields are not supported");
    }

    if cx.types.cxx.contains(&name.rust) {
        if let Some(ety) = cx.types.untrusted.get(&name.rust) {
            let msg = "extern shared struct must be declared in an `unsafe extern` block";
            cx.error(ety, msg);
        }
    }

    for field in &strct.fields {
        if is_unsized(cx, &field.ty) {
            let desc = describe(cx, &field.ty);
            let msg = format!("using {} by value is not supported", desc);
            cx.error(field, msg);
        }
        if let Type::Fn(_) = field.ty {
            cx.error(
                field,
                "function pointers in a struct field are not implemented yet",
            );
        }
    }
}

fn check_api_enum(cx: &mut Check, enm: &Enum) {
    check_reserved_name(cx, &enm.name.rust);

    if enm.variants.is_empty() {
        let span = span_for_enum_error(enm);
        cx.error(span, "enums without any variants are not supported");
    }
}

fn check_api_type(cx: &mut Check, ety: &ExternType) {
    check_reserved_name(cx, &ety.name.rust);

    if let Some(reason) = cx.types.required_trivial.get(&ety.name.rust) {
        let what = match reason {
            TrivialReason::StructField(strct) => format!("a field of `{}`", strct.name.rust),
            TrivialReason::FunctionArgument(efn) => format!("an argument of `{}`", efn.name.rust),
            TrivialReason::FunctionReturn(efn) => format!("a return value of `{}`", efn.name.rust),
        };
        let msg = format!(
            "needs a cxx::ExternType impl in order to be used as {}",
            what,
        );
        cx.error(ety, msg);
    }
}

fn check_api_fn(cx: &mut Check, efn: &ExternFn) {
    if let Some(receiver) = &efn.receiver {
        let ref span = span_for_receiver_error(receiver);

        if receiver.ty.is_self() {
            let mutability = match receiver.mutability {
                Some(_) => "mut ",
                None => "",
            };
            let msg = format!(
                "unnamed receiver type is only allowed if the surrounding \
                 extern block contains exactly one extern type; \
                 use `self: &{mutability}TheType`",
                mutability = mutability,
            );
            cx.error(span, msg);
        } else if !cx.types.structs.contains_key(&receiver.ty.rust)
            && !cx.types.cxx.contains(&receiver.ty.rust)
            && !cx.types.rust.contains(&receiver.ty.rust)
        {
            cx.error(span, "unrecognized receiver type");
        }

        if receiver.lifetime.is_some() {
            cx.error(span, "references with explicit lifetimes are not supported");
        }
    }

    for arg in &efn.args {
        if is_unsized(cx, &arg.ty) {
            let desc = describe(cx, &arg.ty);
            let msg = format!("passing {} by value is not supported", desc);
            cx.error(arg, msg);
        }
        if let Type::Fn(_) = arg.ty {
            if efn.lang == Lang::Rust {
                cx.error(
                    arg,
                    "passing a function pointer from C++ to Rust is not implemented yet",
                );
            }
        }
    }

    if let Some(ty) = &efn.ret {
        if is_unsized(cx, ty) {
            let desc = describe(cx, ty);
            let msg = format!("returning {} by value is not supported", desc);
            cx.error(ty, msg);
        }
        if let Type::Fn(_) = ty {
            cx.error(ty, "returning a function pointer is not implemented yet");
        }
    }

    if efn.lang == Lang::Cxx {
        check_mut_return_restriction(cx, efn);
    }

    check_multiple_arg_lifetimes(cx, efn);
}

fn check_api_impl(cx: &mut Check, imp: &Impl) {
    if let Type::UniquePtr(ty) | Type::CxxVector(ty) = &imp.ty {
        if let Type::Ident(inner) = &ty.inner {
            if Atom::from(&inner.rust).is_none() {
                return;
            }
        }
    }

    cx.error(imp, "unsupported Self type of explicit impl");
}

fn check_mut_return_restriction(cx: &mut Check, efn: &ExternFn) {
    match &efn.ret {
        Some(Type::Ref(ty)) if ty.mutability.is_some() => {}
        _ => return,
    }

    for arg in &efn.args {
        if let Type::Ref(ty) = &arg.ty {
            if ty.mutability.is_some() {
                return;
            }
        }
    }

    cx.error(
        efn,
        "&mut return type is not allowed unless there is a &mut argument",
    );
}

fn check_multiple_arg_lifetimes(cx: &mut Check, efn: &ExternFn) {
    match &efn.ret {
        Some(Type::Ref(_)) => {}
        _ => return,
    }

    let mut reference_args = 0;
    for arg in &efn.args {
        if let Type::Ref(_) = &arg.ty {
            reference_args += 1;
        }
    }

    if efn.receiver.is_some() {
        reference_args += 1;
    }

    if reference_args != 1 {
        cx.error(
            efn,
            "functions that return a reference must take exactly one input reference",
        );
    }
}

fn check_reserved_name(cx: &mut Check, ident: &Ident) {
    if ident == "Box"
        || ident == "UniquePtr"
        || ident == "Vec"
        || ident == "CxxVector"
        || Atom::from(ident).is_some()
    {
        cx.error(ident, "reserved name");
    }
}

fn is_unsized(cx: &mut Check, ty: &Type) -> bool {
    let ident = match ty {
        Type::Ident(ident) => &ident.rust,
        Type::CxxVector(_) | Type::Slice(_) | Type::Void(_) => return true,
        _ => return false,
    };
    ident == CxxString
        || cx.types.cxx.contains(ident)
            && !cx.types.structs.contains_key(ident)
            && !cx.types.enums.contains_key(ident)
            && !(cx.types.aliases.contains_key(ident)
                && cx.types.required_trivial.contains_key(ident))
        || cx.types.rust.contains(ident)
}

fn span_for_struct_error(strct: &Struct) -> TokenStream {
    let struct_token = strct.struct_token;
    let mut brace_token = Group::new(Delimiter::Brace, TokenStream::new());
    brace_token.set_span(strct.brace_token.span);
    quote!(#struct_token #brace_token)
}

fn span_for_enum_error(enm: &Enum) -> TokenStream {
    let enum_token = enm.enum_token;
    let mut brace_token = Group::new(Delimiter::Brace, TokenStream::new());
    brace_token.set_span(enm.brace_token.span);
    quote!(#enum_token #brace_token)
}

fn span_for_receiver_error(receiver: &Receiver) -> TokenStream {
    let ampersand = receiver.ampersand;
    let lifetime = &receiver.lifetime;
    let mutability = receiver.mutability;
    if receiver.shorthand {
        let var = receiver.var;
        quote!(#ampersand #lifetime #mutability #var)
    } else {
        let ty = &receiver.ty;
        quote!(#ampersand #lifetime #mutability #ty)
    }
}

fn describe(cx: &mut Check, ty: &Type) -> String {
    match ty {
        Type::Ident(ident) => {
            if cx.types.structs.contains_key(&ident.rust) {
                "struct".to_owned()
            } else if cx.types.enums.contains_key(&ident.rust) {
                "enum".to_owned()
            } else if cx.types.aliases.contains_key(&ident.rust) {
                "C++ type".to_owned()
            } else if cx.types.cxx.contains(&ident.rust) {
                "opaque C++ type".to_owned()
            } else if cx.types.rust.contains(&ident.rust) {
                "opaque Rust type".to_owned()
            } else if Atom::from(&ident.rust) == Some(CxxString) {
                "C++ string".to_owned()
            } else {
                ident.rust.to_string()
            }
        }
        Type::RustBox(_) => "Box".to_owned(),
        Type::RustVec(_) => "Vec".to_owned(),
        Type::UniquePtr(_) => "unique_ptr".to_owned(),
        Type::Ref(_) => "reference".to_owned(),
        Type::Str(_) => "&str".to_owned(),
        Type::CxxVector(_) => "C++ vector".to_owned(),
        Type::Slice(_) => "slice".to_owned(),
        Type::SliceRefU8(_) => "&[u8]".to_owned(),
        Type::Fn(_) => "function pointer".to_owned(),
        Type::Void(_) => "()".to_owned(),
    }
}
<|endoftext|>