<commit_before>use std::collections::btree_map::BTreeMap;
use std::env;
use std::fs::File;
use std::io::Read;
use std::path::Path;
use std::path::PathBuf;
use toml;
use toml::Parser as TomlParser;

pub struct Config {
    /// Path to the root of the Android SDK.
    pub sdk_path: PathBuf,
    /// Path to the root of the Android NDK.
    pub ndk_path: PathBuf,
    /// How to invoke `ant`.
    pub ant_command: String,

    /// Name that the package will have on the Android machine.
    /// This is the key that Android uses to identify your package, so it should be unique for
    /// for each application and should contain the vendor's name.
    pub package_name: String,
    /// Name of the project to feed to the SDK. This will be the name of the APK file.
    /// Should be a "system-ish" name, like `my-project`.
    pub project_name: String,
    /// Label for the package.
    pub package_label: String,

    /// Name of the launcher icon.
    /// Versions of this icon with different resolutions have to reside in the res folder
    pub package_icon: Option<String>,

    /// List of targets to build the app for. Eg. `arm-linux-androideabi`.
    pub build_targets: Vec<String>,

    /// Version of android for which to compile. TODO: ensure that >=18 because Rustc only supports 18+
    pub android_version: u32,

    /// If `Some`, a path that contains the list of assets to ship as part of the package.
    ///
    /// The assets can later be loaded with the runtime library.
    pub assets_path: Option<PathBuf>,

    /// If `Some`, a path that contains the list of resources to ship as part of the package.
    ///
    /// The resources can later be loaded with the runtime library.
    /// This folder contains for example the launcher icon, the styles and resolution dependent images.
    pub res_path: Option<PathBuf>,

    /// Should we build in release mode?
    pub release: bool,

    /// Should this app be in fullscreen mode (hides the title bar)?
    pub fullscreen: bool,

    // Appends this string to the application attributes in the AndroidManifest.xml
    pub application_attributes: Option<String>,

    // Appends this string to the activity attributes in the AndroidManifest.xml
    pub activity_attributes: Option<String>,
}

pub fn load(manifest_path: &Path) -> Config {
    // Determine the name of the package and the Android-specific metadata from the Cargo.toml
    let (package_name, manifest_content) = {
        let content = {
            let mut file = File::open(manifest_path).unwrap();
            let mut content = String::new();
            file.read_to_string(&mut content).unwrap();
            content
        };

        let toml = TomlParser::new(&content).parse().unwrap();
        let decoded: TomlPackage = toml::decode(toml["package"].clone()).unwrap();
        let package_name = decoded.name.clone();
        (package_name, decoded.metadata.and_then(|m| m.android))
    };

    let ndk_path = env::var("NDK_HOME").expect("Please set the path to the Android NDK with the \
                                                $NDK_HOME environment variable.");

    let sdk_path = {
        let mut try = env::var("ANDROID_SDK_HOME").ok();

        if try.is_none() {
            try = env::var("ANDROID_HOME").ok();
        }

        try.expect("Please set the path to the Android SDK with either the $ANDROID_SDK_HOME or \
                    the $ANDROID_HOME environment variable.")
    };

    // For the moment some fields of the config are dummies.
    Config {
        sdk_path: Path::new(&sdk_path).to_owned(),
        ndk_path: Path::new(&ndk_path).to_owned(),
        ant_command: if cfg!(target_os = "windows") { "ant.bat" } else { "ant" }.to_owned(),
        package_name: manifest_content.as_ref().and_then(|a| a.package_name.clone())
                                       .unwrap_or_else(|| format!("rust.{}", package_name)),
        project_name: package_name.clone(),
        package_label: manifest_content.as_ref().and_then(|a| a.label.clone())
                                       .unwrap_or_else(|| package_name.clone()),
        package_icon: manifest_content.as_ref().and_then(|a| a.icon.clone()),
        build_targets: vec!["arm-linux-androideabi".to_owned()],
        android_version: manifest_content.as_ref().and_then(|a| a.android_version).unwrap_or(18),
        assets_path: manifest_content.as_ref().and_then(|a| a.assets.as_ref())
            .map(|p| manifest_path.parent().unwrap().join(p)),
        res_path: manifest_content.as_ref().and_then(|a| a.res.as_ref())
            .map(|p| manifest_path.parent().unwrap().join(p)),
        release: false,
        fullscreen: manifest_content.as_ref().and_then(|a| a.fullscreen.clone()).unwrap_or(false),
        application_attributes: manifest_content.as_ref().and_then(|a| map_to_string(a.application_attributes.clone())),
        activity_attributes: manifest_content.as_ref().and_then(|a| map_to_string(a.activity_attributes.clone()))
    }
}

fn map_to_string(input_map: Option<BTreeMap<String, String>>) -> Option<String> {
    // TODO rewrite this in functional style
    if let Some(map) = input_map {
        let mut result = String::new();
        for (key, val) in map {
            result.push_str(&format!("\n{}=\"{}\"", key, val))
        }
        Some(result)
    } else {
        None
    }
}

#[derive(Debug, Clone, RustcDecodable)]
struct TomlPackage {
    name: String,
    metadata: Option<TomlMetadata>,
}

#[derive(Debug, Clone, RustcDecodable)]
struct TomlMetadata {
    android: Option<TomlAndroid>,
}

#[derive(Debug, Clone, RustcDecodable)]
struct TomlAndroid {
    package_name: Option<String>,
    label: Option<String>,
    icon: Option<String>,
    assets: Option<String>,
    res: Option<String>,
    android_version: Option<u32>,
    fullscreen: Option<bool>,
    application_attributes: Option<BTreeMap<String, String>>,
    activity_attributes: Option<BTreeMap<String, String>>,
}
<commit_msg>Expose option for multiple build targets<commit_after>use std::collections::btree_map::BTreeMap;
use std::env;
use std::fs::File;
use std::io::Read;
use std::path::Path;
use std::path::PathBuf;
use toml;
use toml::Parser as TomlParser;

pub struct Config {
    /// Path to the root of the Android SDK.
    pub sdk_path: PathBuf,
    /// Path to the root of the Android NDK.
    pub ndk_path: PathBuf,
    /// How to invoke `ant`.
    pub ant_command: String,

    /// Name that the package will have on the Android machine.
    /// This is the key that Android uses to identify your package, so it should be unique for
    /// for each application and should contain the vendor's name.
    pub package_name: String,
    /// Name of the project to feed to the SDK. This will be the name of the APK file.
    /// Should be a "system-ish" name, like `my-project`.
    pub project_name: String,
    /// Label for the package.
    pub package_label: String,

    /// Name of the launcher icon.
    /// Versions of this icon with different resolutions have to reside in the res folder
    pub package_icon: Option<String>,

    /// List of targets to build the app for. Eg. `arm-linux-androideabi`.
    pub build_targets: Vec<String>,

    /// Version of android for which to compile. TODO: ensure that >=18 because Rustc only supports 18+
    pub android_version: u32,

    /// If `Some`, a path that contains the list of assets to ship as part of the package.
    ///
    /// The assets can later be loaded with the runtime library.
    pub assets_path: Option<PathBuf>,

    /// If `Some`, a path that contains the list of resources to ship as part of the package.
    ///
    /// The resources can later be loaded with the runtime library.
    /// This folder contains for example the launcher icon, the styles and resolution dependent images.
    pub res_path: Option<PathBuf>,

    /// Should we build in release mode?
    pub release: bool,

    /// Should this app be in fullscreen mode (hides the title bar)?
    pub fullscreen: bool,

    // Appends this string to the application attributes in the AndroidManifest.xml
    pub application_attributes: Option<String>,

    // Appends this string to the activity attributes in the AndroidManifest.xml
    pub activity_attributes: Option<String>,
}

pub fn load(manifest_path: &Path) -> Config {
    // Determine the name of the package and the Android-specific metadata from the Cargo.toml
    let (package_name, manifest_content) = {
        let content = {
            let mut file = File::open(manifest_path).unwrap();
            let mut content = String::new();
            file.read_to_string(&mut content).unwrap();
            content
        };

        let toml = TomlParser::new(&content).parse().unwrap();
        let decoded: TomlPackage = toml::decode(toml["package"].clone()).unwrap();
        let package_name = decoded.name.clone();
        (package_name, decoded.metadata.and_then(|m| m.android))
    };

    let ndk_path = env::var("NDK_HOME").expect("Please set the path to the Android NDK with the \
                                                $NDK_HOME environment variable.");

    let sdk_path = {
        let mut try = env::var("ANDROID_SDK_HOME").ok();

        if try.is_none() {
            try = env::var("ANDROID_HOME").ok();
        }

        try.expect("Please set the path to the Android SDK with either the $ANDROID_SDK_HOME or \
                    the $ANDROID_HOME environment variable.")
    };

    // For the moment some fields of the config are dummies.
    Config {
        sdk_path: Path::new(&sdk_path).to_owned(),
        ndk_path: Path::new(&ndk_path).to_owned(),
        ant_command: if cfg!(target_os = "windows") { "ant.bat" } else { "ant" }.to_owned(),
        package_name: manifest_content.as_ref().and_then(|a| a.package_name.clone())
                                       .unwrap_or_else(|| format!("rust.{}", package_name)),
        project_name: package_name.clone(),
        package_label: manifest_content.as_ref().and_then(|a| a.label.clone())
                                       .unwrap_or_else(|| package_name.clone()),
        package_icon: manifest_content.as_ref().and_then(|a| a.icon.clone()),
        build_targets: manifest_content.as_ref().and_then(|a| a.build_targets.clone())
                                       .unwrap_or(vec!["arm-linux-androideabi".to_owned()]),
        android_version: manifest_content.as_ref().and_then(|a| a.android_version).unwrap_or(18),
        assets_path: manifest_content.as_ref().and_then(|a| a.assets.as_ref())
            .map(|p| manifest_path.parent().unwrap().join(p)),
        res_path: manifest_content.as_ref().and_then(|a| a.res.as_ref())
            .map(|p| manifest_path.parent().unwrap().join(p)),
        release: false,
        fullscreen: manifest_content.as_ref().and_then(|a| a.fullscreen.clone()).unwrap_or(false),
        application_attributes: manifest_content.as_ref().and_then(|a| map_to_string(a.application_attributes.clone())),
        activity_attributes: manifest_content.as_ref().and_then(|a| map_to_string(a.activity_attributes.clone()))
    }
}

