<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 bluetooth_blacklist::{Blacklist, uuid_is_blacklisted};
use dom::bindings::codegen::Bindings::BluetoothRemoteGATTServiceBinding;
use dom::bindings::codegen::Bindings::BluetoothRemoteGATTServiceBinding::BluetoothRemoteGATTServiceMethods;
use dom::bindings::error::Error::{Security, Type};
use dom::bindings::error::Fallible;
use dom::bindings::global::GlobalRef;
use dom::bindings::js::{JS, MutHeap, Root};
use dom::bindings::reflector::{Reflectable, Reflector, reflect_dom_object};
use dom::bindings::str::DOMString;
use dom::bluetoothcharacteristicproperties::BluetoothCharacteristicProperties;
use dom::bluetoothdevice::BluetoothDevice;
use dom::bluetoothremotegattcharacteristic::BluetoothRemoteGATTCharacteristic;
use dom::bluetoothuuid::{BluetoothCharacteristicUUID, BluetoothServiceUUID, BluetoothUUID};
use ipc_channel::ipc::{self, IpcSender};
use net_traits::bluetooth_thread::BluetoothMethodMsg;

// https://webbluetoothcg.github.io/web-bluetooth/#bluetoothremotegattservice
#[dom_struct]
pub struct BluetoothRemoteGATTService {
    reflector_: Reflector,
    device: MutHeap<JS<BluetoothDevice>>,
    uuid: DOMString,
    isPrimary: bool,
    instanceID: String,
}

impl BluetoothRemoteGATTService {
    pub fn new_inherited(device: &BluetoothDevice,
                         uuid: DOMString,
                         isPrimary: bool,
                         instanceID: String)
                         -> BluetoothRemoteGATTService {
        BluetoothRemoteGATTService {
            reflector_: Reflector::new(),
            device: MutHeap::new(device),
            uuid: uuid,
            isPrimary: isPrimary,
            instanceID: instanceID,
        }
    }

    pub fn new(global: GlobalRef,
               device: &BluetoothDevice,
               uuid: DOMString,
               isPrimary: bool,
               instanceID: String)
               -> Root<BluetoothRemoteGATTService> {
        reflect_dom_object(box BluetoothRemoteGATTService::new_inherited(device,
                                                                         uuid,
                                                                         isPrimary,
                                                                         instanceID),
                           global,
                           BluetoothRemoteGATTServiceBinding::Wrap)
    }

    fn get_bluetooth_thread(&self) -> IpcSender<BluetoothMethodMsg> {
        let global_root = self.global();
        let global_ref = global_root.r();
        global_ref.as_window().bluetooth_thread()
    }

    pub fn get_instance_id(&self) -> String {
        self.instanceID.clone()
    }
}

impl BluetoothRemoteGATTServiceMethods for BluetoothRemoteGATTService {
    // https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetoothremotegattservice-device
    fn Device(&self) -> Root<BluetoothDevice> {
        self.device.get()
    }

    // https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetoothremotegattservice-isprimary
    fn IsPrimary(&self) -> bool {
        self.isPrimary
    }

    // https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetoothremotegattservice-uuid
    fn Uuid(&self) -> DOMString {
        self.uuid.clone()
    }

    // https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetoothremotegattservice-getcharacteristic
    fn GetCharacteristic(&self,
                         characteristic: BluetoothCharacteristicUUID)
                         -> Fallible<Root<BluetoothRemoteGATTCharacteristic>> {
        let uuid = try!(BluetoothUUID::GetCharacteristic(self.global().r(), characteristic)).to_string();
        if uuid_is_blacklisted(uuid.as_ref(), Blacklist::All) {
            return Err(Security)
        }
        let (sender, receiver) = ipc::channel().unwrap();
        self.get_bluetooth_thread().send(
            BluetoothMethodMsg::GetCharacteristic(self.get_instance_id(), uuid, sender)).unwrap();
        let characteristic = receiver.recv().unwrap();
        match characteristic {
            Ok(characteristic) => {
                let properties = BluetoothCharacteristicProperties::new(self.global().r(),
                                                                        characteristic.broadcast,
                                                                        characteristic.read,
                                                                        characteristic.write_without_response,
                                                                        characteristic.write,
                                                                        characteristic.notify,
                                                                        characteristic.indicate,
                                                                        characteristic.authenticated_signed_writes,
                                                                        characteristic.reliable_write,
                                                                        characteristic.writable_auxiliaries);
                Ok(BluetoothRemoteGATTCharacteristic::new(self.global().r(),
                                                          self,
                                                          DOMString::from(characteristic.uuid),
                                                          &properties,
                                                          characteristic.instance_id))
            },
            Err(error) => {
                Err(Type(error))
            },
        }
    }

    // https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetoothremotegattservice-getcharacteristics
    fn GetCharacteristics(&self,
                          characteristic: Option<BluetoothCharacteristicUUID>)
                          -> Fallible<Vec<Root<BluetoothRemoteGATTCharacteristic>>> {
        let mut uuid: Option<String> = None;
        if let Some(c) = characteristic {
            uuid = Some(try!(BluetoothUUID::GetCharacteristic(self.global().r(), c)).to_string());
            if let Some(ref uuid) = uuid {
                if uuid_is_blacklisted(uuid.as_ref(), Blacklist::All) {
                    return Err(Security)
                }
            }
        };
        let mut characteristics = vec!();
        let (sender, receiver) = ipc::channel().unwrap();
        self.get_bluetooth_thread().send(
            BluetoothMethodMsg::GetCharacteristics(self.get_instance_id(), uuid, sender)).unwrap();
        let characteristics_vec = receiver.recv().unwrap();
        match characteristics_vec {
            Ok(characteristic_vec) => {
                for characteristic in characteristic_vec {
                    let properties = BluetoothCharacteristicProperties::new(self.global().r(),
                                                                            characteristic.broadcast,
                                                                            characteristic.read,
                                                                            characteristic.write_without_response,
                                                                            characteristic.write,
                                                                            characteristic.notify,
                                                                            characteristic.indicate,
                                                                            characteristic.authenticated_signed_writes,
                                                                            characteristic.reliable_write,
                                                                            characteristic.writable_auxiliaries);
                    characteristics.push(BluetoothRemoteGATTCharacteristic::new(self.global().r(),
                                                                                self,
                                                                                DOMString::from(characteristic.uuid),
                                                                                &properties,
                                                                                characteristic.instance_id));
                }
                Ok(characteristics)
            },
            Err(error) => {
                Err(Type(error))
            },
        }
    }

    // https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetoothremotegattservice-getincludedservice
    fn GetIncludedService(&self,
                          service: BluetoothServiceUUID)
                          -> Fallible<Root<BluetoothRemoteGATTService>> {
        let uuid = try!(BluetoothUUID::GetService(self.global().r(), service)).to_string();
        let (sender, receiver) = ipc::channel().unwrap();
        self.get_bluetooth_thread().send(
            BluetoothMethodMsg::GetIncludedService(self.get_instance_id(),
                                                   uuid,
                                                   sender)).unwrap();
        let service = receiver.recv().unwrap();
        match service {
            Ok(service) => {
                Ok(BluetoothRemoteGATTService::new(self.global().r(),
                                                   &self.device.get(),
                                                   DOMString::from(service.uuid),
                                                   service.is_primary,
                                                   service.instance_id))
            },
            Err(error) => {
                Err(Type(error))
            },
        }
    }

    // https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetoothremotegattservice-getincludedservices
    fn GetIncludedServices(&self,
                          service: Option<BluetoothServiceUUID>)
                          -> Fallible<Vec<Root<BluetoothRemoteGATTService>>> {
        let mut uuid: Option<String> = None;
        if let Some(s) = service {
            uuid = Some(try!(BluetoothUUID::GetService(self.global().r(), s)).to_string())
        };
        let (sender, receiver) = ipc::channel().unwrap();
        self.get_bluetooth_thread().send(
            BluetoothMethodMsg::GetIncludedServices(self.get_instance_id(),
                                                    uuid,
                                                    sender)).unwrap();
        let services_vec = receiver.recv().unwrap();
        match services_vec {
            Ok(service_vec) => {
                Ok(service_vec.into_iter()
                              .map(|service| BluetoothRemoteGATTService::new(self.global().r(),
                                                                             &self.device.get(),
                                                                             DOMString::from(service.uuid),
                                                                             service.is_primary,
                                                                             service.instance_id))
                              .collect())
            },
            Err(error) => {
                Err(Type(error))
            },
        }
    }
}
<commit_msg>Add blacklist check to GetIncludedSevice(s) functions<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 bluetooth_blacklist::{Blacklist, uuid_is_blacklisted};
use dom::bindings::codegen::Bindings::BluetoothRemoteGATTServiceBinding;
use dom::bindings::codegen::Bindings::BluetoothRemoteGATTServiceBinding::BluetoothRemoteGATTServiceMethods;
use dom::bindings::error::Error::{Security, Type};
use dom::bindings::error::Fallible;
use dom::bindings::global::GlobalRef;
use dom::bindings::js::{JS, MutHeap, Root};
use dom::bindings::reflector::{Reflectable, Reflector, reflect_dom_object};
use dom::bindings::str::DOMString;
use dom::bluetoothcharacteristicproperties::BluetoothCharacteristicProperties;
use dom::bluetoothdevice::BluetoothDevice;
use dom::bluetoothremotegattcharacteristic::BluetoothRemoteGATTCharacteristic;
use dom::bluetoothuuid::{BluetoothCharacteristicUUID, BluetoothServiceUUID, BluetoothUUID};
use ipc_channel::ipc::{self, IpcSender};
use net_traits::bluetooth_thread::BluetoothMethodMsg;

