<commit_before>use crate::config::*;
use crate::errors::RunError;
use cargo_metadata::{diagnostic::DiagnosticLevel, CargoOpt, Message, MetadataCommand};
use log::{error, trace};
use std::env;
use std::path::{Path, PathBuf};
use std::process::{Command, Stdio};
use walkdir::WalkDir;

#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct TestBinary {
    path: PathBuf,
    ty: RunType,
    cargo_dir: Option<PathBuf>,
    pkg_name: Option<String>,
    pkg_version: Option<String>,
    pkg_authors: Option<Vec<String>>,
    should_panic: bool,
}

impl TestBinary {
    pub fn new(path: PathBuf, ty: RunType) -> Self {
        Self {
            path,
            ty,
            pkg_name: None,
            pkg_version: None,
            pkg_authors: None,
            cargo_dir: None,
            should_panic: false,
        }
    }

    pub fn path(&self) -> &Path {
        &self.path
    }

    pub fn run_type(&self) -> RunType {
        self.ty
    }

    pub fn manifest_dir(&self) -> &Option<PathBuf> {
        &self.cargo_dir
    }

    pub fn pkg_name(&self) -> &Option<String> {
        &self.pkg_name
    }

    pub fn pkg_version(&self) -> &Option<String> {
        &self.pkg_version
    }

    pub fn pkg_authors(&self) -> &Option<Vec<String>> {
        &self.pkg_authors
    }

    /// Should be `false` for normal tests and for doctests either `true` or
    /// `false` depending on the test attribute
    pub fn should_panic(&self) -> bool {
        self.should_panic
    }
}

pub fn get_tests(config: &Config) -> Result<Vec<TestBinary>, RunError> {
    let mut result = vec![];
    let manifest = match config.manifest.as_path().to_str() {
        Some(s) => s,
        None => "Cargo.toml",
    };
    let metadata = MetadataCommand::new()
        .manifest_path(manifest)
        .features(CargoOpt::AllFeatures)
        .exec()
        .map_err(|e| RunError::Cargo(e.to_string()))?;

    for ty in &config.run_types {
        let mut cmd = create_command(manifest, config, ty);
        if ty != &RunType::Doctests {
            cmd.stdout(Stdio::piped());
        } else {
            cmd.stdout(Stdio::null());
        }
        if !config.verbose {
            cmd.stderr(Stdio::null());
        }
        trace!("Running command {:?}", cmd);
        let mut child = cmd.spawn().map_err(|e| RunError::Cargo(e.to_string()))?;

        if ty != &RunType::Doctests {
            let mut package_ids = vec![];
            let reader = std::io::BufReader::new(child.stdout.take().unwrap());
            for msg in Message::parse_stream(reader) {
                match msg {
                    Ok(Message::CompilerArtifact(art)) => {
                        if let Some(path) = art.executable {
                            if !art.profile.test && ty == &RunType::Tests {
                                continue;
                            }
                            result.push(TestBinary::new(path, *ty));
                            package_ids.push(art.package_id.clone());
                        }
                    }
                    Ok(Message::CompilerMessage(m)) => match m.message.level {
                        DiagnosticLevel::Error | DiagnosticLevel::Ice => {
                            let _ = child.wait();
                            return Err(RunError::TestCompile(m.message.message));
                        }
                        _ => {}
                    },
                    Err(e) => {
                        error!("Error parsing cargo messages {}", e);
                    }
                    _ => {}
                }
            }
            for (res, package) in result.iter_mut().zip(package_ids.iter()) {
                let package = &metadata[package];
                res.cargo_dir = package.manifest_path.parent().map(|x| x.to_path_buf());
                res.pkg_name = Some(package.name.clone());
                res.pkg_version = Some(package.version.to_string());
                res.pkg_authors = Some(package.authors.clone());
            }
            child.wait().map_err(|e| RunError::Cargo(e.to_string()))?;
        } else {
            // need to wait for compiling to finish before getting doctests
            // also need to wait with output to ensure the stdout buffer doesn't fill up
            let out = child
                .wait_with_output()
                .map_err(|e| RunError::Cargo(e.to_string()))?;
            if !out.status.success() {
                error!("Building doctests failed");
            }
            let walker = WalkDir::new(&config.doctest_dir()).into_iter();
            for dt in walker
                .filter_map(|e| e.ok())
                .filter(|e| match e.metadata() {
                    Ok(ref m) if m.is_file() && m.len() != 0 => true,
                    _ => false,
                })
            {
                trace!("Found doctest binary {}", dt.path().display());
                result.push(TestBinary::new(dt.path().to_path_buf(), *ty));
            }
        }
    }
    Ok(result)
}

fn create_command(manifest_path: &str, config: &Config, ty: &RunType) -> Command {
    let mut test_cmd = Command::new("cargo");
    if *ty == RunType::Doctests {
        if let Some(toolchain) = env::var("RUSTUP_TOOLCHAIN")
            .ok()
            .filter(|t| t.starts_with("nightly"))
        {
            test_cmd.args(&[format!("+{}", toolchain).as_str(), "test"]);
        } else {
            test_cmd.args(&["+nightly", "test"]);
        }
    } else {
        if let Ok(toolchain) = env::var("RUSTUP_TOOLCHAIN") {
            test_cmd.arg(format!("+{}", toolchain));
        }
        test_cmd.args(&["test", "--no-run"]);
    }
    test_cmd.args(&["--message-format", "json", "--manifest-path", manifest_path]);
    match ty {
        RunType::Tests => test_cmd.arg("--tests"),
        RunType::Doctests => test_cmd.arg("--doc"),
        RunType::Benchmarks => test_cmd.arg("--benches"),
        RunType::Examples => test_cmd.arg("--examples"),
    };
    init_args(&mut test_cmd, config);
    setup_environment(&mut test_cmd, config);
    test_cmd
}

fn init_args(test_cmd: &mut Command, config: &Config) {
    if config.debug {
        test_cmd.arg("-vvv");
    }
    if config.locked {
        test_cmd.arg("--locked");
    }
    if config.frozen {
        test_cmd.arg("--frozen");
    }
    if let Some(features) = config.features.as_ref() {
        test_cmd.arg("--features");
        test_cmd.arg(features);
    }
    if config.all_features {
        test_cmd.arg("--all-features");
    }
    if config.no_default_features {
        test_cmd.arg("--no-default-features");
    }
    if config.all {
        test_cmd.arg("--workspace");
    }
    if config.release {
        test_cmd.arg("--release");
    }
    config.packages.iter().for_each(|package| {
        test_cmd.arg("--package");
        test_cmd.arg(package);
    });
    config.exclude.iter().for_each(|package| {
        test_cmd.arg("--exclude");
        test_cmd.arg(package);
    });
    if let Some(target) = config.target.as_ref() {
        test_cmd.args(&["--target", target]);
    }
    let args = vec![
        "--target-dir".to_string(),
        format!("{}", config.target_dir().display()),
    ];
    test_cmd.args(args);
    if config.offline {
        test_cmd.arg("--offline");
    }
    for feat in &config.unstable_features {
        test_cmd.arg(format!("-Z{}", feat));
    }
    if !config.varargs.is_empty() {
        let mut args = vec!["--".to_string()];
        args.extend_from_slice(&config.varargs);
        test_cmd.args(args);
    }
}

fn setup_environment(cmd: &mut Command, config: &Config) {
    cmd.env("TARPAULIN", "1");
    let rustflags = "RUSTFLAGS";
    let common_opts = " -C relocation-model=dynamic-no-pic -C link-dead-code -C debuginfo=2 ";
    let mut value = common_opts.to_string();
    if config.release {
        value = format!("{}-C debug-assertions=off ", value);
    }
    if let Ok(vtemp) = env::var(rustflags) {
        value.push_str(vtemp.as_ref());
    }
    cmd.env(rustflags, value);
    // doesn't matter if we don't use it
    let rustdoc = "RUSTDOCFLAGS";
    let mut value = format!(
        "{} --persist-doctests {} -Z unstable-options ",
        common_opts,
        config.doctest_dir().display()
    );
    if let Ok(vtemp) = env::var(rustdoc) {
        if !vtemp.contains("--persist-doctests") {
            value.push_str(vtemp.as_ref());
        }
    }
    trace!("Setting RUSTDOCFLAGS='{}'", value);
    cmd.env(rustdoc, value);
}
<commit_msg>Start working on mapping doc test exes back to files<commit_after>use crate::config::*;
use crate::errors::RunError;
use cargo_metadata::{diagnostic::DiagnosticLevel, CargoOpt, Message, MetadataCommand};
use log::{error, trace, warn};
use std::collections::HashMap;
use std::env;
use std::path::{Path, PathBuf};
use std::process::{Command, Stdio};
use walkdir::{DirEntry, WalkDir};

