<commit_before>trait Parser<T, E> {
    fn next(&mut self) -> Option<T>;
    fn preview(&self) -> Option<&T>;
    fn current_pos(&self) -> (i32, i32);
    fn error(&self, message: &str) -> E;
}

macro_rules! next {
    ($p:expr) => {
        match $p.next() {
            Some(x) => x,
            None => return Err($p.error("unexpected eof")),
        }
    }
}

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

    struct TP {
        input: VecDeque<i32>,
    }

    impl TP {
        fn new(input: &[i32]) -> TP {
            TP { input: VecDeque::from(input.to_vec()) }
        }
    }

    impl Parser<i32, String> for TP {
        fn next(&mut self) -> Option<i32> {
            self.input.pop_front()
        }

        fn preview(&self) -> Option<&i32> {
            self.input.front()
        }

        fn current_pos(&self) -> (i32, i32) {
            (0, 0)
        }

        fn error(&self, message: &str) -> String {
            message.to_string()
        }
    }

    macro_rules! with_res {
        (test $i:ident, $b:block) => {
            #[test]
            fn $i() {
                let result = || {
                    $b;
                    Ok(())
                };
                result().unwrap()
            }
        };
        (panic $e:expr, $i:ident, $b:block) => {
            #[test]
            #[should_panic(expected = $e)]
            fn $i() {
                let result = || {
                    $b;
                    Ok(())
                };
                result().unwrap()
            }
        };
    }

    with_res!(test next_success, {
        let mut p = TP::new(&[1, 2, 3]);
        assert_eq!(next!(p), 1);
        assert_eq!(next!(p), 2);
        assert_eq!(next!(p), 3);
    });

    with_res!(panic "unexpected eof", next_fail_empty, {
        let mut p = TP::new(&[]);
        assert_eq!(next!(p), 1);
    });
}
<commit_msg>Implementing predicate combinator<commit_after>trait Parser<T, E> {
    fn next(&mut self) -> Option<T>;
    fn preview(&self) -> Option<&T>;
    fn current_pos(&self) -> (i32, i32);
    fn error(&self, message: &str) -> E;
}

macro_rules! next {
    ($p:expr) => {
        match $p.next() {
            Some(x) => x,
            None => return Err($p.error("unexpected eof")),
        }
    }
}

macro_rules! predicate {
    ($p:expr, $pred:expr) => {{
        let x = next!($p);
        if $pred(x) {
            x
        } else {
            return Err($p.error(format!("unexpected token '{}'", x)));
        }
    }}
}

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

    struct TP {
        input: VecDeque<i32>,
    }

    impl TP {
        fn new(input: &[i32]) -> TP {
            TP { input: VecDeque::from(input.to_vec()) }
        }
    }

    impl Parser<i32, String> for TP {
        fn next(&mut self) -> Option<i32> {
            self.input.pop_front()
        }

        fn preview(&self) -> Option<&i32> {
            self.input.front()
        }

        fn current_pos(&self) -> (i32, i32) {
            (0, 0)
        }

        fn error(&self, message: &str) -> String {
            message.to_string()
        }
    }

    macro_rules! with_res {
        (test $i:ident, $b:block) => {
            #[test]
            fn $i() {
                let result = || {
                    $b;
                    Ok(())
                };
                result().unwrap()
            }
        };
        (panic $e:expr, $i:ident, $b:block) => {
            #[test]
            #[should_panic(expected = $e)]
            fn $i() {
                let result = || {
                    $b;
                    Ok(())
                };
                result().unwrap()
            }
        };
    }

    with_res!(test next_success, {
        let mut p = TP::new(&[1, 2, 3]);
        assert_eq!(next!(p), 1);
        assert_eq!(next!(p), 2);
        assert_eq!(next!(p), 3);
    });

    with_res!(panic "unexpected eof", next_fail_empty, {
        let mut p = TP::new(&[]);
        assert_eq!(next!(p), 1);
    });

    with_res!(test predicate_success, {
        let mut p = TP::new(&[2, 4, 6]);
        assert_eq!(predicate!(p, |x| x % 2 == 0), 2);
        assert_eq!(predicate!(p, |x| x % 2 == 0), 4);
        assert_eq!(predicate!(p, |x| x % 2 == 0), 6);
    });

    with_res!(panic "unexpected eof", predicate_fail_empty, {
        let mut p = TP::new(&[]);
        assert_eq!(predicate!(p, |x| x % 2 == 0), 2);
    });

    with_res!(panic "unexpected token '3'", predicate_fail_not_satisfy, {
        let mut p = TP::new(&[3, 5, 7]);
        assert_eq!(predicate!(p, |x| x % 2 == 0), 3);
    });
}
<|endoftext|>


<commit_before>// Copyright 2012 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.

//! Validates all used crates and extern libraries and loads their metadata

use metadata::cstore;
use metadata::decoder;
use metadata::filesearch::FileSearch;
use metadata::loader;

use std::hashmap::HashMap;
use syntax::ast;
use std::vec;
use syntax::attr;
use syntax::attr::AttrMetaMethods;
use syntax::codemap::{span, dummy_sp};
use syntax::diagnostic::span_handler;
use syntax::parse::token;
use syntax::parse::token::ident_interner;
use syntax::oldvisit;

// Traverses an AST, reading all the information about use'd crates and extern
// libraries necessary for later resolving, typechecking, linking, etc.
pub fn read_crates(diag: @mut span_handler,
                   crate: &ast::Crate,
                   cstore: @mut cstore::CStore,
                   filesearch: @FileSearch,
                   os: loader::os,
                   statik: bool,
                   intr: @ident_interner) {
    let e = @mut Env {
        diag: diag,
        filesearch: filesearch,
        cstore: cstore,
        os: os,
        statik: statik,
        crate_cache: @mut ~[],
        next_crate_num: 1,
        intr: intr
    };
    let v =
        oldvisit::mk_simple_visitor(@oldvisit::SimpleVisitor {
            visit_view_item: |a| visit_view_item(e, a),
            visit_item: |a| visit_item(e, a),
            .. *oldvisit::default_simple_visitor()});
    visit_crate(e, crate);
    oldvisit::visit_crate(crate, ((), v));
    dump_crates(*e.crate_cache);
    warn_if_multiple_versions(e, diag, *e.crate_cache);
}

#[deriving(Clone)]
struct cache_entry {
    cnum: int,
    span: span,
    hash: @str,
    metas: @~[@ast::MetaItem]
}

fn dump_crates(crate_cache: &[cache_entry]) {
    debug!("resolved crates:");
    for entry in crate_cache.iter() {
        debug!("cnum: %?", entry.cnum);
        debug!("span: %?", entry.span);
        debug!("hash: %?", entry.hash);
    }
}

fn warn_if_multiple_versions(e: @mut Env,
                             diag: @mut span_handler,
                             crate_cache: &[cache_entry]) {
    if crate_cache.len() != 0u {
        let name = loader::crate_name_from_metas(
            *crate_cache[crate_cache.len() - 1].metas
        );

        let (matches, non_matches) = crate_cache.partitioned(|entry|
            name == loader::crate_name_from_metas(*entry.metas));

        assert!(!matches.is_empty());

        if matches.len() != 1u {
            diag.handler().warn(
                fmt!("using multiple versions of crate `%s`", name));
            for match_ in matches.iter() {
                diag.span_note(match_.span, "used here");
                let attrs = ~[
                    attr::mk_attr(attr::mk_list_item(@"link",
                                                     (*match_.metas).clone()))
                ];
                loader::note_linkage_attrs(e.intr, diag, attrs);
            }
        }

        warn_if_multiple_versions(e, diag, non_matches);
    }
}

struct Env {
    diag: @mut span_handler,
    filesearch: @FileSearch,
    cstore: @mut cstore::CStore,
    os: loader::os,
    statik: bool,
    crate_cache: @mut ~[cache_entry],
    next_crate_num: ast::CrateNum,
    intr: @ident_interner
}

fn visit_crate(e: &Env, c: &ast::Crate) {
    let cstore = e.cstore;

    for a in c.attrs.iter().filter(|m| "link_args" == m.name()) {
        match a.value_str() {
          Some(ref linkarg) => {
            cstore::add_used_link_args(cstore, *linkarg);
          }
          None => {/* fallthrough */ }
        }
    }
}

fn visit_view_item(e: @mut Env, i: &ast::view_item) {
    match i.node {
      ast::view_item_extern_mod(ident, path_opt, ref meta_items, id) => {
          let ident = token::ident_to_str(&ident);
          let meta_items = match path_opt {
              None => meta_items.clone(),
              Some(p) => {
                  let p_path = Path(p);
                  match p_path.filestem() {
                      Some(s) =>
                          vec::append(
                              ~[attr::mk_name_value_item_str(@"package_id", p),
                               attr::mk_name_value_item_str(@"name", s.to_managed())],
                              *meta_items),
                      None => e.diag.span_bug(i.span, "Bad package path in `extern mod` item")
                  }
            }
          };
          debug!("resolving extern mod stmt. ident: %?, meta: %?",
                 ident, meta_items);
          let cnum = resolve_crate(e,
                                   ident,
                                   meta_items,
                                   @"",
                                   i.span);
          cstore::add_extern_mod_stmt_cnum(e.cstore, id, cnum);
      }
      _ => ()
  }
}