// https://webbluetoothcg.github.io/web-bluetooth/#bluetoothremotegattservice
#[dom_struct]
pub struct BluetoothRemoteGATTService {
    reflector_: Reflector,
    device: MutHeap<JS<BluetoothDevice>>,
    uuid: DOMString,
    isPrimary: bool,
    instanceID: String,
}

impl BluetoothRemoteGATTService {
    pub fn new_inherited(device: &BluetoothDevice,
                         uuid: DOMString,
                         isPrimary: bool,
                         instanceID: String)
                         -> BluetoothRemoteGATTService {
        BluetoothRemoteGATTService {
            reflector_: Reflector::new(),
            device: MutHeap::new(device),
            uuid: uuid,
            isPrimary: isPrimary,
            instanceID: instanceID,
        }
    }

    pub fn new(global: GlobalRef,
               device: &BluetoothDevice,
               uuid: DOMString,
               isPrimary: bool,
               instanceID: String)
               -> Root<BluetoothRemoteGATTService> {
        reflect_dom_object(box BluetoothRemoteGATTService::new_inherited(device,
                                                                         uuid,
                                                                         isPrimary,
                                                                         instanceID),
                           global,
                           BluetoothRemoteGATTServiceBinding::Wrap)
    }

    fn get_bluetooth_thread(&self) -> IpcSender<BluetoothMethodMsg> {
        let global_root = self.global();
        let global_ref = global_root.r();
        global_ref.as_window().bluetooth_thread()
    }

    pub fn get_instance_id(&self) -> String {
        self.instanceID.clone()
    }
}

impl BluetoothRemoteGATTServiceMethods for BluetoothRemoteGATTService {
    // https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetoothremotegattservice-device
    fn Device(&self) -> Root<BluetoothDevice> {
        self.device.get()
    }

    // https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetoothremotegattservice-isprimary
    fn IsPrimary(&self) -> bool {
        self.isPrimary
    }

    // https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetoothremotegattservice-uuid
    fn Uuid(&self) -> DOMString {
        self.uuid.clone()
    }

    // https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetoothremotegattservice-getcharacteristic
    fn GetCharacteristic(&self,
                         characteristic: BluetoothCharacteristicUUID)
                         -> Fallible<Root<BluetoothRemoteGATTCharacteristic>> {
        let uuid = try!(BluetoothUUID::GetCharacteristic(self.global().r(), characteristic)).to_string();
        if uuid_is_blacklisted(uuid.as_ref(), Blacklist::All) {
            return Err(Security)
        }
        let (sender, receiver) = ipc::channel().unwrap();
        self.get_bluetooth_thread().send(
            BluetoothMethodMsg::GetCharacteristic(self.get_instance_id(), uuid, sender)).unwrap();
        let characteristic = receiver.recv().unwrap();
        match characteristic {
            Ok(characteristic) => {
                let properties = BluetoothCharacteristicProperties::new(self.global().r(),
                                                                        characteristic.broadcast,
                                                                        characteristic.read,
                                                                        characteristic.write_without_response,
                                                                        characteristic.write,
                                                                        characteristic.notify,
                                                                        characteristic.indicate,
                                                                        characteristic.authenticated_signed_writes,
                                                                        characteristic.reliable_write,
                                                                        characteristic.writable_auxiliaries);
                Ok(BluetoothRemoteGATTCharacteristic::new(self.global().r(),
                                                          self,
                                                          DOMString::from(characteristic.uuid),
                                                          &properties,
                                                          characteristic.instance_id))
            },
            Err(error) => {
                Err(Type(error))
            },
        }
    }

    // https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetoothremotegattservice-getcharacteristics
    fn GetCharacteristics(&self,
                          characteristic: Option<BluetoothCharacteristicUUID>)
                          -> Fallible<Vec<Root<BluetoothRemoteGATTCharacteristic>>> {
        let mut uuid: Option<String> = None;
        if let Some(c) = characteristic {
            uuid = Some(try!(BluetoothUUID::GetCharacteristic(self.global().r(), c)).to_string());
            if let Some(ref uuid) = uuid {
                if uuid_is_blacklisted(uuid.as_ref(), Blacklist::All) {
                    return Err(Security)
                }
            }
        };
        let mut characteristics = vec!();
        let (sender, receiver) = ipc::channel().unwrap();
        self.get_bluetooth_thread().send(
            BluetoothMethodMsg::GetCharacteristics(self.get_instance_id(), uuid, sender)).unwrap();
        let characteristics_vec = receiver.recv().unwrap();
        match characteristics_vec {
            Ok(characteristic_vec) => {
                for characteristic in characteristic_vec {
                    let properties = BluetoothCharacteristicProperties::new(self.global().r(),
                                                                            characteristic.broadcast,
                                                                            characteristic.read,
                                                                            characteristic.write_without_response,
                                                                            characteristic.write,
                                                                            characteristic.notify,
                                                                            characteristic.indicate,
                                                                            characteristic.authenticated_signed_writes,
                                                                            characteristic.reliable_write,
                                                                            characteristic.writable_auxiliaries);
                    characteristics.push(BluetoothRemoteGATTCharacteristic::new(self.global().r(),
                                                                                self,
                                                                                DOMString::from(characteristic.uuid),
                                                                                &properties,
                                                                                characteristic.instance_id));
                }
                Ok(characteristics)
            },
            Err(error) => {
                Err(Type(error))
            },
        }
    }

    // https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetoothremotegattservice-getincludedservice
    fn GetIncludedService(&self,
                          service: BluetoothServiceUUID)
                          -> Fallible<Root<BluetoothRemoteGATTService>> {
        let uuid = try!(BluetoothUUID::GetService(self.global().r(), service)).to_string();
        if uuid_is_blacklisted(uuid.as_ref(), Blacklist::All) {
            return Err(Security)
        }
        let (sender, receiver) = ipc::channel().unwrap();
        self.get_bluetooth_thread().send(
            BluetoothMethodMsg::GetIncludedService(self.get_instance_id(),
                                                   uuid,
                                                   sender)).unwrap();
        let service = receiver.recv().unwrap();
        match service {
            Ok(service) => {
                Ok(BluetoothRemoteGATTService::new(self.global().r(),
                                                   &self.device.get(),
                                                   DOMString::from(service.uuid),
                                                   service.is_primary,
                                                   service.instance_id))
            },
            Err(error) => {
                Err(Type(error))
            },
        }
    }

    // https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetoothremotegattservice-getincludedservices
    fn GetIncludedServices(&self,
                          service: Option<BluetoothServiceUUID>)
                          -> Fallible<Vec<Root<BluetoothRemoteGATTService>>> {
        let mut uuid: Option<String> = None;
        if let Some(s) = service {
            uuid = Some(try!(BluetoothUUID::GetService(self.global().r(), s)).to_string());
            if let Some(ref uuid) = uuid {
                if uuid_is_blacklisted(uuid.as_ref(), Blacklist::All) {
                    return Err(Security)
                }
            }
        };
        let (sender, receiver) = ipc::channel().unwrap();
        self.get_bluetooth_thread().send(
            BluetoothMethodMsg::GetIncludedServices(self.get_instance_id(),
                                                    uuid,
                                                    sender)).unwrap();
        let services_vec = receiver.recv().unwrap();
        match services_vec {
            Ok(service_vec) => {
                Ok(service_vec.into_iter()
                              .map(|service| BluetoothRemoteGATTService::new(self.global().r(),
                                                                             &self.device.get(),
                                                                             DOMString::from(service.uuid),
                                                                             service.is_primary,
                                                                             service.instance_id))
                              .collect())
            },
            Err(error) => {
                Err(Type(error))
            },
        }
    }
}
<|endoftext|>


<commit_before>//! HIR for references to types. Paths in these are not yet resolved. They can
//! be directly created from an ast::TypeRef, without further queries.

use hir_expand::{name::Name, AstId, InFile};
use std::convert::TryInto;
use syntax::{ast, AstNode};

use crate::{body::LowerCtx, intern::Interned, path::Path};

#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
pub enum Mutability {
    Shared,
    Mut,
}

impl Mutability {
    pub fn from_mutable(mutable: bool) -> Mutability {
        if mutable {
            Mutability::Mut
        } else {
            Mutability::Shared
        }
    }

    pub fn as_keyword_for_ref(self) -> &'static str {
        match self {
            Mutability::Shared => "",
            Mutability::Mut => "mut ",
        }
    }

    pub fn as_keyword_for_ptr(self) -> &'static str {
        match self {
            Mutability::Shared => "const ",
            Mutability::Mut => "mut ",
        }
    }
}