#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct TestBinary {
    path: PathBuf,
    ty: RunType,
    cargo_dir: Option<PathBuf>,
    pkg_name: Option<String>,
    pkg_version: Option<String>,
    pkg_authors: Option<Vec<String>>,
    should_panic: bool,
}

impl TestBinary {
    pub fn new(path: PathBuf, ty: RunType) -> Self {
        Self {
            path,
            ty,
            pkg_name: None,
            pkg_version: None,
            pkg_authors: None,
            cargo_dir: None,
            should_panic: false,
        }
    }

    pub fn path(&self) -> &Path {
        &self.path
    }

    pub fn run_type(&self) -> RunType {
        self.ty
    }

    pub fn manifest_dir(&self) -> &Option<PathBuf> {
        &self.cargo_dir
    }

    pub fn pkg_name(&self) -> &Option<String> {
        &self.pkg_name
    }

    pub fn pkg_version(&self) -> &Option<String> {
        &self.pkg_version
    }

    pub fn pkg_authors(&self) -> &Option<Vec<String>> {
        &self.pkg_authors
    }

    /// Should be `false` for normal tests and for doctests either `true` or
    /// `false` depending on the test attribute
    pub fn should_panic(&self) -> bool {
        self.should_panic
    }
}

pub fn get_tests(config: &Config) -> Result<Vec<TestBinary>, RunError> {
    let mut result = vec![];
    let manifest = match config.manifest.as_path().to_str() {
        Some(s) => s,
        None => "Cargo.toml",
    };
    let metadata = MetadataCommand::new()
        .manifest_path(manifest)
        .features(CargoOpt::AllFeatures)
        .exec()
        .map_err(|e| RunError::Cargo(e.to_string()))?;

    for ty in &config.run_types {
        let mut cmd = create_command(manifest, config, ty);
        if ty != &RunType::Doctests {
            cmd.stdout(Stdio::piped());
        } else {
            cmd.stdout(Stdio::null());
        }
        if !config.verbose {
            cmd.stderr(Stdio::null());
        }
        trace!("Running command {:?}", cmd);
        let mut child = cmd.spawn().map_err(|e| RunError::Cargo(e.to_string()))?;

        if ty != &RunType::Doctests {
            let mut package_ids = vec![];
            let reader = std::io::BufReader::new(child.stdout.take().unwrap());
            for msg in Message::parse_stream(reader) {
                match msg {
                    Ok(Message::CompilerArtifact(art)) => {
                        if let Some(path) = art.executable {
                            if !art.profile.test && ty == &RunType::Tests {
                                continue;
                            }
                            result.push(TestBinary::new(path, *ty));
                            package_ids.push(art.package_id.clone());
                        }
                    }
                    Ok(Message::CompilerMessage(m)) => match m.message.level {
                        DiagnosticLevel::Error | DiagnosticLevel::Ice => {
                            let _ = child.wait();
                            return Err(RunError::TestCompile(m.message.message));
                        }
                        _ => {}
                    },
                    Err(e) => {
                        error!("Error parsing cargo messages {}", e);
                    }
                    _ => {}
                }
            }
            for (res, package) in result.iter_mut().zip(package_ids.iter()) {
                let package = &metadata[package];
                res.cargo_dir = package.manifest_path.parent().map(|x| x.to_path_buf());
                res.pkg_name = Some(package.name.clone());
                res.pkg_version = Some(package.version.to_string());
                res.pkg_authors = Some(package.authors.clone());
            }
            child.wait().map_err(|e| RunError::Cargo(e.to_string()))?;
        } else {
            // need to wait for compiling to finish before getting doctests
            // also need to wait with output to ensure the stdout buffer doesn't fill up
            let out = child
                .wait_with_output()
                .map_err(|e| RunError::Cargo(e.to_string()))?;
            if !out.status.success() {
                error!("Building doctests failed");
            }
            let walker = WalkDir::new(&config.doctest_dir()).into_iter();
            let dir_entries = walker
                .filter_map(|e| e.ok())
                .filter(|e| match e.metadata() {
                    Ok(ref m) if m.is_file() && m.len() != 0 => true,
                    _ => false,
                })
                .collect::<Vec<_>>();

            let should_panics = get_panic_candidates(&dir_entries, &config.root());
            for dt in &dir_entries {
                trace!("Found doctest binary {}", dt.path().display());
                result.push(TestBinary::new(dt.path().to_path_buf(), *ty));
            }
        }
    }
    Ok(result)
}

fn convert_to_prefix(p: &Path) -> Option<String> {
    p.to_str()
        .map(|s| s.replace(std::path::MAIN_SEPARATOR, "_").replace(".", "_"))
}

fn is_prefix_match(prefix: &str, entry: &DirEntry) -> bool {
    convert_to_prefix(entry.path())
        .map(|s| prefix.starts_with(&s))
        .unwrap_or(false)
}

/// This returns a map of the string prefixes for the file in the doc test and a list of lines
/// which contain the string `should_panic` it makes no guarantees that all these lines are a
/// doctest attribute showing panic behaviour (but some of them will be)
///
/// Currently all doctest files take the pattern of `{name}_{line}_{number}` where name is the
/// path to the file with directory separators and dots replaced with underscores. Therefore
/// each name could potentially map to many files as `src_some_folder_foo_rs_0_1` could go to
/// `src/some/folder_foo.rs` or `src/some/folder/foo.rs` here we're going to work on a heuristic
/// that any matching file is good because we can't do any better
fn get_panic_candidates(tests: &[DirEntry], root: &Path) -> HashMap<String, Vec<usize>> {
    let mut result = HashMap::new();

    for test in tests {
        if let Some(s) = test.path().to_str() {
            let walker = WalkDir::new(root).into_iter();
            for file in walker
                .filter_entry(|e| is_prefix_match(s, e))
                .filter_map(|e| e.ok())
            {
                trace!(
                    "Assessing {} for `should_panic` doctests",
                    file.path().display()
                );
            }
        } else {
            warn!(
                "Invalid characters in name of doctest {}",
                test.path().display()
            );
        }
    }
    result
}

fn create_command(manifest_path: &str, config: &Config, ty: &RunType) -> Command {
    let mut test_cmd = Command::new("cargo");
    if *ty == RunType::Doctests {
        if let Some(toolchain) = env::var("RUSTUP_TOOLCHAIN")
            .ok()
            .filter(|t| t.starts_with("nightly"))
        {
            test_cmd.args(&[format!("+{}", toolchain).as_str(), "test"]);
        } else {
            test_cmd.args(&["+nightly", "test"]);
        }
    } else {
        if let Ok(toolchain) = env::var("RUSTUP_TOOLCHAIN") {
            test_cmd.arg(format!("+{}", toolchain));
        }
        test_cmd.args(&["test", "--no-run"]);
    }
    test_cmd.args(&["--message-format", "json", "--manifest-path", manifest_path]);
    match ty {
        RunType::Tests => test_cmd.arg("--tests"),
        RunType::Doctests => test_cmd.arg("--doc"),
        RunType::Benchmarks => test_cmd.arg("--benches"),
        RunType::Examples => test_cmd.arg("--examples"),
    };
    init_args(&mut test_cmd, config);
    setup_environment(&mut test_cmd, config);
    test_cmd
}

fn init_args(test_cmd: &mut Command, config: &Config) {
    if config.debug {
        test_cmd.arg("-vvv");
    }
    if config.locked {
        test_cmd.arg("--locked");
    }
    if config.frozen {
        test_cmd.arg("--frozen");
    }
    if let Some(features) = config.features.as_ref() {
        test_cmd.arg("--features");
        test_cmd.arg(features);
    }
    if config.all_features {
        test_cmd.arg("--all-features");
    }
    if config.no_default_features {
        test_cmd.arg("--no-default-features");
    }
    if config.all {
        test_cmd.arg("--workspace");
    }
    if config.release {
        test_cmd.arg("--release");
    }
    config.packages.iter().for_each(|package| {
        test_cmd.arg("--package");
        test_cmd.arg(package);
    });
    config.exclude.iter().for_each(|package| {
        test_cmd.arg("--exclude");
        test_cmd.arg(package);
    });
    if let Some(target) = config.target.as_ref() {
        test_cmd.args(&["--target", target]);
    }
    let args = vec![
        "--target-dir".to_string(),
        format!("{}", config.target_dir().display()),
    ];
    test_cmd.args(args);
    if config.offline {
        test_cmd.arg("--offline");
    }
    for feat in &config.unstable_features {
        test_cmd.arg(format!("-Z{}", feat));
    }
    if !config.varargs.is_empty() {
        let mut args = vec!["--".to_string()];
        args.extend_from_slice(&config.varargs);
        test_cmd.args(args);
    }
}