fn map_to_string(input_map: Option<BTreeMap<String, String>>) -> Option<String> {
    // TODO rewrite this in functional style
    if let Some(map) = input_map {
        let mut result = String::new();
        for (key, val) in map {
            result.push_str(&format!("\n{}=\"{}\"", key, val))
        }
        Some(result)
    } else {
        None
    }
}

#[derive(Debug, Clone, RustcDecodable)]
struct TomlPackage {
    name: String,
    metadata: Option<TomlMetadata>,
}

#[derive(Debug, Clone, RustcDecodable)]
struct TomlMetadata {
    android: Option<TomlAndroid>,
}

#[derive(Debug, Clone, RustcDecodable)]
struct TomlAndroid {
    package_name: Option<String>,
    label: Option<String>,
    icon: Option<String>,
    assets: Option<String>,
    res: Option<String>,
    android_version: Option<u32>,
    fullscreen: Option<bool>,
    application_attributes: Option<BTreeMap<String, String>>,
    activity_attributes: Option<BTreeMap<String, String>>,
    build_targets: Option<Vec<String>>,
}
<|endoftext|>


<commit_before>/* Code to handle method lookups (which can be quite complex) */

import syntax::ast_map;
import middle::typeck::infer::methods; // next_ty_vars
import dvec::{dvec, extensions};

type candidate = {
    self_ty: ty::t,          // type of a in a.b()
    self_substs: ty::substs, // values for any tvars def'd on the class
    rcvr_ty: ty::t,          // type of receiver in the method def
    n_tps_m: uint,           // number of tvars defined on the method
    fty: ty::t,              // type of the method
    entry: method_map_entry
};

class lookup {
    let fcx: @fn_ctxt;
    let expr: @ast::expr;
    let self_expr: @ast::expr;
    let borrow_scope: ast::node_id;
    let node_id: ast::node_id;
    let m_name: ast::ident;
    let mut self_ty: ty::t;
    let mut derefs: uint;
    let candidates: dvec<candidate>;
    let supplied_tps: [ty::t]/~;
    let include_private: bool;

    new(fcx: @fn_ctxt,
        expr: @ast::expr,           //expr for a.b in a.b()
        self_expr: @ast::expr,      //a in a.b(...)
        borrow_scope: ast::node_id, //scope to borrow the expr for
        node_id: ast::node_id,      //node id where to store type of fn
        m_name: ast::ident,         //b in a.b(...)
        self_ty: ty::t,             //type of a in a.b(...)
        supplied_tps: [ty::t]/~,      //Xs in a.b::<Xs>(...)
        include_private: bool) {

        self.fcx = fcx;
        self.expr = expr;
        self.self_expr = self_expr;
        self.borrow_scope = borrow_scope;
        self.node_id = node_id;
        self.m_name = m_name;
        self.self_ty = self_ty;
        self.derefs = 0u;
        self.candidates = dvec();
        self.supplied_tps = supplied_tps;
        self.include_private = include_private;
    }

    // Entrypoint:
    fn method() -> option<method_map_entry> {
        #debug["method lookup(m_name=%s, self_ty=%s)",
               *self.m_name, self.fcx.infcx.ty_to_str(self.self_ty)];

        loop {
            // First, see whether this is an interface-bounded parameter
            alt ty::get(self.self_ty).struct {
              ty::ty_param(n, did) {
                self.add_candidates_from_param(n, did);
              }
              ty::ty_iface(did, substs) {
                self.add_candidates_from_iface(did, substs);
              }
              ty::ty_class(did, substs) {
                self.add_candidates_from_class(did, substs);
              }
              _ { }
            }

            // if we found anything, stop now.  otherwise continue to
            // loop for impls in scope.  Note: I don't love these
            // semantics, but that's what we had so I am preserving
            // it.
            if self.candidates.len() > 0u {
                break;
            }

            self.add_candidates_from_scope();

            // if we found anything, stop before attempting auto-deref.
            if self.candidates.len() > 0u {
                break;
            }

            // check whether we can autoderef and if so loop around again.
            alt ty::deref(self.tcx(), self.self_ty, false) {
              none { break; }
              some(mt) {
                self.self_ty = mt.ty;
                self.derefs += 1u;
              }
            }
        }

        if self.candidates.len() == 0u { ret none; }

        if self.candidates.len() > 1u {
            self.tcx().sess.span_err(
                self.expr.span,
                "multiple applicable methods in scope");

            for self.candidates.eachi { |i, candidate|
                alt candidate.entry.origin {
                  method_static(did) {
                    self.report_static_candidate(i, did);
                  }
                  method_param(p) {
                    self.report_param_candidate(i, p.iface_id);
                  }
                  method_iface(did, _) {
                    self.report_iface_candidate(i, did);
                  }
                }
            }
        }

        some(self.write_mty_from_candidate(self.candidates[0u]))
    }

    fn tcx() -> ty::ctxt { self.fcx.ccx.tcx }

    fn report_static_candidate(idx: uint, did: ast::def_id) {
        let span = if did.crate == ast::local_crate {
            alt check self.tcx().items.get(did.node) {
              ast_map::node_method(m, _, _) { m.span }
            }
        } else {
            self.expr.span
        };
        self.tcx().sess.span_note(
            span,
            #fmt["candidate #%u is `%s`",
                 (idx+1u),
                 ty::item_path_str(self.tcx(), did)]);
    }

    fn report_param_candidate(idx: uint, did: ast::def_id) {
        self.tcx().sess.span_note(
            self.expr.span,
            #fmt["candidate #%u derives from the bound `%s`",
                 (idx+1u),
                 ty::item_path_str(self.tcx(), did)]);
    }

    fn report_iface_candidate(idx: uint, did: ast::def_id) {
        self.tcx().sess.span_note(
            self.expr.span,
            #fmt["candidate #%u derives from the type of the receiver, \
                  which is the iface `%s`",
                 (idx+1u),
                 ty::item_path_str(self.tcx(), did)]);
    }

    fn add_candidates_from_param(n: uint, did: ast::def_id) {
        #debug["candidates_from_param"];

        let tcx = self.tcx();
        let mut iface_bnd_idx = 0u; // count only iface bounds
        let bounds = tcx.ty_param_bounds.get(did.node);
        for vec::each(*bounds) {|bound|
            let (iid, bound_substs) = alt bound {
              ty::bound_copy | ty::bound_send | ty::bound_const {
                cont; /* ok */
              }
              ty::bound_iface(bound_t) {
                alt check ty::get(bound_t).struct {
                  ty::ty_iface(i, substs) { (i, substs) }
                }
              }
            };

            let ifce_methods = ty::iface_methods(tcx, iid);
            alt vec::position(*ifce_methods, {|m| m.ident == self.m_name}) {
              none {
                /* check next bound */
                iface_bnd_idx += 1u;
              }

              some(pos) {
                // Replace any appearance of `self` with the type of the
                // generic parameter itself.  Note that this is the only case
                // where this replacement is necessary: in all other cases, we
                // are either invoking a method directly from an impl or class
                // (where the self type is not permitted), or from a iface
                // type (in which case methods that refer to self are not
                // permitted).
                let substs = {self_ty: some(self.self_ty)
                              with bound_substs};

                self.add_candidates_from_m(
                    substs, ifce_methods[pos],
                    method_param({iface_id:iid,
                                  method_num:pos,
                                  param_num:n,
                                  bound_num:iface_bnd_idx}));
              }
            }
        }

    }

    fn add_candidates_from_iface(did: ast::def_id, iface_substs: ty::substs) {

        #debug["method_from_iface"];

        let ms = *ty::iface_methods(self.tcx(), did);
        for ms.eachi {|i, m|
            if m.ident != self.m_name { cont; }

            let m_fty = ty::mk_fn(self.tcx(), m.fty);

            if ty::type_has_self(m_fty) {
                self.tcx().sess.span_err(
                    self.expr.span,
                    "can not call a method that contains a \
                     self type through a boxed iface");
            }

            if (*m.tps).len() > 0u {
                self.tcx().sess.span_err(
                    self.expr.span,
                    "can not call a generic method through a \
                     boxed iface");
            }

            // Note: although it is illegal to invoke a method that uses self
            // through a iface instance, we use a dummy subst here so that we
            // can soldier on with the compilation.
            let substs = {self_ty: some(self.self_ty)
                          with iface_substs};

            self.add_candidates_from_m(
                substs, m, method_iface(did, i));
        }
    }

    fn add_candidates_from_class(did: ast::def_id, class_substs: ty::substs) {

        #debug["method_from_class"];

        let ms = *ty::iface_methods(self.tcx(), did);

        for ms.each {|m|
            if m.ident != self.m_name { cont; }

            if m.vis == ast::private && !self.include_private {
                self.tcx().sess.span_fatal(
                    self.expr.span,
                    "Call to private method not allowed outside \
                     its defining class");
            }

            // look up method named <name>.
            let m_declared = ty::lookup_class_method_by_name(
                self.tcx(), did, self.m_name, self.expr.span);

            self.add_candidates_from_m(
                class_substs, m, method_static(m_declared));
        }
    }

    fn ty_from_did(did: ast::def_id) -> ty::t {
        alt check ty::get(ty::lookup_item_type(self.tcx(), did).ty).struct {
          ty::ty_fn(fty) {
            ty::mk_fn(self.tcx(), {proto: ast::proto_box with fty})
          }
        }
        /*
        if did.crate == ast::local_crate {
            alt check self.tcx().items.get(did.node) {
              ast_map::node_method(m, _, _) {
                // NDM iface/impl regions
                let mt = ty_of_method(self.fcx.ccx, m, ast::rp_none);
                ty::mk_fn(self.tcx(), {proto: ast::proto_box with mt.fty})
              }
            }
        } else {
            alt check ty::get(csearch::get_type(self.tcx(), did).ty).struct {
              ty::ty_fn(fty) {
                ty::mk_fn(self.tcx(), {proto: ast::proto_box with fty})
              }
            }
        }
        */
    }

    fn add_candidates_from_scope() {
        let impls_vecs = self.fcx.ccx.impl_map.get(self.expr.id);
        let mut added_any = false;

        #debug["method_from_scope"];

        for list::each(impls_vecs) {|impls|
            for vec::each(*impls) {|im|
                // Check whether this impl has a method with the right name.
                for im.methods.find({|m| m.ident == self.m_name}).each {|m|

                    // determine the `self` of the impl with fresh
                    // variables for each parameter:
                    let {substs: impl_substs, ty: impl_ty} =
                        impl_self_ty(self.fcx, im.did);

                    // if we can assign the caller to the callee, that's a
                    // potential match.  Collect those in the vector.
                    let can_assign = self.fcx.can_mk_assignty(
                        self.self_expr, self.borrow_scope,
                        self.self_ty, impl_ty);
                    #debug["can_assign = %?", can_assign];
                    alt can_assign {
                      result::err(_) { /* keep looking */ }
                      result::ok(_) {
                        let fty = self.ty_from_did(m.did);
                        self.candidates.push(
                            {self_ty: self.self_ty,
                             self_substs: impl_substs,
                             rcvr_ty: impl_ty,
                             n_tps_m: m.n_tps,
                             fty: fty,
                             entry: {derefs: self.derefs,
                                     origin: method_static(m.did)}});
                        added_any = true;
                      }
                    }
                }
            }

            // we want to find the innermost scope that has any
            // matches and then ignore outer scopes
            if added_any {ret;}
        }
    }

    fn add_candidates_from_m(self_substs: ty::substs,
                             m: ty::method,
                             origin: method_origin) {
        let tcx = self.fcx.ccx.tcx;

        // a bit hokey, but the method unbound has a bare protocol, whereas
        // a.b has a protocol like fn@() (perhaps eventually fn&()):
        let fty = ty::mk_fn(tcx, {proto: ast::proto_box with m.fty});

        self.candidates.push(
            {self_ty: self.self_ty,
             self_substs: self_substs,
             rcvr_ty: self.self_ty,
             n_tps_m: (*m.tps).len(),
             fty: fty,
             entry: {derefs: self.derefs, origin: origin}});
    }

    fn write_mty_from_candidate(cand: candidate) -> method_map_entry {
        let tcx = self.fcx.ccx.tcx;

        #debug["write_mty_from_candidate(n_tps_m=%u, fty=%s, entry=%?)",
               cand.n_tps_m,
               self.fcx.infcx.ty_to_str(cand.fty),
               cand.entry];

        // Make the actual receiver type (cand.self_ty) assignable to the
        // required receiver type (cand.rcvr_ty).  If this method is not
        // from an impl, this'll basically be a no-nop.
        alt self.fcx.mk_assignty(self.self_expr, self.borrow_scope,
                                 cand.self_ty, cand.rcvr_ty) {
          result::ok(_) {}
          result::err(_) {
            self.tcx().sess.span_bug(
                self.expr.span,
                #fmt["%s was assignable to %s but now is not?",
                     self.fcx.infcx.ty_to_str(cand.self_ty),
                     self.fcx.infcx.ty_to_str(cand.rcvr_ty)]);
          }
        }

        // Construct the full set of type parameters for the method,
        // which is equal to the class tps + the method tps.
        let n_tps_supplied = self.supplied_tps.len();
        let n_tps_m = cand.n_tps_m;
        let m_substs = {
            if n_tps_supplied == 0u {
                self.fcx.infcx.next_ty_vars(n_tps_m)
            } else if n_tps_m == 0u {
                tcx.sess.span_err(
                    self.expr.span,
                    "this method does not take type parameters");
                self.fcx.infcx.next_ty_vars(n_tps_m)
            } else if n_tps_supplied != n_tps_m {
                tcx.sess.span_err(
                    self.expr.span,
                    "incorrect number of type \
                     parameters given for this method");
                self.fcx.infcx.next_ty_vars(n_tps_m)
            } else {
                self.supplied_tps
            }
        };

        let all_substs = {tps: cand.self_substs.tps + m_substs
                          with cand.self_substs};

        self.fcx.write_ty_substs(self.node_id, cand.fty, all_substs);

        ret cand.entry;
    }
}