#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
pub enum Rawness {
    RawPtr,
    Ref,
}

impl Rawness {
    pub fn from_raw(is_raw: bool) -> Rawness {
        if is_raw {
            Rawness::RawPtr
        } else {
            Rawness::Ref
        }
    }
}

#[derive(Clone, PartialEq, Eq, Hash, Debug)]
pub struct TraitRef {
    pub path: Path,
}

impl TraitRef {
    /// Converts an `ast::PathType` to a `hir::TraitRef`.
    pub(crate) fn from_ast(ctx: &LowerCtx, node: ast::Type) -> Option<Self> {
        // FIXME: Use `Path::from_src`
        match node {
            ast::Type::PathType(path) => {
                path.path().and_then(|it| ctx.lower_path(it)).map(|path| TraitRef { path })
            }
            _ => None,
        }
    }
}

/// Compare ty::Ty
///
/// Note: Most users of `TypeRef` that end up in the salsa database intern it using
/// `Interned<TypeRef>` to save space. But notably, nested `TypeRef`s are not interned, since that
/// does not seem to save any noticeable amount of memory.
#[derive(Clone, PartialEq, Eq, Hash, Debug)]
pub enum TypeRef {
    Never,
    Placeholder,
    Tuple(Vec<TypeRef>),
    Path(Path),
    RawPtr(Box<TypeRef>, Mutability),
    Reference(Box<TypeRef>, Option<LifetimeRef>, Mutability),
    // FIXME: for full const generics, the latter element (length) here is going to have to be an
    // expression that is further lowered later in hir_ty.
    Array(Box<TypeRef>, ConstScalar),
    Slice(Box<TypeRef>),
    /// A fn pointer. Last element of the vector is the return type.
    Fn(Vec<(Option<String>, TypeRef)>, bool /*varargs*/),
    // For
    ImplTrait(Vec<Interned<TypeBound>>),
    DynTrait(Vec<Interned<TypeBound>>),
    Macro(AstId<ast::MacroCall>),
    Error,
}

#[derive(Clone, PartialEq, Eq, Hash, Debug)]
pub struct LifetimeRef {
    pub name: Name,
}

impl LifetimeRef {
    pub(crate) fn new_name(name: Name) -> Self {
        LifetimeRef { name }
    }

    pub(crate) fn new(lifetime: &ast::Lifetime) -> Self {
        LifetimeRef { name: Name::new_lifetime(lifetime) }
    }

    pub fn missing() -> LifetimeRef {
        LifetimeRef { name: Name::missing() }
    }
}

#[derive(Clone, PartialEq, Eq, Hash, Debug)]
pub enum TypeBound {
    Path(Path, TraitBoundModifier),
    ForLifetime(Box<[Name]>, Path),
    Lifetime(LifetimeRef),
    Error,
}

/// A modifier on a bound, currently this is only used for `?Sized`, where the
/// modifier is `Maybe`.
#[derive(Clone, PartialEq, Eq, Hash, Debug)]
pub enum TraitBoundModifier {
    None,
    Maybe,
}

impl TypeRef {
    /// Converts an `ast::TypeRef` to a `hir::TypeRef`.
    pub fn from_ast(ctx: &LowerCtx, node: ast::Type) -> Self {
        match node {
            ast::Type::ParenType(inner) => TypeRef::from_ast_opt(ctx, inner.ty()),
            ast::Type::TupleType(inner) => {
                TypeRef::Tuple(inner.fields().map(|it| TypeRef::from_ast(ctx, it)).collect())
            }
            ast::Type::NeverType(..) => TypeRef::Never,
            ast::Type::PathType(inner) => {
                // FIXME: Use `Path::from_src`
                inner
                    .path()
                    .and_then(|it| ctx.lower_path(it))
                    .map(TypeRef::Path)
                    .unwrap_or(TypeRef::Error)
            }
            ast::Type::PtrType(inner) => {
                let inner_ty = TypeRef::from_ast_opt(ctx, inner.ty());
                let mutability = Mutability::from_mutable(inner.mut_token().is_some());
                TypeRef::RawPtr(Box::new(inner_ty), mutability)
            }
            ast::Type::ArrayType(inner) => {
                // FIXME: This is a hack. We should probably reuse the machinery of
                // `hir_def::body::lower` to lower this into an `Expr` and then evaluate it at the
                // `hir_ty` level, which would allow knowing the type of:
                // let v: [u8; 2 + 2] = [0u8; 4];
                let len = inner
                    .expr()
                    .map(ConstScalar::usize_from_literal_expr)
                    .unwrap_or(ConstScalar::Unknown);

                TypeRef::Array(Box::new(TypeRef::from_ast_opt(ctx, inner.ty())), len)
            }
            ast::Type::SliceType(inner) => {
                TypeRef::Slice(Box::new(TypeRef::from_ast_opt(ctx, inner.ty())))
            }
            ast::Type::RefType(inner) => {
                let inner_ty = TypeRef::from_ast_opt(ctx, inner.ty());
                let lifetime = inner.lifetime().map(|lt| LifetimeRef::new(&lt));
                let mutability = Mutability::from_mutable(inner.mut_token().is_some());
                TypeRef::Reference(Box::new(inner_ty), lifetime, mutability)
            }
            ast::Type::InferType(_inner) => TypeRef::Placeholder,
            ast::Type::FnPtrType(inner) => {
                let ret_ty = inner
                    .ret_type()
                    .and_then(|rt| rt.ty())
                    .map(|it| TypeRef::from_ast(ctx, it))
                    .unwrap_or_else(|| TypeRef::Tuple(Vec::new()));
                let mut is_varargs = false;
                let mut params = if let Some(pl) = inner.param_list() {
                    if let Some(param) = pl.params().last() {
                        is_varargs = param.dotdotdot_token().is_some();
                    }

                    pl.params()
                        .map(|p| (p.pat(), p.ty()))
                        .map(|it| {
                            let type_ref = TypeRef::from_ast_opt(ctx, it.1);
                            let name = if it.0.is_some() {
                                Some(it.0.unwrap().syntax().text().to_string())
                            } else {
                                None
                            };
                            (name, type_ref)
                        })
                        .collect()
                } else {
                    Vec::new()
                };
                params.push((None, ret_ty));
                TypeRef::Fn(params, is_varargs)
            }
            // for types are close enough for our purposes to the inner type for now...
            ast::Type::ForType(inner) => TypeRef::from_ast_opt(ctx, inner.ty()),
            ast::Type::ImplTraitType(inner) => {
                TypeRef::ImplTrait(type_bounds_from_ast(ctx, inner.type_bound_list()))
            }
            ast::Type::DynTraitType(inner) => {
                TypeRef::DynTrait(type_bounds_from_ast(ctx, inner.type_bound_list()))
            }
            ast::Type::MacroType(mt) => match mt.macro_call() {
                Some(mc) => ctx
                    .ast_id(&mc)
                    .map(|mc| TypeRef::Macro(InFile::new(ctx.file_id(), mc)))
                    .unwrap_or(TypeRef::Error),
                None => TypeRef::Error,
            },
        }
    }

    pub(crate) fn from_ast_opt(ctx: &LowerCtx, node: Option<ast::Type>) -> Self {
        match node {
            Some(node) => TypeRef::from_ast(ctx, node),
            None => TypeRef::Error,
        }
    }

    pub(crate) fn unit() -> TypeRef {
        TypeRef::Tuple(Vec::new())
    }

    pub fn walk(&self, f: &mut impl FnMut(&TypeRef)) {
        go(self, f);

        fn go(type_ref: &TypeRef, f: &mut impl FnMut(&TypeRef)) {
            f(type_ref);
            match type_ref {
                TypeRef::Fn(params, _) => {
                    params.iter().for_each(|(_, param_type)| go(&param_type, f))
                }
                TypeRef::Tuple(types) => types.iter().for_each(|t| go(t, f)),
                TypeRef::RawPtr(type_ref, _)
                | TypeRef::Reference(type_ref, ..)
                | TypeRef::Array(type_ref, _)
                | TypeRef::Slice(type_ref) => go(type_ref, f),
                TypeRef::ImplTrait(bounds) | TypeRef::DynTrait(bounds) => {
                    for bound in bounds {
                        match bound.as_ref() {
                            TypeBound::Path(path, _) | TypeBound::ForLifetime(_, path) => {
                                go_path(path, f)
                            }
                            TypeBound::Lifetime(_) | TypeBound::Error => (),
                        }
                    }
                }
                TypeRef::Path(path) => go_path(path, f),
                TypeRef::Never | TypeRef::Placeholder | TypeRef::Macro(_) | TypeRef::Error => {}
            };
        }

        fn go_path(path: &Path, f: &mut impl FnMut(&TypeRef)) {
            if let Some(type_ref) = path.type_anchor() {
                go(type_ref, f);
            }
            for segment in path.segments().iter() {
                if let Some(args_and_bindings) = segment.args_and_bindings {
                    for arg in &args_and_bindings.args {
                        match arg {
                            crate::path::GenericArg::Type(type_ref) => {
                                go(type_ref, f);
                            }
                            crate::path::GenericArg::Lifetime(_) => {}
                        }
                    }
                    for binding in &args_and_bindings.bindings {
                        if let Some(type_ref) = &binding.type_ref {
                            go(type_ref, f);
                        }
                        for bound in &binding.bounds {
                            match bound.as_ref() {
                                TypeBound::Path(path, _) | TypeBound::ForLifetime(_, path) => {
                                    go_path(path, f)
                                }
                                TypeBound::Lifetime(_) | TypeBound::Error => (),
                            }
                        }
                    }
                }
            }
        }
    }
}