fn setup_environment(cmd: &mut Command, config: &Config) {
    cmd.env("TARPAULIN", "1");
    let rustflags = "RUSTFLAGS";
    let common_opts = " -C relocation-model=dynamic-no-pic -C link-dead-code -C debuginfo=2 ";
    let mut value = common_opts.to_string();
    if config.release {
        value = format!("{}-C debug-assertions=off ", value);
    }
    if let Ok(vtemp) = env::var(rustflags) {
        value.push_str(vtemp.as_ref());
    }
    cmd.env(rustflags, value);
    // doesn't matter if we don't use it
    let rustdoc = "RUSTDOCFLAGS";
    let mut value = format!(
        "{} --persist-doctests {} -Z unstable-options ",
        common_opts,
        config.doctest_dir().display()
    );
    if let Ok(vtemp) = env::var(rustdoc) {
        if !vtemp.contains("--persist-doctests") {
            value.push_str(vtemp.as_ref());
        }
    }
    trace!("Setting RUSTDOCFLAGS='{}'", value);
    cmd.env(rustdoc, value);
}
<|endoftext|>


<commit_before>#![allow(non_upper_case_globals, dead_code)]

use std::os::raw::{c_char, c_int, c_uint, c_ulong};
use std::fmt;
use std::str;
use std::ffi::CStr;
use std::hash::Hash;
use std::hash::Hasher;
use std::ffi::CString;
use std::mem;

use clang_sys::*;

// Cursor
#[derive(Copy, Clone, Debug)]
pub struct Cursor {
    x: CXCursor,
}

pub type CursorVisitor<'s> = for<'a, 'b> FnMut(&'a Cursor, &'b Cursor) -> CXChildVisitResult + 's;

impl Cursor {
    // common
    pub fn spelling(&self) -> String {
        unsafe { String_ { x: clang_getCursorSpelling(self.x) }.to_string() }
    }

    pub fn kind(&self) -> CXCursorKind {
        unsafe { clang_getCursorKind(self.x) }
    }

    pub fn location(&self) -> SourceLocation {
        unsafe { SourceLocation { x: clang_getCursorLocation(self.x) } }
    }

    pub fn extent(&self) -> CXSourceRange {
        unsafe { clang_getCursorExtent(self.x) }
    }

    pub fn cur_type(&self) -> Type {
        unsafe { Type { x: clang_getCursorType(self.x) } }
    }

    pub fn definition(&self) -> Cursor {
        unsafe { Cursor { x: clang_getCursorDefinition(self.x) } }
    }

    pub fn canonical(&self) -> Cursor {
        unsafe { Cursor { x: clang_getCanonicalCursor(self.x) } }
    }

    pub fn visit<F>(&self, func: F)
        where F: for<'a, 'b> FnMut(&'a Cursor, &'b Cursor) -> CXChildVisitResult
    {
        let mut data: Box<CursorVisitor> = Box::new(func);
        unsafe {
            clang_visitChildren(self.x, visit_children, mem::transmute(&mut data));
        }
    }

    // bitfield
    pub fn bit_width(&self) -> Option<u32> {
        unsafe {
            let w = clang_getFieldDeclBitWidth(self.x);
            if w == -1 {
                None
            } else {
                Some(w as u32)
            }
        }
    }

    // enum
    pub fn enum_type(&self) -> Type {
        unsafe { Type { x: clang_getEnumDeclIntegerType(self.x) } }
    }

    pub fn enum_val(&self) -> i64 {
        unsafe { clang_getEnumConstantDeclValue(self.x) as i64 }
    }

    // typedef
    pub fn typedef_type(&self) -> Type {
        unsafe { Type { x: clang_getTypedefDeclUnderlyingType(self.x) } }
    }

    // function, variable
    pub fn linkage(&self) -> CXLinkageKind {
        unsafe { clang_getCursorLinkage(self.x) }
    }

    // function
    pub fn args(&self) -> Vec<Cursor> {
        unsafe {
            let num = self.num_args() as usize;
            let mut args = vec![];
            for i in 0..num {
                args.push(Cursor { x: clang_Cursor_getArgument(self.x, i as c_uint) });
            }
            args
        }
    }

    pub fn ret_type(&self) -> Type {
        unsafe { Type { x: clang_getCursorResultType(self.x) } }
    }

    pub fn num_args(&self) -> i32 {
        unsafe { clang_Cursor_getNumArguments(self.x) }
    }
}

extern "C" fn visit_children(cur: CXCursor,
                             parent: CXCursor,
                             data: CXClientData)
                             -> CXChildVisitResult {
    let func: &mut Box<CursorVisitor> = unsafe { mem::transmute(data) };
    (*func)(&Cursor { x: cur }, &Cursor { x: parent })
}

impl PartialEq for Cursor {
    fn eq(&self, other: &Cursor) -> bool {
        unsafe { clang_equalCursors(self.x, other.x) == 1 }
    }

    fn ne(&self, other: &Cursor) -> bool {
        !self.eq(other)
    }
}

impl Eq for Cursor {}

impl Hash for Cursor {
    fn hash<H: Hasher>(&self, state: &mut H) {
        self.x.kind.hash(state);
        self.x.xdata.hash(state);
        self.x.data[0].hash(state);
        self.x.data[1].hash(state);
        self.x.data[2].hash(state);
    }
}

// type
pub struct Type {
    x: CXType,
}

impl fmt::Debug for Type {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}: {}", self.name(), self.kind_name())
    }
}

impl Type {
    // common
    pub fn name(&self) -> String {
        String_ { x: unsafe { clang_getTypeSpelling(self.x) } }.to_string()
    }

    pub fn kind(&self) -> CXTypeKind {
        self.x.kind
    }

    pub fn kind_name(&self) -> String {
        String_ { x: unsafe { clang_getTypeKindSpelling(self.x.kind) } }.to_string()
    }

    pub fn declaration(&self) -> Cursor {
        unsafe { Cursor { x: clang_getTypeDeclaration(self.x) } }
    }

    pub fn is_const(&self) -> bool {
        unsafe { clang_isConstQualifiedType(self.x) == 1 }
    }

    pub fn size(&self) -> usize {
        unsafe {
            let val = clang_Type_getSizeOf(self.x);
            if val < 0 {
                0
            } else {
                val as usize
            }
        }
    }

    pub fn align(&self) -> usize {
        unsafe {
            let val = clang_Type_getAlignOf(self.x);
            if val < 0 {
                0
            } else {
                val as usize
            }
        }
    }

    // pointer
    pub fn pointee_type(&self) -> Type {
        unsafe { Type { x: clang_getPointeeType(self.x) } }
    }

    // array
    pub fn elem_type(&self) -> Type {
        unsafe { Type { x: clang_getArrayElementType(self.x) } }
    }

    pub fn array_size(&self) -> usize {
        unsafe { clang_getArraySize(self.x) as usize }
    }

    // typedef
    pub fn canonical_type(&self) -> Type {
        unsafe { Type { x: clang_getCanonicalType(self.x) } }
    }

    // function
    pub fn is_variadic(&self) -> bool {
        unsafe { clang_isFunctionTypeVariadic(self.x) == 1 }
    }

    pub fn arg_types(&self) -> Vec<Type> {
        unsafe {
            let num = clang_getNumArgTypes(self.x) as usize;
            let mut args = vec![];
            for i in 0..num {
                args.push(Type { x: clang_getArgType(self.x, i as c_uint) });
            }
            args
        }
    }

    pub fn ret_type(&self) -> Type {
        unsafe { Type { x: clang_getResultType(self.x) } }
    }

    pub fn call_conv(&self) -> CXCallingConv {
        unsafe { clang_getFunctionTypeCallingConv(self.x) }
    }
}

// SourceLocation
pub struct SourceLocation {
    x: CXSourceLocation,
}

impl SourceLocation {
    pub fn location(&self) -> (File, usize, usize, usize) {
        unsafe {
            let mut file = mem::uninitialized();
            let mut line = 0;
            let mut col = 0;
            let mut off = 0;
            clang_getSpellingLocation(self.x, &mut file, &mut line, &mut col, &mut off);
            (File { x: file }, line as usize, col as usize, off as usize)
        }
    }
}

impl fmt::Display for SourceLocation {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let (file, line, col, _) = self.location();
        if let Some(name) = file.name() {
            write!(f, "{}:{}:{}", name, line, col)
        } else {
            "builtin definitions".fmt(f)
        }
    }
}

// File
pub struct File {
    x: CXFile,
}

impl File {
    pub fn name(&self) -> Option<String> {
        if self.x.0.is_null() {
            return None;
        }
        unsafe { Some(String_ { x: clang_getFileName(self.x) }.to_string()) }
    }
}