<commit_msg>rustc: Don't allow multiple candidate methods from impls with the same def ID<commit_after>/* Code to handle method lookups (which can be quite complex) */

import syntax::ast::def_id;
import syntax::ast_map;
import syntax::ast_util::new_def_hash;
import middle::typeck::infer::methods; // next_ty_vars
import dvec::{dvec, extensions};

type candidate = {
    self_ty: ty::t,          // type of a in a.b()
    self_substs: ty::substs, // values for any tvars def'd on the class
    rcvr_ty: ty::t,          // type of receiver in the method def
    n_tps_m: uint,           // number of tvars defined on the method
    fty: ty::t,              // type of the method
    entry: method_map_entry
};

class lookup {
    let fcx: @fn_ctxt;
    let expr: @ast::expr;
    let self_expr: @ast::expr;
    let borrow_scope: ast::node_id;
    let node_id: ast::node_id;
    let m_name: ast::ident;
    let mut self_ty: ty::t;
    let mut derefs: uint;
    let candidates: dvec<candidate>;
    let candidate_impls: hashmap<def_id, ()>;
    let supplied_tps: [ty::t]/~;
    let include_private: bool;

    new(fcx: @fn_ctxt,
        expr: @ast::expr,           //expr for a.b in a.b()
        self_expr: @ast::expr,      //a in a.b(...)
        borrow_scope: ast::node_id, //scope to borrow the expr for
        node_id: ast::node_id,      //node id where to store type of fn
        m_name: ast::ident,         //b in a.b(...)
        self_ty: ty::t,             //type of a in a.b(...)
        supplied_tps: [ty::t]/~,      //Xs in a.b::<Xs>(...)
        include_private: bool) {

        self.fcx = fcx;
        self.expr = expr;
        self.self_expr = self_expr;
        self.borrow_scope = borrow_scope;
        self.node_id = node_id;
        self.m_name = m_name;
        self.self_ty = self_ty;
        self.derefs = 0u;
        self.candidates = dvec();
        self.candidate_impls = new_def_hash();
        self.supplied_tps = supplied_tps;
        self.include_private = include_private;
    }

    // Entrypoint:
    fn method() -> option<method_map_entry> {
        #debug["method lookup(m_name=%s, self_ty=%s)",
               *self.m_name, self.fcx.infcx.ty_to_str(self.self_ty)];

        loop {
            // First, see whether this is an interface-bounded parameter
            alt ty::get(self.self_ty).struct {
              ty::ty_param(n, did) {
                self.add_candidates_from_param(n, did);
              }
              ty::ty_iface(did, substs) {
                self.add_candidates_from_iface(did, substs);
              }
              ty::ty_class(did, substs) {
                self.add_candidates_from_class(did, substs);
              }
              _ { }
            }

            // if we found anything, stop now.  otherwise continue to
            // loop for impls in scope.  Note: I don't love these
            // semantics, but that's what we had so I am preserving
            // it.
            if self.candidates.len() > 0u {
                break;
            }

            self.add_candidates_from_scope();

            // if we found anything, stop before attempting auto-deref.
            if self.candidates.len() > 0u {
                break;
            }

            // check whether we can autoderef and if so loop around again.
            alt ty::deref(self.tcx(), self.self_ty, false) {
              none { break; }
              some(mt) {
                self.self_ty = mt.ty;
                self.derefs += 1u;
              }
            }
        }

        if self.candidates.len() == 0u { ret none; }

        if self.candidates.len() > 1u {
            self.tcx().sess.span_err(
                self.expr.span,
                "multiple applicable methods in scope");

            for self.candidates.eachi { |i, candidate|
                alt candidate.entry.origin {
                  method_static(did) {
                    self.report_static_candidate(i, did);
                  }
                  method_param(p) {
                    self.report_param_candidate(i, p.iface_id);
                  }
                  method_iface(did, _) {
                    self.report_iface_candidate(i, did);
                  }
                }
            }
        }