pub(crate) fn type_bounds_from_ast(
    lower_ctx: &LowerCtx,
    type_bounds_opt: Option<ast::TypeBoundList>,
) -> Vec<Interned<TypeBound>> {
    if let Some(type_bounds) = type_bounds_opt {
        type_bounds.bounds().map(|it| Interned::new(TypeBound::from_ast(lower_ctx, it))).collect()
    } else {
        vec![]
    }
}

impl TypeBound {
    pub(crate) fn from_ast(ctx: &LowerCtx, node: ast::TypeBound) -> Self {
        let lower_path_type = |path_type: ast::PathType| ctx.lower_path(path_type.path()?);

        match node.kind() {
            ast::TypeBoundKind::PathType(path_type) => {
                let m = match node.question_mark_token() {
                    Some(_) => TraitBoundModifier::Maybe,
                    None => TraitBoundModifier::None,
                };
                lower_path_type(path_type)
                    .map(|p| TypeBound::Path(p, m))
                    .unwrap_or(TypeBound::Error)
            }
            ast::TypeBoundKind::ForType(for_type) => {
                let lt_refs = match for_type.generic_param_list() {
                    Some(gpl) => gpl
                        .lifetime_params()
                        .flat_map(|lp| lp.lifetime().map(|lt| Name::new_lifetime(&lt)))
                        .collect(),
                    None => Box::default(),
                };
                let path = for_type.ty().and_then(|ty| match ty {
                    ast::Type::PathType(path_type) => lower_path_type(path_type),
                    _ => None,
                });
                match path {
                    Some(p) => TypeBound::ForLifetime(lt_refs, p),
                    None => TypeBound::Error,
                }
            }
            ast::TypeBoundKind::Lifetime(lifetime) => {
                TypeBound::Lifetime(LifetimeRef::new(&lifetime))
            }
        }
    }

    pub fn as_path(&self) -> Option<(&Path, &TraitBoundModifier)> {
        match self {
            TypeBound::Path(p, m) => Some((p, m)),
            TypeBound::ForLifetime(_, p) => Some((p, &TraitBoundModifier::None)),
            TypeBound::Lifetime(_) | TypeBound::Error => None,
        }
    }
}

/// A concrete constant value
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum ConstScalar {
    // for now, we only support the trivial case of constant evaluating the length of an array
    // Note that this is u64 because the target usize may be bigger than our usize
    Usize(u64),

    /// Case of an unknown value that rustc might know but we don't
    // FIXME: this is a hack to get around chalk not being able to represent unevaluatable
    // constants
    // https://github.com/rust-analyzer/rust-analyzer/pull/8813#issuecomment-840679177
    // https://rust-lang.zulipchat.com/#narrow/stream/144729-wg-traits/topic/Handling.20non.20evaluatable.20constants'.20equality/near/238386348
    Unknown,
}

impl std::fmt::Display for ConstScalar {
    fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
        match self {
            ConstScalar::Usize(us) => write!(fmt, "{}", us),
            ConstScalar::Unknown => write!(fmt, "_"),
        }
    }
}

impl ConstScalar {
    /// Gets a target usize out of the ConstScalar
    pub fn as_usize(&self) -> Option<u64> {
        match self {
            &ConstScalar::Usize(us) => Some(us),
            _ => None,
        }
    }

    // FIXME: as per the comments on `TypeRef::Array`, this evaluation should not happen at this
    // parse stage.
    fn usize_from_literal_expr(expr: ast::Expr) -> ConstScalar {
        match expr {
            ast::Expr::Literal(lit) => {
                let lkind = lit.kind();
                match lkind {
                    ast::LiteralKind::IntNumber(num)
                        if num.suffix() == None || num.suffix() == Some("usize") =>
                    {
                        num.value().and_then(|v| v.try_into().ok())
                    }
                    _ => None,
                }
            }
            _ => None,
        }
        .map(ConstScalar::Usize)
        .unwrap_or(ConstScalar::Unknown)
    }
}
<commit_msg>use Name instead of String<commit_after>//! HIR for references to types. Paths in these are not yet resolved. They can
//! be directly created from an ast::TypeRef, without further queries.

use hir_expand::{
    name::{AsName, Name},
    AstId, InFile,
};
use std::convert::TryInto;
use syntax::ast::{self, HasName};

use crate::{body::LowerCtx, intern::Interned, path::Path};

#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
pub enum Mutability {
    Shared,
    Mut,
}

impl Mutability {
    pub fn from_mutable(mutable: bool) -> Mutability {
        if mutable {
            Mutability::Mut
        } else {
            Mutability::Shared
        }
    }

    pub fn as_keyword_for_ref(self) -> &'static str {
        match self {
            Mutability::Shared => "",
            Mutability::Mut => "mut ",
        }
    }

    pub fn as_keyword_for_ptr(self) -> &'static str {
        match self {
            Mutability::Shared => "const ",
            Mutability::Mut => "mut ",
        }
    }
}

#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
pub enum Rawness {
    RawPtr,
    Ref,
}

impl Rawness {
    pub fn from_raw(is_raw: bool) -> Rawness {
        if is_raw {
            Rawness::RawPtr
        } else {
            Rawness::Ref
        }
    }
}

#[derive(Clone, PartialEq, Eq, Hash, Debug)]
pub struct TraitRef {
    pub path: Path,
}

impl TraitRef {
    /// Converts an `ast::PathType` to a `hir::TraitRef`.
    pub(crate) fn from_ast(ctx: &LowerCtx, node: ast::Type) -> Option<Self> {
        // FIXME: Use `Path::from_src`
        match node {
            ast::Type::PathType(path) => {
                path.path().and_then(|it| ctx.lower_path(it)).map(|path| TraitRef { path })
            }
            _ => None,
        }
    }
}

/// Compare ty::Ty
///
/// Note: Most users of `TypeRef` that end up in the salsa database intern it using
/// `Interned<TypeRef>` to save space. But notably, nested `TypeRef`s are not interned, since that
/// does not seem to save any noticeable amount of memory.
#[derive(Clone, PartialEq, Eq, Hash, Debug)]
pub enum TypeRef {
    Never,
    Placeholder,
    Tuple(Vec<TypeRef>),
    Path(Path),
    RawPtr(Box<TypeRef>, Mutability),
    Reference(Box<TypeRef>, Option<LifetimeRef>, Mutability),
    // FIXME: for full const generics, the latter element (length) here is going to have to be an
    // expression that is further lowered later in hir_ty.
    Array(Box<TypeRef>, ConstScalar),
    Slice(Box<TypeRef>),
    /// A fn pointer. Last element of the vector is the return type.
    Fn(Vec<(Option<Name>, TypeRef)>, bool /*varargs*/),
    // For
    ImplTrait(Vec<Interned<TypeBound>>),
    DynTrait(Vec<Interned<TypeBound>>),
    Macro(AstId<ast::MacroCall>),
    Error,
}

#[derive(Clone, PartialEq, Eq, Hash, Debug)]
pub struct LifetimeRef {
    pub name: Name,
}

impl LifetimeRef {
    pub(crate) fn new_name(name: Name) -> Self {
        LifetimeRef { name }
    }

    pub(crate) fn new(lifetime: &ast::Lifetime) -> Self {
        LifetimeRef { name: Name::new_lifetime(lifetime) }
    }

    pub fn missing() -> LifetimeRef {
        LifetimeRef { name: Name::missing() }
    }
}

#[derive(Clone, PartialEq, Eq, Hash, Debug)]
pub enum TypeBound {
    Path(Path, TraitBoundModifier),
    ForLifetime(Box<[Name]>, Path),
    Lifetime(LifetimeRef),
    Error,
}

/// A modifier on a bound, currently this is only used for `?Sized`, where the
/// modifier is `Maybe`.
#[derive(Clone, PartialEq, Eq, Hash, Debug)]
pub enum TraitBoundModifier {
    None,
    Maybe,
}