fn visit_item(e: &Env, i: @ast::item) {
    match i.node {
      ast::item_foreign_mod(ref fm) => {
        if fm.abis.is_rust() || fm.abis.is_intrinsic() {
            return;
        }

        let cstore = e.cstore;
        let mut already_added = false;
        let link_args = i.attrs.iter()
            .filter_map(|at| if "link_args" == at.name() {Some(at)} else {None})
            .collect::<~[&ast::Attribute]>();

        match fm.sort {
            ast::named => {
                let link_name = i.attrs.iter()
                    .find(|at| "link_name" == at.name())
                    .chain(|at| at.value_str());

                let foreign_name = match link_name {
                        Some(nn) => {
                            if nn.is_empty() {
                                e.diag.span_fatal(
                                    i.span,
                                    "empty #[link_name] not allowed; use \
                                     #[nolink].");
                            }
                            nn
                        }
                        None => token::ident_to_str(&i.ident)
                    };
                if !attr::contains_name(i.attrs, "nolink") {
                    already_added =
                        !cstore::add_used_library(cstore, foreign_name);
                }
                if !link_args.is_empty() && already_added {
                    e.diag.span_fatal(i.span, ~"library '" + foreign_name +
                               "' already added: can't specify link_args.");
                }
            }
            ast::anonymous => { /* do nothing */ }
        }

        for m in link_args.iter() {
            match m.value_str() {
                Some(linkarg) => {
                    cstore::add_used_link_args(cstore, linkarg);
                }
                None => { /* fallthrough */ }
            }
        }
      }
      _ => { }
    }
}

fn metas_with(ident: @str, key: @str, mut metas: ~[@ast::MetaItem])
    -> ~[@ast::MetaItem] {
    // Check if key isn't there yet.
    if !attr::contains_name(metas, key) {
        metas.push(attr::mk_name_value_item_str(key, ident));
    }
    metas
}

fn metas_with_ident(ident: @str, metas: ~[@ast::MetaItem])
    -> ~[@ast::MetaItem] {
    metas_with(ident, @"name", metas)
}

fn existing_match(e: &Env, metas: &[@ast::MetaItem], hash: &str)
               -> Option<int> {
    for c in e.crate_cache.iter() {
        if loader::metadata_matches(*c.metas, metas)
            && (hash.is_empty() || c.hash.as_slice() == hash) {
            return Some(c.cnum);
        }
    }
    return None;
}

fn resolve_crate(e: @mut Env,
                 ident: @str,
                 metas: ~[@ast::MetaItem],
                 hash: @str,
                 span: span)
              -> ast::CrateNum {
    let metas = metas_with_ident(ident, metas);

    match existing_match(e, metas, hash) {
      None => {
        let load_ctxt = loader::Context {
            diag: e.diag,
            filesearch: e.filesearch,
            span: span,
            ident: ident,
            metas: metas,
            hash: hash,
            os: e.os,
            is_static: e.statik,
            intr: e.intr
        };
        let (lident, ldata) = loader::load_library_crate(&load_ctxt);

        let cfilename = Path(lident);
        let cdata = ldata;

        let attrs = decoder::get_crate_attributes(cdata);
        let linkage_metas = attr::find_linkage_metas(attrs);
        let hash = decoder::get_crate_hash(cdata);

        // Claim this crate number and cache it
        let cnum = e.next_crate_num;
        e.crate_cache.push(cache_entry {
            cnum: cnum,
            span: span,
            hash: hash,
            metas: @linkage_metas
        });
        e.next_crate_num += 1;

        // Now resolve the crates referenced by this crate
        let cnum_map = resolve_crate_deps(e, cdata);

        let cname =
            match attr::last_meta_item_value_str_by_name(load_ctxt.metas,
                                                         "name") {
                Some(v) => v,
                None => ident
            };
        let cmeta = @cstore::crate_metadata {
            name: cname,
            data: cdata,
            cnum_map: cnum_map,
            cnum: cnum
        };

        let cstore = e.cstore;
        cstore::set_crate_data(cstore, cnum, cmeta);
        cstore::add_used_crate_file(cstore, &cfilename);
        return cnum;
      }
      Some(cnum) => {
        return cnum;
      }
    }
}

// Go through the crate metadata and load any crates that it references
fn resolve_crate_deps(e: @mut Env, cdata: @~[u8]) -> cstore::cnum_map {
    debug!("resolving deps of external crate");
    // The map from crate numbers in the crate we're resolving to local crate
    // numbers
    let mut cnum_map = HashMap::new();
    let r = decoder::get_crate_deps(cdata);
    for dep in r.iter() {
        let extrn_cnum = dep.cnum;
        let cname_str = token::ident_to_str(&dep.name);
        let cmetas = metas_with(dep.vers, @"vers", ~[]);
        debug!("resolving dep crate %s ver: %s hash: %s",
               cname_str, dep.vers, dep.hash);
        match existing_match(e,
                             metas_with_ident(cname_str, cmetas.clone()),
                             dep.hash) {
          Some(local_cnum) => {
            debug!("already have it");
            // We've already seen this crate
            cnum_map.insert(extrn_cnum, local_cnum);
          }
          None => {
            debug!("need to load it");
            // This is a new one so we've got to load it
            // FIXME (#2404): Need better error reporting than just a bogus
            // span.
            let fake_span = dummy_sp();
            let local_cnum = resolve_crate(e, cname_str, cmetas, dep.hash,
                                           fake_span);
            cnum_map.insert(extrn_cnum, local_cnum);
          }
        }
    }
    return @mut cnum_map;
}
<commit_msg>Port creader.rs from oldvisit to <V:Visitor> trait API.<commit_after>// Copyright 2012 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.

//! Validates all used crates and extern libraries and loads their metadata

use metadata::cstore;
use metadata::decoder;
use metadata::filesearch::FileSearch;
use metadata::loader;

use std::hashmap::HashMap;
use syntax::ast;
use std::vec;
use syntax::attr;
use syntax::attr::AttrMetaMethods;
use syntax::codemap::{span, dummy_sp};
use syntax::diagnostic::span_handler;
use syntax::parse::token;
use syntax::parse::token::ident_interner;
use syntax::visit;

// Traverses an AST, reading all the information about use'd crates and extern
// libraries necessary for later resolving, typechecking, linking, etc.
pub fn read_crates(diag: @mut span_handler,
                   crate: &ast::Crate,
                   cstore: @mut cstore::CStore,
                   filesearch: @FileSearch,
                   os: loader::os,
                   statik: bool,
                   intr: @ident_interner) {
    let e = @mut Env {
        diag: diag,
        filesearch: filesearch,
        cstore: cstore,
        os: os,
        statik: statik,
        crate_cache: @mut ~[],
        next_crate_num: 1,
        intr: intr
    };
    let mut v = ReadCrateVisitor{ e:e };
    visit_crate(e, crate);
    visit::walk_crate(&mut v, crate, ());
    dump_crates(*e.crate_cache);
    warn_if_multiple_versions(e, diag, *e.crate_cache);
}

struct ReadCrateVisitor { e:@mut Env }
impl visit::Visitor<()> for ReadCrateVisitor {
    fn visit_view_item(&mut self, a:&ast::view_item, _:()) {
        visit_view_item(self.e, a);
        visit::walk_view_item(self, a, ());
    }
    fn visit_item(&mut self, a:@ast::item, _:()) {
        visit_item(self.e, a);
        visit::walk_item(self, a, ());
    }
}

#[deriving(Clone)]
struct cache_entry {
    cnum: int,
    span: span,
    hash: @str,
    metas: @~[@ast::MetaItem]
}

fn dump_crates(crate_cache: &[cache_entry]) {
    debug!("resolved crates:");
    for entry in crate_cache.iter() {
        debug!("cnum: %?", entry.cnum);
        debug!("span: %?", entry.span);
        debug!("hash: %?", entry.hash);
    }
}

fn warn_if_multiple_versions(e: @mut Env,
                             diag: @mut span_handler,
                             crate_cache: &[cache_entry]) {
    if crate_cache.len() != 0u {
        let name = loader::crate_name_from_metas(
            *crate_cache[crate_cache.len() - 1].metas
        );

        let (matches, non_matches) = crate_cache.partitioned(|entry|
            name == loader::crate_name_from_metas(*entry.metas));

        assert!(!matches.is_empty());

        if matches.len() != 1u {
            diag.handler().warn(
                fmt!("using multiple versions of crate `%s`", name));
            for match_ in matches.iter() {
                diag.span_note(match_.span, "used here");
                let attrs = ~[
                    attr::mk_attr(attr::mk_list_item(@"link",
                                                     (*match_.metas).clone()))
                ];
                loader::note_linkage_attrs(e.intr, diag, attrs);
            }
        }

        warn_if_multiple_versions(e, diag, non_matches);
    }
}

struct Env {
    diag: @mut span_handler,
    filesearch: @FileSearch,
    cstore: @mut cstore::CStore,
    os: loader::os,
    statik: bool,
    crate_cache: @mut ~[cache_entry],
    next_crate_num: ast::CrateNum,
    intr: @ident_interner
}

fn visit_crate(e: &Env, c: &ast::Crate) {
    let cstore = e.cstore;

    for a in c.attrs.iter().filter(|m| "link_args" == m.name()) {
        match a.value_str() {
          Some(ref linkarg) => {
            cstore::add_used_link_args(cstore, *linkarg);
          }
          None => {/* fallthrough */ }
        }
    }
}

fn visit_view_item(e: @mut Env, i: &ast::view_item) {
    match i.node {
      ast::view_item_extern_mod(ident, path_opt, ref meta_items, id) => {
          let ident = token::ident_to_str(&ident);
          let meta_items = match path_opt {
              None => meta_items.clone(),
              Some(p) => {
                  let p_path = Path(p);
                  match p_path.filestem() {
                      Some(s) =>
                          vec::append(
                              ~[attr::mk_name_value_item_str(@"package_id", p),
                               attr::mk_name_value_item_str(@"name", s.to_managed())],
                              *meta_items),
                      None => e.diag.span_bug(i.span, "Bad package path in `extern mod` item")
                  }
            }
          };
          debug!("resolving extern mod stmt. ident: %?, meta: %?",
                 ident, meta_items);
          let cnum = resolve_crate(e,
                                   ident,
                                   meta_items,
                                   @"",
                                   i.span);
          cstore::add_extern_mod_stmt_cnum(e.cstore, id, cnum);
      }
      _ => ()
  }
}