        some(self.write_mty_from_candidate(self.candidates[0u]))
    }

    fn tcx() -> ty::ctxt { self.fcx.ccx.tcx }

    fn report_static_candidate(idx: uint, did: ast::def_id) {
        let span = if did.crate == ast::local_crate {
            alt check self.tcx().items.get(did.node) {
              ast_map::node_method(m, _, _) { m.span }
            }
        } else {
            self.expr.span
        };
        self.tcx().sess.span_note(
            span,
            #fmt["candidate #%u is `%s`",
                 (idx+1u),
                 ty::item_path_str(self.tcx(), did)]);
    }

    fn report_param_candidate(idx: uint, did: ast::def_id) {
        self.tcx().sess.span_note(
            self.expr.span,
            #fmt["candidate #%u derives from the bound `%s`",
                 (idx+1u),
                 ty::item_path_str(self.tcx(), did)]);
    }

    fn report_iface_candidate(idx: uint, did: ast::def_id) {
        self.tcx().sess.span_note(
            self.expr.span,
            #fmt["candidate #%u derives from the type of the receiver, \
                  which is the iface `%s`",
                 (idx+1u),
                 ty::item_path_str(self.tcx(), did)]);
    }

    fn add_candidates_from_param(n: uint, did: ast::def_id) {
        #debug["candidates_from_param"];

        let tcx = self.tcx();
        let mut iface_bnd_idx = 0u; // count only iface bounds
        let bounds = tcx.ty_param_bounds.get(did.node);
        for vec::each(*bounds) {|bound|
            let (iid, bound_substs) = alt bound {
              ty::bound_copy | ty::bound_send | ty::bound_const {
                cont; /* ok */
              }
              ty::bound_iface(bound_t) {
                alt check ty::get(bound_t).struct {
                  ty::ty_iface(i, substs) { (i, substs) }
                }
              }
            };

            let ifce_methods = ty::iface_methods(tcx, iid);
            alt vec::position(*ifce_methods, {|m| m.ident == self.m_name}) {
              none {
                /* check next bound */
                iface_bnd_idx += 1u;
              }

              some(pos) {
                // Replace any appearance of `self` with the type of the
                // generic parameter itself.  Note that this is the only case
                // where this replacement is necessary: in all other cases, we
                // are either invoking a method directly from an impl or class
                // (where the self type is not permitted), or from a iface
                // type (in which case methods that refer to self are not
                // permitted).
                let substs = {self_ty: some(self.self_ty)
                              with bound_substs};

                self.add_candidates_from_m(
                    substs, ifce_methods[pos],
                    method_param({iface_id:iid,
                                  method_num:pos,
                                  param_num:n,
                                  bound_num:iface_bnd_idx}));
              }
            }
        }

    }

    fn add_candidates_from_iface(did: ast::def_id, iface_substs: ty::substs) {

        #debug["method_from_iface"];

        let ms = *ty::iface_methods(self.tcx(), did);
        for ms.eachi {|i, m|
            if m.ident != self.m_name { cont; }

            let m_fty = ty::mk_fn(self.tcx(), m.fty);

            if ty::type_has_self(m_fty) {
                self.tcx().sess.span_err(
                    self.expr.span,
                    "can not call a method that contains a \
                     self type through a boxed iface");
            }

            if (*m.tps).len() > 0u {
                self.tcx().sess.span_err(
                    self.expr.span,
                    "can not call a generic method through a \
                     boxed iface");
            }

            // Note: although it is illegal to invoke a method that uses self
            // through a iface instance, we use a dummy subst here so that we
            // can soldier on with the compilation.
            let substs = {self_ty: some(self.self_ty)
                          with iface_substs};

            self.add_candidates_from_m(
                substs, m, method_iface(did, i));
        }
    }

    fn add_candidates_from_class(did: ast::def_id, class_substs: ty::substs) {

        #debug["method_from_class"];

        let ms = *ty::iface_methods(self.tcx(), did);

        for ms.each {|m|
            if m.ident != self.m_name { cont; }

            if m.vis == ast::private && !self.include_private {
                self.tcx().sess.span_fatal(
                    self.expr.span,
                    "Call to private method not allowed outside \
                     its defining class");
            }

            // look up method named <name>.
            let m_declared = ty::lookup_class_method_by_name(
                self.tcx(), did, self.m_name, self.expr.span);

            self.add_candidates_from_m(
                class_substs, m, method_static(m_declared));
        }
    }

    fn ty_from_did(did: ast::def_id) -> ty::t {
        alt check ty::get(ty::lookup_item_type(self.tcx(), did).ty).struct {
          ty::ty_fn(fty) {
            ty::mk_fn(self.tcx(), {proto: ast::proto_box with fty})
          }
        }
        /*
        if did.crate == ast::local_crate {
            alt check self.tcx().items.get(did.node) {
              ast_map::node_method(m, _, _) {
                // NDM iface/impl regions
                let mt = ty_of_method(self.fcx.ccx, m, ast::rp_none);
                ty::mk_fn(self.tcx(), {proto: ast::proto_box with mt.fty})
              }
            }
        } else {
            alt check ty::get(csearch::get_type(self.tcx(), did).ty).struct {
              ty::ty_fn(fty) {
                ty::mk_fn(self.tcx(), {proto: ast::proto_box with fty})
              }
            }
        }
        */
    }

    fn add_candidates_from_scope() {
        let impls_vecs = self.fcx.ccx.impl_map.get(self.expr.id);
        let mut added_any = false;

        #debug["method_from_scope"];

        for list::each(impls_vecs) {|impls|
            for vec::each(*impls) {|im|
                // Check whether this impl has a method with the right name.
                for im.methods.find({|m| m.ident == self.m_name}).each {|m|

                    // determine the `self` of the impl with fresh
                    // variables for each parameter:
                    let {substs: impl_substs, ty: impl_ty} =
                        impl_self_ty(self.fcx, im.did);

                    // if we can assign the caller to the callee, that's a
                    // potential match.  Collect those in the vector.
                    let can_assign = self.fcx.can_mk_assignty(
                        self.self_expr, self.borrow_scope,
                        self.self_ty, impl_ty);
                    #debug["can_assign = %?", can_assign];
                    alt can_assign {
                      result::err(_) { /* keep looking */ }
                      result::ok(_) {
                        if !self.candidate_impls.contains_key(im.did) {
                            let fty = self.ty_from_did(m.did);
                            self.candidates.push(
                                {self_ty: self.self_ty,
                                 self_substs: impl_substs,
                                 rcvr_ty: impl_ty,
                                 n_tps_m: m.n_tps,
                                 fty: fty,
                                 entry: {derefs: self.derefs,
                                         origin: method_static(m.did)}});
                            self.candidate_impls.insert(im.did, ());
                            added_any = true;
                        }
                      }
                    }
                }
            }

            // we want to find the innermost scope that has any
            // matches and then ignore outer scopes
            if added_any {ret;}
        }
    }

    fn add_candidates_from_m(self_substs: ty::substs,
                             m: ty::method,
                             origin: method_origin) {
        let tcx = self.fcx.ccx.tcx;

        // a bit hokey, but the method unbound has a bare protocol, whereas
        // a.b has a protocol like fn@() (perhaps eventually fn&()):
        let fty = ty::mk_fn(tcx, {proto: ast::proto_box with m.fty});

        self.candidates.push(
            {self_ty: self.self_ty,
             self_substs: self_substs,
             rcvr_ty: self.self_ty,
             n_tps_m: (*m.tps).len(),
             fty: fty,
             entry: {derefs: self.derefs, origin: origin}});
    }

    fn write_mty_from_candidate(cand: candidate) -> method_map_entry {
        let tcx = self.fcx.ccx.tcx;

        #debug["write_mty_from_candidate(n_tps_m=%u, fty=%s, entry=%?)",
               cand.n_tps_m,
               self.fcx.infcx.ty_to_str(cand.fty),
               cand.entry];

        // Make the actual receiver type (cand.self_ty) assignable to the
        // required receiver type (cand.rcvr_ty).  If this method is not
        // from an impl, this'll basically be a no-nop.
        alt self.fcx.mk_assignty(self.self_expr, self.borrow_scope,
                                 cand.self_ty, cand.rcvr_ty) {
          result::ok(_) {}
          result::err(_) {
            self.tcx().sess.span_bug(
                self.expr.span,
                #fmt["%s was assignable to %s but now is not?",
                     self.fcx.infcx.ty_to_str(cand.self_ty),
                     self.fcx.infcx.ty_to_str(cand.rcvr_ty)]);
          }
        }

        // Construct the full set of type parameters for the method,
        // which is equal to the class tps + the method tps.
        let n_tps_supplied = self.supplied_tps.len();
        let n_tps_m = cand.n_tps_m;
        let m_substs = {
            if n_tps_supplied == 0u {
                self.fcx.infcx.next_ty_vars(n_tps_m)
            } else if n_tps_m == 0u {
                tcx.sess.span_err(
                    self.expr.span,
                    "this method does not take type parameters");
                self.fcx.infcx.next_ty_vars(n_tps_m)
            } else if n_tps_supplied != n_tps_m {
                tcx.sess.span_err(
                    self.expr.span,
                    "incorrect number of type \
                     parameters given for this method");
                self.fcx.infcx.next_ty_vars(n_tps_m)
            } else {
                self.supplied_tps
            }
        };

        let all_substs = {tps: cand.self_substs.tps + m_substs
                          with cand.self_substs};

        self.fcx.write_ty_substs(self.node_id, cand.fty, all_substs);

        ret cand.entry;
    }
}

<|endoftext|>


<commit_before>//! Checks that all error codes have at least one test to prevent having error
//! codes that are silently not thrown by the compiler anymore.

use std::collections::HashMap;
use std::ffi::OsStr;
use std::fs::read_to_string;
use std::path::Path;

use regex::Regex;

// A few of those error codes can't be tested but all the others can and *should* be tested!
const EXEMPTED_FROM_TEST: &[&str] = &[
    "E0227", "E0279", "E0280", "E0313", "E0377", "E0461", "E0462", "E0464", "E0465", "E0476",
    "E0482", "E0514", "E0519", "E0523", "E0554", "E0640", "E0717", "E0729",
];

// Some error codes don't have any tests apparently...
const IGNORE_EXPLANATION_CHECK: &[&str] = &["E0570", "E0601", "E0602", "E0729"];

// If the file path contains any of these, we don't want to try to extract error codes from it.
//
// We need to declare each path in the windows version (with backslash).
const PATHS_TO_IGNORE_FOR_EXTRACTION: &[&str] =
    &["src/test/", "src\\test\\", "src/doc/", "src\\doc\\", "src/tools/", "src\\tools\\"];

#[derive(Default, Debug)]
struct ErrorCodeStatus {
    has_test: bool,
    has_explanation: bool,
    is_used: bool,
}

fn check_error_code_explanation(
    f: &str,
    error_codes: &mut HashMap<String, ErrorCodeStatus>,
    err_code: String,
) -> bool {
    let mut invalid_compile_fail_format = false;
    let mut found_error_code = false;

    for line in f.lines() {
        let s = line.trim();
        if s.starts_with("```") {
            if s.contains("compile_fail") && s.contains('E') {
                if !found_error_code {
                    error_codes.get_mut(&err_code).map(|x| x.has_test = true);
                    found_error_code = true;
                }
            } else if s.contains("compile-fail") {
                invalid_compile_fail_format = true;
            }
        } else if s.starts_with("#### Note: this error code is no longer emitted by the compiler") {
            if !found_error_code {
                error_codes.get_mut(&err_code).map(|x| x.has_test = true);
                found_error_code = true;
            }
        }
    }
    invalid_compile_fail_format
}

fn check_if_error_code_is_test_in_explanation(f: &str, err_code: &str) -> bool {
    let mut ignore_found = false;

    for line in f.lines() {
        let s = line.trim();
        if s.starts_with("#### Note: this error code is no longer emitted by the compiler") {
            return true;
        }
        if s.starts_with("```") {
            if s.contains("compile_fail") && s.contains(err_code) {
                return true;
            } else if s.contains("ignore") {
                // It's very likely that we can't actually make it fail compilation...
                ignore_found = true;
            }
        }
    }
    ignore_found
}

macro_rules! some_or_continue {
    ($e:expr) => {
        match $e {
            Some(e) => e,
            None => continue,
        }
    };
}