impl TypeRef {
    /// Converts an `ast::TypeRef` to a `hir::TypeRef`.
    pub fn from_ast(ctx: &LowerCtx, node: ast::Type) -> Self {
        match node {
            ast::Type::ParenType(inner) => TypeRef::from_ast_opt(ctx, inner.ty()),
            ast::Type::TupleType(inner) => {
                TypeRef::Tuple(inner.fields().map(|it| TypeRef::from_ast(ctx, it)).collect())
            }
            ast::Type::NeverType(..) => TypeRef::Never,
            ast::Type::PathType(inner) => {
                // FIXME: Use `Path::from_src`
                inner
                    .path()
                    .and_then(|it| ctx.lower_path(it))
                    .map(TypeRef::Path)
                    .unwrap_or(TypeRef::Error)
            }
            ast::Type::PtrType(inner) => {
                let inner_ty = TypeRef::from_ast_opt(ctx, inner.ty());
                let mutability = Mutability::from_mutable(inner.mut_token().is_some());
                TypeRef::RawPtr(Box::new(inner_ty), mutability)
            }
            ast::Type::ArrayType(inner) => {
                // FIXME: This is a hack. We should probably reuse the machinery of
                // `hir_def::body::lower` to lower this into an `Expr` and then evaluate it at the
                // `hir_ty` level, which would allow knowing the type of:
                // let v: [u8; 2 + 2] = [0u8; 4];
                let len = inner
                    .expr()
                    .map(ConstScalar::usize_from_literal_expr)
                    .unwrap_or(ConstScalar::Unknown);

                TypeRef::Array(Box::new(TypeRef::from_ast_opt(ctx, inner.ty())), len)
            }
            ast::Type::SliceType(inner) => {
                TypeRef::Slice(Box::new(TypeRef::from_ast_opt(ctx, inner.ty())))
            }
            ast::Type::RefType(inner) => {
                let inner_ty = TypeRef::from_ast_opt(ctx, inner.ty());
                let lifetime = inner.lifetime().map(|lt| LifetimeRef::new(&lt));
                let mutability = Mutability::from_mutable(inner.mut_token().is_some());
                TypeRef::Reference(Box::new(inner_ty), lifetime, mutability)
            }
            ast::Type::InferType(_inner) => TypeRef::Placeholder,
            ast::Type::FnPtrType(inner) => {
                let ret_ty = inner
                    .ret_type()
                    .and_then(|rt| rt.ty())
                    .map(|it| TypeRef::from_ast(ctx, it))
                    .unwrap_or_else(|| TypeRef::Tuple(Vec::new()));
                let mut is_varargs = false;
                let mut params = if let Some(pl) = inner.param_list() {
                    if let Some(param) = pl.params().last() {
                        is_varargs = param.dotdotdot_token().is_some();
                    }

                    pl.params()
                        .map(|p| (p.pat(), p.ty()))
                        .map(|it| {
                            let type_ref = TypeRef::from_ast_opt(ctx, it.1);
                            let name = match it.0 {
                                Some(ast::Pat::IdentPat(it)) => Some(
                                    it.name().map(|nr| nr.as_name()).unwrap_or_else(Name::missing),
                                ),
                                _ => None,
                            };
                            (name, type_ref)
                        })
                        .collect()
                } else {
                    Vec::new()
                };
                params.push((None, ret_ty));
                TypeRef::Fn(params, is_varargs)
            }
            // for types are close enough for our purposes to the inner type for now...
            ast::Type::ForType(inner) => TypeRef::from_ast_opt(ctx, inner.ty()),
            ast::Type::ImplTraitType(inner) => {
                TypeRef::ImplTrait(type_bounds_from_ast(ctx, inner.type_bound_list()))
            }
            ast::Type::DynTraitType(inner) => {
                TypeRef::DynTrait(type_bounds_from_ast(ctx, inner.type_bound_list()))
            }
            ast::Type::MacroType(mt) => match mt.macro_call() {
                Some(mc) => ctx
                    .ast_id(&mc)
                    .map(|mc| TypeRef::Macro(InFile::new(ctx.file_id(), mc)))
                    .unwrap_or(TypeRef::Error),
                None => TypeRef::Error,
            },
        }
    }

    pub(crate) fn from_ast_opt(ctx: &LowerCtx, node: Option<ast::Type>) -> Self {
        match node {
            Some(node) => TypeRef::from_ast(ctx, node),
            None => TypeRef::Error,
        }
    }

    pub(crate) fn unit() -> TypeRef {
        TypeRef::Tuple(Vec::new())
    }

    pub fn walk(&self, f: &mut impl FnMut(&TypeRef)) {
        go(self, f);

        fn go(type_ref: &TypeRef, f: &mut impl FnMut(&TypeRef)) {
            f(type_ref);
            match type_ref {
                TypeRef::Fn(params, _) => {
                    params.iter().for_each(|(_, param_type)| go(&param_type, f))
                }
                TypeRef::Tuple(types) => types.iter().for_each(|t| go(t, f)),
                TypeRef::RawPtr(type_ref, _)
                | TypeRef::Reference(type_ref, ..)
                | TypeRef::Array(type_ref, _)
                | TypeRef::Slice(type_ref) => go(type_ref, f),
                TypeRef::ImplTrait(bounds) | TypeRef::DynTrait(bounds) => {
                    for bound in bounds {
                        match bound.as_ref() {
                            TypeBound::Path(path, _) | TypeBound::ForLifetime(_, path) => {
                                go_path(path, f)
                            }
                            TypeBound::Lifetime(_) | TypeBound::Error => (),
                        }
                    }
                }
                TypeRef::Path(path) => go_path(path, f),
                TypeRef::Never | TypeRef::Placeholder | TypeRef::Macro(_) | TypeRef::Error => {}
            };
        }

        fn go_path(path: &Path, f: &mut impl FnMut(&TypeRef)) {
            if let Some(type_ref) = path.type_anchor() {
                go(type_ref, f);
            }
            for segment in path.segments().iter() {
                if let Some(args_and_bindings) = segment.args_and_bindings {
                    for arg in &args_and_bindings.args {
                        match arg {
                            crate::path::GenericArg::Type(type_ref) => {
                                go(type_ref, f);
                            }
                            crate::path::GenericArg::Lifetime(_) => {}
                        }
                    }
                    for binding in &args_and_bindings.bindings {
                        if let Some(type_ref) = &binding.type_ref {
                            go(type_ref, f);
                        }
                        for bound in &binding.bounds {
                            match bound.as_ref() {
                                TypeBound::Path(path, _) | TypeBound::ForLifetime(_, path) => {
                                    go_path(path, f)
                                }
                                TypeBound::Lifetime(_) | TypeBound::Error => (),
                            }
                        }
                    }
                }
            }
        }
    }
}

pub(crate) fn type_bounds_from_ast(
    lower_ctx: &LowerCtx,
    type_bounds_opt: Option<ast::TypeBoundList>,
) -> Vec<Interned<TypeBound>> {
    if let Some(type_bounds) = type_bounds_opt {
        type_bounds.bounds().map(|it| Interned::new(TypeBound::from_ast(lower_ctx, it))).collect()
    } else {
        vec![]
    }
}

impl TypeBound {
    pub(crate) fn from_ast(ctx: &LowerCtx, node: ast::TypeBound) -> Self {
        let lower_path_type = |path_type: ast::PathType| ctx.lower_path(path_type.path()?);

        match node.kind() {
            ast::TypeBoundKind::PathType(path_type) => {
                let m = match node.question_mark_token() {
                    Some(_) => TraitBoundModifier::Maybe,
                    None => TraitBoundModifier::None,
                };
                lower_path_type(path_type)
                    .map(|p| TypeBound::Path(p, m))
                    .unwrap_or(TypeBound::Error)
            }
            ast::TypeBoundKind::ForType(for_type) => {
                let lt_refs = match for_type.generic_param_list() {
                    Some(gpl) => gpl
                        .lifetime_params()
                        .flat_map(|lp| lp.lifetime().map(|lt| Name::new_lifetime(&lt)))
                        .collect(),
                    None => Box::default(),
                };
                let path = for_type.ty().and_then(|ty| match ty {
                    ast::Type::PathType(path_type) => lower_path_type(path_type),
                    _ => None,
                });
                match path {
                    Some(p) => TypeBound::ForLifetime(lt_refs, p),
                    None => TypeBound::Error,
                }
            }
            ast::TypeBoundKind::Lifetime(lifetime) => {
                TypeBound::Lifetime(LifetimeRef::new(&lifetime))
            }
        }
    }

    pub fn as_path(&self) -> Option<(&Path, &TraitBoundModifier)> {
        match self {
            TypeBound::Path(p, m) => Some((p, m)),
            TypeBound::ForLifetime(_, p) => Some((p, &TraitBoundModifier::None)),
            TypeBound::Lifetime(_) | TypeBound::Error => None,
        }
    }
}

/// A concrete constant value
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum ConstScalar {
    // for now, we only support the trivial case of constant evaluating the length of an array
    // Note that this is u64 because the target usize may be bigger than our usize
    Usize(u64),

    /// Case of an unknown value that rustc might know but we don't
    // FIXME: this is a hack to get around chalk not being able to represent unevaluatable
    // constants
    // https://github.com/rust-analyzer/rust-analyzer/pull/8813#issuecomment-840679177
    // https://rust-lang.zulipchat.com/#narrow/stream/144729-wg-traits/topic/Handling.20non.20evaluatable.20constants'.20equality/near/238386348
    Unknown,
}