fn visit_item(e: &Env, i: @ast::item) {
    match i.node {
      ast::item_foreign_mod(ref fm) => {
        if fm.abis.is_rust() || fm.abis.is_intrinsic() {
            return;
        }

        let cstore = e.cstore;
        let mut already_added = false;
        let link_args = i.attrs.iter()
            .filter_map(|at| if "link_args" == at.name() {Some(at)} else {None})
            .collect::<~[&ast::Attribute]>();

        match fm.sort {
            ast::named => {
                let link_name = i.attrs.iter()
                    .find(|at| "link_name" == at.name())
                    .chain(|at| at.value_str());

                let foreign_name = match link_name {
                        Some(nn) => {
                            if nn.is_empty() {
                                e.diag.span_fatal(
                                    i.span,
                                    "empty #[link_name] not allowed; use \
                                     #[nolink].");
                            }
                            nn
                        }
                        None => token::ident_to_str(&i.ident)
                    };
                if !attr::contains_name(i.attrs, "nolink") {
                    already_added =
                        !cstore::add_used_library(cstore, foreign_name);
                }
                if !link_args.is_empty() && already_added {
                    e.diag.span_fatal(i.span, ~"library '" + foreign_name +
                               "' already added: can't specify link_args.");
                }
            }
            ast::anonymous => { /* do nothing */ }
        }

        for m in link_args.iter() {
            match m.value_str() {
                Some(linkarg) => {
                    cstore::add_used_link_args(cstore, linkarg);
                }
                None => { /* fallthrough */ }
            }
        }
      }
      _ => { }
    }
}

fn metas_with(ident: @str, key: @str, mut metas: ~[@ast::MetaItem])
    -> ~[@ast::MetaItem] {
    // Check if key isn't there yet.
    if !attr::contains_name(metas, key) {
        metas.push(attr::mk_name_value_item_str(key, ident));
    }
    metas
}

fn metas_with_ident(ident: @str, metas: ~[@ast::MetaItem])
    -> ~[@ast::MetaItem] {
    metas_with(ident, @"name", metas)
}

fn existing_match(e: &Env, metas: &[@ast::MetaItem], hash: &str)
               -> Option<int> {
    for c in e.crate_cache.iter() {
        if loader::metadata_matches(*c.metas, metas)
            && (hash.is_empty() || c.hash.as_slice() == hash) {
            return Some(c.cnum);
        }
    }
    return None;
}

fn resolve_crate(e: @mut Env,
                 ident: @str,
                 metas: ~[@ast::MetaItem],
                 hash: @str,
                 span: span)
              -> ast::CrateNum {
    let metas = metas_with_ident(ident, metas);

    match existing_match(e, metas, hash) {
      None => {
        let load_ctxt = loader::Context {
            diag: e.diag,
            filesearch: e.filesearch,
            span: span,
            ident: ident,
            metas: metas,
            hash: hash,
            os: e.os,
            is_static: e.statik,
            intr: e.intr
        };
        let (lident, ldata) = loader::load_library_crate(&load_ctxt);

        let cfilename = Path(lident);
        let cdata = ldata;

        let attrs = decoder::get_crate_attributes(cdata);
        let linkage_metas = attr::find_linkage_metas(attrs);
        let hash = decoder::get_crate_hash(cdata);

        // Claim this crate number and cache it
        let cnum = e.next_crate_num;
        e.crate_cache.push(cache_entry {
            cnum: cnum,
            span: span,
            hash: hash,
            metas: @linkage_metas
        });
        e.next_crate_num += 1;

        // Now resolve the crates referenced by this crate
        let cnum_map = resolve_crate_deps(e, cdata);

        let cname =
            match attr::last_meta_item_value_str_by_name(load_ctxt.metas,
                                                         "name") {
                Some(v) => v,
                None => ident
            };
        let cmeta = @cstore::crate_metadata {
            name: cname,
            data: cdata,
            cnum_map: cnum_map,
            cnum: cnum
        };

        let cstore = e.cstore;
        cstore::set_crate_data(cstore, cnum, cmeta);
        cstore::add_used_crate_file(cstore, &cfilename);
        return cnum;
      }
      Some(cnum) => {
        return cnum;
      }
    }
}

// Go through the crate metadata and load any crates that it references
fn resolve_crate_deps(e: @mut Env, cdata: @~[u8]) -> cstore::cnum_map {
    debug!("resolving deps of external crate");
    // The map from crate numbers in the crate we're resolving to local crate
    // numbers
    let mut cnum_map = HashMap::new();
    let r = decoder::get_crate_deps(cdata);
    for dep in r.iter() {
        let extrn_cnum = dep.cnum;
        let cname_str = token::ident_to_str(&dep.name);
        let cmetas = metas_with(dep.vers, @"vers", ~[]);
        debug!("resolving dep crate %s ver: %s hash: %s",
               cname_str, dep.vers, dep.hash);
        match existing_match(e,
                             metas_with_ident(cname_str, cmetas.clone()),
                             dep.hash) {
          Some(local_cnum) => {
            debug!("already have it");
            // We've already seen this crate
            cnum_map.insert(extrn_cnum, local_cnum);
          }
          None => {
            debug!("need to load it");
            // This is a new one so we've got to load it
            // FIXME (#2404): Need better error reporting than just a bogus
            // span.
            let fake_span = dummy_sp();
            let local_cnum = resolve_crate(e, cname_str, cmetas, dep.hash,
                                           fake_span);
            cnum_map.insert(extrn_cnum, local_cnum);
          }
        }
    }
    return @mut cnum_map;
}
<|endoftext|>


<commit_before>//! A pass that annotates every item and method with its stability level,
//! propagating default levels lexically from parent to children ast nodes.

pub use self::StabilityLevel::*;

use crate::lint::builtin::BuiltinLintDiagnostics;
use crate::lint::{self, in_derive_expansion, Lint};
use crate::session::{DiagnosticMessageId, Session};
use crate::ty::{self, TyCtxt};
use errors::DiagnosticBuilder;
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
use rustc_feature::GateIssue;
use rustc_hir as hir;
use rustc_hir::def::DefKind;
use rustc_hir::def_id::{CrateNum, DefId, CRATE_DEF_INDEX};
use rustc_hir::{self, HirId};
use rustc_span::symbol::{sym, Symbol};
use rustc_span::{MultiSpan, Span};
use syntax::ast::CRATE_NODE_ID;
use syntax::attr::{self, ConstStability, Deprecation, RustcDeprecation, Stability};
use syntax::errors::Applicability;
use syntax::feature_gate::feature_err_issue;

use std::num::NonZeroU32;

#[derive(PartialEq, Clone, Copy, Debug)]
pub enum StabilityLevel {
    Unstable,
    Stable,
}

impl StabilityLevel {
    pub fn from_attr_level(level: &attr::StabilityLevel) -> Self {
        if level.is_stable() { Stable } else { Unstable }
    }
}

/// An entry in the `depr_map`.
#[derive(Clone, HashStable)]
pub struct DeprecationEntry {
    /// The metadata of the attribute associated with this entry.
    pub attr: Deprecation,
    /// The `DefId` where the attr was originally attached. `None` for non-local
    /// `DefId`'s.
    origin: Option<HirId>,
}

impl DeprecationEntry {
    pub fn local(attr: Deprecation, id: HirId) -> DeprecationEntry {
        DeprecationEntry { attr, origin: Some(id) }
    }

    pub fn external(attr: Deprecation) -> DeprecationEntry {
        DeprecationEntry { attr, origin: None }
    }

    pub fn same_origin(&self, other: &DeprecationEntry) -> bool {
        match (self.origin, other.origin) {
            (Some(o1), Some(o2)) => o1 == o2,
            _ => false,
        }
    }
}

/// A stability index, giving the stability level for items and methods.
#[derive(HashStable)]
pub struct Index<'tcx> {
    /// This is mostly a cache, except the stabilities of local items
    /// are filled by the annotator.
    pub stab_map: FxHashMap<HirId, &'tcx Stability>,
    pub const_stab_map: FxHashMap<HirId, &'tcx ConstStability>,
    pub depr_map: FxHashMap<HirId, DeprecationEntry>,

    /// Maps for each crate whether it is part of the staged API.
    pub staged_api: FxHashMap<CrateNum, bool>,

    /// Features enabled for this crate.
    pub active_features: FxHashSet<Symbol>,
}

impl<'tcx> Index<'tcx> {
    pub fn local_stability(&self, id: HirId) -> Option<&'tcx Stability> {
        self.stab_map.get(&id).cloned()
    }

    pub fn local_const_stability(&self, id: HirId) -> Option<&'tcx ConstStability> {
        self.const_stab_map.get(&id).cloned()
    }

    pub fn local_deprecation_entry(&self, id: HirId) -> Option<DeprecationEntry> {
        self.depr_map.get(&id).cloned()
    }
}