// String
pub struct String_ {
    x: CXString,
}

impl fmt::Display for String_ {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        if self.x.data.is_null() {
            return "".fmt(f);
        }
        unsafe {
            let c_str = clang_getCString(self.x) as *const c_char;
            let p = c_str as *const _;
            str::from_utf8(CStr::from_ptr(p).to_bytes()).unwrap().to_owned().fmt(f)
        }
    }
}

// Index
pub struct Index {
    x: CXIndex,
}

impl Index {
    pub fn create(pch: bool, diag: bool) -> Index {
        unsafe { Index { x: clang_createIndex(pch as c_int, diag as c_int) } }
    }

    pub fn dispose(&self) {
        unsafe {
            clang_disposeIndex(self.x);
        }
    }

    pub fn is_null(&self) -> bool {
        self.x.0.is_null()
    }
}

// Token
pub struct Token {
    pub kind: CXTokenKind,
    pub spelling: String,
}

// TranslationUnit
pub struct TranslationUnit {
    x: CXTranslationUnit,
}

impl TranslationUnit {
    pub fn parse(ix: &Index,
                 file: &str,
                 cmd_args: &[String],
                 unsaved: &[UnsavedFile],
                 opts: CXTranslationUnit_Flags)
                 -> TranslationUnit {
        let fname = CString::new(file.as_bytes()).unwrap();
        let fname = fname.as_ptr();
        let c_args: Vec<CString> = cmd_args.iter()
                                           .map(|s| CString::new(s.as_bytes()).unwrap())
                                           .collect();
        let c_args: Vec<*const c_char> = c_args.iter().map(|s| s.as_ptr()).collect();
        let mut c_unsaved: Vec<CXUnsavedFile> = unsaved.iter().map(|f| f.x).collect();
        let tu = unsafe {
            clang_parseTranslationUnit(ix.x,
                                       fname,
                                       c_args.as_ptr(),
                                       c_args.len() as c_int,
                                       c_unsaved.as_mut_ptr(),
                                       c_unsaved.len() as c_uint,
                                       opts)
        };
        TranslationUnit { x: tu }
    }

    pub fn reparse(&self, unsaved: &[UnsavedFile], opts: CXReparse_Flags) -> bool {
        let mut c_unsaved: Vec<CXUnsavedFile> = unsaved.iter().map(|f| f.x).collect();

        unsafe {
            clang_reparseTranslationUnit(self.x,
                                         c_unsaved.len() as c_uint,
                                         c_unsaved.as_mut_ptr(),
                                         opts) == CXErrorCode::Success
        }
    }

    pub fn diags(&self) -> Vec<Diagnostic> {
        unsafe {
            let num = clang_getNumDiagnostics(self.x) as usize;
            let mut diags = vec![];
            for i in 0..num {
                diags.push(Diagnostic { x: clang_getDiagnostic(self.x, i as c_uint) });
            }
            diags
        }
    }

    pub fn cursor(&self) -> Cursor {
        unsafe { Cursor { x: clang_getTranslationUnitCursor(self.x) } }
    }

    pub fn dispose(&self) {
        unsafe {
            clang_disposeTranslationUnit(self.x);
        }
    }

    pub fn is_null(&self) -> bool {
        self.x.0.is_null()
    }

    pub fn tokens(&self, cursor: &Cursor) -> Option<Vec<Token>> {
        let range = cursor.extent();
        let mut tokens = vec![];
        unsafe {
            let mut token_ptr = ::std::ptr::null_mut();
            let mut num_tokens: c_uint = 0;
            clang_tokenize(self.x, range, &mut token_ptr, &mut num_tokens);
            if token_ptr.is_null() {
                return None;
            }
            let token_array = ::std::slice::from_raw_parts(token_ptr, num_tokens as usize);
            for &token in token_array.iter() {
                let kind = clang_getTokenKind(token);
                let spelling = String_ { x: clang_getTokenSpelling(self.x, token) }.to_string();
                tokens.push(Token {
                    kind: kind,
                    spelling: spelling,
                });
            }
            clang_disposeTokens(self.x, token_ptr, num_tokens);
        }
        Some(tokens)
    }
}

// Diagnostic
pub struct Diagnostic {
    x: CXDiagnostic,
}

impl Diagnostic {
    pub fn default_opts() -> CXDiagnosticDisplayOptions {
        unsafe { clang_defaultDiagnosticDisplayOptions() }
    }

    pub fn format(&self, opts: CXDiagnosticDisplayOptions) -> String {
        unsafe { String_ { x: clang_formatDiagnostic(self.x, opts) }.to_string() }
    }

    pub fn severity(&self) -> CXDiagnosticSeverity {
        unsafe { clang_getDiagnosticSeverity(self.x) }
    }

    pub fn dispose(&self) {
        unsafe {
            clang_disposeDiagnostic(self.x);
        }
    }
}

// UnsavedFile
pub struct UnsavedFile {
    x: CXUnsavedFile,
    name: CString,
    contents: CString,
}

impl UnsavedFile {
    pub fn new(name: &str, contents: &str) -> UnsavedFile {
        let name = CString::new(name.as_bytes()).unwrap();
        let contents = CString::new(contents.as_bytes()).unwrap();
        let x = CXUnsavedFile {
            Filename: name.as_ptr(),
            Contents: contents.as_ptr(),
            Length: contents.as_bytes().len() as c_ulong,
        };
        UnsavedFile {
            x: x,
            name: name,
            contents: contents,
        }
    }
}

// Debug
pub fn ast_dump(c: &Cursor, depth: isize) -> CXChildVisitResult {
    fn print_indent(depth: isize, s: &str) {
        let mut i = 0;
        while i < depth {
            print!("\t");
            i += 1;
        }
        println!("{}", s);
    }
    let ct = c.cur_type().kind();
    print_indent(depth,
                 &format!("({:?} {} {:?}", c.kind(), c.spelling(), ct)[..]);
    c.visit(|s, _: &Cursor| ast_dump(s, depth + 1));
    print_indent(depth, ")");
    CXChildVisitResult::Continue
}
<commit_msg>Remove unnecessary transmute<commit_after>#![allow(non_upper_case_globals, dead_code)]

use std::os::raw::{c_char, c_int, c_uint, c_ulong};
use std::fmt;
use std::str;
use std::ffi::CStr;
use std::hash::Hash;
use std::hash::Hasher;
use std::ffi::CString;
use std::mem;

use clang_sys::*;

// Cursor
#[derive(Copy, Clone, Debug)]
pub struct Cursor {
    x: CXCursor,
}

impl Cursor {
    // common
    pub fn spelling(&self) -> String {
        unsafe { String_ { x: clang_getCursorSpelling(self.x) }.to_string() }
    }

    pub fn kind(&self) -> CXCursorKind {
        unsafe { clang_getCursorKind(self.x) }
    }

    pub fn location(&self) -> SourceLocation {
        unsafe { SourceLocation { x: clang_getCursorLocation(self.x) } }
    }

    pub fn extent(&self) -> CXSourceRange {
        unsafe { clang_getCursorExtent(self.x) }
    }

    pub fn cur_type(&self) -> Type {
        unsafe { Type { x: clang_getCursorType(self.x) } }
    }

    pub fn definition(&self) -> Cursor {
        unsafe { Cursor { x: clang_getCursorDefinition(self.x) } }
    }

    pub fn canonical(&self) -> Cursor {
        unsafe { Cursor { x: clang_getCanonicalCursor(self.x) } }
    }

    pub fn visit<F>(&self, func: F)
        where F: for<'a, 'b> FnMut(&'a Cursor, &'b Cursor) -> CXChildVisitResult
    {
        unsafe {
            clang_visitChildren(self.x, visit_children::<F>, &func as *const F as CXClientData);
        }
    }

    // bitfield
    pub fn bit_width(&self) -> Option<u32> {
        unsafe {
            let w = clang_getFieldDeclBitWidth(self.x);
            if w == -1 {
                None
            } else {
                Some(w as u32)
            }
        }
    }

    // enum
    pub fn enum_type(&self) -> Type {
        unsafe { Type { x: clang_getEnumDeclIntegerType(self.x) } }
    }

    pub fn enum_val(&self) -> i64 {
        unsafe { clang_getEnumConstantDeclValue(self.x) as i64 }
    }

    // typedef
    pub fn typedef_type(&self) -> Type {
        unsafe { Type { x: clang_getTypedefDeclUnderlyingType(self.x) } }
    }

    // function, variable
    pub fn linkage(&self) -> CXLinkageKind {
        unsafe { clang_getCursorLinkage(self.x) }
    }