fn extract_error_codes(
    f: &str,
    error_codes: &mut HashMap<String, ErrorCodeStatus>,
    path: &Path,
    errors: &mut Vec<String>,
) {
    let mut reached_no_explanation = false;

    for line in f.lines() {
        let s = line.trim();
        if !reached_no_explanation && s.starts_with('E') && s.contains("include_str!(\"") {
            let err_code = s
                .split_once(':')
                .expect(
                    format!(
                        "Expected a line with the format `E0xxx: include_str!(\"..\")`, but got {} \
                         without a `:` delimiter",
                        s,
                    )
                    .as_str(),
                )
                .0
                .to_owned();
            error_codes.entry(err_code.clone()).or_default().has_explanation = true;

            // Now we extract the tests from the markdown file!
            let md_file_name = match s.split_once("include_str!(\"") {
                None => continue,
                Some((_, md)) => match md.split_once("\")") {
                    None => continue,
                    Some((file_name, _)) => file_name,
                },
            };
            let path = some_or_continue!(path.parent())
                .join(md_file_name)
                .canonicalize()
                .expect("failed to canonicalize error explanation file path");
            match read_to_string(&path) {
                Ok(content) => {
                    let has_test = check_if_error_code_is_test_in_explanation(&content, &err_code);
                    if !has_test && !IGNORE_EXPLANATION_CHECK.contains(&err_code.as_str()) {
                        errors.push(format!(
                            "`{}` doesn't use its own error code in compile_fail example",
                            path.display(),
                        ));
                    } else if has_test && IGNORE_EXPLANATION_CHECK.contains(&err_code.as_str()) {
                        errors.push(format!(
                            "`{}` has a compile_fail example with its own error code, it shouldn't \
                             be listed in IGNORE_EXPLANATION_CHECK!",
                            path.display(),
                        ));
                    }
                    if check_error_code_explanation(&content, error_codes, err_code) {
                        errors.push(format!(
                            "`{}` uses invalid tag `compile-fail` instead of `compile_fail`",
                            path.display(),
                        ));
                    }
                }
                Err(e) => {
                    eprintln!("Couldn't read `{}`: {}", path.display(), e);
                }
            }
        } else if reached_no_explanation && s.starts_with('E') {
            let err_code = match s.split_once(',') {
                None => s,
                Some((err_code, _)) => err_code,
            }
            .to_string();
            if !error_codes.contains_key(&err_code) {
                // this check should *never* fail!
                error_codes.insert(err_code, ErrorCodeStatus::default());
            }
        } else if s == ";" {
            reached_no_explanation = true;
        }
    }
}

fn extract_error_codes_from_tests(f: &str, error_codes: &mut HashMap<String, ErrorCodeStatus>) {
    for line in f.lines() {
        let s = line.trim();
        if s.starts_with("error[E") || s.starts_with("warning[E") {
            let err_code = match s.split_once(']') {
                None => continue,
                Some((err_code, _)) => match err_code.split_once('[') {
                    None => continue,
                    Some((_, err_code)) => err_code,
                },
            };
            error_codes.entry(err_code.to_owned()).or_default().has_test = true;
        }
    }
}

fn extract_error_codes_from_source(
    f: &str,
    error_codes: &mut HashMap<String, ErrorCodeStatus>,
    regex: &Regex,
) {
    for line in f.lines() {
        if line.trim_start().starts_with("//") {
            continue;
        }
        for cap in regex.captures_iter(line) {
            if let Some(error_code) = cap.get(1) {
                error_codes.entry(error_code.as_str().to_owned()).or_default().is_used = true;
            }
        }
    }
}