pub fn report_unstable(
    sess: &Session,
    feature: Symbol,
    reason: Option<Symbol>,
    issue: Option<NonZeroU32>,
    is_soft: bool,
    span: Span,
    soft_handler: impl FnOnce(&'static lint::Lint, Span, &str),
) {
    let msg = match reason {
        Some(r) => format!("use of unstable library feature '{}': {}", feature, r),
        None => format!("use of unstable library feature '{}'", &feature),
    };

    let msp: MultiSpan = span.into();
    let cm = &sess.parse_sess.source_map();
    let span_key = msp.primary_span().and_then(|sp: Span| {
        if !sp.is_dummy() {
            let file = cm.lookup_char_pos(sp.lo()).file;
            if file.name.is_macros() { None } else { Some(span) }
        } else {
            None
        }
    });

    let error_id = (DiagnosticMessageId::StabilityId(issue), span_key, msg.clone());
    let fresh = sess.one_time_diagnostics.borrow_mut().insert(error_id);
    if fresh {
        if is_soft {
            soft_handler(lint::builtin::SOFT_UNSTABLE, span, &msg)
        } else {
            feature_err_issue(&sess.parse_sess, feature, span, GateIssue::Library(issue), &msg)
                .emit();
        }
    }
}

/// Checks whether an item marked with `deprecated(since="X")` is currently
/// deprecated (i.e., whether X is not greater than the current rustc version).
pub fn deprecation_in_effect(since: &str) -> bool {
    fn parse_version(ver: &str) -> Vec<u32> {
        // We ignore non-integer components of the version (e.g., "nightly").
        ver.split(|c| c == '.' || c == '-').flat_map(|s| s.parse()).collect()
    }

    if let Some(rustc) = option_env!("CFG_RELEASE") {
        let since: Vec<u32> = parse_version(since);
        let rustc: Vec<u32> = parse_version(rustc);
        // We simply treat invalid `since` attributes as relating to a previous
        // Rust version, thus always displaying the warning.
        if since.len() != 3 {
            return true;
        }
        since <= rustc
    } else {
        // By default, a deprecation warning applies to
        // the current version of the compiler.
        true
    }
}

pub fn deprecation_suggestion(
    diag: &mut DiagnosticBuilder<'_>,
    suggestion: Option<Symbol>,
    span: Span,
) {
    if let Some(suggestion) = suggestion {
        diag.span_suggestion(
            span,
            "replace the use of the deprecated item",
            suggestion.to_string(),
            Applicability::MachineApplicable,
        );
    }
}

fn deprecation_message_common(message: String, reason: Option<Symbol>) -> String {
    match reason {
        Some(reason) => format!("{}: {}", message, reason),
        None => message,
    }
}

pub fn deprecation_message(depr: &Deprecation, path: &str) -> (String, &'static Lint) {
    let message = format!("use of deprecated item '{}'", path);
    (deprecation_message_common(message, depr.note), lint::builtin::DEPRECATED)
}

pub fn rustc_deprecation_message(depr: &RustcDeprecation, path: &str) -> (String, &'static Lint) {
    let (message, lint) = if deprecation_in_effect(&depr.since.as_str()) {
        (format!("use of deprecated item '{}'", path), lint::builtin::DEPRECATED)
    } else {
        (
            format!(
                "use of item '{}' that will be deprecated in future version {}",
                path, depr.since
            ),
            lint::builtin::DEPRECATED_IN_FUTURE,
        )
    };
    (deprecation_message_common(message, Some(depr.reason)), lint)
}

pub fn early_report_deprecation(
    lint_buffer: &'a mut lint::LintBuffer,
    message: &str,
    suggestion: Option<Symbol>,
    lint: &'static Lint,
    span: Span,
) {
    if in_derive_expansion(span) {
        return;
    }

    let diag = BuiltinLintDiagnostics::DeprecatedMacro(suggestion, span);
    lint_buffer.buffer_lint_with_diagnostic(lint, CRATE_NODE_ID, span, message, diag);
}

fn late_report_deprecation(
    tcx: TyCtxt<'_>,
    message: &str,
    suggestion: Option<Symbol>,
    lint: &'static Lint,
    span: Span,
    def_id: DefId,
    hir_id: HirId,
) {
    if in_derive_expansion(span) {
        return;
    }

    let mut diag = tcx.struct_span_lint_hir(lint, hir_id, span, message);
    if let hir::Node::Expr(_) = tcx.hir().get(hir_id) {
        deprecation_suggestion(&mut diag, suggestion, span);
    }
    diag.emit();
    if hir_id == hir::DUMMY_HIR_ID {
        span_bug!(span, "emitted a {} lint with dummy HIR id: {:?}", lint.name, def_id);
    }
}

/// Result of `TyCtxt::eval_stability`.
pub enum EvalResult {
    /// We can use the item because it is stable or we provided the
    /// corresponding feature gate.
    Allow,
    /// We cannot use the item because it is unstable and we did not provide the
    /// corresponding feature gate.
    Deny { feature: Symbol, reason: Option<Symbol>, issue: Option<NonZeroU32>, is_soft: bool },
    /// The item does not have the `#[stable]` or `#[unstable]` marker assigned.
    Unmarked,
}

impl<'tcx> TyCtxt<'tcx> {
    // See issue #38412.
    fn skip_stability_check_due_to_privacy(self, mut def_id: DefId) -> bool {
        // Check if `def_id` is a trait method.
        match self.def_kind(def_id) {
            Some(DefKind::Method) | Some(DefKind::AssocTy) | Some(DefKind::AssocConst) => {
                if let ty::TraitContainer(trait_def_id) = self.associated_item(def_id).container {
                    // Trait methods do not declare visibility (even
                    // for visibility info in cstore). Use containing
                    // trait instead, so methods of `pub` traits are
                    // themselves considered `pub`.
                    def_id = trait_def_id;
                }
            }
            _ => {}
        }

        let visibility = self.visibility(def_id);

        match visibility {
            // Must check stability for `pub` items.
            ty::Visibility::Public => false,

            // These are not visible outside crate; therefore
            // stability markers are irrelevant, if even present.
            ty::Visibility::Restricted(..) | ty::Visibility::Invisible => true,
        }
    }

    /// Evaluates the stability of an item.
    ///
    /// Returns `EvalResult::Allow` if the item is stable, or unstable but the corresponding
    /// `#![feature]` has been provided. Returns `EvalResult::Deny` which describes the offending
    /// unstable feature otherwise.
    ///
    /// If `id` is `Some(_)`, this function will also check if the item at `def_id` has been
    /// deprecated. If the item is indeed deprecated, we will emit a deprecation lint attached to
    /// `id`.
    pub fn eval_stability(self, def_id: DefId, id: Option<HirId>, span: Span) -> EvalResult {
        // Deprecated attributes apply in-crate and cross-crate.
        if let Some(id) = id {
            if let Some(depr_entry) = self.lookup_deprecation_entry(def_id) {
                let parent_def_id = self.hir().local_def_id(self.hir().get_parent_item(id));
                let skip = self
                    .lookup_deprecation_entry(parent_def_id)
                    .map_or(false, |parent_depr| parent_depr.same_origin(&depr_entry));

                if !skip {
                    let (message, lint) =
                        deprecation_message(&depr_entry.attr, &self.def_path_str(def_id));
                    late_report_deprecation(self, &message, None, lint, span, def_id, id);
                }
            };
        }

        let is_staged_api =
            self.lookup_stability(DefId { index: CRATE_DEF_INDEX, ..def_id }).is_some();
        if !is_staged_api {
            return EvalResult::Allow;
        }

        let stability = self.lookup_stability(def_id);
        debug!(
            "stability: \
                inspecting def_id={:?} span={:?} of stability={:?}",
            def_id, span, stability
        );

        if let Some(id) = id {
            if let Some(stability) = stability {
                if let Some(depr) = &stability.rustc_depr {
                    let (message, lint) =
                        rustc_deprecation_message(depr, &self.def_path_str(def_id));
                    late_report_deprecation(
                        self,
                        &message,
                        depr.suggestion,
                        lint,
                        span,
                        def_id,
                        id,
                    );
                }
            }
        }

        // Only the cross-crate scenario matters when checking unstable APIs
        let cross_crate = !def_id.is_local();
        if !cross_crate {
            return EvalResult::Allow;
        }

        // Issue #38412: private items lack stability markers.
        if self.skip_stability_check_due_to_privacy(def_id) {
            return EvalResult::Allow;
        }

        match stability {
            Some(&Stability {
                level: attr::Unstable { reason, issue, is_soft }, feature, ..
            }) => {
                if span.allows_unstable(feature) {
                    debug!("stability: skipping span={:?} since it is internal", span);
                    return EvalResult::Allow;
                }
                if self.stability().active_features.contains(&feature) {
                    return EvalResult::Allow;
                }

                // When we're compiling the compiler itself we may pull in
                // crates from crates.io, but those crates may depend on other
                // crates also pulled in from crates.io. We want to ideally be
                // able to compile everything without requiring upstream
                // modifications, so in the case that this looks like a
                // `rustc_private` crate (e.g., a compiler crate) and we also have
                // the `-Z force-unstable-if-unmarked` flag present (we're
                // compiling a compiler crate), then let this missing feature
                // annotation slide.
                if feature == sym::rustc_private && issue == NonZeroU32::new(27812) {
                    if self.sess.opts.debugging_opts.force_unstable_if_unmarked {
                        return EvalResult::Allow;
                    }
                }

                EvalResult::Deny { feature, reason, issue, is_soft }
            }
            Some(_) => {
                // Stable APIs are always ok to call and deprecated APIs are
                // handled by the lint emitting logic above.
                EvalResult::Allow
            }
            None => EvalResult::Unmarked,
        }
    }

    /// Checks if an item is stable or error out.
    ///
    /// If the item defined by `def_id` is unstable and the corresponding `#![feature]` does not
    /// exist, emits an error.
    ///
    /// Additionally, this function will also check if the item is deprecated. If so, and `id` is
    /// not `None`, a deprecated lint attached to `id` will be emitted.
    pub fn check_stability(self, def_id: DefId, id: Option<HirId>, span: Span) {
        let soft_handler =
            |lint, span, msg: &_| self.lint_hir(lint, id.unwrap_or(hir::CRATE_HIR_ID), span, msg);
        match self.eval_stability(def_id, id, span) {
            EvalResult::Allow => {}
            EvalResult::Deny { feature, reason, issue, is_soft } => {
                report_unstable(self.sess, feature, reason, issue, is_soft, span, soft_handler)
            }
            EvalResult::Unmarked => {
                // The API could be uncallable for other reasons, for example when a private module
                // was referenced.
                self.sess.delay_span_bug(span, &format!("encountered unmarked API: {:?}", def_id));
            }
        }
    }
}

impl<'tcx> TyCtxt<'tcx> {
    pub fn lookup_deprecation(self, id: DefId) -> Option<Deprecation> {
        self.lookup_deprecation_entry(id).map(|depr| depr.attr)
    }
}
<commit_msg>Remove private methods from TyCtxt impl block: rustc::middle::stability.<commit_after>//! A pass that annotates every item and method with its stability level,
//! propagating default levels lexically from parent to children ast nodes.

pub use self::StabilityLevel::*;

use crate::lint::builtin::BuiltinLintDiagnostics;
use crate::lint::{self, in_derive_expansion, Lint};
use crate::session::{DiagnosticMessageId, Session};
use crate::ty::{self, TyCtxt};
use errors::DiagnosticBuilder;
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
use rustc_feature::GateIssue;
use rustc_hir as hir;
use rustc_hir::def::DefKind;
use rustc_hir::def_id::{CrateNum, DefId, CRATE_DEF_INDEX};
use rustc_hir::{self, HirId};
use rustc_span::symbol::{sym, Symbol};
use rustc_span::{MultiSpan, Span};
use syntax::ast::CRATE_NODE_ID;
use syntax::attr::{self, ConstStability, Deprecation, RustcDeprecation, Stability};
use syntax::errors::Applicability;
use syntax::feature_gate::feature_err_issue;

use std::num::NonZeroU32;

#[derive(PartialEq, Clone, Copy, Debug)]
pub enum StabilityLevel {
    Unstable,
    Stable,
}

impl StabilityLevel {
    pub fn from_attr_level(level: &attr::StabilityLevel) -> Self {
        if level.is_stable() { Stable } else { Unstable }
    }
}

/// An entry in the `depr_map`.
#[derive(Clone, HashStable)]
pub struct DeprecationEntry {
    /// The metadata of the attribute associated with this entry.
    pub attr: Deprecation,
    /// The `DefId` where the attr was originally attached. `None` for non-local
    /// `DefId`'s.
    origin: Option<HirId>,
}

impl DeprecationEntry {
    pub fn local(attr: Deprecation, id: HirId) -> DeprecationEntry {
        DeprecationEntry { attr, origin: Some(id) }
    }

    pub fn external(attr: Deprecation) -> DeprecationEntry {
        DeprecationEntry { attr, origin: None }
    }

    pub fn same_origin(&self, other: &DeprecationEntry) -> bool {
        match (self.origin, other.origin) {
            (Some(o1), Some(o2)) => o1 == o2,
            _ => false,
        }
    }
}

/// A stability index, giving the stability level for items and methods.
#[derive(HashStable)]
pub struct Index<'tcx> {
    /// This is mostly a cache, except the stabilities of local items
    /// are filled by the annotator.
    pub stab_map: FxHashMap<HirId, &'tcx Stability>,
    pub const_stab_map: FxHashMap<HirId, &'tcx ConstStability>,
    pub depr_map: FxHashMap<HirId, DeprecationEntry>,

    /// Maps for each crate whether it is part of the staged API.
    pub staged_api: FxHashMap<CrateNum, bool>,

    /// Features enabled for this crate.
    pub active_features: FxHashSet<Symbol>,
}