    // function
    pub fn args(&self) -> Vec<Cursor> {
        unsafe {
            let num = self.num_args() as usize;
            let mut args = vec![];
            for i in 0..num {
                args.push(Cursor { x: clang_Cursor_getArgument(self.x, i as c_uint) });
            }
            args
        }
    }

    pub fn ret_type(&self) -> Type {
        unsafe { Type { x: clang_getCursorResultType(self.x) } }
    }

    pub fn num_args(&self) -> i32 {
        unsafe { clang_Cursor_getNumArguments(self.x) }
    }
}

extern "C" fn visit_children<F>(cur: CXCursor,
                                parent: CXCursor,
                                data: CXClientData)
                                -> CXChildVisitResult
    where F: for<'a, 'b> FnMut(&'a Cursor, &'b Cursor) -> CXChildVisitResult
{
    let func = unsafe { &mut *(data as *mut F) };
    func(&Cursor { x: cur }, &Cursor { x: parent })
}

impl PartialEq for Cursor {
    fn eq(&self, other: &Cursor) -> bool {
        unsafe { clang_equalCursors(self.x, other.x) == 1 }
    }

    fn ne(&self, other: &Cursor) -> bool {
        !self.eq(other)
    }
}

impl Eq for Cursor {}

impl Hash for Cursor {
    fn hash<H: Hasher>(&self, state: &mut H) {
        self.x.kind.hash(state);
        self.x.xdata.hash(state);
        self.x.data[0].hash(state);
        self.x.data[1].hash(state);
        self.x.data[2].hash(state);
    }
}

// type
pub struct Type {
    x: CXType,
}

impl fmt::Debug for Type {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}: {}", self.name(), self.kind_name())
    }
}

impl Type {
    // common
    pub fn name(&self) -> String {
        String_ { x: unsafe { clang_getTypeSpelling(self.x) } }.to_string()
    }

    pub fn kind(&self) -> CXTypeKind {
        self.x.kind
    }

    pub fn kind_name(&self) -> String {
        String_ { x: unsafe { clang_getTypeKindSpelling(self.x.kind) } }.to_string()
    }

    pub fn declaration(&self) -> Cursor {
        unsafe { Cursor { x: clang_getTypeDeclaration(self.x) } }
    }

    pub fn is_const(&self) -> bool {
        unsafe { clang_isConstQualifiedType(self.x) == 1 }
    }

    pub fn size(&self) -> usize {
        unsafe {
            let val = clang_Type_getSizeOf(self.x);
            if val < 0 {
                0
            } else {
                val as usize
            }
        }
    }

    pub fn align(&self) -> usize {
        unsafe {
            let val = clang_Type_getAlignOf(self.x);
            if val < 0 {
                0
            } else {
                val as usize
            }
        }
    }

    // pointer
    pub fn pointee_type(&self) -> Type {
        unsafe { Type { x: clang_getPointeeType(self.x) } }
    }

    // array
    pub fn elem_type(&self) -> Type {
        unsafe { Type { x: clang_getArrayElementType(self.x) } }
    }

    pub fn array_size(&self) -> usize {
        unsafe { clang_getArraySize(self.x) as usize }
    }

    // typedef
    pub fn canonical_type(&self) -> Type {
        unsafe { Type { x: clang_getCanonicalType(self.x) } }
    }

    // function
    pub fn is_variadic(&self) -> bool {
        unsafe { clang_isFunctionTypeVariadic(self.x) == 1 }
    }

    pub fn arg_types(&self) -> Vec<Type> {
        unsafe {
            let num = clang_getNumArgTypes(self.x) as usize;
            let mut args = vec![];
            for i in 0..num {
                args.push(Type { x: clang_getArgType(self.x, i as c_uint) });
            }
            args
        }
    }

    pub fn ret_type(&self) -> Type {
        unsafe { Type { x: clang_getResultType(self.x) } }
    }

    pub fn call_conv(&self) -> CXCallingConv {
        unsafe { clang_getFunctionTypeCallingConv(self.x) }
    }
}

// SourceLocation
pub struct SourceLocation {
    x: CXSourceLocation,
}

impl SourceLocation {
    pub fn location(&self) -> (File, usize, usize, usize) {
        unsafe {
            let mut file = mem::uninitialized();
            let mut line = 0;
            let mut col = 0;
            let mut off = 0;
            clang_getSpellingLocation(self.x, &mut file, &mut line, &mut col, &mut off);
            (File { x: file }, line as usize, col as usize, off as usize)
        }
    }
}

impl fmt::Display for SourceLocation {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let (file, line, col, _) = self.location();
        if let Some(name) = file.name() {
            write!(f, "{}:{}:{}", name, line, col)
        } else {
            "builtin definitions".fmt(f)
        }
    }
}

// File
pub struct File {
    x: CXFile,
}

impl File {
    pub fn name(&self) -> Option<String> {
        if self.x.0.is_null() {
            return None;
        }
        unsafe { Some(String_ { x: clang_getFileName(self.x) }.to_string()) }
    }
}

// String
pub struct String_ {
    x: CXString,
}

impl fmt::Display for String_ {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        if self.x.data.is_null() {
            return "".fmt(f);
        }
        unsafe {
            let c_str = clang_getCString(self.x) as *const c_char;
            let p = c_str as *const _;
            str::from_utf8(CStr::from_ptr(p).to_bytes()).unwrap().to_owned().fmt(f)
        }
    }
}

// Index
pub struct Index {
    x: CXIndex,
}

impl Index {
    pub fn create(pch: bool, diag: bool) -> Index {
        unsafe { Index { x: clang_createIndex(pch as c_int, diag as c_int) } }
    }

    pub fn dispose(&self) {
        unsafe {
            clang_disposeIndex(self.x);
        }
    }

    pub fn is_null(&self) -> bool {
        self.x.0.is_null()
    }
}

// Token
pub struct Token {
    pub kind: CXTokenKind,
    pub spelling: String,
}

// TranslationUnit
pub struct TranslationUnit {
    x: CXTranslationUnit,
}

impl TranslationUnit {
    pub fn parse(ix: &Index,
                 file: &str,
                 cmd_args: &[String],
                 unsaved: &[UnsavedFile],
                 opts: CXTranslationUnit_Flags)
                 -> TranslationUnit {
        let fname = CString::new(file.as_bytes()).unwrap();
        let fname = fname.as_ptr();
        let c_args: Vec<CString> = cmd_args.iter()
                                           .map(|s| CString::new(s.as_bytes()).unwrap())
                                           .collect();
        let c_args: Vec<*const c_char> = c_args.iter().map(|s| s.as_ptr()).collect();
        let mut c_unsaved: Vec<CXUnsavedFile> = unsaved.iter().map(|f| f.x).collect();
        let tu = unsafe {
            clang_parseTranslationUnit(ix.x,
                                       fname,
                                       c_args.as_ptr(),
                                       c_args.len() as c_int,
                                       c_unsaved.as_mut_ptr(),
                                       c_unsaved.len() as c_uint,
                                       opts)
        };
        TranslationUnit { x: tu }
    }

    pub fn reparse(&self, unsaved: &[UnsavedFile], opts: CXReparse_Flags) -> bool {
        let mut c_unsaved: Vec<CXUnsavedFile> = unsaved.iter().map(|f| f.x).collect();

        unsafe {
            clang_reparseTranslationUnit(self.x,
                                         c_unsaved.len() as c_uint,
                                         c_unsaved.as_mut_ptr(),
                                         opts) == CXErrorCode::Success
        }
    }

    pub fn diags(&self) -> Vec<Diagnostic> {
        unsafe {
            let num = clang_getNumDiagnostics(self.x) as usize;
            let mut diags = vec![];
            for i in 0..num {
                diags.push(Diagnostic { x: clang_getDiagnostic(self.x, i as c_uint) });
            }
            diags
        }
    }

    pub fn cursor(&self) -> Cursor {
        unsafe { Cursor { x: clang_getTranslationUnitCursor(self.x) } }
    }

    pub fn dispose(&self) {
        unsafe {
            clang_disposeTranslationUnit(self.x);
        }
    }

    pub fn is_null(&self) -> bool {
        self.x.0.is_null()
    }

    pub fn tokens(&self, cursor: &Cursor) -> Option<Vec<Token>> {
        let range = cursor.extent();
        let mut tokens = vec![];
        unsafe {
            let mut token_ptr = ::std::ptr::null_mut();
            let mut num_tokens: c_uint = 0;
            clang_tokenize(self.x, range, &mut token_ptr, &mut num_tokens);
            if token_ptr.is_null() {
                return None;
            }
            let token_array = ::std::slice::from_raw_parts(token_ptr, num_tokens as usize);
            for &token in token_array.iter() {
                let kind = clang_getTokenKind(token);
                let spelling = String_ { x: clang_getTokenSpelling(self.x, token) }.to_string();
                tokens.push(Token {
                    kind: kind,
                    spelling: spelling,
                });
            }
            clang_disposeTokens(self.x, token_ptr, num_tokens);
        }
        Some(tokens)
    }
}

