<commit_before>//! This modules implements a function to resolve a path `foo::bar::baz` to a
//! def, which is used within the name resolution.
//!
//! When name resolution is finished, the result of resolving a path is either
//! `Some(def)` or `None`. However, when we are in process of resolving imports
//! or macros, there's a third possibility:
//!
//!   I can't resolve this path right now, but I might be resolve this path
//!   later, when more macros are expanded.
//!
//! `ReachedFixedPoint` signals about this.

use base_db::Edition;
use hir_expand::name;
use hir_expand::name::Name;

use crate::{
    db::DefDatabase,
    item_scope::BUILTIN_SCOPE,
    nameres::{BuiltinShadowMode, DefMap},
    path::{ModPath, PathKind},
    per_ns::PerNs,
    visibility::{RawVisibility, Visibility},
    AdtId, CrateId, EnumVariantId, LocalModuleId, ModuleDefId,
};

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(super) enum ResolveMode {
    Import,
    Other,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(super) enum ReachedFixedPoint {
    Yes,
    No,
}

#[derive(Debug, Clone)]
pub(super) struct ResolvePathResult {
    pub(super) resolved_def: PerNs,
    pub(super) segment_index: Option<usize>,
    pub(super) reached_fixedpoint: ReachedFixedPoint,
    pub(super) krate: Option<CrateId>,
}

impl ResolvePathResult {
    fn empty(reached_fixedpoint: ReachedFixedPoint) -> ResolvePathResult {
        ResolvePathResult::with(PerNs::none(), reached_fixedpoint, None, None)
    }

    fn with(
        resolved_def: PerNs,
        reached_fixedpoint: ReachedFixedPoint,
        segment_index: Option<usize>,
        krate: Option<CrateId>,
    ) -> ResolvePathResult {
        ResolvePathResult { resolved_def, reached_fixedpoint, segment_index, krate }
    }
}

impl DefMap {
    pub(super) fn resolve_name_in_extern_prelude(&self, name: &Name) -> PerNs {
        if name == &name!(self) {
            cov_mark::hit!(extern_crate_self_as);
            return PerNs::types(self.module_id(self.root).into(), Visibility::Public);
        }
        self.extern_prelude
            .get(name)
            .map_or(PerNs::none(), |&it| PerNs::types(it, Visibility::Public))
    }

    pub(crate) fn resolve_visibility(
        &self,
        db: &dyn DefDatabase,
        original_module: LocalModuleId,
        visibility: &RawVisibility,
    ) -> Option<Visibility> {
        let mut vis = match visibility {
            RawVisibility::Module(path) => {
                let (result, remaining) =
                    self.resolve_path(db, original_module, &path, BuiltinShadowMode::Module);
                if remaining.is_some() {
                    return None;
                }
                let types = result.take_types()?;
                match types {
                    ModuleDefId::ModuleId(m) => Visibility::Module(m),
                    _ => {
                        // error: visibility needs to refer to module
                        return None;
                    }
                }
            }
            RawVisibility::Public => Visibility::Public,
        };

        // In block expressions, `self` normally refers to the containing non-block module, and
        // `super` to its parent (etc.). However, visibilities must only refer to a module in the
        // DefMap they're written in, so we restrict them when that happens.
        if let Visibility::Module(m) = vis {
            if self.block_id() != m.block {
                cov_mark::hit!(adjust_vis_in_block_def_map);
                vis = Visibility::Module(self.module_id(self.root()));
                log::debug!("visibility {:?} points outside DefMap, adjusting to {:?}", m, vis);
            }
        }

        Some(vis)
    }

    // Returns Yes if we are sure that additions to `ItemMap` wouldn't change
    // the result.
    pub(super) fn resolve_path_fp_with_macro(
        &self,
        db: &dyn DefDatabase,
        mode: ResolveMode,
        mut original_module: LocalModuleId,
        path: &ModPath,
        shadow: BuiltinShadowMode,
    ) -> ResolvePathResult {
        let mut result = ResolvePathResult::empty(ReachedFixedPoint::No);

        let mut arc;
        let mut current_map = self;
        loop {
            let new = current_map.resolve_path_fp_with_macro_single(
                db,
                mode,
                original_module,
                path,
                shadow,
            );

            // Merge `new` into `result`.
            result.resolved_def = result.resolved_def.or(new.resolved_def);
            if result.reached_fixedpoint == ReachedFixedPoint::No {
                result.reached_fixedpoint = new.reached_fixedpoint;
            }
            // FIXME: this doesn't seem right; what if the different namespace resolutions come from different crates?
            result.krate = result.krate.or(new.krate);
            result.segment_index = match (result.segment_index, new.segment_index) {
                (Some(idx), None) => Some(idx),
                (Some(old), Some(new)) => Some(old.max(new)),
                (None, new) => new,
            };

            match &current_map.block {
                Some(block) => {
                    original_module = block.parent.local_id;
                    arc = block.parent.def_map(db);
                    current_map = &*arc;
                }
                None => return result,
            }
        }
    }

    pub(super) fn resolve_path_fp_with_macro_single(
        &self,
        db: &dyn DefDatabase,
        mode: ResolveMode,
        original_module: LocalModuleId,
        path: &ModPath,
        shadow: BuiltinShadowMode,
    ) -> ResolvePathResult {
        let mut segments = path.segments().iter().enumerate();
        let mut curr_per_ns: PerNs = match path.kind {
            PathKind::DollarCrate(krate) => {
                if krate == self.krate {
                    cov_mark::hit!(macro_dollar_crate_self);
                    PerNs::types(self.crate_root(db).into(), Visibility::Public)
                } else {
                    let def_map = db.crate_def_map(krate);
                    let module = def_map.module_id(def_map.root);
                    cov_mark::hit!(macro_dollar_crate_other);
                    PerNs::types(module.into(), Visibility::Public)
                }
            }
            PathKind::Crate => PerNs::types(self.crate_root(db).into(), Visibility::Public),
            // plain import or absolute path in 2015: crate-relative with
            // fallback to extern prelude (with the simplification in
            // rust-lang/rust#57745)
            // FIXME there must be a nicer way to write this condition
            PathKind::Plain | PathKind::Abs
                if self.edition == Edition::Edition2015
                    && (path.kind == PathKind::Abs || mode == ResolveMode::Import) =>
            {
                let (_, segment) = match segments.next() {
                    Some((idx, segment)) => (idx, segment),
                    None => return ResolvePathResult::empty(ReachedFixedPoint::Yes),
                };
                log::debug!("resolving {:?} in crate root (+ extern prelude)", segment);
                self.resolve_name_in_crate_root_or_extern_prelude(&segment)
            }
            PathKind::Plain => {
                let (_, segment) = match segments.next() {
                    Some((idx, segment)) => (idx, segment),
                    None => return ResolvePathResult::empty(ReachedFixedPoint::Yes),
                };
                // The first segment may be a builtin type. If the path has more
                // than one segment, we first try resolving it as a module
                // anyway.
                // FIXME: If the next segment doesn't resolve in the module and
                // BuiltinShadowMode wasn't Module, then we need to try
                // resolving it as a builtin.
                let prefer_module =
                    if path.segments().len() == 1 { shadow } else { BuiltinShadowMode::Module };

                log::debug!("resolving {:?} in module", segment);
                self.resolve_name_in_module(db, original_module, &segment, prefer_module)
            }
            PathKind::Super(lvl) => {
                let mut module = original_module;
                for i in 0..lvl {
                    match self.modules[module].parent {
                        Some(it) => module = it,
                        None => match &self.block {
                            Some(block) => {
                                // Look up remaining path in parent `DefMap`
                                let new_path = ModPath::from_segments(
                                    PathKind::Super(lvl - i),
                                    path.segments().to_vec(),
                                );
                                log::debug!("`super` path: {} -> {} in parent map", path, new_path);
                                return block.parent.def_map(db).resolve_path_fp_with_macro(
                                    db,
                                    mode,
                                    block.parent.local_id,
                                    &new_path,
                                    shadow,
                                );
                            }
                            None => {
                                log::debug!("super path in root module");
                                return ResolvePathResult::empty(ReachedFixedPoint::Yes);
                            }
                        },
                    }
                }

                // Resolve `self` to the containing crate-rooted module if we're a block
                self.with_ancestor_maps(db, module, &mut |def_map, module| {
                    if def_map.block.is_some() {
                        None // keep ascending
                    } else {
                        Some(PerNs::types(def_map.module_id(module).into(), Visibility::Public))
                    }
                })
                .expect("block DefMap not rooted in crate DefMap")
            }
            PathKind::Abs => {
                // 2018-style absolute path -- only extern prelude
                let segment = match segments.next() {
                    Some((_, segment)) => segment,
                    None => return ResolvePathResult::empty(ReachedFixedPoint::Yes),
                };
                if let Some(def) = self.extern_prelude.get(&segment) {
                    log::debug!("absolute path {:?} resolved to crate {:?}", path, def);
                    PerNs::types(*def, Visibility::Public)
                } else {
                    return ResolvePathResult::empty(ReachedFixedPoint::No); // extern crate declarations can add to the extern prelude
                }
            }
        };

        for (i, segment) in segments {
            let (curr, vis) = match curr_per_ns.take_types_vis() {
                Some(r) => r,
                None => {
                    // we still have path segments left, but the path so far
                    // didn't resolve in the types namespace => no resolution
                    // (don't break here because `curr_per_ns` might contain
                    // something in the value namespace, and it would be wrong
                    // to return that)
                    return ResolvePathResult::empty(ReachedFixedPoint::No);
                }
            };
            // resolve segment in curr

            curr_per_ns = match curr {
                ModuleDefId::ModuleId(module) => {
                    if module.krate != self.krate {
                        let path = ModPath::from_segments(
                            PathKind::Super(0),
                            path.segments()[i..].iter().cloned(),
                        );
                        log::debug!("resolving {:?} in other crate", path);
                        let defp_map = module.def_map(db);
                        let (def, s) = defp_map.resolve_path(db, module.local_id, &path, shadow);
                        return ResolvePathResult::with(
                            def,
                            ReachedFixedPoint::Yes,
                            s.map(|s| s + i),
                            Some(module.krate),
                        );
                    }

                    let def_map;
                    let module_data = if module.block == self.block_id() {
                        &self[module.local_id]
                    } else {
                        def_map = module.def_map(db);
                        &def_map[module.local_id]
                    };

                    // Since it is a qualified path here, it should not contains legacy macros
                    module_data.scope.get(&segment)
                }
                ModuleDefId::AdtId(AdtId::EnumId(e)) => {
                    // enum variant
                    cov_mark::hit!(can_import_enum_variant);
                    let enum_data = db.enum_data(e);
                    match enum_data.variant(&segment) {
                        Some(local_id) => {
                            let variant = EnumVariantId { parent: e, local_id };
                            match &*enum_data.variants[local_id].variant_data {
                                crate::adt::VariantData::Record(_) => {
                                    PerNs::types(variant.into(), Visibility::Public)
                                }
                                crate::adt::VariantData::Tuple(_)
                                | crate::adt::VariantData::Unit => {
                                    PerNs::both(variant.into(), variant.into(), Visibility::Public)
                                }
                            }
                        }
                        None => {
                            return ResolvePathResult::with(
                                PerNs::types(e.into(), vis),
                                ReachedFixedPoint::Yes,
                                Some(i),
                                Some(self.krate),
                            );
                        }
                    }
                }
                s => {
                    // could be an inherent method call in UFCS form
                    // (`Struct::method`), or some other kind of associated item
                    log::debug!(
                        "path segment {:?} resolved to non-module {:?}, but is not last",
                        segment,
                        curr,
                    );

                    return ResolvePathResult::with(
                        PerNs::types(s, vis),
                        ReachedFixedPoint::Yes,
                        Some(i),
                        Some(self.krate),
                    );
                }
            };
        }

        ResolvePathResult::with(curr_per_ns, ReachedFixedPoint::Yes, None, Some(self.krate))
    }

    fn resolve_name_in_module(
        &self,
        db: &dyn DefDatabase,
        module: LocalModuleId,
        name: &Name,
        shadow: BuiltinShadowMode,
    ) -> PerNs {
        // Resolve in:
        //  - legacy scope of macro
        //  - current module / scope
        //  - extern prelude
        //  - std prelude
        let from_legacy_macro = self[module]
            .scope
            .get_legacy_macro(name)
            .map_or_else(PerNs::none, |m| PerNs::macros(m, Visibility::Public));
        let from_scope = self[module].scope.get(name);
        let from_builtin = BUILTIN_SCOPE.get(name).copied().unwrap_or_else(PerNs::none);
        let from_scope_or_builtin = match shadow {
            BuiltinShadowMode::Module => from_scope.or(from_builtin),
            BuiltinShadowMode::Other => {
                if let Some(ModuleDefId::ModuleId(_)) = from_scope.take_types() {
                    from_builtin.or(from_scope)
                } else {
                    from_scope.or(from_builtin)
                }
            }
        };
        // Give precedence to names in outer `DefMap`s over the extern prelude; only check prelude
        // from the crate DefMap.
        let from_extern_prelude = match self.block {
            Some(_) => PerNs::none(),
            None => self
                .extern_prelude
                .get(name)
                .map_or(PerNs::none(), |&it| PerNs::types(it, Visibility::Public)),
        };

        let from_prelude = self.resolve_in_prelude(db, name);

        from_legacy_macro.or(from_scope_or_builtin).or(from_extern_prelude).or(from_prelude)
    }

    fn resolve_name_in_crate_root_or_extern_prelude(&self, name: &Name) -> PerNs {
        let from_crate_root = self[self.root].scope.get(name);
        let from_extern_prelude = self.resolve_name_in_extern_prelude(name);

        from_crate_root.or(from_extern_prelude)
    }

    fn resolve_in_prelude(&self, db: &dyn DefDatabase, name: &Name) -> PerNs {
        if let Some(prelude) = self.prelude {
            let keep;
            let def_map = if prelude.krate == self.krate {
                self
            } else {
                // Extend lifetime
                keep = prelude.def_map(db);
                &keep
            };
            def_map[prelude.local_id].scope.get(name)
        } else {
            PerNs::none()
        }
    }
}
<commit_msg>Undo path resolution hack for extern prelude<commit_after>//! This modules implements a function to resolve a path `foo::bar::baz` to a
//! def, which is used within the name resolution.
//!
//! When name resolution is finished, the result of resolving a path is either
//! `Some(def)` or `None`. However, when we are in process of resolving imports
//! or macros, there's a third possibility:
//!
//!   I can't resolve this path right now, but I might be resolve this path
//!   later, when more macros are expanded.
//!
//! `ReachedFixedPoint` signals about this.

use base_db::Edition;
use hir_expand::name;
use hir_expand::name::Name;

use crate::{
    db::DefDatabase,
    item_scope::BUILTIN_SCOPE,
    nameres::{BuiltinShadowMode, DefMap},
    path::{ModPath, PathKind},
    per_ns::PerNs,
    visibility::{RawVisibility, Visibility},
    AdtId, CrateId, EnumVariantId, LocalModuleId, ModuleDefId,
};

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(super) enum ResolveMode {
    Import,
    Other,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(super) enum ReachedFixedPoint {
    Yes,
    No,
}

#[derive(Debug, Clone)]
pub(super) struct ResolvePathResult {
    pub(super) resolved_def: PerNs,
    pub(super) segment_index: Option<usize>,
    pub(super) reached_fixedpoint: ReachedFixedPoint,
    pub(super) krate: Option<CrateId>,
}

impl ResolvePathResult {
    fn empty(reached_fixedpoint: ReachedFixedPoint) -> ResolvePathResult {
        ResolvePathResult::with(PerNs::none(), reached_fixedpoint, None, None)
    }

    fn with(
        resolved_def: PerNs,
        reached_fixedpoint: ReachedFixedPoint,
        segment_index: Option<usize>,
        krate: Option<CrateId>,
    ) -> ResolvePathResult {
        ResolvePathResult { resolved_def, reached_fixedpoint, segment_index, krate }
    }
}

impl DefMap {
    pub(super) fn resolve_name_in_extern_prelude(&self, name: &Name) -> PerNs {
        if name == &name!(self) {
            cov_mark::hit!(extern_crate_self_as);
            return PerNs::types(self.module_id(self.root).into(), Visibility::Public);
        }
        self.extern_prelude
            .get(name)
            .map_or(PerNs::none(), |&it| PerNs::types(it, Visibility::Public))
    }

    pub(crate) fn resolve_visibility(
        &self,
        db: &dyn DefDatabase,
        original_module: LocalModuleId,
        visibility: &RawVisibility,
    ) -> Option<Visibility> {
        let mut vis = match visibility {
            RawVisibility::Module(path) => {
                let (result, remaining) =
                    self.resolve_path(db, original_module, &path, BuiltinShadowMode::Module);
                if remaining.is_some() {
                    return None;
                }
                let types = result.take_types()?;
                match types {
                    ModuleDefId::ModuleId(m) => Visibility::Module(m),
                    _ => {
                        // error: visibility needs to refer to module
                        return None;
                    }
                }
            }
            RawVisibility::Public => Visibility::Public,
        };

        // In block expressions, `self` normally refers to the containing non-block module, and
        // `super` to its parent (etc.). However, visibilities must only refer to a module in the
        // DefMap they're written in, so we restrict them when that happens.
        if let Visibility::Module(m) = vis {
            if self.block_id() != m.block {
                cov_mark::hit!(adjust_vis_in_block_def_map);
                vis = Visibility::Module(self.module_id(self.root()));
                log::debug!("visibility {:?} points outside DefMap, adjusting to {:?}", m, vis);
            }
        }

        Some(vis)
    }

    // Returns Yes if we are sure that additions to `ItemMap` wouldn't change
    // the result.
    pub(super) fn resolve_path_fp_with_macro(
        &self,
        db: &dyn DefDatabase,
        mode: ResolveMode,
        mut original_module: LocalModuleId,
        path: &ModPath,
        shadow: BuiltinShadowMode,
    ) -> ResolvePathResult {
        let mut result = ResolvePathResult::empty(ReachedFixedPoint::No);

        let mut arc;
        let mut current_map = self;
        loop {
            let new = current_map.resolve_path_fp_with_macro_single(
                db,
                mode,
                original_module,
                path,
                shadow,
            );

            // Merge `new` into `result`.
            result.resolved_def = result.resolved_def.or(new.resolved_def);
            if result.reached_fixedpoint == ReachedFixedPoint::No {
                result.reached_fixedpoint = new.reached_fixedpoint;
            }
            // FIXME: this doesn't seem right; what if the different namespace resolutions come from different crates?
            result.krate = result.krate.or(new.krate);
            result.segment_index = match (result.segment_index, new.segment_index) {
                (Some(idx), None) => Some(idx),
                (Some(old), Some(new)) => Some(old.max(new)),
                (None, new) => new,
            };

            match &current_map.block {
                Some(block) => {
                    original_module = block.parent.local_id;
                    arc = block.parent.def_map(db);
                    current_map = &*arc;
                }
                None => return result,
            }
        }
    }

    pub(super) fn resolve_path_fp_with_macro_single(
        &self,
        db: &dyn DefDatabase,
        mode: ResolveMode,
        original_module: LocalModuleId,
        path: &ModPath,
        shadow: BuiltinShadowMode,
    ) -> ResolvePathResult {
        let mut segments = path.segments().iter().enumerate();
        let mut curr_per_ns: PerNs = match path.kind {
            PathKind::DollarCrate(krate) => {
                if krate == self.krate {
                    cov_mark::hit!(macro_dollar_crate_self);
                    PerNs::types(self.crate_root(db).into(), Visibility::Public)
                } else {
                    let def_map = db.crate_def_map(krate);
                    let module = def_map.module_id(def_map.root);
                    cov_mark::hit!(macro_dollar_crate_other);
                    PerNs::types(module.into(), Visibility::Public)
                }
            }
            PathKind::Crate => PerNs::types(self.crate_root(db).into(), Visibility::Public),
            // plain import or absolute path in 2015: crate-relative with
            // fallback to extern prelude (with the simplification in
            // rust-lang/rust#57745)
            // FIXME there must be a nicer way to write this condition
            PathKind::Plain | PathKind::Abs
                if self.edition == Edition::Edition2015
                    && (path.kind == PathKind::Abs || mode == ResolveMode::Import) =>
            {
                let (_, segment) = match segments.next() {
                    Some((idx, segment)) => (idx, segment),
                    None => return ResolvePathResult::empty(ReachedFixedPoint::Yes),
                };
                log::debug!("resolving {:?} in crate root (+ extern prelude)", segment);
                self.resolve_name_in_crate_root_or_extern_prelude(&segment)
            }
            PathKind::Plain => {
                let (_, segment) = match segments.next() {
                    Some((idx, segment)) => (idx, segment),
                    None => return ResolvePathResult::empty(ReachedFixedPoint::Yes),
                };
                // The first segment may be a builtin type. If the path has more
                // than one segment, we first try resolving it as a module
                // anyway.
                // FIXME: If the next segment doesn't resolve in the module and
                // BuiltinShadowMode wasn't Module, then we need to try
                // resolving it as a builtin.
                let prefer_module =
                    if path.segments().len() == 1 { shadow } else { BuiltinShadowMode::Module };

                log::debug!("resolving {:?} in module", segment);
                self.resolve_name_in_module(db, original_module, &segment, prefer_module)
            }
            PathKind::Super(lvl) => {
                let mut module = original_module;
                for i in 0..lvl {
                    match self.modules[module].parent {
                        Some(it) => module = it,
                        None => match &self.block {
                            Some(block) => {
                                // Look up remaining path in parent `DefMap`
                                let new_path = ModPath::from_segments(
                                    PathKind::Super(lvl - i),
                                    path.segments().to_vec(),
                                );
                                log::debug!("`super` path: {} -> {} in parent map", path, new_path);
                                return block.parent.def_map(db).resolve_path_fp_with_macro(
                                    db,
                                    mode,
                                    block.parent.local_id,
                                    &new_path,
                                    shadow,
                                );
                            }
                            None => {
                                log::debug!("super path in root module");
                                return ResolvePathResult::empty(ReachedFixedPoint::Yes);
                            }
                        },
                    }
                }

                // Resolve `self` to the containing crate-rooted module if we're a block
                self.with_ancestor_maps(db, module, &mut |def_map, module| {
                    if def_map.block.is_some() {
                        None // keep ascending
                    } else {
                        Some(PerNs::types(def_map.module_id(module).into(), Visibility::Public))
                    }
                })
                .expect("block DefMap not rooted in crate DefMap")
            }
            PathKind::Abs => {
                // 2018-style absolute path -- only extern prelude
                let segment = match segments.next() {
                    Some((_, segment)) => segment,
                    None => return ResolvePathResult::empty(ReachedFixedPoint::Yes),
                };
                if let Some(def) = self.extern_prelude.get(&segment) {
                    log::debug!("absolute path {:?} resolved to crate {:?}", path, def);
                    PerNs::types(*def, Visibility::Public)
                } else {
                    return ResolvePathResult::empty(ReachedFixedPoint::No); // extern crate declarations can add to the extern prelude
                }
            }
        };

        for (i, segment) in segments {
            let (curr, vis) = match curr_per_ns.take_types_vis() {
                Some(r) => r,
                None => {
                    // we still have path segments left, but the path so far
                    // didn't resolve in the types namespace => no resolution
                    // (don't break here because `curr_per_ns` might contain
                    // something in the value namespace, and it would be wrong
                    // to return that)
                    return ResolvePathResult::empty(ReachedFixedPoint::No);
                }
            };
            // resolve segment in curr

            curr_per_ns = match curr {
                ModuleDefId::ModuleId(module) => {
                    if module.krate != self.krate {
                        let path = ModPath::from_segments(
                            PathKind::Super(0),
                            path.segments()[i..].iter().cloned(),
                        );
                        log::debug!("resolving {:?} in other crate", path);
                        let defp_map = module.def_map(db);
                        let (def, s) = defp_map.resolve_path(db, module.local_id, &path, shadow);
                        return ResolvePathResult::with(
                            def,
                            ReachedFixedPoint::Yes,
                            s.map(|s| s + i),
                            Some(module.krate),
                        );
                    }

                    let def_map;
                    let module_data = if module.block == self.block_id() {
                        &self[module.local_id]
                    } else {
                        def_map = module.def_map(db);
                        &def_map[module.local_id]
                    };

                    // Since it is a qualified path here, it should not contains legacy macros
                    module_data.scope.get(&segment)
                }
                ModuleDefId::AdtId(AdtId::EnumId(e)) => {
                    // enum variant
                    cov_mark::hit!(can_import_enum_variant);
                    let enum_data = db.enum_data(e);
                    match enum_data.variant(&segment) {
                        Some(local_id) => {
                            let variant = EnumVariantId { parent: e, local_id };
                            match &*enum_data.variants[local_id].variant_data {
                                crate::adt::VariantData::Record(_) => {
                                    PerNs::types(variant.into(), Visibility::Public)
                                }
                                crate::adt::VariantData::Tuple(_)
                                | crate::adt::VariantData::Unit => {
                                    PerNs::both(variant.into(), variant.into(), Visibility::Public)
                                }
                            }
                        }
                        None => {
                            return ResolvePathResult::with(
                                PerNs::types(e.into(), vis),
                                ReachedFixedPoint::Yes,
                                Some(i),
                                Some(self.krate),
                            );
                        }
                    }
                }
                s => {
                    // could be an inherent method call in UFCS form
                    // (`Struct::method`), or some other kind of associated item
                    log::debug!(
                        "path segment {:?} resolved to non-module {:?}, but is not last",
                        segment,
                        curr,
                    );

                    return ResolvePathResult::with(
                        PerNs::types(s, vis),
                        ReachedFixedPoint::Yes,
                        Some(i),
                        Some(self.krate),
                    );
                }
            };
        }

        ResolvePathResult::with(curr_per_ns, ReachedFixedPoint::Yes, None, Some(self.krate))
    }

    fn resolve_name_in_module(
        &self,
        db: &dyn DefDatabase,
        module: LocalModuleId,
        name: &Name,
        shadow: BuiltinShadowMode,
    ) -> PerNs {
        // Resolve in:
        //  - legacy scope of macro
        //  - current module / scope
        //  - extern prelude
        //  - std prelude
        let from_legacy_macro = self[module]
            .scope
            .get_legacy_macro(name)
            .map_or_else(PerNs::none, |m| PerNs::macros(m, Visibility::Public));
        let from_scope = self[module].scope.get(name);
        let from_builtin = BUILTIN_SCOPE.get(name).copied().unwrap_or_else(PerNs::none);
        let from_scope_or_builtin = match shadow {
            BuiltinShadowMode::Module => from_scope.or(from_builtin),
            BuiltinShadowMode::Other => {
                if let Some(ModuleDefId::ModuleId(_)) = from_scope.take_types() {
                    from_builtin.or(from_scope)
                } else {
                    from_scope.or(from_builtin)
                }
            }
        };
        let from_extern_prelude = self
            .extern_prelude
            .get(name)
            .map_or(PerNs::none(), |&it| PerNs::types(it, Visibility::Public));

        let from_prelude = self.resolve_in_prelude(db, name);

        from_legacy_macro.or(from_scope_or_builtin).or(from_extern_prelude).or(from_prelude)
    }

    fn resolve_name_in_crate_root_or_extern_prelude(&self, name: &Name) -> PerNs {
        let from_crate_root = self[self.root].scope.get(name);
        let from_extern_prelude = self.resolve_name_in_extern_prelude(name);

        from_crate_root.or(from_extern_prelude)
    }

    fn resolve_in_prelude(&self, db: &dyn DefDatabase, name: &Name) -> PerNs {
        if let Some(prelude) = self.prelude {
            let keep;
            let def_map = if prelude.krate == self.krate {
                self
            } else {
                // Extend lifetime
                keep = prelude.def_map(db);
                &keep
            };
            def_map[prelude.local_id].scope.get(name)
        } else {
            PerNs::none()
        }
    }
}
<|endoftext|>


<commit_before>use analysis::c_type::rustify_pointers;
use analysis::rust_type::Result;
use env::Env;
use library;
use library::*;
use nameutil::crate_name;
use traits::*;

// FIXME: This module needs redundant allocations audit
// TODO: ffi_type computations should be cached

pub fn ffi_type(env: &Env, tid: library::TypeId, c_type: &str) -> Result {
    let (ptr, inner) = rustify_pointers(c_type);
    let res = if ptr.is_empty() {
        if let Some(c_tid) = env.library.find_type(0, c_type) {
            // Fast track plain fundamental types avoiding some checks
            if env.library.type_(c_tid).maybe_ref_as::<Fundamental>().is_some() {
                if let &library::Type::FixedArray(_, size) = env.library.type_(tid) {
                    ffi_inner(env, c_tid, c_type.into())
                        .map_any(|s| format!("[{}; {}]", s, size))
                }
                else {
                    ffi_inner(env, c_tid, c_type.into())
                }
            }
            else { // c_type isn't fundamental
                ffi_inner(env, tid, inner)
            }
        }
        else { // c_type doesn't match any type in the library by name
            ffi_inner(env, tid, inner)
        }
    }
    else { // ptr not empty
        ffi_inner(env, tid, inner)
            .map_any(|s| format!("{} {}", ptr, s))
    };
    trace!("ffi_type({:?}, {}) -> {:?}", tid, c_type, res);
    res
}

fn ffi_inner(env: &Env, tid: library::TypeId, mut inner: String) -> Result {
    let volatile = inner.starts_with("volatile ");
    if volatile {
        inner = inner["volatile ".len()..].into();
    }

    let typ = env.library.type_(tid);
    let res = match *typ {
        Type::Fundamental(fund) => {
            use library::Fundamental::*;
            let inner = match fund {
                None => "c_void",
                Boolean => "gboolean",
                Int8 => "i8",
                UInt8 => "u8",
                Int16 => "i16",
                UInt16 => "u16",
                Int32 => "i32",
                UInt32 => "u32",
                Int64 => "i64",
                UInt64 => "u64",
                Char => "c_char",
                UChar => "c_uchar",
                Short => "c_short",
                UShort => "c_ushort",
                Int => "c_int",
                UInt => "c_uint",
                Long => "c_long",
                ULong => "c_ulong",
                Size => "size_t",
                SSize => "ssize_t",
                Float => "c_float",
                Double => "c_double",
                UniChar => "u32",
                Utf8 => "c_char",
                Filename => "c_char",
                Type => "GType",
                Pointer => match &inner[..]  {
                    "void" => "c_void",
                    "tm" => return Err(inner),  //TODO: try use time:Tm
                    _ => &*inner,
                },
                Unsupported => return Err(format!("[Unsupported type {}]", inner)),
                VarArgs => panic!("Should not reach here"),
            };
            Ok(inner.into())
        }
        Type::Record(..) | Type::Alias(..) | Type::Function(..) => {
            if let Some(declared_c_type) = typ.get_glib_name() {
                if declared_c_type != inner {
                    let msg = format!("[c:type mismatch `{}` != `{}` of `{}`]",
                                      inner, declared_c_type, typ.get_name());
                    warn!("{}", msg);
                    return Err(msg);
                }
            }
            else {
                warn!("Type `{}` missing c_type", typ.get_name());
            }
            fix_name(env, tid, &inner)
        }
        Type::CArray(inner_tid) => ffi_inner(env, inner_tid, inner),
        Type::FixedArray(inner_tid, size) => {
            match ffi_inner(env, inner_tid, inner) {
                Ok(s) => Ok(format!("[{}; {}]", s, size)),
                Err(s) => Err(format!("[{}; {}]", s, size)),
            }
        }
        Type::Array(..) | Type::PtrArray(..)
                | Type::List(..) | Type::SList(..) | Type::HashTable(..) => {
            fix_name(env, tid, &inner)
        }
        _ => {
            if let Some(glib_name) = env.library.type_(tid).get_glib_name() {
                if inner != glib_name {
                    if implements_c_type(&env.library, tid, &inner) {
                        info!("[c:type {} of {} <: {}, fixing]", glib_name,
                            env.library.type_(tid).get_name(), inner);
                        fix_name(env, tid, &glib_name)
                    }
                    else {
                        let msg = format!("[c:type mismatch {} != {} of {}]", inner, glib_name,
                            env.library.type_(tid).get_name());
                        warn!("{}", msg);
                        Err(msg)
                    }
                }
                else {
                    fix_name(env, tid, &inner)
                }
            }
            else {
                let msg = format!("[Missing glib_name of {}, can't match != {}]",
                                  env.library.type_(tid).get_name(), inner);
                warn!("{}", msg);
                Err(msg)
            }
        }
    };

    if volatile {
        res.map(|s| format!("Volatile<{}>", s))
    }
    else {
        res
    }
}

fn fix_name(env: &Env, type_id: library::TypeId, name: &str) -> Result {
    if type_id.ns_id == library::INTERNAL_NAMESPACE {
        match *env.library.type_(type_id) {
            Type::Array(..) | Type::PtrArray(..)
                    | Type::List(..) | Type::SList(..) | Type::HashTable(..) => {
                if Some(MAIN_NAMESPACE) == env.library.find_namespace("GLib") {
                    Ok(name.into())
                }
                else {
                    Ok(format!("{}::{}", crate_name("GLib"), name))
                }
            }
            _ => Ok(name.into())
        }
    } else {
        let name_with_prefix = if type_id.ns_id == library::MAIN_NAMESPACE {
            name.into()
        } else {
            format!("{}::{}", fix_namespace(env, type_id), name)
        };
        if env.type_status_sys(&type_id.full_name(&env.library)).ignored() {
            Err(name_with_prefix)
        } else {
            Ok(name_with_prefix)
        }
    }
}

//TODO: check if need to use in non sys codegen
fn fix_namespace(env: &Env, type_id: library::TypeId) -> String {
    crate_name(&env.library.namespace(type_id.ns_id).name)
}

fn implements_c_type(library: &library::Library, tid: TypeId, c_type: &str) -> bool {
    if let Some(ref klass) = library.type_(tid).maybe_ref_as::<library::Class>() {
        klass.implements.iter().chain(klass.parents.iter())
            .any(|&super_tid| library.type_(super_tid).get_glib_name() == Some(c_type))
    }
    else if let Some(ref iface) = library.type_(tid).maybe_ref_as::<library::Interface>() {
        iface.prereq_parents.iter()
            .any(|&super_tid| library.type_(super_tid).get_glib_name() == Some(c_type))
    }
    else {
        false
    }
}
<commit_msg>Continuation of #116: use the proper type for objects when c:type is gpointer<commit_after>use analysis::c_type::rustify_pointers;
use analysis::rust_type::Result;
use env::Env;
use library;
use library::*;
use nameutil::crate_name;
use traits::*;

// FIXME: This module needs redundant allocations audit
// TODO: ffi_type computations should be cached

pub fn ffi_type(env: &Env, tid: library::TypeId, c_type: &str) -> Result {
    let (ptr, inner) = rustify_pointers(c_type);
    let res = if ptr.is_empty() {
        if let Some(c_tid) = env.library.find_type(0, c_type) {
            // Fast track plain fundamental types avoiding some checks
            if env.library.type_(c_tid).maybe_ref_as::<Fundamental>().is_some() {
                match *env.library.type_(tid) {
                    Type::FixedArray(_, size) => {
                        ffi_inner(env, c_tid, c_type.into())
                            .map_any(|s| format!("[{}; {}]", s, size))
                    }
                    Type::Class(Class { c_type: ref expected, .. }) |
                            Type::Interface(Interface { c_type: ref expected, .. })
                            if c_type == "gpointer" => {
                        info!("[c:type `gpointer` instead of `*mut {}`, fixing]", expected);
                        ffi_inner(env, tid, expected.clone())
                            .map_any(|s| format!("*mut {}", s))
                    }
                    _ => {
                        ffi_inner(env, c_tid, c_type.into())
                    }
                }
            }
            else { // c_type isn't fundamental
                ffi_inner(env, tid, inner)
            }
        }
        else { // c_type doesn't match any type in the library by name
            ffi_inner(env, tid, inner)
        }
    }
    else { // ptr not empty
        ffi_inner(env, tid, inner)
            .map_any(|s| format!("{} {}", ptr, s))
    };
    trace!("ffi_type({:?}, {}) -> {:?}", tid, c_type, res);
    res
}

fn ffi_inner(env: &Env, tid: library::TypeId, mut inner: String) -> Result {
    let volatile = inner.starts_with("volatile ");
    if volatile {
        inner = inner["volatile ".len()..].into();
    }

    let typ = env.library.type_(tid);
    let res = match *typ {
        Type::Fundamental(fund) => {
            use library::Fundamental::*;
            let inner = match fund {
                None => "c_void",
                Boolean => "gboolean",
                Int8 => "i8",
                UInt8 => "u8",
                Int16 => "i16",
                UInt16 => "u16",
                Int32 => "i32",
                UInt32 => "u32",
                Int64 => "i64",
                UInt64 => "u64",
                Char => "c_char",
                UChar => "c_uchar",
                Short => "c_short",
                UShort => "c_ushort",
                Int => "c_int",
                UInt => "c_uint",
                Long => "c_long",
                ULong => "c_ulong",
                Size => "size_t",
                SSize => "ssize_t",
                Float => "c_float",
                Double => "c_double",
                UniChar => "u32",
                Utf8 => "c_char",
                Filename => "c_char",
                Type => "GType",
                Pointer => match &inner[..]  {
                    "void" => "c_void",
                    "tm" => return Err(inner),  //TODO: try use time:Tm
                    _ => &*inner,
                },
                Unsupported => return Err(format!("[Unsupported type {}]", inner)),
                VarArgs => panic!("Should not reach here"),
            };
            Ok(inner.into())
        }
        Type::Record(..) | Type::Alias(..) | Type::Function(..) => {
            if let Some(declared_c_type) = typ.get_glib_name() {
                if declared_c_type != inner {
                    let msg = format!("[c:type mismatch `{}` != `{}` of `{}`]",
                                      inner, declared_c_type, typ.get_name());
                    warn!("{}", msg);
                    return Err(msg);
                }
            }
            else {
                warn!("Type `{}` missing c_type", typ.get_name());
            }
            fix_name(env, tid, &inner)
        }
        Type::CArray(inner_tid) => ffi_inner(env, inner_tid, inner),
        Type::FixedArray(inner_tid, size) => {
            match ffi_inner(env, inner_tid, inner) {
                Ok(s) => Ok(format!("[{}; {}]", s, size)),
                Err(s) => Err(format!("[{}; {}]", s, size)),
            }
        }
        Type::Array(..) | Type::PtrArray(..)
                | Type::List(..) | Type::SList(..) | Type::HashTable(..) => {
            fix_name(env, tid, &inner)
        }
        _ => {
            if let Some(glib_name) = env.library.type_(tid).get_glib_name() {
                if inner != glib_name {
                    if implements_c_type(&env.library, tid, &inner) {
                        info!("[c:type {} of {} <: {}, fixing]", glib_name,
                            env.library.type_(tid).get_name(), inner);
                        fix_name(env, tid, &glib_name)
                    }
                    else {
                        let msg = format!("[c:type mismatch {} != {} of {}]", inner, glib_name,
                            env.library.type_(tid).get_name());
                        warn!("{}", msg);
                        Err(msg)
                    }
                }
                else {
                    fix_name(env, tid, &inner)
                }
            }
            else {
                let msg = format!("[Missing glib_name of {}, can't match != {}]",
                                  env.library.type_(tid).get_name(), inner);
                warn!("{}", msg);
                Err(msg)
            }
        }
    };

    if volatile {
        res.map(|s| format!("Volatile<{}>", s))
    }
    else {
        res
    }
}

fn fix_name(env: &Env, type_id: library::TypeId, name: &str) -> Result {
    if type_id.ns_id == library::INTERNAL_NAMESPACE {
        match *env.library.type_(type_id) {
            Type::Array(..) | Type::PtrArray(..)
                    | Type::List(..) | Type::SList(..) | Type::HashTable(..) => {
                if Some(MAIN_NAMESPACE) == env.library.find_namespace("GLib") {
                    Ok(name.into())
                }
                else {
                    Ok(format!("{}::{}", crate_name("GLib"), name))
                }
            }
            _ => Ok(name.into())
        }
    } else {
        let name_with_prefix = if type_id.ns_id == library::MAIN_NAMESPACE {
            name.into()
        } else {
            format!("{}::{}", fix_namespace(env, type_id), name)
        };
        if env.type_status_sys(&type_id.full_name(&env.library)).ignored() {
            Err(name_with_prefix)
        } else {
            Ok(name_with_prefix)
        }
    }
}

//TODO: check if need to use in non sys codegen
fn fix_namespace(env: &Env, type_id: library::TypeId) -> String {
    crate_name(&env.library.namespace(type_id.ns_id).name)
}

fn implements_c_type(library: &library::Library, tid: TypeId, c_type: &str) -> bool {
    if let Some(ref klass) = library.type_(tid).maybe_ref_as::<library::Class>() {
        klass.implements.iter().chain(klass.parents.iter())
            .any(|&super_tid| library.type_(super_tid).get_glib_name() == Some(c_type))
    }
    else if let Some(ref iface) = library.type_(tid).maybe_ref_as::<library::Interface>() {
        iface.prereq_parents.iter()
            .any(|&super_tid| library.type_(super_tid).get_glib_name() == Some(c_type))
    }
    else {
        false
    }
}
<|endoftext|>


<commit_before>use std::iter::Iterator;

#[deriving(Clone, PartialEq, Show)]
pub enum SequenceItem {
    Number(uint),
    Range(Box<SequenceItem>, Box<SequenceItem>),
    Wildcard
}

// TODO: Find a way to handle sequences in O(1) as currently, the memory usage
// of the vec returned by this function scales at O(n).
pub fn iterator(sequence_set: Vec<SequenceItem>, max_id: uint) -> Vec<uint> {
    // If the number of possible messages is 0, we return and empty vec.
    if max_id == 0 { return Vec::new() }

    let stop = max_id + 1;

    let mut items = Vec::new();
    for item in sequence_set.iter() {
        match item {
            & Number(num) => { items.push(num) },
            & Range(ref a, ref b) => {
                let a = match **a {
                    Number(num) => { num },
                    Wildcard => { max_id }
                    Range(_, _) => {
                        error!("A range of ranges is invalid.");
                        continue;
                    }
                };
                let b = match **b {
                    Number(num) => { num },
                    Wildcard => { max_id }
                    Range(_, _) => {
                        error!("A range of ranges is invalid.");
                        continue;
                    }
                };
                let mut min = 0;
                let mut max = 0;
                if a <= b {
                    min = a;
                    max = b;
                } else {
                    min = b;
                    max = a;
                }
                if min > stop { min = stop; }
                if max > stop { max = stop; }
                let seq_range: Vec<uint> = range(min, max + 1).collect();
                items.push_all(seq_range.as_slice());
            },
            & Wildcard => {
                // If the sequence set contains the wildcard operator, it spans
                // the entire possible range of messages.
                return range(1, stop).collect()
            }
        }
    }

    // Sort and remove duplicates.
    items.sort();
    items.dedup();
    // Remove all elements that are greater than the maximum.
    let items: Vec<uint> = items.move_iter().filter(|&x| x <= max_id).collect();
    return items;
}

#[test]
fn test_sequence_num() {
    assert_eq!(iterator(vec![Number(4324)], 5000), vec![4324]);
    assert_eq!(iterator(vec![Number(23), Number(44)], 5000), vec![23, 44]);
    assert_eq!(iterator(vec![Number(6), Number(6), Number(2)], 5000), vec![2, 6]);
}

#[test]
fn test_sequence_past_end() {
    assert_eq!(iterator(vec![Number(4324)], 100), Vec::new());
    assert_eq!(iterator(vec![Number(23), Number(44)], 30), vec![23]);
    assert_eq!(iterator(vec![Number(6), Number(6), Number(2)], 4), vec![2]);
}

#[test]
fn test_sequence_range() {
    assert_eq!(iterator(vec![Range(box Number(6), box Wildcard)], 10), vec![6, 7, 8, 9, 10]);
    assert_eq!(iterator(vec![Range(box Number(1), box Number(10))], 4), vec![1, 2, 3, 4]);
    assert_eq!(iterator(vec![Range(box Wildcard, box Number(8)), Number(9), Number(2), Number(2)], 12), vec![2, 8, 9, 10, 11, 12]);
}

#[test]
fn test_sequence_wildcard() {
    assert_eq!(iterator(vec![Range(box Number(10), box Wildcard), Wildcard], 6), vec![1, 2, 3, 4, 5, 6]);
    assert_eq!(iterator(vec![Wildcard, Number(8)], 3), vec![1, 2, 3]);
}

#[test]
fn test_sequence_complex() {
    assert_eq!(iterator(
            vec![Number(1), Number(3), Range(box Number(5), box Number(7)), Number(9), Number(12), Range(box Number(15), box Wildcard)], 13),
            vec![1, 3, 5, 6, 7, 9, 12, 13]);
}
<commit_msg>Fix deprecation<commit_after>use std::iter::Iterator;

#[deriving(Clone, PartialEq, Show)]
pub enum SequenceItem {
    Number(uint),
    Range(Box<SequenceItem>, Box<SequenceItem>),
    Wildcard
}

// TODO: Find a way to handle sequences in O(1) as currently, the memory usage
// of the vec returned by this function scales at O(n).
pub fn iterator(sequence_set: Vec<SequenceItem>, max_id: uint) -> Vec<uint> {
    // If the number of possible messages is 0, we return and empty vec.
    if max_id == 0 { return Vec::new() }

    let stop = max_id + 1;

    let mut items = Vec::new();
    for item in sequence_set.iter() {
        match item {
            & Number(num) => { items.push(num) },
            & Range(ref a, ref b) => {
                let a = match **a {
                    Number(num) => { num },
                    Wildcard => { max_id }
                    Range(_, _) => {
                        error!("A range of ranges is invalid.");
                        continue;
                    }
                };
                let b = match **b {
                    Number(num) => { num },
                    Wildcard => { max_id }
                    Range(_, _) => {
                        error!("A range of ranges is invalid.");
                        continue;
                    }
                };
                let mut min = 0;
                let mut max = 0;
                if a <= b {
                    min = a;
                    max = b;
                } else {
                    min = b;
                    max = a;
                }
                if min > stop { min = stop; }
                if max > stop { max = stop; }
                let seq_range: Vec<uint> = range(min, max + 1).collect();
                items.push_all(seq_range.as_slice());
            },
            & Wildcard => {
                // If the sequence set contains the wildcard operator, it spans
                // the entire possible range of messages.
                return range(1, stop).collect()
            }
        }
    }

    // Sort and remove duplicates.
    items.sort();
    items.dedup();
    // Remove all elements that are greater than the maximum.
    let items: Vec<uint> = items.into_iter().filter(|&x| x <= max_id).collect();
    return items;
}

#[test]
fn test_sequence_num() {
    assert_eq!(iterator(vec![Number(4324)], 5000), vec![4324]);
    assert_eq!(iterator(vec![Number(23), Number(44)], 5000), vec![23, 44]);
    assert_eq!(iterator(vec![Number(6), Number(6), Number(2)], 5000), vec![2, 6]);
}

#[test]
fn test_sequence_past_end() {
    assert_eq!(iterator(vec![Number(4324)], 100), Vec::new());
    assert_eq!(iterator(vec![Number(23), Number(44)], 30), vec![23]);
    assert_eq!(iterator(vec![Number(6), Number(6), Number(2)], 4), vec![2]);
}

#[test]
fn test_sequence_range() {
    assert_eq!(iterator(vec![Range(box Number(6), box Wildcard)], 10), vec![6, 7, 8, 9, 10]);
    assert_eq!(iterator(vec![Range(box Number(1), box Number(10))], 4), vec![1, 2, 3, 4]);
    assert_eq!(iterator(vec![Range(box Wildcard, box Number(8)), Number(9), Number(2), Number(2)], 12), vec![2, 8, 9, 10, 11, 12]);
}

#[test]
fn test_sequence_wildcard() {
    assert_eq!(iterator(vec![Range(box Number(10), box Wildcard), Wildcard], 6), vec![1, 2, 3, 4, 5, 6]);
    assert_eq!(iterator(vec![Wildcard, Number(8)], 3), vec![1, 2, 3]);
}

#[test]
fn test_sequence_complex() {
    assert_eq!(iterator(
            vec![Number(1), Number(3), Range(box Number(5), box Number(7)), Number(9), Number(12), Range(box Number(15), box Wildcard)], 13),
            vec![1, 3, 5, 6, 7, 9, 12, 13]);
}
<|endoftext|>


<commit_before>import syntax::{visit, ast_util};
import syntax::ast::*;
import std::list::{list, nil, cons, tail};
import std::{vec, list, option};

// Last use analysis pass.
//
// Finds the last read of each value stored in a local variable or
// callee-owned argument (arguments with by-move or by-copy passing
// style). This is a limited form of liveness analysis, peformed
// (perhaps foolishly) directly on the AST.
//
// The algorithm walks the AST, keeping a set of (def, last_use)
// pairs. When the function is exited, or the local is overwritten,
// the current set of last uses is marked with 'true' in a table.
// Other branches may later overwrite them with 'false' again, since
// they may find a use coming after them. (Marking an expression as a
// last use is only done if it has not already been marked with
// 'false'.)
//
// Some complexity is added to deal with joining control flow branches
// (by `break` or conditionals), and for handling loops.

// Marks expr_paths that are last uses.
type last_uses = std::map::hashmap<node_id, ()>;

tag seen { unset; seen(node_id); }
tag block_type { func; loop; }

type set = [{def: node_id, exprs: list<node_id>}];
type bl = @{type: block_type, mutable second: bool, mutable exits: [set]};

type ctx = {last_uses: std::map::hashmap<node_id, bool>,
            def_map: resolve::def_map,
            ref_map: alias::ref_map,
            tcx: ty::ctxt,
            // The current set of local last uses
            mutable current: set,
            mutable blocks: list<bl>};

fn find_last_uses(c: @crate, def_map: resolve::def_map,
                  ref_map: alias::ref_map, tcx: ty::ctxt) -> last_uses {
    let v = visit::mk_vt(@{visit_expr: visit_expr,
                           visit_fn: visit_fn
                           with *visit::default_visitor()});
    let cx = {last_uses: std::map::new_int_hash(),
              def_map: def_map,
              ref_map: ref_map,
              tcx: tcx,
              mutable current: [],
              mutable blocks: nil};
    visit::visit_crate(*c, cx, v);
    let mini_table = std::map::new_int_hash();
    cx.last_uses.items {|key, val|
        if val {
            mini_table.insert(key, ());
            let def_node = ast_util::def_id_of_def(def_map.get(key)).node;
            mini_table.insert(def_node, ());
        }
    }
    ret mini_table;
}

fn visit_expr(ex: @expr, cx: ctx, v: visit::vt<ctx>) {
    alt ex.node {
      expr_ret(oexpr) {
        visit::visit_expr_opt(oexpr, cx, v);
        if !add_block_exit(cx, func) { leave_fn(cx); }
      }
      expr_fail(oexpr) {
        visit::visit_expr_opt(oexpr, cx, v);
        leave_fn(cx);
      }
      expr_break. { add_block_exit(cx, loop); }
      expr_while(_, _) | expr_do_while(_, _) {
        visit_block(loop, cx) {|| visit::visit_expr(ex, cx, v);}
      }
      expr_for(_, coll, blk) {
        v.visit_expr(coll, cx, v);
        visit_block(loop, cx) {|| visit::visit_block(blk, cx, v);}
      }
      expr_ternary(_, _, _) {
        v.visit_expr(ast_util::ternary_to_if(ex), cx, v);
      }
      expr_alt(input, arms) {
        v.visit_expr(input, cx, v);
        let before = cx.current, sets = [];
        for arm in arms {
            cx.current = before;
            v.visit_arm(arm, cx, v);
            sets += [cx.current];
        }
        cx.current = join_branches(sets);
      }
      expr_if(cond, then, els) {
        v.visit_expr(cond, cx, v);
        let cur = cx.current;
        visit::visit_block(then, cx, v);
        cx.current <-> cur;
        visit::visit_expr_opt(els, cx, v);
        cx.current = join_branches([cur, cx.current]);
      }
      expr_path(_) {
        let my_def = ast_util::def_id_of_def(cx.def_map.get(ex.id)).node;
        alt cx.ref_map.find(my_def) {
          option::some(root_id) { clear_in_current(cx, root_id, false); }
          _ {
            alt clear_if_path(cx, ex, v, false) {
              option::some(my_def) {
                cx.current += [{def: my_def, exprs: cons(ex.id, @nil)}];
              }
              _ {}
            }
          }
        }
      }
      expr_swap(lhs, rhs) {
        clear_if_path(cx, lhs, v, false);
        clear_if_path(cx, rhs, v, false);
      }
      expr_move(dest, src) | expr_assign(dest, src) {
        v.visit_expr(src, cx, v);
        clear_if_path(cx, dest, v, true);
      }
      expr_assign_op(_, dest, src) {
        v.visit_expr(src, cx, v);
        v.visit_expr(dest, cx, v);
        clear_if_path(cx, dest, v, true);
      }
      expr_call(f, args, _) {
        v.visit_expr(f, cx, v);
        let i = 0u;
        for arg_t in ty::ty_fn_args(cx.tcx, ty::expr_ty(cx.tcx, f)) {
            alt arg_t.mode {
              by_mut_ref. { clear_if_path(cx, args[i], v, false); }
              _ { v.visit_expr(args[i], cx, v); }
            }
            i += 1u;
        }
      }
      _ { visit::visit_expr(ex, cx, v); }
    }
}

fn visit_fn(f: _fn, tps: [ty_param], sp: syntax::codemap::span,
            ident: fn_ident, id: node_id, cx: ctx, v: visit::vt<ctx>) {
    if f.proto == proto_block {
        visit_block(func, cx, {||
            visit::visit_fn(f, tps, sp, ident, id, cx, v);
        });
    } else {
        let old = nil;
        cx.blocks <-> old;
        visit::visit_fn(f, tps, sp, ident, id, cx, v);
        cx.blocks <-> old;
        leave_fn(cx);
    }
}

fn visit_block(tp: block_type, cx: ctx, visit: block()) {
    let local = @{type: tp, mutable second: false, mutable exits: []};
    cx.blocks = cons(local, @cx.blocks);
    visit();
    local.second = true;
    visit();
    cx.blocks = tail(cx.blocks);
    cx.current = join_branches(local.exits);
}

fn add_block_exit(cx: ctx, tp: block_type) -> bool {
    let cur = cx.blocks;
    while cur != nil {
        alt cur {
          cons(b, tail) {
            if (b.type == tp) {
                if !b.second { b.exits += [cx.current]; }
                ret true;
            }
            cur = *tail;
          }
        }
    }
    ret false;
}

fn join_branches(branches: [set]) -> set {
    let found: set = [], i = 0u, l = vec::len(branches);
    for set in branches {
        i += 1u;
        for {def, exprs} in set {
            if !vec::any({|v| v.def == def}, found) {
                let j = i, ne = exprs;
                while j < l {
                    for {def: d2, exprs} in branches[j] {
                        if d2 == def {
                            list::iter(exprs) {|e|
                                if !list::has(ne, e) { ne = cons(e, @ne); }
                            }
                        }
                    }
                    j += 1u;
                }
                found += [{def: def, exprs: ne}];
            }
        }
    }
    ret found;
}

fn leave_fn(cx: ctx) {
    for {def, exprs} in cx.current {
        list::iter(exprs) {|ex_id|
            if !cx.last_uses.contains_key(ex_id) {
                cx.last_uses.insert(ex_id, true);
            }
        }
    }
}

fn clear_in_current(cx: ctx, my_def: node_id, to: bool) {
    for {def, exprs} in cx.current {
        if def == my_def {
            list::iter(exprs) {|expr|
                if !to || !cx.last_uses.contains_key(expr) {
                     cx.last_uses.insert(expr, to);
                }
            }
            cx.current = vec::filter({|x| x.def != my_def},
                                     copy cx.current);
            break;
        }
    }
}

fn clear_if_path(cx: ctx, ex: @expr, v: visit::vt<ctx>, to: bool)
    -> option::t<node_id> {
    alt ex.node {
      expr_path(_) {
        alt cx.def_map.get(ex.id) {
          def_local(def_id, let_copy.) | def_arg(def_id, by_copy.) |
          def_arg(def_id, by_move.) {
            clear_in_current(cx, def_id.node, to);
            ret option::some(def_id.node);
          }
          _ {}
        }
      }
      _ { v.visit_expr(ex, cx, v); }
    }
    ret option::none;
}
<commit_msg>Fix mistake in last use pass<commit_after>import syntax::{visit, ast_util};
import syntax::ast::*;
import std::list::{list, nil, cons, tail};
import std::{vec, list, option};

// Last use analysis pass.
//
// Finds the last read of each value stored in a local variable or
// callee-owned argument (arguments with by-move or by-copy passing
// style). This is a limited form of liveness analysis, peformed
// (perhaps foolishly) directly on the AST.
//
// The algorithm walks the AST, keeping a set of (def, last_use)
// pairs. When the function is exited, or the local is overwritten,
// the current set of last uses is marked with 'true' in a table.
// Other branches may later overwrite them with 'false' again, since
// they may find a use coming after them. (Marking an expression as a
// last use is only done if it has not already been marked with
// 'false'.)
//
// Some complexity is added to deal with joining control flow branches
// (by `break` or conditionals), and for handling loops.

// Marks expr_paths that are last uses.
type last_uses = std::map::hashmap<node_id, ()>;

tag seen { unset; seen(node_id); }
tag block_type { func; loop; }

type set = [{def: node_id, exprs: list<node_id>}];
type bl = @{type: block_type, mutable second: bool, mutable exits: [set]};

type ctx = {last_uses: std::map::hashmap<node_id, bool>,
            def_map: resolve::def_map,
            ref_map: alias::ref_map,
            tcx: ty::ctxt,
            // The current set of local last uses
            mutable current: set,
            mutable blocks: list<bl>};

fn find_last_uses(c: @crate, def_map: resolve::def_map,
                  ref_map: alias::ref_map, tcx: ty::ctxt) -> last_uses {
    let v = visit::mk_vt(@{visit_expr: visit_expr,
                           visit_fn: visit_fn
                           with *visit::default_visitor()});
    let cx = {last_uses: std::map::new_int_hash(),
              def_map: def_map,
              ref_map: ref_map,
              tcx: tcx,
              mutable current: [],
              mutable blocks: nil};
    visit::visit_crate(*c, cx, v);
    let mini_table = std::map::new_int_hash();
    cx.last_uses.items {|key, val|
        if val {
            mini_table.insert(key, ());
            let def_node = ast_util::def_id_of_def(def_map.get(key)).node;
            mini_table.insert(def_node, ());
        }
    }
    ret mini_table;
}

fn visit_expr(ex: @expr, cx: ctx, v: visit::vt<ctx>) {
    alt ex.node {
      expr_ret(oexpr) {
        visit::visit_expr_opt(oexpr, cx, v);
        if !add_block_exit(cx, func) { leave_fn(cx); }
      }
      expr_fail(oexpr) {
        visit::visit_expr_opt(oexpr, cx, v);
        leave_fn(cx);
      }
      expr_break. { add_block_exit(cx, loop); }
      expr_while(_, _) | expr_do_while(_, _) {
        visit_block(loop, cx) {|| visit::visit_expr(ex, cx, v);}
      }
      expr_for(_, coll, blk) {
        v.visit_expr(coll, cx, v);
        visit_block(loop, cx) {|| visit::visit_block(blk, cx, v);}
      }
      expr_ternary(_, _, _) {
        v.visit_expr(ast_util::ternary_to_if(ex), cx, v);
      }
      expr_alt(input, arms) {
        v.visit_expr(input, cx, v);
        let before = cx.current, sets = [];
        for arm in arms {
            cx.current = before;
            v.visit_arm(arm, cx, v);
            sets += [cx.current];
        }
        cx.current = join_branches(sets);
      }
      expr_if(cond, then, els) {
        v.visit_expr(cond, cx, v);
        let cur = cx.current;
        visit::visit_block(then, cx, v);
        cx.current <-> cur;
        visit::visit_expr_opt(els, cx, v);
        cx.current = join_branches([cur, cx.current]);
      }
      expr_path(_) {
        let my_def = ast_util::def_id_of_def(cx.def_map.get(ex.id)).node;
        alt cx.ref_map.find(my_def) {
          option::some(root_id) { clear_in_current(cx, root_id, false); }
          _ {
            alt clear_if_path(cx, ex, v, false) {
              option::some(my_def) {
                cx.current += [{def: my_def, exprs: cons(ex.id, @nil)}];
              }
              _ {}
            }
          }
        }
      }
      expr_swap(lhs, rhs) {
        clear_if_path(cx, lhs, v, false);
        clear_if_path(cx, rhs, v, false);
      }
      expr_move(dest, src) | expr_assign(dest, src) {
        v.visit_expr(src, cx, v);
        clear_if_path(cx, dest, v, true);
      }
      expr_assign_op(_, dest, src) {
        v.visit_expr(src, cx, v);
        v.visit_expr(dest, cx, v);
        clear_if_path(cx, dest, v, true);
      }
      expr_call(f, args, _) {
        v.visit_expr(f, cx, v);
        let i = 0u, fns = [];
        let arg_ts = ty::ty_fn_args(cx.tcx, ty::expr_ty(cx.tcx, f));
        for arg in args {
            alt arg.node {
              expr_fn(_) { fns += [arg]; }
              _ {
                alt arg_ts[i].mode {
                  by_mut_ref. { clear_if_path(cx, arg, v, false); }
                  _ { v.visit_expr(arg, cx, v); }
                }
              }
            }
            i += 1u;
        }
        for f in fns { v.visit_expr(f, cx, v); }
      }
      _ { visit::visit_expr(ex, cx, v); }
    }
}

fn visit_fn(f: _fn, tps: [ty_param], sp: syntax::codemap::span,
            ident: fn_ident, id: node_id, cx: ctx, v: visit::vt<ctx>) {
    if f.proto == proto_block {
        visit_block(func, cx, {||
            visit::visit_fn(f, tps, sp, ident, id, cx, v);
        });
    } else {
        let old = nil;
        cx.blocks <-> old;
        visit::visit_fn(f, tps, sp, ident, id, cx, v);
        cx.blocks <-> old;
        leave_fn(cx);
    }
}

fn visit_block(tp: block_type, cx: ctx, visit: block()) {
    let local = @{type: tp, mutable second: false, mutable exits: []};
    cx.blocks = cons(local, @cx.blocks);
    visit();
    local.second = true;
    visit();
    cx.blocks = tail(cx.blocks);
    cx.current = join_branches(local.exits);
}

fn add_block_exit(cx: ctx, tp: block_type) -> bool {
    let cur = cx.blocks;
    while cur != nil {
        alt cur {
          cons(b, tail) {
            if (b.type == tp) {
                if !b.second { b.exits += [cx.current]; }
                ret true;
            }
            cur = *tail;
          }
        }
    }
    ret false;
}

fn join_branches(branches: [set]) -> set {
    let found: set = [], i = 0u, l = vec::len(branches);
    for set in branches {
        i += 1u;
        for {def, exprs} in set {
            if !vec::any({|v| v.def == def}, found) {
                let j = i, ne = exprs;
                while j < l {
                    for {def: d2, exprs} in branches[j] {
                        if d2 == def {
                            list::iter(exprs) {|e|
                                if !list::has(ne, e) { ne = cons(e, @ne); }
                            }
                        }
                    }
                    j += 1u;
                }
                found += [{def: def, exprs: ne}];
            }
        }
    }
    ret found;
}

fn leave_fn(cx: ctx) {
    for {def, exprs} in cx.current {
        list::iter(exprs) {|ex_id|
            if !cx.last_uses.contains_key(ex_id) {
                cx.last_uses.insert(ex_id, true);
            }
        }
    }
}

fn clear_in_current(cx: ctx, my_def: node_id, to: bool) {
    for {def, exprs} in cx.current {
        if def == my_def {
            list::iter(exprs) {|expr|
                if !to || !cx.last_uses.contains_key(expr) {
                     cx.last_uses.insert(expr, to);
                }
            }
            cx.current = vec::filter({|x| x.def != my_def},
                                     copy cx.current);
            break;
        }
    }
}

fn clear_if_path(cx: ctx, ex: @expr, v: visit::vt<ctx>, to: bool)
    -> option::t<node_id> {
    alt ex.node {
      expr_path(_) {
        alt cx.def_map.get(ex.id) {
          def_local(def_id, let_copy.) | def_arg(def_id, by_copy.) |
          def_arg(def_id, by_move.) {
            clear_in_current(cx, def_id.node, to);
            ret option::some(def_id.node);
          }
          _ {}
        }
      }
      _ { v.visit_expr(ex, cx, v); }
    }
    ret option::none;
}
<|endoftext|>


<commit_before>#![allow(clippy::too_many_arguments)]

extern crate pq_sys;

use self::pq_sys::*;
use std::ffi::{CStr, CString};
use std::os::raw as libc;
use std::ptr::NonNull;
use std::{ptr, str};

use crate::result::*;

#[allow(missing_debug_implementations, missing_copy_implementations)]
pub struct RawConnection {
    internal_connection: NonNull<PGconn>,
}

impl RawConnection {
    pub fn establish(database_url: &str) -> ConnectionResult<Self> {
        use self::ConnStatusType::*;

        let connection_string = CString::new(database_url)?;
        let connection_ptr = unsafe { PQconnectdb(connection_string.as_ptr()) };
        let connection_status = unsafe { PQstatus(connection_ptr) };

        match connection_status {
            CONNECTION_OK => {
                let connection_ptr = unsafe { NonNull::new_unchecked(connection_ptr) };
                Ok(RawConnection {
                    internal_connection: connection_ptr,
                })
            }
            _ => {
                let message = last_error_message(connection_ptr);

                if !connection_ptr.is_null() {
                     // Note that even if the server connection attempt fails (as indicated by PQstatus),
                     // the application should call PQfinish to free the memory used by the PGconn object.
                     // https://www.postgresql.org/docs/current/libpq-connect.html
                     unsafe { PQfinish(connection_ptr) }
                }

                Err(ConnectionError::BadConnection(message))
            }
        }
    }

    pub fn last_error_message(&self) -> String {
        last_error_message(self.internal_connection.as_ptr())
    }

    pub fn set_notice_processor(&self, notice_processor: NoticeProcessor) {
        unsafe {
            PQsetNoticeProcessor(
                self.internal_connection.as_ptr(),
                Some(notice_processor),
                ptr::null_mut(),
            );
        }
    }

    pub unsafe fn exec(&self, query: *const libc::c_char) -> QueryResult<RawResult> {
        RawResult::new(PQexec(self.internal_connection.as_ptr(), query), self)
    }

    pub unsafe fn exec_prepared(
        &self,
        stmt_name: *const libc::c_char,
        param_count: libc::c_int,
        param_values: *const *const libc::c_char,
        param_lengths: *const libc::c_int,
        param_formats: *const libc::c_int,
        result_format: libc::c_int,
    ) -> QueryResult<RawResult> {
        let ptr = PQexecPrepared(
            self.internal_connection.as_ptr(),
            stmt_name,
            param_count,
            param_values,
            param_lengths,
            param_formats,
            result_format,
        );
        RawResult::new(ptr, self)
    }

    pub unsafe fn prepare(
        &self,
        stmt_name: *const libc::c_char,
        query: *const libc::c_char,
        param_count: libc::c_int,
        param_types: *const Oid,
    ) -> QueryResult<RawResult> {
        let ptr = PQprepare(
            self.internal_connection.as_ptr(),
            stmt_name,
            query,
            param_count,
            param_types,
        );
        RawResult::new(ptr, self)
    }
}

pub type NoticeProcessor = extern "C" fn(arg: *mut libc::c_void, message: *const libc::c_char);

impl Drop for RawConnection {
    fn drop(&mut self) {
        unsafe { PQfinish(self.internal_connection.as_ptr()) };
    }
}

fn last_error_message(conn: *const PGconn) -> String {
    unsafe {
        let error_ptr = PQerrorMessage(conn);
        let bytes = CStr::from_ptr(error_ptr).to_bytes();
        String::from_utf8_lossy(bytes).to_string()
    }
}

/// Internal wrapper around a `*mut PGresult` which is known to be not-null, and
/// have no aliases.  This wrapper is to ensure that it's always properly
/// dropped.
///
/// If `Unique` is ever stabilized, we should use it here.
#[allow(missing_debug_implementations)]
pub struct RawResult(NonNull<PGresult>);

unsafe impl Send for RawResult {}
unsafe impl Sync for RawResult {}

impl RawResult {
    #[allow(clippy::new_ret_no_self)]
    fn new(ptr: *mut PGresult, conn: &RawConnection) -> QueryResult<Self> {
        NonNull::new(ptr).map(RawResult).ok_or_else(|| {
            Error::DatabaseError(
                DatabaseErrorKind::UnableToSendCommand,
                Box::new(conn.last_error_message()),
            )
        })
    }

    pub fn as_ptr(&self) -> *mut PGresult {
        self.0.as_ptr()
    }

    pub fn error_message(&self) -> &str {
        let ptr = unsafe { PQresultErrorMessage(self.0.as_ptr()) };
        let cstr = unsafe { CStr::from_ptr(ptr) };
        cstr.to_str().unwrap_or_default()
    }
}

impl Drop for RawResult {
    fn drop(&mut self) {
        unsafe { PQclear(self.0.as_ptr()) }
    }
}
<commit_msg>Run cargo fmt<commit_after>#![allow(clippy::too_many_arguments)]

extern crate pq_sys;

use self::pq_sys::*;
use std::ffi::{CStr, CString};
use std::os::raw as libc;
use std::ptr::NonNull;
use std::{ptr, str};

use crate::result::*;

#[allow(missing_debug_implementations, missing_copy_implementations)]
pub struct RawConnection {
    internal_connection: NonNull<PGconn>,
}

impl RawConnection {
    pub fn establish(database_url: &str) -> ConnectionResult<Self> {
        use self::ConnStatusType::*;

        let connection_string = CString::new(database_url)?;
        let connection_ptr = unsafe { PQconnectdb(connection_string.as_ptr()) };
        let connection_status = unsafe { PQstatus(connection_ptr) };

        match connection_status {
            CONNECTION_OK => {
                let connection_ptr = unsafe { NonNull::new_unchecked(connection_ptr) };
                Ok(RawConnection {
                    internal_connection: connection_ptr,
                })
            }
            _ => {
                let message = last_error_message(connection_ptr);

                if !connection_ptr.is_null() {
                    // Note that even if the server connection attempt fails (as indicated by PQstatus),
                    // the application should call PQfinish to free the memory used by the PGconn object.
                    // https://www.postgresql.org/docs/current/libpq-connect.html
                    unsafe { PQfinish(connection_ptr) }
                }

                Err(ConnectionError::BadConnection(message))
            }
        }
    }

    pub fn last_error_message(&self) -> String {
        last_error_message(self.internal_connection.as_ptr())
    }

    pub fn set_notice_processor(&self, notice_processor: NoticeProcessor) {
        unsafe {
            PQsetNoticeProcessor(
                self.internal_connection.as_ptr(),
                Some(notice_processor),
                ptr::null_mut(),
            );
        }
    }

    pub unsafe fn exec(&self, query: *const libc::c_char) -> QueryResult<RawResult> {
        RawResult::new(PQexec(self.internal_connection.as_ptr(), query), self)
    }

    pub unsafe fn exec_prepared(
        &self,
        stmt_name: *const libc::c_char,
        param_count: libc::c_int,
        param_values: *const *const libc::c_char,
        param_lengths: *const libc::c_int,
        param_formats: *const libc::c_int,
        result_format: libc::c_int,
    ) -> QueryResult<RawResult> {
        let ptr = PQexecPrepared(
            self.internal_connection.as_ptr(),
            stmt_name,
            param_count,
            param_values,
            param_lengths,
            param_formats,
            result_format,
        );
        RawResult::new(ptr, self)
    }

    pub unsafe fn prepare(
        &self,
        stmt_name: *const libc::c_char,
        query: *const libc::c_char,
        param_count: libc::c_int,
        param_types: *const Oid,
    ) -> QueryResult<RawResult> {
        let ptr = PQprepare(
            self.internal_connection.as_ptr(),
            stmt_name,
            query,
            param_count,
            param_types,
        );
        RawResult::new(ptr, self)
    }
}

pub type NoticeProcessor = extern "C" fn(arg: *mut libc::c_void, message: *const libc::c_char);

impl Drop for RawConnection {
    fn drop(&mut self) {
        unsafe { PQfinish(self.internal_connection.as_ptr()) };
    }
}

fn last_error_message(conn: *const PGconn) -> String {
    unsafe {
        let error_ptr = PQerrorMessage(conn);
        let bytes = CStr::from_ptr(error_ptr).to_bytes();
        String::from_utf8_lossy(bytes).to_string()
    }
}

/// Internal wrapper around a `*mut PGresult` which is known to be not-null, and
/// have no aliases.  This wrapper is to ensure that it's always properly
/// dropped.
///
/// If `Unique` is ever stabilized, we should use it here.
#[allow(missing_debug_implementations)]
pub struct RawResult(NonNull<PGresult>);

unsafe impl Send for RawResult {}
unsafe impl Sync for RawResult {}

impl RawResult {
    #[allow(clippy::new_ret_no_self)]
    fn new(ptr: *mut PGresult, conn: &RawConnection) -> QueryResult<Self> {
        NonNull::new(ptr).map(RawResult).ok_or_else(|| {
            Error::DatabaseError(
                DatabaseErrorKind::UnableToSendCommand,
                Box::new(conn.last_error_message()),
            )
        })
    }

    pub fn as_ptr(&self) -> *mut PGresult {
        self.0.as_ptr()
    }

    pub fn error_message(&self) -> &str {
        let ptr = unsafe { PQresultErrorMessage(self.0.as_ptr()) };
        let cstr = unsafe { CStr::from_ptr(ptr) };
        cstr.to_str().unwrap_or_default()
    }
}

impl Drop for RawResult {
    fn drop(&mut self) {
        unsafe { PQclear(self.0.as_ptr()) }
    }
}
<|endoftext|>


<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use std::io::{File, IoResult};
use std::io::fs::PathExtensions;
use std::os;
use std::path::Path;

pub fn resources_dir_path() -> Path {
    // FIXME: Find a way to not rely on the executable being under `<servo source>/target`.
    let mut path = os::self_exe_path().expect("can't get exe path");
    path.pop();
    path.push("resources");
    if !path.is_dir() {
        path.pop();
        path.pop();
        path.push("resources");
    }
    path
}

pub fn read_resource_file(relative_path_components: &[&str]) -> IoResult<Vec<u8>> {
    let mut path = resources_dir_path();
    path.push_many(relative_path_components);
    let mut file = try!(File::open(&path));
    file.read_to_end()
}
<commit_msg>Redirect android resource folder (user-agent.css and friends) to /sdcard/servo. This is a temporary solution, until they are packaged properly.<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use std::io::{File, IoResult};
use std::path::Path;

#[cfg(not(target_os = "android"))]
use std::io::fs::PathExtensions;
#[cfg(not(target_os = "android"))]
use std::os;

#[cfg(target_os = "android")]
pub fn resources_dir_path() -> Path {
    Path::new("/sdcard/servo/")
}

#[cfg(not(target_os = "android"))]
pub fn resources_dir_path() -> Path {
    // FIXME: Find a way to not rely on the executable being under `<servo source>/target`.
    let mut path = os::self_exe_path().expect("can't get exe path");
    path.pop();
    path.push("resources");
    if !path.is_dir() {
        path.pop();
        path.pop();
        path.push("resources");
    }
    path
}

pub fn read_resource_file(relative_path_components: &[&str]) -> IoResult<Vec<u8>> {
    let mut path = resources_dir_path();
    path.push_many(relative_path_components);
    let mut file = try!(File::open(&path));
    file.read_to_end()
}
<|endoftext|>


<commit_before>pub use defaults::Defaults;
pub use serde::{de, Deserialize, Deserializer, Serialize};
pub use serde_json::Value;
pub use serde_with::skip_serializing_none;
use std::collections::BTreeMap;
pub use std::sync::Arc;

/// The set of primitive (non-Entity) node types
#[derive(Clone, Debug, Serialize, Deserialize)]
#[serde(untagged)]
pub enum Primitive {
    Null,
    Boolean(Boolean),
    Integer(Integer),
    Number(Number),
    String(String),
    Object(Object),
    Array(Array),
}

/// A boolean value
pub type Boolean = bool;

/// An integer value
///
/// Uses `i64` for maximum precision.
pub type Integer = i64;

/// A floating point value (a.k.a real number)
///
/// Uses `i64` for maximum precision.
pub type Number = f64;

/// An array value (a.k.a. vector)
pub type Array = Vec<Primitive>;

/// An object value (a.k.a map, dictionary)
///
/// Uses `BTreeMap` to preserve order.
pub type Object = BTreeMap<String, Primitive>;

// Checks the `type` property during deserialization.
// See notes in TypesScript function `interfaceSchemaToEnum`
// and https://github.com/serde-rs/serde/issues/760.
#[macro_export]
macro_rules! impl_type {
    ($type:ident) => {
        impl $type {
            pub fn deserialize_type<'de, D>(d: D) -> Result<String, D::Error>
            where
                D: Deserializer<'de>,
            {
                let value = String::deserialize(d)?;
                if value != stringify!($type) {
                    return Err(de::Error::invalid_value(
                        de::Unexpected::Str(value.as_str()),
                        &stringify!($type),
                    ));
                }
                Ok(value)
            }
        }
    };
}
<commit_msg>feat(Rust bindings): Add type name and id traits<commit_after>pub use defaults::Defaults;
pub use serde::{de, Deserialize, Deserializer, Serialize};
pub use serde_json::Value;
pub use serde_with::skip_serializing_none;
use std::collections::BTreeMap;
pub use std::sync::Arc;

/// The set of primitive (non-Entity) node types
#[derive(Clone, Debug, Serialize, Deserialize)]
#[serde(untagged)]
pub enum Primitive {
    Null,
    Boolean(Boolean),
    Integer(Integer),
    Number(Number),
    String(String),
    Object(Object),
    Array(Array),
}

/// A boolean value
pub type Boolean = bool;

/// An integer value
///
/// Uses `i64` for maximum precision.
pub type Integer = i64;

/// A floating point value (a.k.a real number)
///
/// Uses `i64` for maximum precision.
pub type Number = f64;

/// An array value (a.k.a. vector)
pub type Array = Vec<Primitive>;

/// An object value (a.k.a map, dictionary)
///
/// Uses `BTreeMap` to preserve order.
pub type Object = BTreeMap<String, Primitive>;

/// A trait to retrieve the `type` of entities
/// Needs to be called `type_name` because `type` is a reserved word
pub trait TypeName {
    fn type_name(&self) -> String;
}

/// A trait to retrieve the `id` of entities
pub trait Id {
    fn id(&self) -> Option<String>;
}

/// Macro to implement functions and types for a schema type
#[macro_export]
macro_rules! impl_type {
    ($type:ident) => {
        impl $type {
            /// Deserialize the `type` property
            ///
            /// See notes in TypesScript function `interfaceSchemaToEnum`
            /// and https://github.com/serde-rs/serde/issues/760.
            pub fn deserialize_type<'de, D>(d: D) -> Result<String, D::Error>
            where
                D: Deserializer<'de>,
            {
                let value = String::deserialize(d)?;
                if value != stringify!($type) {
                    return Err(de::Error::invalid_value(
                        de::Unexpected::Str(value.as_str()),
                        &stringify!($type),
                    ));
                }
                Ok(value)
            }
        }

        impl TypeName for $type {
            fn type_name(&self) -> String {
                return self.type_.clone();
            }
        }

        impl Id for $type {
            fn id(&self) -> Option<String> {
                return self.id.clone();
            }
        }
    };
}
<|endoftext|>


<commit_before>use crate::{
    analysis::{
        imports::Imports,
        properties::{get_property_ref_modes, Property},
        rust_type::*,
    },
    config::{self, GObject},
    env::Env,
    library,
    traits::*,
};
use std::collections::HashSet;

pub fn analyze(
    env: &Env,
    props: &[library::Property],
    type_tid: library::TypeId,
    obj: &GObject,
    imports: &mut Imports,
) -> Vec<Property> {
    if !obj.generate_builder {
        return Vec::new();
    }

    let mut names = HashSet::<String>::new();
    let mut builder_properties = analyze_properties(env, props, obj, imports, &mut names);

    for &super_tid in env.class_hierarchy.supertypes(type_tid) {
        let type_ = env.type_(super_tid);

        let super_properties = match type_ {
            library::Type::Class(class) => &class.properties,
            library::Type::Interface(iface) => &iface.properties,
            _ => continue,
        };

        //Note: used config of child object to provide more control
        let new_builder_properties =
            analyze_properties(env, super_properties, obj, imports, &mut names);
        builder_properties.extend(new_builder_properties);
    }

    builder_properties
}

fn analyze_properties(
    env: &Env,
    props: &[library::Property],
    obj: &GObject,
    imports: &mut Imports,
    names: &mut HashSet<String>,
) -> Vec<Property> {
    let mut builder_properties = Vec::new();

    for prop in props {
        if names.contains(&prop.name) {
            continue;
        }
        let configured_properties = obj.properties.matched(&prop.name);
        if configured_properties.iter().any(|f| f.ignore) {
            continue;
        }

        if env.is_totally_deprecated(prop.deprecated_version) {
            continue;
        }
        let builder = analyze_property(env, prop, &configured_properties, imports);
        if let Some(builder) = builder {
            builder_properties.push(builder);
            names.insert(prop.name.clone());
        }
    }

    builder_properties
}

fn analyze_property(
    env: &Env,
    prop: &library::Property,
    configured_properties: &[&config::properties::Property],
    imports: &mut Imports,
) -> Option<Property> {
    let name = prop.name.clone();

    let prop_version = configured_properties
        .iter()
        .filter_map(|f| f.version)
        .min()
        .or(prop.version)
        .or_else(|| Some(env.config.min_cfg_version));

    let for_builder = prop.construct_only || prop.construct || prop.writable;
    if !for_builder {
        return None;
    }
    if let Ok(ref s) = used_rust_type(env, prop.typ, false) {
        if !s.contains("GString") {
            imports.add_used_type_with_version(s, prop_version);
        }
    }

    let (get_out_ref_mode, set_in_ref_mode, nullable) = get_property_ref_modes(env, prop);

    Some(Property {
        name: name.clone(),
        var_name: String::new(),
        typ: prop.typ,
        is_get: false,
        func_name: String::new(),
        nullable,
        get_out_ref_mode,
        set_in_ref_mode,
        set_bound: None,
        version: prop_version,
        deprecated_version: prop.deprecated_version,
    })
}
<commit_msg>Use parent config object for parent properties<commit_after>use crate::{
    analysis::{
        imports::Imports,
        properties::{get_property_ref_modes, Property},
        rust_type::*,
    },
    config::{self, GObject},
    env::Env,
    library,
    traits::*,
};
use std::collections::HashSet;

pub fn analyze(
    env: &Env,
    props: &[library::Property],
    type_tid: library::TypeId,
    obj: &GObject,
    imports: &mut Imports,
) -> Vec<Property> {
    if !obj.generate_builder {
        return Vec::new();
    }

    let mut names = HashSet::<String>::new();
    let mut builder_properties = analyze_properties(env, props, obj, imports, &mut names);

    for &super_tid in env.class_hierarchy.supertypes(type_tid) {
        let type_ = env.type_(super_tid);

        let super_properties = match type_ {
            library::Type::Class(class) => &class.properties,
            library::Type::Interface(iface) => &iface.properties,
            _ => continue,
        };
        let super_obj =
            if let Some(super_obj) = env.config.objects.get(&super_tid.full_name(&env.library)) {
                super_obj
            } else {
                continue;
            };

        let new_builder_properties =
            analyze_properties(env, super_properties, super_obj, imports, &mut names);
        builder_properties.extend(new_builder_properties);
    }

    builder_properties
}

fn analyze_properties(
    env: &Env,
    props: &[library::Property],
    obj: &GObject,
    imports: &mut Imports,
    names: &mut HashSet<String>,
) -> Vec<Property> {
    let mut builder_properties = Vec::new();

    for prop in props {
        if names.contains(&prop.name) {
            continue;
        }
        let configured_properties = obj.properties.matched(&prop.name);
        if configured_properties.iter().any(|f| f.ignore) {
            continue;
        }

        if env.is_totally_deprecated(prop.deprecated_version) {
            continue;
        }
        let builder = analyze_property(env, prop, &configured_properties, imports);
        if let Some(builder) = builder {
            builder_properties.push(builder);
            names.insert(prop.name.clone());
        }
    }

    builder_properties
}

fn analyze_property(
    env: &Env,
    prop: &library::Property,
    configured_properties: &[&config::properties::Property],
    imports: &mut Imports,
) -> Option<Property> {
    let name = prop.name.clone();

    let prop_version = configured_properties
        .iter()
        .filter_map(|f| f.version)
        .min()
        .or(prop.version)
        .or_else(|| Some(env.config.min_cfg_version));

    let for_builder = prop.construct_only || prop.construct || prop.writable;
    if !for_builder {
        return None;
    }
    if let Ok(ref s) = used_rust_type(env, prop.typ, false) {
        if !s.contains("GString") {
            imports.add_used_type_with_version(s, prop_version);
        }
    }

    let (get_out_ref_mode, set_in_ref_mode, nullable) = get_property_ref_modes(env, prop);

    Some(Property {
        name: name.clone(),
        var_name: String::new(),
        typ: prop.typ,
        is_get: false,
        func_name: String::new(),
        nullable,
        get_out_ref_mode,
        set_in_ref_mode,
        set_bound: None,
        version: prop_version,
        deprecated_version: prop.deprecated_version,
    })
}
<|endoftext|>