impl<'tcx> Index<'tcx> {
    pub fn local_stability(&self, id: HirId) -> Option<&'tcx Stability> {
        self.stab_map.get(&id).cloned()
    }

    pub fn local_const_stability(&self, id: HirId) -> Option<&'tcx ConstStability> {
        self.const_stab_map.get(&id).cloned()
    }

    pub fn local_deprecation_entry(&self, id: HirId) -> Option<DeprecationEntry> {
        self.depr_map.get(&id).cloned()
    }
}

pub fn report_unstable(
    sess: &Session,
    feature: Symbol,
    reason: Option<Symbol>,
    issue: Option<NonZeroU32>,
    is_soft: bool,
    span: Span,
    soft_handler: impl FnOnce(&'static lint::Lint, Span, &str),
) {
    let msg = match reason {
        Some(r) => format!("use of unstable library feature '{}': {}", feature, r),
        None => format!("use of unstable library feature '{}'", &feature),
    };

    let msp: MultiSpan = span.into();
    let cm = &sess.parse_sess.source_map();
    let span_key = msp.primary_span().and_then(|sp: Span| {
        if !sp.is_dummy() {
            let file = cm.lookup_char_pos(sp.lo()).file;
            if file.name.is_macros() { None } else { Some(span) }
        } else {
            None
        }
    });

    let error_id = (DiagnosticMessageId::StabilityId(issue), span_key, msg.clone());
    let fresh = sess.one_time_diagnostics.borrow_mut().insert(error_id);
    if fresh {
        if is_soft {
            soft_handler(lint::builtin::SOFT_UNSTABLE, span, &msg)
        } else {
            feature_err_issue(&sess.parse_sess, feature, span, GateIssue::Library(issue), &msg)
                .emit();
        }
    }
}

/// Checks whether an item marked with `deprecated(since="X")` is currently
/// deprecated (i.e., whether X is not greater than the current rustc version).
pub fn deprecation_in_effect(since: &str) -> bool {
    fn parse_version(ver: &str) -> Vec<u32> {
        // We ignore non-integer components of the version (e.g., "nightly").
        ver.split(|c| c == '.' || c == '-').flat_map(|s| s.parse()).collect()
    }

    if let Some(rustc) = option_env!("CFG_RELEASE") {
        let since: Vec<u32> = parse_version(since);
        let rustc: Vec<u32> = parse_version(rustc);
        // We simply treat invalid `since` attributes as relating to a previous
        // Rust version, thus always displaying the warning.
        if since.len() != 3 {
            return true;
        }
        since <= rustc
    } else {
        // By default, a deprecation warning applies to
        // the current version of the compiler.
        true
    }
}

pub fn deprecation_suggestion(
    diag: &mut DiagnosticBuilder<'_>,
    suggestion: Option<Symbol>,
    span: Span,
) {
    if let Some(suggestion) = suggestion {
        diag.span_suggestion(
            span,
            "replace the use of the deprecated item",
            suggestion.to_string(),
            Applicability::MachineApplicable,
        );
    }
}

fn deprecation_message_common(message: String, reason: Option<Symbol>) -> String {
    match reason {
        Some(reason) => format!("{}: {}", message, reason),
        None => message,
    }
}

pub fn deprecation_message(depr: &Deprecation, path: &str) -> (String, &'static Lint) {
    let message = format!("use of deprecated item '{}'", path);
    (deprecation_message_common(message, depr.note), lint::builtin::DEPRECATED)
}

pub fn rustc_deprecation_message(depr: &RustcDeprecation, path: &str) -> (String, &'static Lint) {
    let (message, lint) = if deprecation_in_effect(&depr.since.as_str()) {
        (format!("use of deprecated item '{}'", path), lint::builtin::DEPRECATED)
    } else {
        (
            format!(
                "use of item '{}' that will be deprecated in future version {}",
                path, depr.since
            ),
            lint::builtin::DEPRECATED_IN_FUTURE,
        )
    };
    (deprecation_message_common(message, Some(depr.reason)), lint)
}

pub fn early_report_deprecation(
    lint_buffer: &'a mut lint::LintBuffer,
    message: &str,
    suggestion: Option<Symbol>,
    lint: &'static Lint,
    span: Span,
) {
    if in_derive_expansion(span) {
        return;
    }

    let diag = BuiltinLintDiagnostics::DeprecatedMacro(suggestion, span);
    lint_buffer.buffer_lint_with_diagnostic(lint, CRATE_NODE_ID, span, message, diag);
}

fn late_report_deprecation(
    tcx: TyCtxt<'_>,
    message: &str,
    suggestion: Option<Symbol>,
    lint: &'static Lint,
    span: Span,
    def_id: DefId,
    hir_id: HirId,
) {
    if in_derive_expansion(span) {
        return;
    }

    let mut diag = tcx.struct_span_lint_hir(lint, hir_id, span, message);
    if let hir::Node::Expr(_) = tcx.hir().get(hir_id) {
        deprecation_suggestion(&mut diag, suggestion, span);
    }
    diag.emit();
    if hir_id == hir::DUMMY_HIR_ID {
        span_bug!(span, "emitted a {} lint with dummy HIR id: {:?}", lint.name, def_id);
    }
}

/// Result of `TyCtxt::eval_stability`.
pub enum EvalResult {
    /// We can use the item because it is stable or we provided the
    /// corresponding feature gate.
    Allow,
    /// We cannot use the item because it is unstable and we did not provide the
    /// corresponding feature gate.
    Deny { feature: Symbol, reason: Option<Symbol>, issue: Option<NonZeroU32>, is_soft: bool },
    /// The item does not have the `#[stable]` or `#[unstable]` marker assigned.
    Unmarked,
}

// See issue #38412.
fn skip_stability_check_due_to_privacy(tcx: TyCtxt<'_>, mut def_id: DefId) -> bool {
    // Check if `def_id` is a trait method.
    match tcx.def_kind(def_id) {
        Some(DefKind::Method) | Some(DefKind::AssocTy) | Some(DefKind::AssocConst) => {
            if let ty::TraitContainer(trait_def_id) = tcx.associated_item(def_id).container {
                // Trait methods do not declare visibility (even
                // for visibility info in cstore). Use containing
                // trait instead, so methods of `pub` traits are
                // themselves considered `pub`.
                def_id = trait_def_id;
            }
        }
        _ => {}
    }

    let visibility = tcx.visibility(def_id);

    match visibility {
        // Must check stability for `pub` items.
        ty::Visibility::Public => false,

        // These are not visible outside crate; therefore
        // stability markers are irrelevant, if even present.
        ty::Visibility::Restricted(..) | ty::Visibility::Invisible => true,
    }
}

impl<'tcx> TyCtxt<'tcx> {
    /// Evaluates the stability of an item.
    ///
    /// Returns `EvalResult::Allow` if the item is stable, or unstable but the corresponding
    /// `#![feature]` has been provided. Returns `EvalResult::Deny` which describes the offending
    /// unstable feature otherwise.
    ///
    /// If `id` is `Some(_)`, this function will also check if the item at `def_id` has been
    /// deprecated. If the item is indeed deprecated, we will emit a deprecation lint attached to
    /// `id`.
    pub fn eval_stability(self, def_id: DefId, id: Option<HirId>, span: Span) -> EvalResult {
        // Deprecated attributes apply in-crate and cross-crate.
        if let Some(id) = id {
            if let Some(depr_entry) = self.lookup_deprecation_entry(def_id) {
                let parent_def_id = self.hir().local_def_id(self.hir().get_parent_item(id));
                let skip = self
                    .lookup_deprecation_entry(parent_def_id)
                    .map_or(false, |parent_depr| parent_depr.same_origin(&depr_entry));

                if !skip {
                    let (message, lint) =
                        deprecation_message(&depr_entry.attr, &self.def_path_str(def_id));
                    late_report_deprecation(self, &message, None, lint, span, def_id, id);
                }
            };
        }

        let is_staged_api =
            self.lookup_stability(DefId { index: CRATE_DEF_INDEX, ..def_id }).is_some();
        if !is_staged_api {
            return EvalResult::Allow;
        }

        let stability = self.lookup_stability(def_id);
        debug!(
            "stability: \
                inspecting def_id={:?} span={:?} of stability={:?}",
            def_id, span, stability
        );

        if let Some(id) = id {
            if let Some(stability) = stability {
                if let Some(depr) = &stability.rustc_depr {
                    let (message, lint) =
                        rustc_deprecation_message(depr, &self.def_path_str(def_id));
                    late_report_deprecation(
                        self,
                        &message,
                        depr.suggestion,
                        lint,
                        span,
                        def_id,
                        id,
                    );
                }
            }
        }

        // Only the cross-crate scenario matters when checking unstable APIs
        let cross_crate = !def_id.is_local();
        if !cross_crate {
            return EvalResult::Allow;
        }

        // Issue #38412: private items lack stability markers.
        if skip_stability_check_due_to_privacy(self, def_id) {
            return EvalResult::Allow;
        }

        match stability {
            Some(&Stability {
                level: attr::Unstable { reason, issue, is_soft }, feature, ..
            }) => {
                if span.allows_unstable(feature) {
                    debug!("stability: skipping span={:?} since it is internal", span);
                    return EvalResult::Allow;
                }
                if self.stability().active_features.contains(&feature) {
                    return EvalResult::Allow;
                }

                // When we're compiling the compiler itself we may pull in
                // crates from crates.io, but those crates may depend on other
                // crates also pulled in from crates.io. We want to ideally be
                // able to compile everything without requiring upstream
                // modifications, so in the case that this looks like a
                // `rustc_private` crate (e.g., a compiler crate) and we also have
                // the `-Z force-unstable-if-unmarked` flag present (we're
                // compiling a compiler crate), then let this missing feature
                // annotation slide.
                if feature == sym::rustc_private && issue == NonZeroU32::new(27812) {
                    if self.sess.opts.debugging_opts.force_unstable_if_unmarked {
                        return EvalResult::Allow;
                    }
                }

                EvalResult::Deny { feature, reason, issue, is_soft }
            }
            Some(_) => {
                // Stable APIs are always ok to call and deprecated APIs are
                // handled by the lint emitting logic above.
                EvalResult::Allow
            }
            None => EvalResult::Unmarked,
        }
    }

    /// Checks if an item is stable or error out.
    ///
    /// If the item defined by `def_id` is unstable and the corresponding `#![feature]` does not
    /// exist, emits an error.
    ///
    /// Additionally, this function will also check if the item is deprecated. If so, and `id` is
    /// not `None`, a deprecated lint attached to `id` will be emitted.
    pub fn check_stability(self, def_id: DefId, id: Option<HirId>, span: Span) {
        let soft_handler =
            |lint, span, msg: &_| self.lint_hir(lint, id.unwrap_or(hir::CRATE_HIR_ID), span, msg);
        match self.eval_stability(def_id, id, span) {
            EvalResult::Allow => {}
            EvalResult::Deny { feature, reason, issue, is_soft } => {
                report_unstable(self.sess, feature, reason, issue, is_soft, span, soft_handler)
            }
            EvalResult::Unmarked => {
                // The API could be uncallable for other reasons, for example when a private module
                // was referenced.
                self.sess.delay_span_bug(span, &format!("encountered unmarked API: {:?}", def_id));
            }
        }
    }

    pub fn lookup_deprecation(self, id: DefId) -> Option<Deprecation> {
        self.lookup_deprecation_entry(id).map(|depr| depr.attr)
    }
}
<|endoftext|>


<commit_before>use rustc_index::bit_set::BitSet;
use rustc_middle::mir::{self, Local};

/// The set of locals in a MIR body that do not have `StorageLive`/`StorageDead` annotations.
///
/// These locals have fixed storage for the duration of the body.
//
// FIXME: Currently, we need to traverse the entire MIR to compute this. We should instead store it
// as a field in the `LocalDecl` for each `Local`.
#[derive(Debug, Clone)]
pub struct AlwaysLiveLocals(BitSet<Local>);

impl AlwaysLiveLocals {
    pub fn new(body: &mir::Body<'tcx>) -> Self {
        let mut locals = BitSet::new_filled(body.local_decls.len());

        // FIXME: Use a visitor for this when `visit_body` can take a plain `Body`.
        for block in body.basic_blocks().iter() {
            for stmt in &block.statements {
                if let mir::StatementKind::StorageLive(l) | mir::StatementKind::StorageDead(l) =
                    stmt.kind
                {
                    locals.remove(l);
                }
            }
        }

        AlwaysLiveLocals(locals)
    }

    pub fn into_inner(self) -> BitSet<Local> {
        self.0
    }
}

impl std::ops::Deref for AlwaysLiveLocals {
    type Target = BitSet<Local>;

    fn deref(&self) -> &Self::Target {
        &self.0
    }
}
<commit_msg>Use `Visitor` for `AlwaysLiveLocals`<commit_after>use rustc_index::bit_set::BitSet;
use rustc_middle::mir::visit::Visitor;
use rustc_middle::mir::{self, Local, Location};

/// The set of locals in a MIR body that do not have `StorageLive`/`StorageDead` annotations.
///
/// These locals have fixed storage for the duration of the body.
//
// FIXME: Currently, we need to traverse the entire MIR to compute this. We should instead store it
// as a field in the `LocalDecl` for each `Local`.
#[derive(Debug, Clone)]
pub struct AlwaysLiveLocals(BitSet<Local>);

impl AlwaysLiveLocals {
    pub fn new(body: &mir::Body<'tcx>) -> Self {
        let mut ret = AlwaysLiveLocals(BitSet::new_filled(body.local_decls.len()));

        let mut vis = StorageAnnotationVisitor(&mut ret);
        vis.visit_body(body);

        ret
    }

    pub fn into_inner(self) -> BitSet<Local> {
        self.0
    }
}

impl std::ops::Deref for AlwaysLiveLocals {
    type Target = BitSet<Local>;

    fn deref(&self) -> &Self::Target {
        &self.0
    }
}

/// Removes locals that have `Storage*` annotations from `AlwaysLiveLocals`.
struct StorageAnnotationVisitor<'a>(&'a mut AlwaysLiveLocals);