// Diagnostic
pub struct Diagnostic {
    x: CXDiagnostic,
}

impl Diagnostic {
    pub fn default_opts() -> CXDiagnosticDisplayOptions {
        unsafe { clang_defaultDiagnosticDisplayOptions() }
    }

    pub fn format(&self, opts: CXDiagnosticDisplayOptions) -> String {
        unsafe { String_ { x: clang_formatDiagnostic(self.x, opts) }.to_string() }
    }

    pub fn severity(&self) -> CXDiagnosticSeverity {
        unsafe { clang_getDiagnosticSeverity(self.x) }
    }

    pub fn dispose(&self) {
        unsafe {
            clang_disposeDiagnostic(self.x);
        }
    }
}

// UnsavedFile
pub struct UnsavedFile {
    x: CXUnsavedFile,
    name: CString,
    contents: CString,
}

impl UnsavedFile {
    pub fn new(name: &str, contents: &str) -> UnsavedFile {
        let name = CString::new(name.as_bytes()).unwrap();
        let contents = CString::new(contents.as_bytes()).unwrap();
        let x = CXUnsavedFile {
            Filename: name.as_ptr(),
            Contents: contents.as_ptr(),
            Length: contents.as_bytes().len() as c_ulong,
        };
        UnsavedFile {
            x: x,
            name: name,
            contents: contents,
        }
    }
}

// Debug
pub fn ast_dump(c: &Cursor, depth: isize) -> CXChildVisitResult {
    fn print_indent(depth: isize, s: &str) {
        let mut i = 0;
        while i < depth {
            print!("\t");
            i += 1;
        }
        println!("{}", s);
    }
    let ct = c.cur_type().kind();
    print_indent(depth,
                 &format!("({:?} {} {:?}", c.kind(), c.spelling(), ct)[..]);
    c.visit(|s, _: &Cursor| ast_dump(s, depth + 1));
    print_indent(depth, ")");
    CXChildVisitResult::Continue
}
<|endoftext|>


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

use crate::{
    additional_cpp_generator::AdditionalNeed,
    conversion::{
        api::{Api, Use},
        ConvertError,
    },
    known_types::KNOWN_TYPES,
    type_to_cpp::type_to_cpp,
    types::{make_ident, Namespace, TypeName},
};
use quote::quote;
use std::collections::{HashMap, HashSet};
use syn::{
    parse_quote, punctuated::Punctuated, ForeignItem, GenericArgument, Item, PathArguments,
    PathSegment, Type, TypePath, TypePtr,
};

use super::non_pod_struct::new_non_pod_struct;

/// Results of some type conversion, annotated with a list of every type encountered,
/// and optionally any extra API we need in order to use this type.
pub(crate) struct Annotated<T> {
    pub(crate) ty: T,
    pub(crate) types_encountered: HashSet<TypeName>,
    pub(crate) extra_apis: Vec<Api>,
}

impl<T> Annotated<T> {
    fn new(ty: T, types_encountered: HashSet<TypeName>, extra_apis: Vec<Api>) -> Self {
        Self {
            ty,
            types_encountered,
            extra_apis,
        }
    }

    fn map<T2, F: FnOnce(T) -> T2>(self, fun: F) -> Annotated<T2> {
        Annotated {
            ty: fun(self.ty),
            types_encountered: self.types_encountered,
            extra_apis: self.extra_apis,
        }
    }
}

pub(crate) struct TypeConverter {
    types_found: Vec<TypeName>,
    typedefs: HashMap<TypeName, Type>,
    concrete_templates: HashMap<String, TypeName>,
}

impl TypeConverter {
    pub(crate) fn new() -> Self {
        Self {
            types_found: Vec::new(),
            typedefs: HashMap::new(),
            concrete_templates: HashMap::new(),
        }
    }

    pub(crate) fn push(&mut self, ty: TypeName) {
        self.types_found.push(ty);
    }

    pub(crate) fn insert_typedef(&mut self, id: TypeName, target: Type) {
        self.typedefs.insert(id, target);
    }

    pub(crate) fn convert_boxed_type(
        &mut self,
        ty: Box<Type>,
        ns: &Namespace,
    ) -> Result<Annotated<Box<Type>>, ConvertError> {
        Ok(self.convert_type(*ty, ns)?.map(Box::new))
    }

    pub(crate) fn convert_type(
        &mut self,
        ty: Type,
        ns: &Namespace,
    ) -> Result<Annotated<Type>, ConvertError> {
        let result = match ty {
            Type::Path(p) => {
                let newp = self.convert_type_path(p, ns)?;
                if let Type::Path(newpp) = &newp.ty {
                    // Special handling because rust_Str (as emitted by bindgen)
                    // doesn't simply get renamed to a different type _identifier_.
                    // This plain type-by-value (as far as bindgen is concerned)
                    // is actually a &str.
                    if KNOWN_TYPES.should_dereference_in_cpp(newpp) {
                        Annotated::new(
                            Type::Reference(parse_quote! {
                                &str
                            }),
                            newp.types_encountered,
                            newp.extra_apis,
                        )
                    } else {
                        newp
                    }
                } else {
                    newp
                }
            }
            Type::Reference(mut r) => {
                let innerty = self.convert_boxed_type(r.elem, ns)?;
                r.elem = innerty.ty;
                Annotated::new(
                    Type::Reference(r),
                    innerty.types_encountered,
                    innerty.extra_apis,
                )
            }
            Type::Ptr(ptr) => self.convert_ptr_to_reference(ptr, ns)?,
            _ => Annotated::new(ty, HashSet::new(), Vec::new()),
        };
        Ok(result)
    }