pub fn check(paths: &[&Path], bad: &mut bool) {
    let mut errors = Vec::new();
    let mut found_explanations = 0;
    let mut found_tests = 0;
    let mut error_codes: HashMap<String, ErrorCodeStatus> = HashMap::new();
    // We want error codes which match the following cases:
    //
    // * foo(a, E0111, a)
    // * foo(a, E0111)
    // * foo(E0111, a)
    // * #[error = "E0111"]
    let regex = Regex::new(r#"[(,"\s](E\d{4})[,)"]"#).unwrap();

    println!("Checking which error codes lack tests...");

    for path in paths {
        super::walk(path, &mut |path| super::filter_dirs(path), &mut |entry, contents| {
            let file_name = entry.file_name();
            if file_name == "error_codes.rs" {
                extract_error_codes(contents, &mut error_codes, entry.path(), &mut errors);
                found_explanations += 1;
            } else if entry.path().extension() == Some(OsStr::new("stderr")) {
                extract_error_codes_from_tests(contents, &mut error_codes);
                found_tests += 1;
            } else if entry.path().extension() == Some(OsStr::new("rs")) {
                let path = entry.path().to_string_lossy();
                if PATHS_TO_IGNORE_FOR_EXTRACTION.iter().all(|c| !path.contains(c)) {
                    extract_error_codes_from_source(contents, &mut error_codes, &regex);
                }
            }
        });
    }
    if found_explanations == 0 {
        eprintln!("No error code explanation was tested!");
        *bad = true;
    }
    if found_tests == 0 {
        eprintln!("No error code was found in compilation errors!");
        *bad = true;
    }
    if errors.is_empty() {
        println!("Found {} error codes", error_codes.len());

        for (err_code, error_status) in &error_codes {
            if !error_status.has_test && !EXEMPTED_FROM_TEST.contains(&err_code.as_str()) {
                errors.push(format!("Error code {} needs to have at least one UI test!", err_code));
            } else if error_status.has_test && EXEMPTED_FROM_TEST.contains(&err_code.as_str()) {
                errors.push(format!(
                    "Error code {} has a UI test, it shouldn't be listed into EXEMPTED_FROM_TEST!",
                    err_code
                ));
            }
            if !error_status.is_used && !error_status.has_explanation {
                errors.push(format!(
                    "Error code {} isn't used and doesn't have an error explanation, it should be \
                     commented in error_codes.rs file",
                    err_code
                ));
            }
        }
    }
    if errors.is_empty() {
        // Checking if local constants need to be cleaned.
        for err_code in EXEMPTED_FROM_TEST {
            match error_codes.get(err_code.to_owned()) {
                Some(status) => {
                    if status.has_test {
                        errors.push(format!(
                            "{} error code has a test and therefore should be \
                            removed from the `EXEMPTED_FROM_TEST` constant",
                            err_code
                        ));
                    }
                }
                None => errors.push(format!(
                    "{} error code isn't used anymore and therefore should be removed \
                        from `EXEMPTED_FROM_TEST` constant",
                    err_code
                )),
            }
        }
    }
    errors.sort();
    for err in &errors {
        eprintln!("{}", err);
    }
    println!("Found {} error codes with no tests", errors.len());
    if !errors.is_empty() {
        *bad = true;
    }
    println!("Done!");
}
<commit_msg>Allow the E0482 to be tested<commit_after>//! Checks that all error codes have at least one test to prevent having error
//! codes that are silently not thrown by the compiler anymore.

use std::collections::HashMap;
use std::ffi::OsStr;
use std::fs::read_to_string;
use std::path::Path;

use regex::Regex;

// A few of those error codes can't be tested but all the others can and *should* be tested!
const EXEMPTED_FROM_TEST: &[&str] = &[
    "E0227", "E0279", "E0280", "E0313", "E0377", "E0461", "E0462", "E0464", "E0465", "E0476",
    "E0514", "E0519", "E0523", "E0554", "E0640", "E0717", "E0729",
];

// Some error codes don't have any tests apparently...
const IGNORE_EXPLANATION_CHECK: &[&str] = &["E0570", "E0601", "E0602", "E0729"];

// If the file path contains any of these, we don't want to try to extract error codes from it.
//
// We need to declare each path in the windows version (with backslash).
const PATHS_TO_IGNORE_FOR_EXTRACTION: &[&str] =
    &["src/test/", "src\\test\\", "src/doc/", "src\\doc\\", "src/tools/", "src\\tools\\"];

#[derive(Default, Debug)]
struct ErrorCodeStatus {
    has_test: bool,
    has_explanation: bool,
    is_used: bool,
}

fn check_error_code_explanation(
    f: &str,
    error_codes: &mut HashMap<String, ErrorCodeStatus>,
    err_code: String,
) -> bool {
    let mut invalid_compile_fail_format = false;
    let mut found_error_code = false;

    for line in f.lines() {
        let s = line.trim();
        if s.starts_with("```") {
            if s.contains("compile_fail") && s.contains('E') {
                if !found_error_code {
                    error_codes.get_mut(&err_code).map(|x| x.has_test = true);
                    found_error_code = true;
                }
            } else if s.contains("compile-fail") {
                invalid_compile_fail_format = true;
            }
        } else if s.starts_with("#### Note: this error code is no longer emitted by the compiler") {
            if !found_error_code {
                error_codes.get_mut(&err_code).map(|x| x.has_test = true);
                found_error_code = true;
            }
        }
    }
    invalid_compile_fail_format
}

fn check_if_error_code_is_test_in_explanation(f: &str, err_code: &str) -> bool {
    let mut ignore_found = false;

    for line in f.lines() {
        let s = line.trim();
        if s.starts_with("#### Note: this error code is no longer emitted by the compiler") {
            return true;
        }
        if s.starts_with("```") {
            if s.contains("compile_fail") && s.contains(err_code) {
                return true;
            } else if s.contains("ignore") {
                // It's very likely that we can't actually make it fail compilation...
                ignore_found = true;
            }
        }
    }
    ignore_found
}

macro_rules! some_or_continue {
    ($e:expr) => {
        match $e {
            Some(e) => e,
            None => continue,
        }
    };
}

fn extract_error_codes(
    f: &str,
    error_codes: &mut HashMap<String, ErrorCodeStatus>,
    path: &Path,
    errors: &mut Vec<String>,
) {
    let mut reached_no_explanation = false;

    for line in f.lines() {
        let s = line.trim();
        if !reached_no_explanation && s.starts_with('E') && s.contains("include_str!(\"") {
            let err_code = s
                .split_once(':')
                .expect(
                    format!(
                        "Expected a line with the format `E0xxx: include_str!(\"..\")`, but got {} \
                         without a `:` delimiter",
                        s,
                    )
                    .as_str(),
                )
                .0
                .to_owned();
            error_codes.entry(err_code.clone()).or_default().has_explanation = true;

            // Now we extract the tests from the markdown file!
            let md_file_name = match s.split_once("include_str!(\"") {
                None => continue,
                Some((_, md)) => match md.split_once("\")") {
                    None => continue,
                    Some((file_name, _)) => file_name,
                },
            };
            let path = some_or_continue!(path.parent())
                .join(md_file_name)
                .canonicalize()
                .expect("failed to canonicalize error explanation file path");
            match read_to_string(&path) {
                Ok(content) => {
                    let has_test = check_if_error_code_is_test_in_explanation(&content, &err_code);
                    if !has_test && !IGNORE_EXPLANATION_CHECK.contains(&err_code.as_str()) {
                        errors.push(format!(
                            "`{}` doesn't use its own error code in compile_fail example",
                            path.display(),
                        ));
                    } else if has_test && IGNORE_EXPLANATION_CHECK.contains(&err_code.as_str()) {
                        errors.push(format!(
                            "`{}` has a compile_fail example with its own error code, it shouldn't \
                             be listed in IGNORE_EXPLANATION_CHECK!",
                            path.display(),
                        ));
                    }
                    if check_error_code_explanation(&content, error_codes, err_code) {
                        errors.push(format!(
                            "`{}` uses invalid tag `compile-fail` instead of `compile_fail`",
                            path.display(),
                        ));
                    }
                }
                Err(e) => {
                    eprintln!("Couldn't read `{}`: {}", path.display(), e);
                }
            }
        } else if reached_no_explanation && s.starts_with('E') {
            let err_code = match s.split_once(',') {
                None => s,
                Some((err_code, _)) => err_code,
            }
            .to_string();
            if !error_codes.contains_key(&err_code) {
                // this check should *never* fail!
                error_codes.insert(err_code, ErrorCodeStatus::default());
            }
        } else if s == ";" {
            reached_no_explanation = true;
        }
    }
}

fn extract_error_codes_from_tests(f: &str, error_codes: &mut HashMap<String, ErrorCodeStatus>) {
    for line in f.lines() {
        let s = line.trim();
        if s.starts_with("error[E") || s.starts_with("warning[E") {
            let err_code = match s.split_once(']') {
                None => continue,
                Some((err_code, _)) => match err_code.split_once('[') {
                    None => continue,
                    Some((_, err_code)) => err_code,
                },
            };
            error_codes.entry(err_code.to_owned()).or_default().has_test = true;
        }
    }
}

fn extract_error_codes_from_source(
    f: &str,
    error_codes: &mut HashMap<String, ErrorCodeStatus>,
    regex: &Regex,
) {
    for line in f.lines() {
        if line.trim_start().starts_with("//") {
            continue;
        }
        for cap in regex.captures_iter(line) {
            if let Some(error_code) = cap.get(1) {
                error_codes.entry(error_code.as_str().to_owned()).or_default().is_used = true;
            }
        }
    }
}

pub fn check(paths: &[&Path], bad: &mut bool) {
    let mut errors = Vec::new();
    let mut found_explanations = 0;
    let mut found_tests = 0;
    let mut error_codes: HashMap<String, ErrorCodeStatus> = HashMap::new();
    // We want error codes which match the following cases:
    //
    // * foo(a, E0111, a)
    // * foo(a, E0111)
    // * foo(E0111, a)
    // * #[error = "E0111"]
    let regex = Regex::new(r#"[(,"\s](E\d{4})[,)"]"#).unwrap();

    println!("Checking which error codes lack tests...");

    for path in paths {
        super::walk(path, &mut |path| super::filter_dirs(path), &mut |entry, contents| {
            let file_name = entry.file_name();
            if file_name == "error_codes.rs" {
                extract_error_codes(contents, &mut error_codes, entry.path(), &mut errors);
                found_explanations += 1;
            } else if entry.path().extension() == Some(OsStr::new("stderr")) {
                extract_error_codes_from_tests(contents, &mut error_codes);
                found_tests += 1;
            } else if entry.path().extension() == Some(OsStr::new("rs")) {
                let path = entry.path().to_string_lossy();
                if PATHS_TO_IGNORE_FOR_EXTRACTION.iter().all(|c| !path.contains(c)) {
                    extract_error_codes_from_source(contents, &mut error_codes, &regex);
                }
            }
        });
    }
    if found_explanations == 0 {
        eprintln!("No error code explanation was tested!");
        *bad = true;
    }
    if found_tests == 0 {
        eprintln!("No error code was found in compilation errors!");
        *bad = true;
    }
    if errors.is_empty() {
        println!("Found {} error codes", error_codes.len());

        for (err_code, error_status) in &error_codes {
            if !error_status.has_test && !EXEMPTED_FROM_TEST.contains(&err_code.as_str()) {
                errors.push(format!("Error code {} needs to have at least one UI test!", err_code));
            } else if error_status.has_test && EXEMPTED_FROM_TEST.contains(&err_code.as_str()) {
                errors.push(format!(
                    "Error code {} has a UI test, it shouldn't be listed into EXEMPTED_FROM_TEST!",
                    err_code
                ));
            }
            if !error_status.is_used && !error_status.has_explanation {
                errors.push(format!(
                    "Error code {} isn't used and doesn't have an error explanation, it should be \
                     commented in error_codes.rs file",
                    err_code
                ));
            }
        }
    }
    if errors.is_empty() {
        // Checking if local constants need to be cleaned.
        for err_code in EXEMPTED_FROM_TEST {
            match error_codes.get(err_code.to_owned()) {
                Some(status) => {
                    if status.has_test {
                        errors.push(format!(
                            "{} error code has a test and therefore should be \
                            removed from the `EXEMPTED_FROM_TEST` constant",
                            err_code
                        ));
                    }
                }
                None => errors.push(format!(
                    "{} error code isn't used anymore and therefore should be removed \
                        from `EXEMPTED_FROM_TEST` constant",
                    err_code
                )),
            }
        }
    }
    errors.sort();
    for err in &errors {
        eprintln!("{}", err);
    }
    println!("Found {} error codes with no tests", errors.len());
    if !errors.is_empty() {
        *bad = true;
    }
    println!("Done!");
}
<|endoftext|>


<commit_before>#![allow(unstable)]
#![feature(box_syntax)]

extern crate coatcheck;
extern crate test;

use test::Bencher;
use std::collections::HashMap;

use coatcheck::*;

#[bench]
fn bench_hash_map(b: &mut Bencher) {
    b.iter(|| {
        let mut map = HashMap::with_capacity(0);
        let mut res = Vec::with_capacity(20);
        for i in 0..20 {
            map.insert(i, "something");
            res.push(i);
        }
        for i in res.into_iter() {
            map.remove(&i);
        }
    });
}

#[bench]
fn bench_coat_check(b: &mut Bencher) {
    b.iter(|| {
        let mut cc = CoatCheck::with_capacity(0);
        let mut res = Vec::with_capacity(20);
        for _ in 0..20 {
            res.push(cc.check("something"));
        }
        for t in res.into_iter() {
            let _ = test::black_box(cc.claim(t));
        }
    });
}

#[bench]
fn bench_box(b: &mut Bencher) {
    b.iter(|| {
        let mut res = Vec::with_capacity(20);
        for _ in 0..20 {
            res.push(Box::new("something"));
        }
        for t in res.into_iter() {
            let box item = t;
            test::black_box(item);
        }
    });
}

#[bench]
fn bench_coat_check_init(b: &mut Bencher) {
    b.iter(|| {
        for _ in 0..1000 {
            test::black_box(CoatCheck::<u64>::new());
        }
    });
}

#[bench]
fn bench_coat_check_access(b: &mut Bencher) {
    let mut cc = CoatCheck::new();
    let mut tickets = Vec::with_capacity(100);
    for _ in 0..100 {
        tickets.push(cc.check("something"));
    }
    let ref t = tickets[20];
    b.iter(|&: | {
        test::black_box(&cc[*t]);
    });
}

#[bench]
fn bench_hash_map_access(b: &mut Bencher) {
    let mut map = HashMap::with_capacity(100);
    for i in 0i32..100 {
        map.insert(i, "something");
    }
    b.iter(|&:| {
        test::black_box(&map[20i32]);
    });
}

<commit_msg>Better benchmarks.<commit_after>#![allow(unstable)]
#![feature(box_syntax)]

extern crate coatcheck;
extern crate test;

use test::Bencher;
use std::collections::HashMap;

use coatcheck::*;

#[bench]
fn bench_hash_map(b: &mut Bencher) {
    let mut map = HashMap::with_capacity(6);
    b.iter(|| {
        map.insert(0us, "something");
        map.insert(1, "something");
        let _ = map.remove(&0);
        map.insert(2, "something");
        map.insert(3, "something");
        map.insert(4, "something");
        let _ = map.remove(&3);
        map.insert(5, "something");
        let _ = map.remove(&2);
        let _ = map.remove(&1);
        let _ = map.remove(&4);
        let _ = map.remove(&5);
    });
}

#[bench]
fn bench_coat_check(b: &mut Bencher) {
    let mut cc = CoatCheck::with_capacity(6);
    b.iter(|| {
        let t1 = cc.check("something");
        let t2 = cc.check("something");
        let _ = cc.claim(t1);
        let t3 = cc.check("something");
        let t4 = cc.check("something");
        let t5 = cc.check("something");
        let _ = cc.claim(t4);
        let t6 = cc.check("something");
        let _ = cc.claim(t3);
        let _ = cc.claim(t2);
        let _ = cc.claim(t5);
        let _ = cc.claim(t6);
    });
}

#[bench]
fn bench_box(b: &mut Bencher) {
    b.iter(|| {
        let b1 = Box::new("something");
        let b2 = Box::new("something");
        drop(b1);
        let b3 = Box::new("something");
        let b4 = Box::new("something");
        let b5 = Box::new("something");
        drop(b4);
        let b6 = Box::new("something");
        drop(b3);
        drop(b2);
        drop(b5);
        drop(b6);
    });
}

#[bench]
fn bench_coat_check_init(b: &mut Bencher) {
    b.iter(|| {
        for _ in 0..1000 {
            test::black_box(CoatCheck::<u64>::new());
        }
    });
}

#[bench]
fn bench_coat_check_access(b: &mut Bencher) {
    let mut cc = CoatCheck::new();
    let mut tickets = Vec::with_capacity(100);
    for _ in 0..100 {
        tickets.push(cc.check("something"));
    }
    let ref t = tickets[20];
    b.iter(|&: | {
        test::black_box(&cc[*t]);
    });
}

#[bench]
fn bench_hash_map_access(b: &mut Bencher) {
    let mut map = HashMap::with_capacity(100);
    for i in 0i32..100 {
        map.insert(i, "something");
    }
    b.iter(|&:| {
        test::black_box(&map[20i32]);
    });
}

<|endoftext|>


<commit_before>#![feature(test)]
extern crate test;
extern crate bytes;

mod tests {
    use test::Bencher;
    use bytes::{BytesMut, BufMut};
    use std::fmt::{self, Write};
    use std::mem::transmute;

    #[bench]
    fn bench_push_to_buffer(b : &mut Bencher) {
        let mut buffer = BytesMut::new();

        b.iter(|| push(&mut buffer, &[72, 84, 84, 80, 47, 49, 46, 49, 32]));
    }

    #[bench]
    fn bench_extend_from_slice(b : &mut Bencher) {
        let mut buffer = BytesMut::new();

        b.iter(|| buffer.extend_from_slice(&[72, 84, 84, 80, 47, 49, 46, 49, 32]))
    }

    #[bench]
    fn bench_fastwrite(b : &mut Bencher) {
        let mut buffer = BytesMut::new();

        b.iter(|| write!(FastWrite(&mut buffer), "HTTP/1.1 ").unwrap() )
    }

    #[bench]
    fn bench_all_combined(b : &mut Bencher) {
        let mut buffer = BytesMut::new();

        b.iter(|| {
            push(&mut buffer, &[72, 84, 84]);
            buffer.extend_from_slice(&[80, 47, 49]);
            write!(FastWrite(&mut buffer), ".1 ").unwrap();
        });
    }

    #[bench]
    fn bench_convert_and_push(b : &mut Bencher) {
        let mut buffer = BytesMut::new();

        b.iter(|| {
            let length = "HTTP/1.1 200 OK".len() as u32;
            let data: [u8; 4] = unsafe { transmute(length.to_be()) };

            push(&mut buffer, &data);
        });
    }

    #[bench]
    fn bench_string_to_push(b : &mut Bencher) {
        let mut buffer = BytesMut::new();
        let data = String::from("200 OK");

        b.iter(|| {
            push(&mut buffer, data.as_bytes());
        });
    }

    fn push(buf: &mut BytesMut, data: &[u8]) {
        buf.reserve(data.len());
        unsafe {
            buf.bytes_mut()[..data.len()].copy_from_slice(data);
            buf.advance_mut(data.len());
        }
    }

    struct FastWrite<'a>(&'a mut BytesMut);

    impl<'a> fmt::Write for FastWrite<'a> {
        fn write_str(&mut self, s: &str) -> fmt::Result {
            push(&mut *self.0, s.as_bytes());
            Ok(())
        }

        fn write_fmt(&mut self, args: fmt::Arguments) -> fmt::Result {
            fmt::write(self, args)
        }
    }
}
<commit_msg>Convert to real ASCII bytes<commit_after>#![feature(test)]
extern crate test;
extern crate bytes;

mod tests {
    use test::Bencher;
    use bytes::{BytesMut, BufMut};
    use std::fmt::{self, Write};
    use std::mem::transmute;

    #[bench]
    fn bench_push_to_buffer(b : &mut Bencher) {
        let mut buffer = BytesMut::new();

        b.iter(|| push(&mut buffer, &[72, 84, 84, 80, 47, 49, 46, 49, 32]));
    }

    #[bench]
    fn bench_extend_from_slice(b : &mut Bencher) {
        let mut buffer = BytesMut::new();

        b.iter(|| buffer.extend_from_slice(&[72, 84, 84, 80, 47, 49, 46, 49, 32]))
    }

    #[bench]
    fn bench_fastwrite(b : &mut Bencher) {
        let mut buffer = BytesMut::new();

        b.iter(|| write!(FastWrite(&mut buffer), "HTTP/1.1 ").unwrap() )
    }

    #[bench]
    fn bench_all_combined(b : &mut Bencher) {
        let mut buffer = BytesMut::new();

        b.iter(|| {
            push(&mut buffer, &[72, 84, 84]);
            buffer.extend_from_slice(&[80, 47, 49]);
            write!(FastWrite(&mut buffer), ".1 ").unwrap();
        });
    }

    #[bench]
    fn bench_convert_and_push(b : &mut Bencher) {
        let mut buffer = BytesMut::new();

        b.iter(|| {
            let mut length = "HTTP/1.1 200 OK".len() as u16;

            let mut data : [u8; 4] = [0; 4];

            for i in 1..5 {
                data[4 - i] = (48 + (length % (10 * i) as u16)) as u8;
                length = &length / (10 * i) as u16;
            }

            push(&mut buffer, &data);
        });
    }

    #[bench]
    fn bench_string_to_push(b : &mut Bencher) {
        let mut buffer = BytesMut::new();
        let data = String::from("200 OK");

        b.iter(|| {
            push(&mut buffer, data.as_bytes());
        });
    }

    fn push(buf: &mut BytesMut, data: &[u8]) {
        buf.reserve(data.len());
        unsafe {
            buf.bytes_mut()[..data.len()].copy_from_slice(data);
            buf.advance_mut(data.len());
        }
    }

    struct FastWrite<'a>(&'a mut BytesMut);

    impl<'a> fmt::Write for FastWrite<'a> {
        fn write_str(&mut self, s: &str) -> fmt::Result {
            push(&mut *self.0, s.as_bytes());
            Ok(())
        }

        fn write_fmt(&mut self, args: fmt::Arguments) -> fmt::Result {
            fmt::write(self, args)
        }
    }
}
<|endoftext|>