impl Visitor<'tcx> for StorageAnnotationVisitor<'_> {
    fn visit_statement(&mut self, statement: &mir::Statement<'tcx>, _location: Location) {
        use mir::StatementKind::{StorageDead, StorageLive};
        if let StorageLive(l) | StorageDead(l) = statement.kind {
            (self.0).0.remove(l);
        }
    }
}
<|endoftext|>


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

//! An interface for reading, writing, and creating new settings

pub use binaryninjacore_sys::BNSettingsScope as SettingsScope;
use binaryninjacore_sys::*;

use crate::binaryview::BinaryView;
use crate::rc::*;
use crate::string::{BnStrCompatible, BnString};

use std::ptr;

#[derive(PartialEq, Eq, Hash)]
pub struct Settings {
    pub(crate) handle: *mut BNSettings,
}

unsafe impl Send for Settings {}
unsafe impl Sync for Settings {}

impl Settings {
    pub(crate) unsafe fn from_raw(handle: *mut BNSettings) -> Ref<Self> {
        debug_assert!(!handle.is_null());

        Ref::new(Self { handle })
    }

    pub fn new<S: BnStrCompatible>(instance_id: S) -> Ref<Self> {
        let instance_id = instance_id.as_bytes_with_nul();
        unsafe {
            let handle = BNCreateSettings(instance_id.as_ref().as_ptr() as *mut _);

            debug_assert!(!handle.is_null());

            Ref::new(Self { handle })
        }
    }