    fn convert_type_path(
        &mut self,
        mut typ: TypePath,
        ns: &Namespace,
    ) -> Result<Annotated<Type>, ConvertError> {
        let mut types_encountered = HashSet::new();
        if typ.path.segments.iter().next().unwrap().ident != "root" {
            let ty = TypeName::from_type_path(&typ);
            // If the type looks like it is unqualified, check we know it
            // already, and if not, qualify it according to the current
            // namespace. This is a bit of a shortcut compared to having a full
            // resolution pass which can search all known namespaces.
            if !KNOWN_TYPES.is_known_type(&ty) {
                let num_segments = typ.path.segments.len();
                if num_segments > 1 {
                    return Err(ConvertError::UnsupportedBuiltInType(ty));
                }
                if !self.types_found.contains(&ty) {
                    typ.path.segments = std::iter::once(&"root".to_string())
                        .chain(ns.iter())
                        .map(|s| {
                            let i = make_ident(s);
                            parse_quote! { #i }
                        })
                        .chain(typ.path.segments.into_iter())
                        .collect();
                }
            }
        }

        typ.path.segments = typ
            .path
            .segments
            .into_iter()
            .map(|s| -> Result<PathSegment, ConvertError> {
                let ident = &s.ident;
                let args = match s.arguments {
                    PathArguments::AngleBracketed(mut ab) => {
                        let mut innerty = self.convert_punctuated(ab.args, ns)?;
                        ab.args = innerty.ty;
                        types_encountered.extend(innerty.types_encountered.drain());
                        PathArguments::AngleBracketed(ab)
                    }
                    _ => s.arguments,
                };
                Ok(parse_quote!( #ident #args ))
            })
            .collect::<Result<_, _>>()?;

        let mut last_seg_args = Self::get_last_segment_args(&typ);

        let mut tn = TypeName::from_type_path(&typ);
        types_encountered.insert(tn.clone());
        // Let's see if this is a typedef.
        let typ = match self.resolve_typedef(&tn) {
            None => typ,
            Some(Type::Path(resolved_tp)) => {
                types_encountered.insert(TypeName::from_type_path(&resolved_tp));
                let typedef_target_args = Self::get_last_segment_args(&resolved_tp);
                if let Some(typedef_target_args) = typedef_target_args {
                    if last_seg_args.is_some() {
                        return Err(ConvertError::ConflictingTemplatedArgsWithTypedef(tn));
                    }
                    last_seg_args = Some(typedef_target_args);
                    tn = TypeName::from_type_path(&resolved_tp);
                }
                resolved_tp.clone()
            }
            Some(other) => return Ok(Annotated::new(other.clone(), HashSet::new(), Vec::new())),
        };

        // This will strip off any path arguments...
        let mut typ = KNOWN_TYPES.known_type_substitute_path(&typ).unwrap_or(typ);
        let mut extra_apis = Vec::new();
        // but then we'll put them back again as necessary.
        if let Some(last_seg_args) = last_seg_args {
            let last_seg = typ.path.segments.last_mut().unwrap();
            last_seg.arguments = last_seg_args;
            // Is it one of the things built into cxx?
            if !KNOWN_TYPES.is_cxx_acceptable_generic(&tn) {
                // Oh poop. It's a generic type which cxx won't be able to handle.
                // We'll have to come up with a concrete type in both the cxx::bridge (in Rust)
                // and a corresponding typedef in C++.
                let (new_tn, api) = self.get_templated_typename(&Type::Path(typ));
                typ = new_tn.to_type_path();
                extra_apis.extend(api.into_iter());
                types_encountered.remove(&tn);
                types_encountered.insert(new_tn);
            }
        }
        Ok(Annotated::new(
            Type::Path(typ),
            types_encountered,
            extra_apis,
        ))
    }

    fn get_last_segment_args(typ: &TypePath) -> Option<PathArguments> {
        let mut seg_iter = typ.path.segments.iter().peekable();
        while let Some(seg) = seg_iter.next() {
            if !seg.arguments.is_empty() {
                if seg_iter.peek().is_some() {
                    panic!("Did not expect bindgen to create a type with path arguments on a non-final segment")
                } else {
                    return Some(seg.arguments.clone());
                }
            }
        }
        None
    }

    fn convert_punctuated<P>(
        &mut self,
        pun: Punctuated<GenericArgument, P>,
        ns: &Namespace,
    ) -> Result<Annotated<Punctuated<GenericArgument, P>>, ConvertError>
    where
        P: Default,
    {
        let mut new_pun = Punctuated::new();
        let mut types_encountered = HashSet::new();
        let mut extra_apis = Vec::new();
        for arg in pun.into_iter() {
            new_pun.push(match arg {
                GenericArgument::Type(t) => {
                    let mut innerty = self.convert_type(t, ns)?;
                    types_encountered.extend(innerty.types_encountered.drain());
                    extra_apis.extend(innerty.extra_apis.drain(..));
                    GenericArgument::Type(innerty.ty)
                }
                _ => arg,
            });
        }
        Ok(Annotated::new(new_pun, types_encountered, extra_apis))
    }

    fn resolve_typedef<'b>(&'b self, tn: &TypeName) -> Option<&'b Type> {
        self.typedefs.get(&tn).and_then(|resolution| match resolution {
            Type::Path(typ) => {
                let tn = TypeName::from_type_path(typ);
                Some(self.resolve_typedef(&tn).unwrap_or(resolution))
            }
            _ => Some(resolution),
        })
    }

    fn convert_ptr_to_reference(
        &mut self,
        ptr: TypePtr,
        ns: &Namespace,
    ) -> Result<Annotated<Type>, ConvertError> {
        let mutability = ptr.mutability;
        let elem = self.convert_boxed_type(ptr.elem, ns)?;
        // TODO - in the future, we should check if this is a rust::Str and throw
        // a wobbler if not. rust::Str should only be seen _by value_ in C++
        // headers; it manifests as &str in Rust but on the C++ side it must
        // be a plain value. We should detect and abort.
        Ok(elem.map(|elem| match mutability {
            Some(_) => Type::Path(parse_quote! {
                Pin < & #mutability #elem >
            }),
            None => Type::Reference(parse_quote! {
                & #elem
            }),
        }))
    }

    fn add_concrete_type(&self, tyname: &TypeName, rs_definition: &Type) -> Api {
        let final_ident = make_ident(tyname.get_final_ident());
        let bridge_item = Some(Item::Impl(parse_quote! {
            impl UniquePtr<#final_ident> {}
        }));
        let mut fulltypath: Vec<_> = ["bindgen", "root"].iter().map(|x| make_ident(x)).collect();
        fulltypath.push(final_ident.clone());
        let tynamestring = tyname.to_cpp_name();
        Api {
            ns: tyname.get_namespace().clone(),
            id: final_ident.clone(),
            use_stmt: Use::Unused,
            global_items: vec![Item::Impl(parse_quote! {
                unsafe impl cxx::ExternType for #(#fulltypath)::* {
                    type Id = cxx::type_id!(#tynamestring);
                    type Kind = cxx::kind::Opaque;
                }
            })],
            bridge_item,
            extern_c_mod_item: Some(ForeignItem::Verbatim(quote! {
                type #final_ident = super::bindgen::root::#final_ident;
            })),
            additional_cpp: Some(AdditionalNeed::ConcreteTemplatedTypeTypedef(
                tyname.clone(),
                Box::new(rs_definition.clone()),
            )),
            deps: HashSet::new(),
            id_for_allowlist: None,
            bindgen_mod_item: Some(Item::Struct(new_non_pod_struct(final_ident))),
            impl_entry: None,
        }
    }

    fn get_templated_typename(&mut self, rs_definition: &Type) -> (TypeName, Option<Api>) {
        let count = self.concrete_templates.len();
        // We just use this as a hash key, essentially.
        let cpp_definition = type_to_cpp(rs_definition);
        let e = self.concrete_templates.get(&cpp_definition);
        match e {
            Some(tn) => (tn.clone(), None),
            None => {
                let tn = TypeName::new(&Namespace::new(), &format!("AutocxxConcrete{}", count));
                self.concrete_templates
                    .insert(cpp_definition.clone(), tn.clone());
                let api = self.add_concrete_type(&tn, rs_definition);
                (tn, Some(api))
            }
        }
    }
}
<commit_msg>Unrelated clippy improvement.<commit_after>// Copyright 2020 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//    https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use crate::{
    additional_cpp_generator::AdditionalNeed,
    conversion::{
        api::{Api, Use},
        ConvertError,
    },
    known_types::KNOWN_TYPES,
    type_to_cpp::type_to_cpp,
    types::{make_ident, Namespace, TypeName},
};
use quote::quote;
use std::collections::{HashMap, HashSet};
use syn::{
    parse_quote, punctuated::Punctuated, ForeignItem, GenericArgument, Item, PathArguments,
    PathSegment, Type, TypePath, TypePtr,
};

use super::non_pod_struct::new_non_pod_struct;

/// Results of some type conversion, annotated with a list of every type encountered,
/// and optionally any extra API we need in order to use this type.
pub(crate) struct Annotated<T> {
    pub(crate) ty: T,
    pub(crate) types_encountered: HashSet<TypeName>,
    pub(crate) extra_apis: Vec<Api>,
}

impl<T> Annotated<T> {
    fn new(ty: T, types_encountered: HashSet<TypeName>, extra_apis: Vec<Api>) -> Self {
        Self {
            ty,
            types_encountered,
            extra_apis,
        }
    }

    fn map<T2, F: FnOnce(T) -> T2>(self, fun: F) -> Annotated<T2> {
        Annotated {
            ty: fun(self.ty),
            types_encountered: self.types_encountered,
            extra_apis: self.extra_apis,
        }
    }
}

pub(crate) struct TypeConverter {
    types_found: Vec<TypeName>,
    typedefs: HashMap<TypeName, Type>,
    concrete_templates: HashMap<String, TypeName>,
}

impl TypeConverter {
    pub(crate) fn new() -> Self {
        Self {
            types_found: Vec::new(),
            typedefs: HashMap::new(),
            concrete_templates: HashMap::new(),
        }
    }

    pub(crate) fn push(&mut self, ty: TypeName) {
        self.types_found.push(ty);
    }

    pub(crate) fn insert_typedef(&mut self, id: TypeName, target: Type) {
        self.typedefs.insert(id, target);
    }

    pub(crate) fn convert_boxed_type(
        &mut self,
        ty: Box<Type>,
        ns: &Namespace,
    ) -> Result<Annotated<Box<Type>>, ConvertError> {
        Ok(self.convert_type(*ty, ns)?.map(Box::new))
    }

    pub(crate) fn convert_type(
        &mut self,
        ty: Type,
        ns: &Namespace,
    ) -> Result<Annotated<Type>, ConvertError> {
        let result = match ty {
            Type::Path(p) => {
                let newp = self.convert_type_path(p, ns)?;
                if let Type::Path(newpp) = &newp.ty {
                    // Special handling because rust_Str (as emitted by bindgen)
                    // doesn't simply get renamed to a different type _identifier_.
                    // This plain type-by-value (as far as bindgen is concerned)
                    // is actually a &str.
                    if KNOWN_TYPES.should_dereference_in_cpp(newpp) {
                        Annotated::new(
                            Type::Reference(parse_quote! {
                                &str
                            }),
                            newp.types_encountered,
                            newp.extra_apis,
                        )
                    } else {
                        newp
                    }
                } else {
                    newp
                }
            }
            Type::Reference(mut r) => {
                let innerty = self.convert_boxed_type(r.elem, ns)?;
                r.elem = innerty.ty;
                Annotated::new(
                    Type::Reference(r),
                    innerty.types_encountered,
                    innerty.extra_apis,
                )
            }
            Type::Ptr(ptr) => self.convert_ptr_to_reference(ptr, ns)?,
            _ => Annotated::new(ty, HashSet::new(), Vec::new()),
        };
        Ok(result)
    }