<commit_before><commit_msg>Add str_to_bitmer function<commit_after><|endoftext|>


<commit_before><commit_msg>Init perl runtime in embedding test.<commit_after><|endoftext|>


<commit_before>#![recursion_limit = "1024"]
#![forbid(unsafe_code)]
#[macro_use]
extern crate quote;
extern crate proc_macro;

use proc_macro::TokenStream;
use proc_macro2::TokenStream as TokenStream2;
use std::{env, path::Path};
use syn::{Data, DeriveInput, Fields, Lit, Meta, MetaNameValue};

fn embedded(ident: &syn::Ident, folder_path: String, prefix: Option<&str>) -> TokenStream2 {
  extern crate rust_embed_utils;

  let mut match_values = Vec::<TokenStream2>::new();
  let mut list_values = Vec::<String>::new();

  for rust_embed_utils::FileEntry { rel_path, full_canonical_path } in rust_embed_utils::get_files(folder_path) {
    match_values.push(embed_file(&rel_path, &full_canonical_path));

    list_values.push(if let Some(prefix) = prefix {
      format!("{}{}", prefix, rel_path)
    } else {
      rel_path
    });
  }

  let array_len = list_values.len();

  // If debug-embed is on, unconditionally include the code below. Otherwise,
  // make it conditional on cfg(not(debug_assertions)).
  let not_debug_attr = if cfg!(feature = "debug-embed") {
    quote! {}
  } else {
    quote! { #[cfg(not(debug_assertions))]}
  };

  let handle_prefix = if let Some(prefix) = prefix {
    quote! {
      let file_path = file_path.strip_prefix(#prefix)?;
    }
  } else {
    TokenStream2::new()
  };

  quote! {
      #not_debug_attr
      impl #ident {
          pub fn get(file_path: &str) -> Option<rust_embed::EmbeddedFile> {
            #handle_prefix
            match file_path.replace("\\", "/").as_str() {
                #(#match_values)*
                _ => None,
            }
          }

          fn names() -> std::slice::Iter<'static, &'static str> {
              const items: [&str; #array_len] = [#(#list_values),*];
              items.iter()
          }

          pub fn iter() -> impl Iterator<Item = std::borrow::Cow<'static, str>> {
              Self::names().map(|x| std::borrow::Cow::from(*x))
          }
      }

      #not_debug_attr
      impl rust_embed::RustEmbed for #ident {
        fn get(file_path: &str) -> Option<rust_embed::EmbeddedFile> {
          #ident::get(file_path)
        }
        fn iter() -> rust_embed::Filenames {
          rust_embed::Filenames::Embedded(#ident::names())
        }
      }
  }
}

fn dynamic(ident: &syn::Ident, folder_path: String, prefix: Option<&str>) -> TokenStream2 {
  let (handle_prefix, map_iter) = if let Some(prefix) = prefix {
    (
      quote! { let file_path = file_path.strip_prefix(#prefix)?; },
      quote! { std::borrow::Cow::Owned(format!("{}{}", #prefix, e.rel_path)) },
    )
  } else {
    (TokenStream2::new(), quote! { std::borrow::Cow::from(e.rel_path) })
  };

  quote! {
      #[cfg(debug_assertions)]
      impl #ident {
          pub fn get(file_path: &str) -> Option<rust_embed::EmbeddedFile> {
              #handle_prefix

              let file_path = std::path::Path::new(#folder_path).join(file_path.replace("\\", "/"));
              rust_embed::utils::read_file_from_fs(&file_path).ok()
          }

          pub fn iter() -> impl Iterator<Item = std::borrow::Cow<'static, str>> {
              use std::path::Path;
              rust_embed::utils::get_files(String::from(#folder_path))
                  .map(|e| #map_iter)
          }
      }

      #[cfg(debug_assertions)]
      impl rust_embed::RustEmbed for #ident {
        fn get(file_path: &str) -> Option<rust_embed::EmbeddedFile> {
          #ident::get(file_path)
        }
        fn iter() -> rust_embed::Filenames {
          // the return type of iter() is unnamable, so we have to box it
          rust_embed::Filenames::Dynamic(Box::new(#ident::iter()))
        }
      }
  }
}

fn generate_assets(ident: &syn::Ident, folder_path: String, prefix: Option<String>) -> TokenStream2 {
  let embedded_impl = embedded(ident, folder_path.clone(), prefix.as_deref());
  if cfg!(feature = "debug-embed") {
    return embedded_impl;
  }

  let dynamic_impl = dynamic(ident, folder_path, prefix.as_deref());

  quote! {
      #embedded_impl
      #dynamic_impl
  }
}

fn embed_file(rel_path: &str, full_canonical_path: &str) -> TokenStream2 {
  let file = rust_embed_utils::read_file_from_fs(Path::new(full_canonical_path)).expect("File should be readable");
  let hash = file.metadata.sha256_hash();
  let last_modified = match file.metadata.last_modified() {
    Some(last_modified) => quote! { Some(#last_modified) },
    None => quote! { None },
  };

  let embedding_code = if cfg!(feature = "compression") {
    quote! {
        let bytes = &include_bytes!(#full_canonical_path)[..];
    }
  } else {
    quote! {
        rust_embed::flate!(static FILE: [u8] from #full_canonical_path);
        let bytes = &FILE[..];
    }
  };

  quote! {
      #rel_path => {
          #embedding_code

          Some(rust_embed::EmbeddedFile {
              data: std::borrow::Cow::from(bytes),
              metadata: rust_embed::Metadata::__rust_embed_new([#(#hash),*], #last_modified)
          })
      }
  }
}

/// Find a `name = "value"` attribute from the derive input
fn find_attribute_value(ast: &syn::DeriveInput, attr_name: &str) -> Option<String> {
  ast
    .attrs
    .iter()
    .find(|value| value.path.is_ident(attr_name))
    .and_then(|attr| attr.parse_meta().ok())
    .and_then(|meta| match meta {
      Meta::NameValue(MetaNameValue { lit: Lit::Str(val), .. }) => Some(val.value()),
      _ => None,
    })
}

fn impl_rust_embed(ast: &syn::DeriveInput) -> TokenStream2 {
  match ast.data {
    Data::Struct(ref data) => match data.fields {
      Fields::Unit => {}
      _ => panic!("RustEmbed can only be derived for unit structs"),
    },
    _ => panic!("RustEmbed can only be derived for unit structs"),
  };

  let folder_path = find_attribute_value(ast, "folder").expect("#[derive(RustEmbed)] should contain one attribute like this #[folder = \"examples/public/\"]");
  let prefix = find_attribute_value(ast, "prefix");

  #[cfg(feature = "interpolate-folder-path")]
  let folder_path = shellexpand::full(&folder_path).unwrap().to_string();

  // Base relative paths on the Cargo.toml location
  let folder_path = if Path::new(&folder_path).is_relative() {
    Path::new(&env::var("CARGO_MANIFEST_DIR").unwrap())
      .join(folder_path)
      .to_str()
      .unwrap()
      .to_owned()
  } else {
    folder_path
  };

  if !Path::new(&folder_path).exists() {
    let mut message = format!(
      "#[derive(RustEmbed)] folder '{}' does not exist. cwd: '{}'",
      folder_path,
      std::env::current_dir().unwrap().to_str().unwrap()
    );

    // Add a message about the interpolate-folder-path feature if the path may
    // include a variable
    if folder_path.contains('$') && cfg!(not(feature = "interpolate-folder-path")) {
      message += "\nA variable has been detected. RustEmbed can expand variables \
                  when the `interpolate-folder-path` feature is enabled.";
    }

    panic!("{}", message);
  };

  generate_assets(&ast.ident, folder_path, prefix)
}

#[proc_macro_derive(RustEmbed, attributes(folder, prefix))]
pub fn derive_input_object(input: TokenStream) -> TokenStream {
  let ast: DeriveInput = syn::parse(input).unwrap();
  let gen = impl_rust_embed(&ast);
  gen.into()
}
<commit_msg>Fix backwards compression codegen<commit_after>#![recursion_limit = "1024"]
#![forbid(unsafe_code)]
#[macro_use]
extern crate quote;
extern crate proc_macro;

use proc_macro::TokenStream;
use proc_macro2::TokenStream as TokenStream2;
use std::{env, path::Path};
use syn::{Data, DeriveInput, Fields, Lit, Meta, MetaNameValue};

fn embedded(ident: &syn::Ident, folder_path: String, prefix: Option<&str>) -> TokenStream2 {
  extern crate rust_embed_utils;

  let mut match_values = Vec::<TokenStream2>::new();
  let mut list_values = Vec::<String>::new();

  for rust_embed_utils::FileEntry { rel_path, full_canonical_path } in rust_embed_utils::get_files(folder_path) {
    match_values.push(embed_file(&rel_path, &full_canonical_path));

    list_values.push(if let Some(prefix) = prefix {
      format!("{}{}", prefix, rel_path)
    } else {
      rel_path
    });
  }

  let array_len = list_values.len();

  // If debug-embed is on, unconditionally include the code below. Otherwise,
  // make it conditional on cfg(not(debug_assertions)).
  let not_debug_attr = if cfg!(feature = "debug-embed") {
    quote! {}
  } else {
    quote! { #[cfg(not(debug_assertions))]}
  };

  let handle_prefix = if let Some(prefix) = prefix {
    quote! {
      let file_path = file_path.strip_prefix(#prefix)?;
    }
  } else {
    TokenStream2::new()
  };

  quote! {
      #not_debug_attr
      impl #ident {
          pub fn get(file_path: &str) -> Option<rust_embed::EmbeddedFile> {
            #handle_prefix
            match file_path.replace("\\", "/").as_str() {
                #(#match_values)*
                _ => None,
            }
          }

          fn names() -> std::slice::Iter<'static, &'static str> {
              const items: [&str; #array_len] = [#(#list_values),*];
              items.iter()
          }

          pub fn iter() -> impl Iterator<Item = std::borrow::Cow<'static, str>> {
              Self::names().map(|x| std::borrow::Cow::from(*x))
          }
      }

      #not_debug_attr
      impl rust_embed::RustEmbed for #ident {
        fn get(file_path: &str) -> Option<rust_embed::EmbeddedFile> {
          #ident::get(file_path)
        }
        fn iter() -> rust_embed::Filenames {
          rust_embed::Filenames::Embedded(#ident::names())
        }
      }
  }
}

fn dynamic(ident: &syn::Ident, folder_path: String, prefix: Option<&str>) -> TokenStream2 {
  let (handle_prefix, map_iter) = if let Some(prefix) = prefix {
    (
      quote! { let file_path = file_path.strip_prefix(#prefix)?; },
      quote! { std::borrow::Cow::Owned(format!("{}{}", #prefix, e.rel_path)) },
    )
  } else {
    (TokenStream2::new(), quote! { std::borrow::Cow::from(e.rel_path) })
  };

  quote! {
      #[cfg(debug_assertions)]
      impl #ident {
          pub fn get(file_path: &str) -> Option<rust_embed::EmbeddedFile> {
              #handle_prefix

              let file_path = std::path::Path::new(#folder_path).join(file_path.replace("\\", "/"));
              rust_embed::utils::read_file_from_fs(&file_path).ok()
          }

          pub fn iter() -> impl Iterator<Item = std::borrow::Cow<'static, str>> {
              use std::path::Path;
              rust_embed::utils::get_files(String::from(#folder_path))
                  .map(|e| #map_iter)
          }
      }

      #[cfg(debug_assertions)]
      impl rust_embed::RustEmbed for #ident {
        fn get(file_path: &str) -> Option<rust_embed::EmbeddedFile> {
          #ident::get(file_path)
        }
        fn iter() -> rust_embed::Filenames {
          // the return type of iter() is unnamable, so we have to box it
          rust_embed::Filenames::Dynamic(Box::new(#ident::iter()))
        }
      }
  }
}

fn generate_assets(ident: &syn::Ident, folder_path: String, prefix: Option<String>) -> TokenStream2 {
  let embedded_impl = embedded(ident, folder_path.clone(), prefix.as_deref());
  if cfg!(feature = "debug-embed") {
    return embedded_impl;
  }

  let dynamic_impl = dynamic(ident, folder_path, prefix.as_deref());

  quote! {
      #embedded_impl
      #dynamic_impl
  }
}

fn embed_file(rel_path: &str, full_canonical_path: &str) -> TokenStream2 {
  let file = rust_embed_utils::read_file_from_fs(Path::new(full_canonical_path)).expect("File should be readable");
  let hash = file.metadata.sha256_hash();
  let last_modified = match file.metadata.last_modified() {
    Some(last_modified) => quote! { Some(#last_modified) },
    None => quote! { None },
  };

  let embedding_code = if cfg!(feature = "compression") {
    quote! {
      rust_embed::flate!(static FILE: [u8] from #full_canonical_path);
      let bytes = &FILE[..];
    }
  } else {
    quote! {
      let bytes = &include_bytes!(#full_canonical_path)[..];
    }
  };

  quote! {
      #rel_path => {
          #embedding_code

          Some(rust_embed::EmbeddedFile {
              data: std::borrow::Cow::from(bytes),
              metadata: rust_embed::Metadata::__rust_embed_new([#(#hash),*], #last_modified)
          })
      }
  }
}

/// Find a `name = "value"` attribute from the derive input
fn find_attribute_value(ast: &syn::DeriveInput, attr_name: &str) -> Option<String> {
  ast
    .attrs
    .iter()
    .find(|value| value.path.is_ident(attr_name))
    .and_then(|attr| attr.parse_meta().ok())
    .and_then(|meta| match meta {
      Meta::NameValue(MetaNameValue { lit: Lit::Str(val), .. }) => Some(val.value()),
      _ => None,
    })
}

fn impl_rust_embed(ast: &syn::DeriveInput) -> TokenStream2 {
  match ast.data {
    Data::Struct(ref data) => match data.fields {
      Fields::Unit => {}
      _ => panic!("RustEmbed can only be derived for unit structs"),
    },
    _ => panic!("RustEmbed can only be derived for unit structs"),
  };

  let folder_path = find_attribute_value(ast, "folder").expect("#[derive(RustEmbed)] should contain one attribute like this #[folder = \"examples/public/\"]");
  let prefix = find_attribute_value(ast, "prefix");

  #[cfg(feature = "interpolate-folder-path")]
  let folder_path = shellexpand::full(&folder_path).unwrap().to_string();

  // Base relative paths on the Cargo.toml location
  let folder_path = if Path::new(&folder_path).is_relative() {
    Path::new(&env::var("CARGO_MANIFEST_DIR").unwrap())
      .join(folder_path)
      .to_str()
      .unwrap()
      .to_owned()
  } else {
    folder_path
  };

  if !Path::new(&folder_path).exists() {
    let mut message = format!(
      "#[derive(RustEmbed)] folder '{}' does not exist. cwd: '{}'",
      folder_path,
      std::env::current_dir().unwrap().to_str().unwrap()
    );

    // Add a message about the interpolate-folder-path feature if the path may
    // include a variable
    if folder_path.contains('$') && cfg!(not(feature = "interpolate-folder-path")) {
      message += "\nA variable has been detected. RustEmbed can expand variables \
                  when the `interpolate-folder-path` feature is enabled.";
    }

    panic!("{}", message);
  };

  generate_assets(&ast.ident, folder_path, prefix)
}

#[proc_macro_derive(RustEmbed, attributes(folder, prefix))]
pub fn derive_input_object(input: TokenStream) -> TokenStream {
  let ast: DeriveInput = syn::parse(input).unwrap();
  let gen = impl_rust_embed(&ast);
  gen.into()
}
<|endoftext|>


<commit_before>#![feature(plugin)]
#![feature(test)]
#![plugin(clippy)]
#![feature(btree_range, collections_bound)]

#[macro_use]
extern crate log;
extern crate protobuf;
extern crate env_logger;
#[macro_use]
extern crate tikv;
extern crate rand;
extern crate rocksdb;
extern crate tempdir;
extern crate uuid;
extern crate test;

mod raftserver;
<commit_msg>benches: fix compile<commit_after>#![feature(plugin)]
#![feature(test)]
#![plugin(clippy)]
#![feature(btree_range, collections_bound)]

#[macro_use]
extern crate log;
extern crate protobuf;
extern crate env_logger;
#[macro_use]
extern crate tikv;
extern crate rand;
extern crate rocksdb;
extern crate tempdir;
extern crate uuid;
extern crate test;
extern crate kvproto;

mod raftserver;
<|endoftext|>