    pub fn set_resource_id<S: BnStrCompatible>(&self, resource_id: S) {
        let resource_id = resource_id.as_bytes_with_nul();
        unsafe { BNSettingsSetResourceId(self.handle, resource_id.as_ref().as_ptr() as *mut _) };
    }

    pub fn serialize_schema(&self) -> BnString {
        unsafe { BnString::from_raw(BNSettingsSerializeSchema(self.handle)) }
    }

    pub fn deserialize_schema<S: BnStrCompatible>(&self, schema: S) -> bool {
        let schema = schema.as_bytes_with_nul();
        unsafe {
            BNSettingsDeserializeSchema(
                self.handle,
                schema.as_ref().as_ptr() as *mut _,
                BNSettingsScope::SettingsAutoScope,
                true,
            )
        }
    }

    pub fn contains<S: BnStrCompatible>(&self, key: S) -> bool {
        let key = key.as_bytes_with_nul();

        unsafe { BNSettingsContains(self.handle, key.as_ref().as_ptr() as *mut _) }
    }

    pub fn get_bool<S: BnStrCompatible>(
        &self,
        key: S,
        view: Option<BinaryView>,
        scope: Option<Box<SettingsScope>>,
    ) -> bool {
        let key = key.as_bytes_with_nul();
        let view_handle = match view {
            Some(view) => view.handle,
            _ => ptr::null_mut() as *mut _,
        };
        let scope_ptr = match scope {
            Some(mut scope) => scope.as_mut(),
            _ => ptr::null_mut() as *mut _,
        };
        unsafe {
            BNSettingsGetBool(
                self.handle,
                key.as_ref().as_ptr() as *mut _,
                view_handle,
                scope_ptr,
            )
        }
    }

    pub fn get_double<S: BnStrCompatible>(
        &self,
        key: S,
        view: Option<BinaryView>,
        scope: Option<Box<SettingsScope>>,
    ) -> f64 {
        let key = key.as_bytes_with_nul();
        let view_handle = match view {
            Some(view) => view.handle,
            _ => ptr::null_mut() as *mut _,
        };
        let scope_ptr = match scope {
            Some(mut scope) => scope.as_mut(),
            _ => ptr::null_mut() as *mut _,
        };
        unsafe {
            BNSettingsGetDouble(
                self.handle,
                key.as_ref().as_ptr() as *mut _,
                view_handle,
                scope_ptr,
            )
        }
    }

    pub fn get_integer<S: BnStrCompatible>(
        &self,
        key: S,
        view: Option<BinaryView>,
        scope: Option<Box<SettingsScope>>,
    ) -> u64 {
        let key = key.as_bytes_with_nul();
        let view_handle = match view {
            Some(view) => view.handle,
            _ => ptr::null_mut() as *mut _,
        };
        let scope_ptr = match scope {
            Some(mut scope) => scope.as_mut(),
            _ => ptr::null_mut() as *mut _,
        };
        unsafe {
            BNSettingsGetUInt64(
                self.handle,
                key.as_ref().as_ptr() as *mut _,
                view_handle,
                scope_ptr,
            )
        }
    }

    pub fn get_string<S: BnStrCompatible>(
        &self,
        key: S,
        view: Option<BinaryView>,
        scope: Option<Box<SettingsScope>>,
    ) -> BnString {
        let key = key.as_bytes_with_nul();
        let view_handle = match view {
            Some(view) => view.handle,
            _ => ptr::null_mut() as *mut _,
        };
        let scope_ptr = match scope {
            Some(mut scope) => scope.as_mut(),
            _ => ptr::null_mut() as *mut _,
        };
        unsafe {
            BnString::from_raw(BNSettingsGetString(
                self.handle,
                key.as_ref().as_ptr() as *mut _,
                view_handle,
                scope_ptr,
            ))
        }
    }

    // TODO : get_string_list
    // TODO : get_json

    pub fn set_bool<S: BnStrCompatible>(
        &self,
        key: S,
        value: bool,
        view: Option<BinaryView>,
        scope: Option<SettingsScope>,
    ) {
        let key = key.as_bytes_with_nul();
        let view_handle = match view {
            Some(view) => view.handle,
            _ => ptr::null_mut() as *mut _,
        };
        let scope = match scope {
            Some(scope) => scope,
            _ => SettingsScope::SettingsAutoScope,
        };
        unsafe {
            BNSettingsSetBool(
                self.handle,
                view_handle,
                scope,
                key.as_ref().as_ptr() as *mut _,
                value,
            );
        }
    }

    pub fn set_double<S: BnStrCompatible>(
        &self,
        key: S,
        value: f64,
        view: Option<BinaryView>,
        scope: Option<SettingsScope>,
    ) {
        let key = key.as_bytes_with_nul();
        let view_handle = match view {
            Some(view) => view.handle,
            _ => ptr::null_mut() as *mut _,
        };
        let scope = match scope {
            Some(scope) => scope,
            _ => SettingsScope::SettingsAutoScope,
        };
        unsafe {
            BNSettingsSetDouble(
                self.handle,
                view_handle,
                scope,
                key.as_ref().as_ptr() as *mut _,
                value,
            );
        }
    }

    pub fn set_integer<S: BnStrCompatible>(
        &self,
        key: S,
        value: u64,
        view: Option<BinaryView>,
        scope: Option<SettingsScope>,
    ) {
        let key = key.as_bytes_with_nul();
        let view_handle = match view {
            Some(view) => view.handle,
            _ => ptr::null_mut() as *mut _,
        };
        let scope = match scope {
            Some(scope) => scope,
            _ => SettingsScope::SettingsAutoScope,
        };
        unsafe {
            BNSettingsSetUInt64(
                self.handle,
                view_handle,
                scope,
                key.as_ref().as_ptr() as *mut _,
                value,
            );
        }
    }

    pub fn set_string<S: BnStrCompatible>(
        &self,
        key: S,
        value: S,
        view: Option<BinaryView>,
        scope: Option<SettingsScope>,
    ) {
        let key = key.as_bytes_with_nul();
        let value = value.as_bytes_with_nul();
        let view_handle = match view {
            Some(view) => view.handle,
            _ => ptr::null_mut() as *mut _,
        };
        let scope = match scope {
            Some(scope) => scope,
            _ => SettingsScope::SettingsAutoScope,
        };
        unsafe {
            BNSettingsSetString(
                self.handle,
                view_handle,
                scope,
                key.as_ref().as_ptr() as *mut _,
                value.as_ref().as_ptr() as *mut _,
            );
        }
    }

    // TODO : set_string_list

    pub fn set_json<S1: BnStrCompatible, S2: BnStrCompatible>(
        &self,
        key: S1,
        value: S2,
        view: Option<&BinaryView>,
        scope: Option<SettingsScope>,
    ) -> bool {
        let key = key.as_bytes_with_nul();
        let value = value.as_bytes_with_nul();

        let view_handle = match view {
            Some(view) => view.handle,
            None => ptr::null_mut() as *mut _,
        };

        let scope = match scope {
            Some(scope) => scope,
            None => SettingsScope::SettingsAutoScope,
        };

        unsafe {
            BNSettingsSetJson(
                self.handle,
                view_handle,
                scope,
                key.as_ref().as_ptr() as *mut _,
                value.as_ref().as_ptr() as *mut _,
            )
        }
    }
}

impl AsRef<Settings> for Settings {
    fn as_ref(&self) -> &Self {
        self
    }
}

impl ToOwned for Settings {
    type Owned = Ref<Self>;

    fn to_owned(&self) -> Self::Owned {
        unsafe { RefCountable::inc_ref(self) }
    }
}

unsafe impl RefCountable for Settings {
    unsafe fn inc_ref(handle: &Self) -> Ref<Self> {
        Ref::new(Self {
            handle: BNNewSettingsReference(handle.handle),
        })
    }

    unsafe fn dec_ref(handle: &Self) {
        BNFreeSettings(handle.handle);
    }
}
<commit_msg>[Rust API] Settings string_list and json<commit_after>// Copyright 2021-2022 Vector 35 Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

//! An interface for reading, writing, and creating new settings

pub use binaryninjacore_sys::BNSettingsScope as SettingsScope;
use binaryninjacore_sys::*;
use std::iter::FromIterator;
use std::os::raw::c_char;

use crate::binaryview::BinaryView;
use crate::rc::*;
use crate::string::{BnStrCompatible, BnString};

use std::ptr;

#[derive(PartialEq, Eq, Hash)]
pub struct Settings {
    pub(crate) handle: *mut BNSettings,
}

unsafe impl Send for Settings {}
unsafe impl Sync for Settings {}

impl Settings {
    pub(crate) unsafe fn from_raw(handle: *mut BNSettings) -> Ref<Self> {
        debug_assert!(!handle.is_null());

        Ref::new(Self { handle })
    }

    pub fn new<S: BnStrCompatible>(instance_id: S) -> Ref<Self> {
        let instance_id = instance_id.as_bytes_with_nul();
        unsafe {
            let handle = BNCreateSettings(instance_id.as_ref().as_ptr() as *mut _);

            debug_assert!(!handle.is_null());

            Ref::new(Self { handle })
        }
    }

    pub fn set_resource_id<S: BnStrCompatible>(&self, resource_id: S) {
        let resource_id = resource_id.as_bytes_with_nul();
        unsafe { BNSettingsSetResourceId(self.handle, resource_id.as_ref().as_ptr() as *mut _) };
    }

    pub fn serialize_schema(&self) -> BnString {
        unsafe { BnString::from_raw(BNSettingsSerializeSchema(self.handle)) }
    }

    pub fn deserialize_schema<S: BnStrCompatible>(&self, schema: S) -> bool {
        let schema = schema.as_bytes_with_nul();
        unsafe {
            BNSettingsDeserializeSchema(
                self.handle,
                schema.as_ref().as_ptr() as *mut _,
                BNSettingsScope::SettingsAutoScope,
                true,
            )
        }
    }