impl std::fmt::Display for ConstScalar {
    fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
        match self {
            ConstScalar::Usize(us) => write!(fmt, "{}", us),
            ConstScalar::Unknown => write!(fmt, "_"),
        }
    }
}

impl ConstScalar {
    /// Gets a target usize out of the ConstScalar
    pub fn as_usize(&self) -> Option<u64> {
        match self {
            &ConstScalar::Usize(us) => Some(us),
            _ => None,
        }
    }

    // FIXME: as per the comments on `TypeRef::Array`, this evaluation should not happen at this
    // parse stage.
    fn usize_from_literal_expr(expr: ast::Expr) -> ConstScalar {
        match expr {
            ast::Expr::Literal(lit) => {
                let lkind = lit.kind();
                match lkind {
                    ast::LiteralKind::IntNumber(num)
                        if num.suffix() == None || num.suffix() == Some("usize") =>
                    {
                        num.value().and_then(|v| v.try_into().ok())
                    }
                    _ => None,
                }
            }
            _ => None,
        }
        .map(ConstScalar::Usize)
        .unwrap_or(ConstScalar::Unknown)
    }
}
<|endoftext|>


<commit_before>//! Macro input conditioning.

use syntax::{
    ast::{self, AttrsOwner},
    AstNode, SyntaxNode,
};

use crate::{
    db::AstDatabase,
    name::{name, AsName},
    AttrId, LazyMacroId, MacroCallKind, MacroCallLoc,
};

pub(crate) fn process_macro_input(
    db: &dyn AstDatabase,
    node: SyntaxNode,
    id: LazyMacroId,
) -> SyntaxNode {
    let loc: MacroCallLoc = db.lookup_intern_macro(id);

    match loc.kind {
        MacroCallKind::FnLike { .. } => node,
        MacroCallKind::Derive { derive_attr, .. } => {
            let item = match ast::Item::cast(node.clone()) {
                Some(item) => item,
                None => return node,
            };

            remove_derives_up_to(item, derive_attr).syntax().clone()
        }
    }
}

/// Removes `#[derive]` attributes from `item`, up to `attr`.
fn remove_derives_up_to(item: ast::Item, attr: AttrId) -> ast::Item {
    let item = item.clone_for_update();
    let idx = attr.0 as usize;
    for attr in item.attrs().take(idx + 1) {
        if let Some(name) =
            attr.path().and_then(|path| path.as_single_segment()).and_then(|seg| seg.name_ref())
        {
            if name.as_name() == name![derive] {
                attr.syntax().detach();
            }
        }
    }
    item
}

#[cfg(test)]
mod tests {
    use base_db::fixture::WithFixture;
    use base_db::SourceDatabase;
    use expect_test::{expect, Expect};

    use crate::test_db::TestDB;

    use super::*;

    fn test_remove_derives_up_to(attr: AttrId, ra_fixture: &str, expect: Expect) {
        let (db, file_id) = TestDB::with_single_file(&ra_fixture);
        let parsed = db.parse(file_id);

        let mut items: Vec<_> =
            parsed.syntax_node().descendants().filter_map(ast::Item::cast).collect();
        assert_eq!(items.len(), 1);

        let item = remove_derives_up_to(items.pop().unwrap(), attr);
        expect.assert_eq(&item.to_string());
    }