    fn convert_type_path(
        &mut self,
        mut typ: TypePath,
        ns: &Namespace,
    ) -> Result<Annotated<Type>, ConvertError> {
        let mut types_encountered = HashSet::new();
        if typ.path.segments.iter().next().unwrap().ident != "root" {
            let ty = TypeName::from_type_path(&typ);
            // If the type looks like it is unqualified, check we know it
            // already, and if not, qualify it according to the current
            // namespace. This is a bit of a shortcut compared to having a full
            // resolution pass which can search all known namespaces.
            if !KNOWN_TYPES.is_known_type(&ty) {
                let num_segments = typ.path.segments.len();
                if num_segments > 1 {
                    return Err(ConvertError::UnsupportedBuiltInType(ty));
                }
                if !self.types_found.contains(&ty) {
                    typ.path.segments = std::iter::once(&"root".to_string())
                        .chain(ns.iter())
                        .map(|s| {
                            let i = make_ident(s);
                            parse_quote! { #i }
                        })
                        .chain(typ.path.segments.into_iter())
                        .collect();
                }
            }
        }

        typ.path.segments = typ
            .path
            .segments
            .into_iter()
            .map(|s| -> Result<PathSegment, ConvertError> {
                let ident = &s.ident;
                let args = match s.arguments {
                    PathArguments::AngleBracketed(mut ab) => {
                        let mut innerty = self.convert_punctuated(ab.args, ns)?;
                        ab.args = innerty.ty;
                        types_encountered.extend(innerty.types_encountered.drain());
                        PathArguments::AngleBracketed(ab)
                    }
                    _ => s.arguments,
                };
                Ok(parse_quote!( #ident #args ))
            })
            .collect::<Result<_, _>>()?;

        let mut last_seg_args = Self::get_last_segment_args(&typ);

        let mut tn = TypeName::from_type_path(&typ);
        types_encountered.insert(tn.clone());
        // Let's see if this is a typedef.
        let typ = match self.resolve_typedef(&tn) {
            None => typ,
            Some(Type::Path(resolved_tp)) => {
                types_encountered.insert(TypeName::from_type_path(&resolved_tp));
                let typedef_target_args = Self::get_last_segment_args(&resolved_tp);
                if let Some(typedef_target_args) = typedef_target_args {
                    if last_seg_args.is_some() {
                        return Err(ConvertError::ConflictingTemplatedArgsWithTypedef(tn));
                    }
                    last_seg_args = Some(typedef_target_args);
                    tn = TypeName::from_type_path(&resolved_tp);
                }
                resolved_tp.clone()
            }
            Some(other) => return Ok(Annotated::new(other.clone(), HashSet::new(), Vec::new())),
        };

        // This will strip off any path arguments...
        let mut typ = KNOWN_TYPES.known_type_substitute_path(&typ).unwrap_or(typ);
        let mut extra_apis = Vec::new();
        // but then we'll put them back again as necessary.
        if let Some(last_seg_args) = last_seg_args {
            let last_seg = typ.path.segments.last_mut().unwrap();
            last_seg.arguments = last_seg_args;
            // Is it one of the things built into cxx?
            if !KNOWN_TYPES.is_cxx_acceptable_generic(&tn) {
                // Oh poop. It's a generic type which cxx won't be able to handle.
                // We'll have to come up with a concrete type in both the cxx::bridge (in Rust)
                // and a corresponding typedef in C++.
                let (new_tn, api) = self.get_templated_typename(&Type::Path(typ));
                typ = new_tn.to_type_path();
                extra_apis.extend(api.into_iter());
                types_encountered.remove(&tn);
                types_encountered.insert(new_tn);
            }
        }
        Ok(Annotated::new(
            Type::Path(typ),
            types_encountered,
            extra_apis,
        ))
    }

    fn get_last_segment_args(typ: &TypePath) -> Option<PathArguments> {
        let mut seg_iter = typ.path.segments.iter().peekable();
        while let Some(seg) = seg_iter.next() {
            if !seg.arguments.is_empty() {
                if seg_iter.peek().is_some() {
                    panic!("Did not expect bindgen to create a type with path arguments on a non-final segment")
                } else {
                    return Some(seg.arguments.clone());
                }
            }
        }
        None
    }

    fn convert_punctuated<P>(
        &mut self,
        pun: Punctuated<GenericArgument, P>,
        ns: &Namespace,
    ) -> Result<Annotated<Punctuated<GenericArgument, P>>, ConvertError>
    where
        P: Default,
    {
        let mut new_pun = Punctuated::new();
        let mut types_encountered = HashSet::new();
        let mut extra_apis = Vec::new();
        for arg in pun.into_iter() {
            new_pun.push(match arg {
                GenericArgument::Type(t) => {
                    let mut innerty = self.convert_type(t, ns)?;
                    types_encountered.extend(innerty.types_encountered.drain());
                    extra_apis.extend(innerty.extra_apis.drain(..));
                    GenericArgument::Type(innerty.ty)
                }
                _ => arg,
            });
        }
        Ok(Annotated::new(new_pun, types_encountered, extra_apis))
    }

    fn resolve_typedef<'b>(&'b self, tn: &TypeName) -> Option<&'b Type> {
        self.typedefs.get(&tn).map(|resolution| match resolution {
            Type::Path(typ) => {
                let tn = TypeName::from_type_path(typ);
                self.resolve_typedef(&tn).unwrap_or(resolution)
            }
            _ => resolution,
        })
    }

    fn convert_ptr_to_reference(
        &mut self,
        ptr: TypePtr,
        ns: &Namespace,
    ) -> Result<Annotated<Type>, ConvertError> {
        let mutability = ptr.mutability;
        let elem = self.convert_boxed_type(ptr.elem, ns)?;
        // TODO - in the future, we should check if this is a rust::Str and throw
        // a wobbler if not. rust::Str should only be seen _by value_ in C++
        // headers; it manifests as &str in Rust but on the C++ side it must
        // be a plain value. We should detect and abort.
        Ok(elem.map(|elem| match mutability {
            Some(_) => Type::Path(parse_quote! {
                Pin < & #mutability #elem >
            }),
            None => Type::Reference(parse_quote! {
                & #elem
            }),
        }))
    }

    fn add_concrete_type(&self, tyname: &TypeName, rs_definition: &Type) -> Api {
        let final_ident = make_ident(tyname.get_final_ident());
        let bridge_item = Some(Item::Impl(parse_quote! {
            impl UniquePtr<#final_ident> {}
        }));
        let mut fulltypath: Vec<_> = ["bindgen", "root"].iter().map(|x| make_ident(x)).collect();
        fulltypath.push(final_ident.clone());
        let tynamestring = tyname.to_cpp_name();
        Api {
            ns: tyname.get_namespace().clone(),
            id: final_ident.clone(),
            use_stmt: Use::Unused,
            global_items: vec![Item::Impl(parse_quote! {
                unsafe impl cxx::ExternType for #(#fulltypath)::* {
                    type Id = cxx::type_id!(#tynamestring);
                    type Kind = cxx::kind::Opaque;
                }
            })],
            bridge_item,
            extern_c_mod_item: Some(ForeignItem::Verbatim(quote! {
                type #final_ident = super::bindgen::root::#final_ident;
            })),
            additional_cpp: Some(AdditionalNeed::ConcreteTemplatedTypeTypedef(
                tyname.clone(),
                Box::new(rs_definition.clone()),
            )),
            deps: HashSet::new(),
            id_for_allowlist: None,
            bindgen_mod_item: Some(Item::Struct(new_non_pod_struct(final_ident))),
            impl_entry: None,
        }
    }

    fn get_templated_typename(&mut self, rs_definition: &Type) -> (TypeName, Option<Api>) {
        let count = self.concrete_templates.len();
        // We just use this as a hash key, essentially.
        let cpp_definition = type_to_cpp(rs_definition);
        let e = self.concrete_templates.get(&cpp_definition);
        match e {
            Some(tn) => (tn.clone(), None),
            None => {
                let tn = TypeName::new(&Namespace::new(), &format!("AutocxxConcrete{}", count));
                self.concrete_templates
                    .insert(cpp_definition.clone(), tn.clone());
                let api = self.add_concrete_type(&tn, rs_definition);
                (tn, Some(api))
            }
        }
    }
}
<|endoftext|>