    pub fn contains<S: BnStrCompatible>(&self, key: S) -> bool {
        let key = key.as_bytes_with_nul();

        unsafe { BNSettingsContains(self.handle, key.as_ref().as_ptr() as *mut _) }
    }

    pub fn get_bool<S: BnStrCompatible>(
        &self,
        key: S,
        view: Option<&BinaryView>,
        scope: Option<Box<SettingsScope>>,
    ) -> bool {
        let key = key.as_bytes_with_nul();
        let view_handle = match view {
            Some(view) => view.handle,
            _ => ptr::null_mut() as *mut _,
        };
        let scope_ptr = match scope {
            Some(mut scope) => scope.as_mut(),
            _ => ptr::null_mut() as *mut _,
        };
        unsafe {
            BNSettingsGetBool(
                self.handle,
                key.as_ref().as_ptr() as *mut _,
                view_handle,
                scope_ptr,
            )
        }
    }

    pub fn get_double<S: BnStrCompatible>(
        &self,
        key: S,
        view: Option<&BinaryView>,
        scope: Option<Box<SettingsScope>>,
    ) -> f64 {
        let key = key.as_bytes_with_nul();
        let view_handle = match view {
            Some(view) => view.handle,
            _ => ptr::null_mut() as *mut _,
        };
        let scope_ptr = match scope {
            Some(mut scope) => scope.as_mut(),
            _ => ptr::null_mut() as *mut _,
        };
        unsafe {
            BNSettingsGetDouble(
                self.handle,
                key.as_ref().as_ptr() as *mut _,
                view_handle,
                scope_ptr,
            )
        }
    }

    pub fn get_integer<S: BnStrCompatible>(
        &self,
        key: S,
        view: Option<&BinaryView>,
        scope: Option<Box<SettingsScope>>,
    ) -> u64 {
        let key = key.as_bytes_with_nul();
        let view_handle = match view {
            Some(view) => view.handle,
            _ => ptr::null_mut() as *mut _,
        };
        let scope_ptr = match scope {
            Some(mut scope) => scope.as_mut(),
            _ => ptr::null_mut() as *mut _,
        };
        unsafe {
            BNSettingsGetUInt64(
                self.handle,
                key.as_ref().as_ptr() as *mut _,
                view_handle,
                scope_ptr,
            )
        }
    }

    pub fn get_string<S: BnStrCompatible>(
        &self,
        key: S,
        view: Option<&BinaryView>,
        scope: Option<Box<SettingsScope>>,
    ) -> BnString {
        let key = key.as_bytes_with_nul();
        let view_handle = match view {
            Some(view) => view.handle,
            _ => ptr::null_mut() as *mut _,
        };
        let scope_ptr = match scope {
            Some(mut scope) => scope.as_mut(),
            _ => ptr::null_mut() as *mut _,
        };
        unsafe {
            BnString::from_raw(BNSettingsGetString(
                self.handle,
                key.as_ref().as_ptr() as *mut _,
                view_handle,
                scope_ptr,
            ))
        }
    }

    pub fn get_string_list<S: BnStrCompatible>(
        &self,
        key: S,
        view: Option<&BinaryView>,
        scope: Option<Box<SettingsScope>>,
    ) -> Array<BnString> {
        let key = key.as_bytes_with_nul();
        let view_handle = match view {
            Some(view) => view.handle,
            _ => ptr::null_mut() as *mut _,
        };
        let scope_ptr = match scope {
            Some(mut scope) => scope.as_mut(),
            _ => ptr::null_mut() as *mut _,
        };
        let mut size: usize = 0;
        unsafe {
            Array::new(
                BNSettingsGetStringList(
                    self.handle,
                    key.as_ref().as_ptr() as *mut _,
                    view_handle,
                    scope_ptr,
                    &mut size,
                ) as *mut *mut c_char,
                size,
                (),
            )
        }
    }

    pub fn get_json<S: BnStrCompatible>(
        &self,
        key: S,
        view: Option<&BinaryView>,
        scope: Option<Box<SettingsScope>>,
    ) -> BnString {
        let key = key.as_bytes_with_nul();
        let view_handle = match view {
            Some(view) => view.handle,
            _ => ptr::null_mut() as *mut _,
        };
        let scope_ptr = match scope {
            Some(mut scope) => scope.as_mut(),
            _ => ptr::null_mut() as *mut _,
        };
        unsafe {
            BnString::from_raw(BNSettingsGetJson(
                self.handle,
                key.as_ref().as_ptr() as *mut _,
                view_handle,
                scope_ptr,
            ))
        }
    }

    pub fn set_bool<S: BnStrCompatible>(
        &self,
        key: S,
        value: bool,
        view: Option<&BinaryView>,
        scope: Option<SettingsScope>,
    ) {
        let key = key.as_bytes_with_nul();
        let view_handle = match view {
            Some(view) => view.handle,
            _ => ptr::null_mut() as *mut _,
        };
        let scope = match scope {
            Some(scope) => scope,
            _ => SettingsScope::SettingsAutoScope,
        };
        unsafe {
            BNSettingsSetBool(
                self.handle,
                view_handle,
                scope,
                key.as_ref().as_ptr() as *mut _,
                value,
            );
        }
    }

    pub fn set_double<S: BnStrCompatible>(
        &self,
        key: S,
        value: f64,
        view: Option<&BinaryView>,
        scope: Option<SettingsScope>,
    ) {
        let key = key.as_bytes_with_nul();
        let view_handle = match view {
            Some(view) => view.handle,
            _ => ptr::null_mut() as *mut _,
        };
        let scope = match scope {
            Some(scope) => scope,
            _ => SettingsScope::SettingsAutoScope,
        };
        unsafe {
            BNSettingsSetDouble(
                self.handle,
                view_handle,
                scope,
                key.as_ref().as_ptr() as *mut _,
                value,
            );
        }
    }

    pub fn set_integer<S: BnStrCompatible>(
        &self,
        key: S,
        value: u64,
        view: Option<&BinaryView>,
        scope: Option<SettingsScope>,
    ) {
        let key = key.as_bytes_with_nul();
        let view_handle = match view {
            Some(view) => view.handle,
            _ => ptr::null_mut() as *mut _,
        };
        let scope = match scope {
            Some(scope) => scope,
            _ => SettingsScope::SettingsAutoScope,
        };
        unsafe {
            BNSettingsSetUInt64(
                self.handle,
                view_handle,
                scope,
                key.as_ref().as_ptr() as *mut _,
                value,
            );
        }
    }

    pub fn set_string<S: BnStrCompatible>(
        &self,
        key: S,
        value: S,
        view: Option<&BinaryView>,
        scope: Option<SettingsScope>,
    ) {
        let key = key.as_bytes_with_nul();
        let value = value.as_bytes_with_nul();
        let view_handle = match view {
            Some(view) => view.handle,
            _ => ptr::null_mut() as *mut _,
        };
        let scope = match scope {
            Some(scope) => scope,
            _ => SettingsScope::SettingsAutoScope,
        };
        unsafe {
            BNSettingsSetString(
                self.handle,
                view_handle,
                scope,
                key.as_ref().as_ptr() as *mut _,
                value.as_ref().as_ptr() as *mut _,
            );
        }
    }

    pub fn set_string_list<S1: BnStrCompatible, S2: BnStrCompatible, I: Iterator<Item = S2>>(
        &self,
        key: S1,
        value: I,
        view: Option<&BinaryView>,
        scope: Option<SettingsScope>,
    ) -> bool {
        let key = key.as_bytes_with_nul();
        let v = Vec::from_iter(value);

        let mut value = vec![];
        for item in v {
            value.push(item.as_bytes_with_nul().as_ref().as_ptr() as *const c_char);
        }

        let view_handle = match view {
            Some(view) => view.handle,
            None => ptr::null_mut() as *mut _,
        };

        let scope = match scope {
            Some(scope) => scope,
            None => SettingsScope::SettingsAutoScope,
        };

        unsafe {
            BNSettingsSetStringList(
                self.handle,
                view_handle,
                scope,
                key.as_ref().as_ptr() as *mut _,
                value.as_mut_ptr(),
                value.len(),
            )
        }
    }

    pub fn set_json<S1: BnStrCompatible, S2: BnStrCompatible>(
        &self,
        key: S1,
        value: S2,
        view: Option<&BinaryView>,
        scope: Option<SettingsScope>,
    ) -> bool {
        let key = key.as_bytes_with_nul();
        let value = value.as_bytes_with_nul();

        let view_handle = match view {
            Some(view) => view.handle,
            None => ptr::null_mut() as *mut _,
        };

        let scope = match scope {
            Some(scope) => scope,
            None => SettingsScope::SettingsAutoScope,
        };

        unsafe {
            BNSettingsSetJson(
                self.handle,
                view_handle,
                scope,
                key.as_ref().as_ptr() as *mut _,
                value.as_ref().as_ptr() as *mut _,
            )
        }
    }
}

impl AsRef<Settings> for Settings {
    fn as_ref(&self) -> &Self {
        self
    }
}

impl ToOwned for Settings {
    type Owned = Ref<Self>;

    fn to_owned(&self) -> Self::Owned {
        unsafe { RefCountable::inc_ref(self) }
    }
}

unsafe impl RefCountable for Settings {
    unsafe fn inc_ref(handle: &Self) -> Ref<Self> {
        Ref::new(Self {
            handle: BNNewSettingsReference(handle.handle),
        })
    }

    unsafe fn dec_ref(handle: &Self) {
        BNFreeSettings(handle.handle);
    }
}
<|endoftext|>