    #[test]
    fn remove_derive() {
        test_remove_derives_up_to(
            AttrId(2),
            r#"
#[allow(unused)]
#[derive(Copy)]
#[derive(Hello)]
#[derive(Clone)]
struct A {
    bar: u32
}
        "#,
            expect![[r#"
#[allow(unused)]

#[derive(Clone)]
struct A {
    bar: u32
}"#]],
        );
    }
}
<commit_msg>Revert "Use `name![derive]`"<commit_after>//! Macro input conditioning.

use syntax::{
    ast::{self, AttrsOwner},
    AstNode, SyntaxNode,
};

use crate::{db::AstDatabase, name::AsName, AttrId, LazyMacroId, MacroCallKind, MacroCallLoc};

pub(crate) fn process_macro_input(
    db: &dyn AstDatabase,
    node: SyntaxNode,
    id: LazyMacroId,
) -> SyntaxNode {
    let loc: MacroCallLoc = db.lookup_intern_macro(id);

    match loc.kind {
        MacroCallKind::FnLike { .. } => node,
        MacroCallKind::Derive { derive_attr, .. } => {
            let item = match ast::Item::cast(node.clone()) {
                Some(item) => item,
                None => return node,
            };

            remove_derives_up_to(item, derive_attr).syntax().clone()
        }
    }
}

/// Removes `#[derive]` attributes from `item`, up to `attr`.
fn remove_derives_up_to(item: ast::Item, attr: AttrId) -> ast::Item {
    let item = item.clone_for_update();
    let idx = attr.0 as usize;
    for attr in item.attrs().take(idx + 1) {
        if let Some(name) =
            attr.path().and_then(|path| path.as_single_segment()).and_then(|seg| seg.name_ref())
        {
            if name.as_name().to_string() == "derive" {
                attr.syntax().detach();
            }
        }
    }
    item
}

#[cfg(test)]
mod tests {
    use base_db::fixture::WithFixture;
    use base_db::SourceDatabase;
    use expect_test::{expect, Expect};

    use crate::test_db::TestDB;

    use super::*;

    fn test_remove_derives_up_to(attr: AttrId, ra_fixture: &str, expect: Expect) {
        let (db, file_id) = TestDB::with_single_file(&ra_fixture);
        let parsed = db.parse(file_id);

        let mut items: Vec<_> =
            parsed.syntax_node().descendants().filter_map(ast::Item::cast).collect();
        assert_eq!(items.len(), 1);

        let item = remove_derives_up_to(items.pop().unwrap(), attr);
        expect.assert_eq(&item.to_string());
    }

    #[test]
    fn remove_derive() {
        test_remove_derives_up_to(
            AttrId(2),
            r#"
#[allow(unused)]
#[derive(Copy)]
#[derive(Hello)]
#[derive(Clone)]
struct A {
    bar: u32
}
        "#,
            expect![[r#"
#[allow(unused)]

#[derive(Clone)]
struct A {
    bar: u32
}"#]],
        );
    }
}
<|endoftext|>


<commit_before>use std::io::Read;
use std::collections::HashMap;

use rustc_serialize::json::{self, Json};

use system::System;
use search::document::DocumentSource;
use search::store::IndexStore;

use api::persistent;
use api::iron::prelude::*;
use api::iron::status;
use api::utils::{json_response, index_not_found_response};

pub fn view_post_bulk(req: &mut Request) -> IronResult<Response> {
    let ref system = get_system!(req);

    // Lock index array
    let mut indices = system.indices.write().unwrap();

    // Load data from body
    let mut payload = String::new();
    req.body.read_to_string(&mut payload).unwrap();

    let mut items = Vec::new();

    // Iterate
    let mut payload_lines = payload.split('\n');
    loop {
        let action_line = payload_lines.next();

        // Check if end of input
        if action_line == None || action_line == Some("") {
            break;
        }

        // Parse action line
        let action_json = parse_json!(&action_line.unwrap());

        // Check action
        // Action should be an object with only one key, the key name indicates the action and
        // the value is the parameters for that action
        let action_name = action_json.as_object().unwrap().keys().nth(0).unwrap();
        let action_params = action_json.as_object()
                                       .unwrap()
                                       .get(action_name)
                                       .unwrap()
                                       .as_object()
                                       .unwrap();

        let doc_id = action_params.get("_id").unwrap().as_string().unwrap();
        let doc_type = action_params.get("_type").unwrap().as_string().unwrap();
        let doc_index = action_params.get("_index").unwrap().as_string().unwrap();

        match action_name.as_ref() {
            "index" => {
                let doc_line = payload_lines.next();
                let doc_json = parse_json!(&doc_line.unwrap());;

                // Find index
                let mut index = match indices.get_mut(doc_index) {
                    Some(index) => index,
                    None => {
                        return Ok(index_not_found_response());
                    }
                };

                let doc = {
                    // Find mapping
                    let mapping = match index.get_mapping_by_name(doc_type) {
                        Some(mapping) => mapping,
                        None => {
                            return Ok(json_response(status::NotFound, "{\"message\": \"Mapping not found\"}"));
                        }
                    };

                    // Create document
                    let document_source = DocumentSource {
                        key: doc_id.to_string(),
                        data: doc_json,
                    };
                    document_source.prepare(mapping)
                };

                index.store.insert_or_update_document(doc);

                // Insert into "items" array
                let mut item = HashMap::new();
                // TODO: "create" may not always be right
                item.insert("create", action_params.clone());
                items.push(item);
            }
            _ => {
                warn!("Unrecognised action! {}", action_name);
            }
        }
    }

    return Ok(json_response(status::Ok,
                            format!("{{\"took\": {}, \"items\": {}}}",
                                    items.len(),
                                    json::encode(&items).unwrap())));
}
<commit_msg>Use get_index_or_404! macro<commit_after>use std::io::Read;
use std::collections::HashMap;

use rustc_serialize::json::{self, Json};

use system::System;
use search::document::DocumentSource;
use search::store::IndexStore;

use api::persistent;
use api::iron::prelude::*;
use api::iron::status;
use api::utils::{json_response, index_not_found_response};

pub fn view_post_bulk(req: &mut Request) -> IronResult<Response> {
    let ref system = get_system!(req);

    // Lock index array
    let mut indices = system.indices.write().unwrap();

    // Load data from body
    let mut payload = String::new();
    req.body.read_to_string(&mut payload).unwrap();

    let mut items = Vec::new();

    // Iterate
    let mut payload_lines = payload.split('\n');
    loop {
        let action_line = payload_lines.next();

        // Check if end of input
        if action_line == None || action_line == Some("") {
            break;
        }

        // Parse action line
        let action_json = parse_json!(&action_line.unwrap());

        // Check action
        // Action should be an object with only one key, the key name indicates the action and
        // the value is the parameters for that action
        let action_name = action_json.as_object().unwrap().keys().nth(0).unwrap();
        let action_params = action_json.as_object()
                                       .unwrap()
                                       .get(action_name)
                                       .unwrap()
                                       .as_object()
                                       .unwrap();

        let doc_id = action_params.get("_id").unwrap().as_string().unwrap();
        let doc_type = action_params.get("_type").unwrap().as_string().unwrap();
        let doc_index = action_params.get("_index").unwrap().as_string().unwrap();

        match action_name.as_ref() {
            "index" => {
                let doc_line = payload_lines.next();
                let doc_json = parse_json!(&doc_line.unwrap());;

                // Find index
                let mut index = get_index_or_404!(indices, doc_index);

                let doc = {
                    // Find mapping
                    let mapping = match index.get_mapping_by_name(doc_type) {
                        Some(mapping) => mapping,
                        None => {
                            return Ok(json_response(status::NotFound, "{\"message\": \"Mapping not found\"}"));
                        }
                    };

                    // Create document
                    let document_source = DocumentSource {
                        key: doc_id.to_string(),
                        data: doc_json,
                    };
                    document_source.prepare(mapping)
                };

                index.store.insert_or_update_document(doc);

                // Insert into "items" array
                let mut item = HashMap::new();
                // TODO: "create" may not always be right
                item.insert("create", action_params.clone());
                items.push(item);
            }
            _ => {
                warn!("Unrecognised action! {}", action_name);
            }
        }
    }

    return Ok(json_response(status::Ok,
                            format!("{{\"took\": {}, \"items\": {}}}",
                                    items.len(),
                                    json::encode(&items).unwrap())));
}
<|endoftext|>


<commit_before>use alloc::Vec;
use cpuio::UnsafePort;
use spin::Mutex;

const SECTOR_SIZE: usize = 0x200;

const PORT_DATA:            u16 = 0x1F0;
const PORT_LBA0:            u16 = 0x1F2;
const PORT_LBA1:            u16 = 0x1F3;
const PORT_LBA2:            u16 = 0x1F4;
const PORT_LBA3:            u16 = 0x1F5;
const PORT_DRIVE_SELECT:    u16 = 0x1F6;
const PORT_COMMAND:         u16 = 0x1F7;
const PORT_DEV_CTRL:        u16 = 0x3F6;

#[derive(Debug, Clone)]
pub struct DriveProperties {
    lba28_sectors: u32,
    lba48_sectors: Option<u64>,
}
impl DriveProperties {
    fn supports_lba48(&self) -> bool {
        self.lba48_sectors.is_some()
    }

    fn sector_count(&self) -> u64 {
        self.lba48_sectors.unwrap_or(self.lba28_sectors as u64)
    }
}

pub struct AtaPio {
    properties: Option<DriveProperties>
}
impl AtaPio {
    pub const fn new() -> AtaPio {
        AtaPio {
            properties: None
        }
    }

    pub fn init(&mut self) {
        unsafe {
            self.check_floating_bus();
            self.reset_drives();
            self.properties = Some(self.identify());
        }
    }

    unsafe fn reset_drives(&self) {
        // https://wiki.osdev.org/ATA_PIO_Mode#Resetting_a_drive_.2F_Software_Reset

        // TODO: currently using (primary bus, master drive) only

        let mut ctrl = UnsafePort::<u8>::new(PORT_DEV_CTRL);

        // Disable interupts, run software reset
        ctrl.write(0);

        // Wait for BSY to be clear and RDY set
        for _ in 0..4 { // 400ns delay
            let _ = ctrl.read();
        }

        loop {
            let v = ctrl.read();
            if (v & 0xc0) == 0x40 { // BSY clear, RDY set?
                break;
            }
        }
    }

    unsafe fn select_drive(&self) {
        // https://wiki.osdev.org/ATA_PIO_Mode#400ns_delays

        // // TODO: currently using (primary bus, master drive) only
        // let mut status_port = UnsafePort::<u8>::new(PORT_COMMAND);

        // let mut port = UnsafePort::<u8>::new(PORT_DRIVE_SELECT);
        // port.write(0xA0);

        // for _ in 0..4 {
        //     let _ = status_port.read();
        // }

        // let value = status_port.read();

        self.reset_drives(); // HACK: selects master drive
    }

    unsafe fn check_floating_bus(&self) {
        let mut status_port = UnsafePort::<u8>::new(PORT_COMMAND);
        let data: u8 = status_port.read();
        if data == 0xFF {
            panic!("No ATA drives attached.");
        }
    }

    /// Polls ATA controller to see if the drive is ready
    #[inline]
    unsafe fn is_ready(&self) -> bool {
        let mut status_port = UnsafePort::<u8>::new(PORT_COMMAND);
        for _ in 0..4 {
            let _ = status_port.read();
        }
        let data: u8 = status_port.read();
        (data & 0xc0) == 0x40 // BSY clear, RDY set?
    }

    unsafe fn wait_ready(&self) {
        while !self.is_ready() {}
    }

    pub unsafe fn identify(&self) -> DriveProperties {
        // https://wiki.osdev.org/ATA_PIO_Mode#IDENTIFY_command

        // Clear LBA_N ports
        let mut port_lba0 = UnsafePort::<u8>::new(PORT_LBA0); port_lba0.write(0);
        let mut port_lba1 = UnsafePort::<u8>::new(PORT_LBA1); port_lba1.write(0);
        let mut port_lba2 = UnsafePort::<u8>::new(PORT_LBA2); port_lba2.write(0);
        let mut port_lba3 = UnsafePort::<u8>::new(PORT_LBA3); port_lba3.write(0);

        // Send IDENTIFY command
        let mut cmd_port = UnsafePort::<u8>::new(PORT_COMMAND);
        cmd_port.write(0xEC);

        use time::sleep_ms;
        sleep_ms(1);

        let mut status_port = UnsafePort::<u8>::new(PORT_COMMAND);

        let mut first_cleared = true;
        loop {
            let data: u8 = status_port.read();

            if data == 0 {
                panic!("ATA_PIO: Drive does not exist");
            }

            if (data & 1) != 0 {
                panic!("ATA_PIO: Drive controller error on IDENTIFY");
            }

            if (data & (1 << 7)) != 0 { // is busy
                continue;
            }

            if first_cleared {
                first_cleared = false;
                let v1 = port_lba1.read();
                let v2 = port_lba2.read();
                if v1 != 0 || v2 != 0 {
                    panic!("ATA_PIO: Not an ATA drive");
                }
                continue;
            }

            if (data & (1 << 3)) != 0 {
                break;
            }
        }

        let mut data_port = UnsafePort::<u16>::new(PORT_DATA);
        let mut data: [u16; 256] = [0; 256];

        for i in 0..256 {
            data[i] = data_port.read();
            sleep_ms(1);
        }

        let lba48_supported = (data[83] & (1 << 10)) != 0;
        let lba28_sectors = (data[60] as u32) | ((data[61] as u32) << 0x10);
        let mut lba48_sectors: Option<u64> = None;
        if lba48_supported {
            lba48_sectors = Some(
                ( data[100] as u64) |
                ((data[101] as u64) << 0x10) |
                ((data[102] as u64) << 0x20) |
                ((data[103] as u64) << 0x30)
            );
        }

        if lba28_sectors == 0 && (lba48_sectors.is_none() || lba48_sectors == Some(0)) {
            panic!("ATA_PIO: The drive controller does not support LBA.");
        }

        DriveProperties {
            lba28_sectors,
            lba48_sectors
        }
    }

    pub unsafe fn read(&self, lba: u32, sectors: u8) -> Vec<u8> {
        // https://wiki.osdev.org/ATA_read/write_sectors#Read_in_LBA_mode

        assert!(sectors > 0);

        // Send bits 24-27 of LBA, drive number and LBA mode
        let mut port = UnsafePort::<u8>::new(0x01F6);
        let mut bits24_27: u8 = (lba >> 24) as u8;
        assert!(bits24_27 < 8);
        bits24_27 |= 0b11100000; // LBA mode
        port.write(bits24_27);

        // Send number of sectors
        let mut port = UnsafePort::<u8>::new(0x01F2);
        port.write(sectors);

        // Send bits 0-7 of LBA
        let mut port = UnsafePort::<u8>::new(0x01F3);
        port.write((lba & 0xFF) as u8);

        // Send bits 8-15 of LBA
        let mut port = UnsafePort::<u8>::new(0x01F4);
        port.write(((lba & 0xFF00) >> 0x8) as u8);

        // Send bits 16-23 of LBA
        let mut port = UnsafePort::<u8>::new(0x01F5);
        port.write(((lba & 0xFF0000) >> 0x10) as u8);

        // Send command
        let mut cmd_port = UnsafePort::<u8>::new(PORT_COMMAND);
        cmd_port.write(0x20); // Read with retry

        self.wait_ready();

        let mut data_port = UnsafePort::<u16>::new(PORT_DATA);
        let u16_per_sector = SECTOR_SIZE / 2;

        let mut result: Vec<u8> = Vec::new();
        for _ in 0..sectors {
            for _ in 0..u16_per_sector {
                let word: u16 = data_port.read();
                result.push((word & 0xFF) as u8);
                result.push(((word & 0xFF00) >> 0x8) as u8);
            }
        }

        result
    }
}

// Create static pointer mutex with spinlock to make TERMINAL thread-safe
pub static ATA_PIO: Mutex<AtaPio> = Mutex::new(AtaPio::new());

pub fn init() {
    ATA_PIO.lock().init();
}<commit_msg>Clean up ATA PIO driver<commit_after>use alloc::Vec;
use cpuio::UnsafePort;
use spin::Mutex;

const SECTOR_SIZE: usize = 0x200;

const PORT_DATA:            u16 = 0x1F0;
const PORT_SECCOUNT:        u16 = 0x1F2;
const PORT_LBA0:            u16 = 0x1F3;
const PORT_LBA1:            u16 = 0x1F4;
const PORT_LBA2:            u16 = 0x1F5;
const PORT_LBA3:            u16 = 0x1F6;
const PORT_COMMAND:         u16 = 0x1F7;
const PORT_DEV_CTRL:        u16 = 0x3F6;

#[derive(Debug, Clone)]
pub struct DriveProperties {
    lba28_sectors: u32,
    lba48_sectors: Option<u64>,
}
impl DriveProperties {
    fn supports_lba48(&self) -> bool {
        self.lba48_sectors.is_some()
    }

    fn sector_count(&self) -> u64 {
        self.lba48_sectors.unwrap_or(self.lba28_sectors as u64)
    }
}

pub struct AtaPio {
    properties: Option<DriveProperties>
}
impl AtaPio {
    pub const fn new() -> AtaPio {
        AtaPio {
            properties: None
        }
    }

    pub fn init(&mut self) {
        unsafe {
            self.check_floating_bus();
            self.reset_drives();
            self.properties = Some(self.identify());
        }
        rprintln!("LBA48 support: {}", self.properties.clone().unwrap().supports_lba48());
    }

    #[inline]
    unsafe fn send_command(&self, cmd: u8) {
        let mut cmd_port = UnsafePort::<u8>::new(PORT_COMMAND);
        cmd_port.write(cmd);
    }

    #[inline]
    unsafe fn read_status(&self) -> u8 {
        let mut status_port = UnsafePort::<u8>::new(PORT_COMMAND);
        status_port.read()
    }

    unsafe fn reset_drives(&self) {
        // https://wiki.osdev.org/ATA_PIO_Mode#Resetting_a_drive_.2F_Software_Reset

        // TODO: currently using (primary bus, master drive) only

        let mut ctrl = UnsafePort::<u8>::new(PORT_DEV_CTRL);

        // Disable interupts, run software reset
        ctrl.write(0);

        // Wait for BSY to be clear and RDY set
        for _ in 0..4 { // 400ns delay
            let _ = ctrl.read();
        }

        loop {
            let v = ctrl.read();
            if (v & 0xc0) == 0x40 { // BSY clear, RDY set?
                break;
            }
        }
    }

    unsafe fn select_drive(&self) {
        // https://wiki.osdev.org/ATA_PIO_Mode#400ns_delays

        self.reset_drives(); // HACK: selects master drive
    }

    unsafe fn check_floating_bus(&self) {
        let data: u8 = self.read_status();
        if data == 0xFF {
            panic!("No ATA drives attached.");
        }
    }

    /// Polls ATA controller to see if the drive is ready
    #[inline]
    unsafe fn is_ready(&self) -> bool {
        for _ in 0..4 {
            let _ = self.read_status();
        }
        let data: u8 = self.read_status();
        (data & 0xc0) == 0x40 // BSY clear, RDY set?
    }

    unsafe fn wait_ready(&self) {
        while !self.is_ready() {}
    }

    unsafe fn identify(&self) -> DriveProperties {
        // https://wiki.osdev.org/ATA_PIO_Mode#IDENTIFY_command

        // Clear LBA_N ports
        let mut port_lba0 = UnsafePort::<u8>::new(PORT_LBA0); port_lba0.write(0);
        let mut port_lba1 = UnsafePort::<u8>::new(PORT_LBA1); port_lba1.write(0);
        let mut port_lba2 = UnsafePort::<u8>::new(PORT_LBA2); port_lba2.write(0);
        let mut port_lba3 = UnsafePort::<u8>::new(PORT_LBA3); port_lba3.write(0);

        // Send IDENTIFY command
        self.send_command(0xEC);

        use time::sleep_ms;
        sleep_ms(1);

        let mut first_cleared = true;
        loop {
            let data: u8 = self.read_status();

            if data == 0 {
                panic!("ATA_PIO: Drive does not exist");
            }

            if (data & 1) != 0 {
                panic!("ATA_PIO: Drive controller error on IDENTIFY");
            }

            if (data & (1 << 7)) != 0 { // is busy
                continue;
            }

            if first_cleared {
                first_cleared = false;
                let v1 = port_lba1.read();
                let v2 = port_lba2.read();
                if v1 != 0 || v2 != 0 {
                    panic!("ATA_PIO: Not an ATA drive");
                }
                continue;
            }

            if (data & (1 << 3)) != 0 {
                break;
            }
        }

        let mut data_port = UnsafePort::<u16>::new(PORT_DATA);
        let mut data: [u16; 256] = [0; 256];

        for i in 0..256 {
            data[i] = data_port.read();
            sleep_ms(1);
        }

        let lba48_supported = (data[83] & (1 << 10)) != 0;
        let lba28_sectors = (data[60] as u32) | ((data[61] as u32) << 0x10);
        let mut lba48_sectors: Option<u64> = None;
        if lba48_supported {
            lba48_sectors = Some(
                ( data[100] as u64) |
                ((data[101] as u64) << 0x10) |
                ((data[102] as u64) << 0x20) |
                ((data[103] as u64) << 0x30)
            );
        }

        if lba28_sectors == 0 && (lba48_sectors.is_none() || lba48_sectors == Some(0)) {
            panic!("ATA_PIO: The drive controller does not support LBA.");
        }

        DriveProperties {
            lba28_sectors,
            lba48_sectors
        }
    }

    pub unsafe fn read(&self, lba: u32, sectors: u8) -> Vec<u8> {
        // https://wiki.osdev.org/ATA_read/write_sectors#Read_in_LBA_mode

        assert!(sectors > 0);

        // Send bits 24-27 of LBA, drive number and LBA mode
        let mut port = UnsafePort::<u8>::new(PORT_LBA3);
        let mut bits24_27: u8 = (lba >> 24) as u8;
        assert!(bits24_27 < 8);
        bits24_27 |= 0b11100000; // LBA mode
        port.write(bits24_27);

        // Send number of sectors
        let mut port = UnsafePort::<u8>::new(PORT_SECCOUNT);
        port.write(sectors);

        // Send bits 0-7 of LBA
        let mut port = UnsafePort::<u8>::new(PORT_LBA0);
        port.write((lba & 0xFF) as u8);

        // Send bits 8-15 of LBA
        let mut port = UnsafePort::<u8>::new(PORT_LBA1);
        port.write(((lba & 0xFF00) >> 0x8) as u8);

        // Send bits 16-23 of LBA
        let mut port = UnsafePort::<u8>::new(PORT_LBA2);
        port.write(((lba & 0xFF0000) >> 0x10) as u8);

        // Send command
        self.send_command(0x20); // Read with retry

        self.wait_ready();

        let mut data_port = UnsafePort::<u16>::new(PORT_DATA);
        let u16_per_sector = SECTOR_SIZE / 2;

        let mut result: Vec<u8> = Vec::new();
        for _ in 0..sectors {
            for _ in 0..u16_per_sector {
                let word: u16 = data_port.read();
                result.push((word & 0xFF) as u8);
                result.push(((word & 0xFF00) >> 0x8) as u8);
            }
        }

        result
    }
}

// Create static pointer mutex with spinlock to make TERMINAL thread-safe
pub static ATA_PIO: Mutex<AtaPio> = Mutex::new(AtaPio::new());

pub fn init() {
    ATA_PIO.lock().init();
}<|endoftext|>