<commit_before>use std::{
    cell::{Cell, RefCell},
    collections::HashSet,
};

use crate::{prelude::*, utils::SelectionMode as SelMode};

use super::behaviors::{MouseBehavior, SelectionBehavior};

static CONTAINER: &'static str = "container";
static ITEMS_PANEL: &'static str = "items_panel";

type SelectedItem = Option<Entity>;

#[derive(Default, AsAny)]
pub struct ComboBoxItemState {
    request_selection_toggle: Cell<bool>,
    selected_container: Entity,
    combo_box: Entity,
    builder: WidgetBuildContext,
}

impl ComboBoxItemState {
    fn toggle_selection(&self) {
        self.request_selection_toggle.set(true);
    }
}

impl State for ComboBoxItemState {
    fn update(&mut self, _: &mut Registry, ctx: &mut Context<'_>) {
        if !ctx.widget().get::<bool>("enabled") || !self.request_selection_toggle.get() {
            return;
        }
        self.request_selection_toggle.set(false);

        // let selected = *ctx.widget().get::<bool>("selected");

        let entity = ctx.entity;
        let index: u32 = *ctx.widget().get("index");

        // unselect previous selected item.
        if let Some(item) = ctx
            .get_widget(self.combo_box)
            .clone::<SelectedItem>("selected_item")
        {
            ctx.get_widget(item).set("selected", false);
            ctx.get_widget(item).update_theme_by_state(false);
        }

        ctx.widget().set("selected", true);
        ctx.widget().update_theme_by_state(false);
        ctx.get_widget(self.combo_box)
            .set("selected_index", index as i32);
        ctx.get_widget(self.combo_box)
            .set("selected_item", Some(entity));
        // let index = ctx.index_as_child(entity).unwrap();

        // if let Some(parent) = &mut ctx.get_widget(self.combo_box) {
        //     // let selection_mode = *parent.get::<SelectionMode>("selection_mode");
        //     // deselect item
        //     if selected {
        //         // parent
        //         //     .get_mut::<SelectedEntities>("selected_entities")
        //         //     .0
        //         //     .remove(&entity);
        //         // parent
        //         //     .get_mut::<SelectedIndices>("selected_indices")
        //         //     .0
        //         //     .remove(&index);
        //         return;
        //     }

        //     // if parent
        //     //     .get::<SelectedEntities>("selected_entities")
        //     //     .0
        //     //     .contains(&entity)
        //     //     || selection_mode == SelMode::None
        //     // {
        //     //     return;
        //     // }

        //     // if selection_mode == SelMode::Single {
        //     //     parent
        //     //         .get_mut::<SelectedEntities>("selected_entities")
        //     //         .0
        //     //         .clear();
        //     //     parent
        //     //         .get_mut::<SelectedIndices>("selected_indices")
        //     //         .0
        //     //         .clear();
        //     // }

        //     // parent
        //     //     .get_mut::<SelectedEntities>("selected_entities")
        //     //     .0
        //     //     .insert(entity);
        //     // parent
        //     //     .get_mut::<SelectedIndices>("selected_indices")
        //     //     .0
        //     //     .insert(index);
        // }
    }
}

widget!(
    ComboBoxItem<ComboBoxItemState>: MouseHandler {
        /// Sets or shares the background property.
        background: Brush,

        /// Sets or shares the border radius property.
        border_radius: f64,

        /// Sets or shares the border thickness property.
        border_width: Thickness,

        /// Sets or shares the border brush property.
        border_brush: Brush,

        /// Sets or shares the foreground property.
        foreground: Brush,

        /// Sets or share the font size property.
        font_size: f64,

        /// Sets or shares the font property.
        font: String,

        /// Sets or shares the padding property.
        padding: Thickness,

        /// Sets or shares the css selector property.
        selector: Selector,

        /// Sets or shares the pressed property.
        pressed: bool,

        /// Sets or shares the selected property.
        selected: bool,

        /// Sets or shares the index inside of the combobox item collection.
        index: u32
    }
);

impl ComboBoxItem {
    fn selected_container(mut self, selected_container: impl Into<Entity>) -> Self {
        self.state_mut().selected_container = selected_container.into();
        self
    }

    fn combo_box(mut self, combo_box: impl Into<Entity>) -> Self {
        self.state_mut().combo_box = combo_box.into();
        self
    }

    // Define the template build function for the selected content of the ComboBoxItems.
    fn items_builder<F: Fn(&mut BuildContext, usize) -> Entity + 'static + Clone>(
        mut self,
        builder: F,
    ) -> Self {
        self.state_mut().builder = Some(Box::new(builder));
        self
    }
}

impl Template for ComboBoxItem {
    fn template(self, id: Entity, _: &mut BuildContext) -> Self {
        self.name("ComboBoxItem")
            .min_width(64.0)
            .height(24.0)
            .selected(false)
            .pressed(false)
            .selector("list-view-item")
            .padding(0.0)
            .background("transparent")
            .border_radius(0.0)
            .border_width(0.0)
            .border_brush("transparent")
            .foreground(colors::LINK_WATER_COLOR)
            .font_size(32.0)
            .font("Roboto Regular")
            .on_click(move |states, _| {
                states.get::<ComboBoxItemState>(id).toggle_selection();
                false
            })
    }

    fn render_object(&self) -> Box<dyn RenderObject> {
        Box::new(RectangleRenderObject)
    }

    fn layout(&self) -> Box<dyn Layout> {
        Box::new(PaddingLayout::new())
    }
}

/// The `ComboBoxState` is used to manipulate the position of the thumb of the slider widget.
#[derive(Default, AsAny)]
pub struct ComboBoxState {
    builder: WidgetBuildContext,
    count: usize,
    items_panel: Entity,
    selected_container: Entity,
}

impl State for ComboBoxState {
    fn update(&mut self, _: &mut Registry, ctx: &mut Context<'_>) {
        let count = ctx.widget().clone_or_default::<usize>("count");
        let entity = ctx.entity;

        if count != self.count {
            if let Some(builder) = &self.builder {
                ctx.clear_children_of(self.items_panel);

                for i in 0..count {
                    let item = {
                        let build_context = &mut ctx.build_context();
                        let child = builder(build_context, i);
                        let item = ComboBoxItem::create()
                            .index(i as u32)
                            .combo_box(entity)
                            .selected_container(self.selected_container);

                        // if let Some(builder) = self.builder {
                        //     item.items_builder((*builder).clone());
                        // }

                        let item = item.build(build_context);

                        let mouse_behavior = MouseBehavior::create().build(build_context);
                        build_context.register_shared_property::<Selector>(
                            "selector",
                            mouse_behavior,
                            item,
                        );
                        build_context.register_shared_property::<bool>(
                            "pressed",
                            mouse_behavior,
                            item,
                        );
                        build_context.append_child(item, mouse_behavior);

                        build_context.register_shared_property::<Brush>("foreground", child, item);
                        build_context.register_shared_property::<f32>("opacity", item, entity);
                        build_context.register_shared_property::<f32>("opacity", child, entity);
                        build_context.register_shared_property::<f64>("font_size", child, item);
                        build_context.append_child(self.items_panel, item);
                        build_context.append_child(mouse_behavior, child);

                        item
                    };
                    ctx.get_widget(item).update_properties_by_theme();
                }
            }

            self.count = count;
        }
    }

    fn update_post_layout(&mut self, _: &mut Registry, ctx: &mut Context<'_>) {
        // for index in ctx
        //     .widget()
        //     .get::<SelectedEntities>("selected_entities")
        //     .0
        //     .clone()
        //     .symmetric_difference(&*self.selected_entities.borrow())
        // {
        //     let mut widget = ctx.get_widget(*index);
        //     widget.set("selected", !widget.get::<bool>("selected"));

        //     widget.update_theme_by_state(false);
        // }

        // *self.selected_entities.borrow_mut() = ctx
        //     .widget()
        //     .get::<SelectedEntities>("selected_entities")
        //     .0
        //     .clone();
    }
}

// todo use code of list view item, by create combobox item insert entity of popup container

widget!(
    /// The `ComboBox` represents an selection widget with a drop-down list.
    ///
    /// **CSS element:** `ComboBox`
    ComboBox<ComboBoxState>: MouseHandler, ChangedHandler {
        /// Sets or shares the background property.
        background: Brush,

        /// Sets or shares the border radius property.
        border_radius: f64,

        /// Sets or shares the border thickness property.
        border_width: Thickness,

        /// Sets or shares the border brush property.
        border_brush: Brush,

        /// Sets or shares the foreground property.
        foreground: Brush,

        /// Sets or share the font size property.
        font_size: f64,

        /// Sets or shares the font property.
        font: String,

        /// Sets or shared the count.
        count: usize,

        /// Sets or shares the css selector property.
        selector: Selector,

        /// Sets or shares the selected index. If the value is -1 no item is selected.
        selected_index: i32,

        /// The entity of the selected item.
        selected_item: SelectedItem,

        /// Sets or shares the padding property.
        padding: Thickness,

        /// Sets or shares the pressed property.
        pressed: bool,

        /// Sets or shares the flag if the drop down is open.
        selected: bool
    }
);

impl ComboBox {
    /// Define the template build function for the content of the ComboBoxItems.
    pub fn items_builder<F: Fn(&mut BuildContext, usize) -> Entity + 'static + Clone>(
        mut self,
        builder: F,
    ) -> Self {
        self.state_mut().builder = Some(Box::new(builder));
        self
    }
}

impl Template for ComboBox {
    fn template(mut self, id: Entity, ctx: &mut BuildContext) -> Self {
        let container = Container::create()
            .selector(Selector::default().id(CONTAINER))
            .background(id)
            .border_radius(id)
            .border_width(id)
            .border_brush(id)
            .padding(id)
            .build(ctx);
        self.state_mut().selected_container = container;

        let items_panel = Stack::create()
            .vertical_alignment("start")
            .selector(Selector::default().id(ITEMS_PANEL))
            .orientation("vertical")
            .build(ctx);

        self.state_mut().items_panel = items_panel;
        let scroll_viewer = ScrollViewer::create()
            .scroll_viewer_mode(("disabled", "auto"))
            .child(items_panel)
            .build(ctx);

        let popup = Popup::create()
            .height(200.0)
            .open(("selected", id))
            .child(scroll_viewer)
            .child(
                ScrollIndicator::create()
                    .padding(2.0)
                    .content_id(items_panel.0)
                    .scroll_offset(scroll_viewer)
                    .opacity(id)
                    .build(ctx),
            )
            .target(container.0)
            .build(ctx);

        let _ = ctx.append_child_to_overlay(popup);

        self.name("ComboBox")
            .selector("combo_box")
            .height(32.0)
            .min_width(80.0)
            .selected(false)
            .selected_index(-1)
            .child(
                MouseBehavior::create()
                    .pressed(id)
                    .enabled(id)
                    .selector(id)
                    .child(
                        SelectionBehavior::create()
                            .selected(id)
                            .enabled(id)
                            .selector(id)
                            .parent(id.0)
                            .child(container)
                            .build(ctx),
                    )
                    .build(ctx),
            )
    }
}
<commit_msg>Add selected content to container.<commit_after>use std::{
    sync::Arc,
    cell::{Cell, RefCell},
    collections::HashSet,
};

use crate::{prelude::*, utils::SelectionMode as SelMode};

use super::behaviors::{MouseBehavior, SelectionBehavior};

static CONTAINER: &'static str = "container";
static ITEMS_PANEL: &'static str = "items_panel";

type SelectedItem = Option<Entity>;

#[derive(Default, AsAny)]
pub struct ComboBoxItemState {
    request_selection_toggle: Cell<bool>,
    index: usize,
    selected_container: Entity,
    combo_box: Entity,
    // ugly work around for item builder context clone, todo make it better 😉
    builder: Option<Arc<RefCell<dyn Fn(&mut BuildContext, usize) -> Entity + 'static>>>,
}

impl ComboBoxItemState {
    fn toggle_selection(&self) {
        self.request_selection_toggle.set(true);
    }
}

impl State for ComboBoxItemState {
    fn update(&mut self, _: &mut Registry, ctx: &mut Context<'_>) {
        if !ctx.widget().get::<bool>("enabled") || !self.request_selection_toggle.get() {
            return;
        }
        self.request_selection_toggle.set(false);

        // let selected = *ctx.widget().get::<bool>("selected");

        let entity = ctx.entity;

        // unselect previous selected item.
        if let Some(item) = ctx
            .get_widget(self.combo_box)
            .clone::<SelectedItem>("selected_item")
        {
            ctx.get_widget(item).set("selected", false);
            ctx.get_widget(item).update_theme_by_state(false);
        }

        ctx.widget().set("selected", true);
        ctx.widget().update_theme_by_state(false);
        ctx.get_widget(self.combo_box)
            .set("selected_index", self.index as i32);
        ctx.get_widget(self.combo_box)
            .set("selected_item", Some(entity));

        // Add selected content to combobox
        let index = self.index;
        let selected_container = self.selected_container;
        if let Some(builder) = &self.builder {
            ctx.clear_children_of(selected_container);
            let build_context = &mut ctx.build_context();
            let selected_content = builder.borrow()(build_context, index);
            build_context.append_child(selected_container, selected_content);
        }

        // ctx.get_widget(&self.selected_container.into())
        // let index = ctx.index_as_child(entity).unwrap();

        // if let Some(parent) = &mut ctx.get_widget(self.combo_box) {
        //     // let selection_mode = *parent.get::<SelectionMode>("selection_mode");
        //     // deselect item
        //     if selected {
        //         // parent
        //         //     .get_mut::<SelectedEntities>("selected_entities")
        //         //     .0
        //         //     .remove(&entity);
        //         // parent
        //         //     .get_mut::<SelectedIndices>("selected_indices")
        //         //     .0
        //         //     .remove(&index);
        //         return;
        //     }

        //     // if parent
        //     //     .get::<SelectedEntities>("selected_entities")
        //     //     .0
        //     //     .contains(&entity)
        //     //     || selection_mode == SelMode::None
        //     // {
        //     //     return;
        //     // }

        //     // if selection_mode == SelMode::Single {
        //     //     parent
        //     //         .get_mut::<SelectedEntities>("selected_entities")
        //     //         .0
        //     //         .clear();
        //     //     parent
        //     //         .get_mut::<SelectedIndices>("selected_indices")
        //     //         .0
        //     //         .clear();
        //     // }

        //     // parent
        //     //     .get_mut::<SelectedEntities>("selected_entities")
        //     //     .0
        //     //     .insert(entity);
        //     // parent
        //     //     .get_mut::<SelectedIndices>("selected_indices")
        //     //     .0
        //     //     .insert(index);
        // }
    }
}

widget!(
    ComboBoxItem<ComboBoxItemState>: MouseHandler {
        /// Sets or shares the background property.
        background: Brush,

        /// Sets or shares the border radius property.
        border_radius: f64,

        /// Sets or shares the border thickness property.
        border_width: Thickness,

        /// Sets or shares the border brush property.
        border_brush: Brush,

        /// Sets or shares the foreground property.
        foreground: Brush,

        /// Sets or share the font size property.
        font_size: f64,

        /// Sets or shares the font property.
        font: String,

        /// Sets or shares the padding property.
        padding: Thickness,

        /// Sets or shares the css selector property.
        selector: Selector,

        /// Sets or shares the pressed property.
        pressed: bool,

        /// Sets or shares the selected property.
        selected: bool
    }
);

impl ComboBoxItem {
    fn selected_container(mut self, selected_container: impl Into<Entity>) -> Self {
        self.state_mut().selected_container = selected_container.into();
        self
    }

    fn index(mut self, index: usize) -> Self {
        self.state_mut().index = index;
        self
    }

    fn combo_box(mut self, combo_box: impl Into<Entity>) -> Self {
        self.state_mut().combo_box = combo_box.into();
        self
    }

    // Define the template build function for the selected content of the ComboBoxItems.
    fn items_builder(
        &mut self,
        builder: &Arc<RefCell<dyn Fn(&mut BuildContext, usize) -> Entity + 'static>>,
    ) {
        self.state_mut().builder = Some(builder.clone());
    }
}

impl Template for ComboBoxItem {
    fn template(self, id: Entity, _: &mut BuildContext) -> Self {
        self.name("ComboBoxItem")
            .min_width(64.0)
            .height(24.0)
            .selected(false)
            .pressed(false)
            .selector("list-view-item")
            .padding(0.0)
            .background("transparent")
            .border_radius(0.0)
            .border_width(0.0)
            .border_brush("transparent")
            .foreground(colors::LINK_WATER_COLOR)
            .font_size(32.0)
            .font("Roboto Regular")
            .on_click(move |states, _| {
                states.get::<ComboBoxItemState>(id).toggle_selection();
                false
            })
    }

    fn render_object(&self) -> Box<dyn RenderObject> {
        Box::new(RectangleRenderObject)
    }

    fn layout(&self) -> Box<dyn Layout> {
        Box::new(PaddingLayout::new())
    }
}

/// The `ComboBoxState` is used to manipulate the position of the thumb of the slider widget.
#[derive(Default, AsAny)]
pub struct ComboBoxState {
    builder: Option<Arc<RefCell<dyn Fn(&mut BuildContext, usize) -> Entity + 'static>>>,
    count: usize,
    items_panel: Entity,
    selected_container: Entity,
}

impl State for ComboBoxState {
    fn update(&mut self, _: &mut Registry, ctx: &mut Context<'_>) {
        let count = ctx.widget().clone_or_default::<usize>("count");
        let entity = ctx.entity;

        if count != self.count {
            if let Some(builder) = &self.builder {
                ctx.clear_children_of(self.items_panel);

                for i in 0..count {
                    let item = {
                        let build_context = &mut ctx.build_context();
                        let child = builder.borrow()(build_context, i);
                        let mut item = ComboBoxItem::create()
                            .index(i)
                            .combo_box(entity)
                            .selected_container(self.selected_container);

                        if let Some(builder) = &self.builder {
                           item.items_builder(builder);
                        }

                        let item = item.build(build_context);

                        let mouse_behavior = MouseBehavior::create().build(build_context);
                        build_context.register_shared_property::<Selector>(
                            "selector",
                            mouse_behavior,
                            item,
                        );
                        build_context.register_shared_property::<bool>(
                            "pressed",
                            mouse_behavior,
                            item,
                        );
                        build_context.append_child(item, mouse_behavior);

                        build_context.register_shared_property::<Brush>("foreground", child, item);
                        build_context.register_shared_property::<f32>("opacity", item, entity);
                        build_context.register_shared_property::<f32>("opacity", child, entity);
                        build_context.register_shared_property::<f64>("font_size", child, item);
                        build_context.append_child(self.items_panel, item);
                        build_context.append_child(mouse_behavior, child);

                        item
                    };
                    ctx.get_widget(item).update_properties_by_theme();
                }
            }

            self.count = count;
        }
    }

    fn update_post_layout(&mut self, _: &mut Registry, ctx: &mut Context<'_>) {
        // for index in ctx
        //     .widget()
        //     .get::<SelectedEntities>("selected_entities")
        //     .0
        //     .clone()
        //     .symmetric_difference(&*self.selected_entities.borrow())
        // {
        //     let mut widget = ctx.get_widget(*index);
        //     widget.set("selected", !widget.get::<bool>("selected"));

        //     widget.update_theme_by_state(false);
        // }

        // *self.selected_entities.borrow_mut() = ctx
        //     .widget()
        //     .get::<SelectedEntities>("selected_entities")
        //     .0
        //     .clone();
    }
}

// todo use code of list view item, by create combobox item insert entity of popup container

widget!(
    /// The `ComboBox` represents an selection widget with a drop-down list.
    ///
    /// **CSS element:** `ComboBox`
    ComboBox<ComboBoxState>: MouseHandler, ChangedHandler {
        /// Sets or shares the background property.
        background: Brush,

        /// Sets or shares the border radius property.
        border_radius: f64,

        /// Sets or shares the border thickness property.
        border_width: Thickness,

        /// Sets or shares the border brush property.
        border_brush: Brush,

        /// Sets or shares the foreground property.
        foreground: Brush,

        /// Sets or share the font size property.
        font_size: f64,

        /// Sets or shares the font property.
        font: String,

        /// Sets or shared the count.
        count: usize,

        /// Sets or shares the css selector property.
        selector: Selector,

        /// Sets or shares the selected index. If the value is -1 no item is selected.
        selected_index: i32,

        /// The entity of the selected item.
        selected_item: SelectedItem,

        /// Sets or shares the padding property.
        padding: Thickness,

        /// Sets or shares the pressed property.
        pressed: bool,

        /// Sets or shares the flag if the drop down is open.
        selected: bool
    }
);

impl ComboBox {
    /// Define the template build function for the content of the ComboBoxItems.
    pub fn items_builder<F: Fn(&mut BuildContext, usize) -> Entity + 'static + Clone>(
        mut self,
        builder: F,
    ) -> Self {
        self.state_mut().builder = Some(Arc::new(RefCell::new(builder)));
        self
    }
}

impl Template for ComboBox {
    fn template(mut self, id: Entity, ctx: &mut BuildContext) -> Self {
        let container = Container::create()
            .selector(Selector::default().id(CONTAINER))
            .background(id)
            .border_radius(id)
            .border_width(id)
            .border_brush(id)
            .padding(id)
            .build(ctx);
        self.state_mut().selected_container = container;

        let items_panel = Stack::create()
            .vertical_alignment("start")
            .selector(Selector::default().id(ITEMS_PANEL))
            .orientation("vertical")
            .build(ctx);

        self.state_mut().items_panel = items_panel;
        let scroll_viewer = ScrollViewer::create()
            .scroll_viewer_mode(("disabled", "auto"))
            .child(items_panel)
            .build(ctx);

        let popup = Popup::create()
            .height(200.0)
            .open(("selected", id))
            .child(scroll_viewer)
            .child(
                ScrollIndicator::create()
                    .padding(2.0)
                    .content_id(items_panel.0)
                    .scroll_offset(scroll_viewer)
                    .opacity(id)
                    .build(ctx),
            )
            .target(container.0)
            .build(ctx);

        let _ = ctx.append_child_to_overlay(popup);

        self.name("ComboBox")
            .selector("combo_box")
            .height(32.0)
            .min_width(80.0)
            .selected(false)
            .selected_index(-1)
            .child(
                MouseBehavior::create()
                    .pressed(id)
                    .enabled(id)
                    .selector(id)
                    .child(
                        SelectionBehavior::create()
                            .selected(id)
                            .enabled(id)
                            .selector(id)
                            .parent(id.0)
                            .child(container)
                            .build(ctx),
                    )
                    .build(ctx),
            )
    }
}
<|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/. */

#![cfg_attr(feature = "nightly", feature(nonzero))]
#![cfg_attr(feature = "serde_derive", feature(rustc_attrs, rustc_macro, structural_match))]

extern crate app_units;
extern crate byteorder;
#[cfg(feature = "nightly")]
extern crate core;
extern crate euclid;
extern crate gleam;
extern crate heapsize;
extern crate ipc_channel;
extern crate offscreen_gl_context;
extern crate serde;
#[cfg(feature = "serde_derive")]
#[macro_use]
extern crate serde_derive;

#[cfg(target_os = "macos")] extern crate core_graphics;

#[cfg(feature = "serde_codegen")]
include!(concat!(env!("OUT_DIR"), "/types.rs"));

#[cfg(feature = "serde_derive")]
include!("types.rs");

mod api;
mod display_item;
mod display_list;
mod stacking_context;
mod webgl;

pub use api::RenderApi;
pub use display_list::{AuxiliaryListsBuilder, DisplayListBuilder};
<commit_msg>Switch rustc_macro to proc_macro<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/. */

#![cfg_attr(feature = "nightly", feature(nonzero))]
#![cfg_attr(feature = "serde_derive", feature(proc_macro, rustc_attrs, structural_match))]

extern crate app_units;
extern crate byteorder;
#[cfg(feature = "nightly")]
extern crate core;
extern crate euclid;
extern crate gleam;
extern crate heapsize;
extern crate ipc_channel;
extern crate offscreen_gl_context;
extern crate serde;
#[cfg(feature = "serde_derive")]
#[macro_use]
extern crate serde_derive;

#[cfg(target_os = "macos")] extern crate core_graphics;

#[cfg(feature = "serde_codegen")]
include!(concat!(env!("OUT_DIR"), "/types.rs"));

#[cfg(feature = "serde_derive")]
include!("types.rs");

mod api;
mod display_item;
mod display_list;
mod stacking_context;
mod webgl;

pub use api::RenderApi;
pub use display_list::{AuxiliaryListsBuilder, DisplayListBuilder};
<|endoftext|>


<commit_before>use crate::symbol::{sym, Symbol};
use std::fmt;
use std::str::FromStr;

use rustc_macros::HashStable_Generic;

/// The edition of the compiler. (See [RFC 2052](https://github.com/rust-lang/rfcs/blob/master/text/2052-epochs.md).)
#[derive(Clone, Copy, Hash, PartialEq, PartialOrd, Debug, Encodable, Decodable, Eq)]
#[derive(HashStable_Generic)]
pub enum Edition {
    // When adding new editions, be sure to do the following:
    //
    // - update the `ALL_EDITIONS` const
    // - update the `EDITION_NAME_LIST` const
    // - add a `rust_####()` function to the session
    // - update the enum in Cargo's sources as well
    //
    // Editions *must* be kept in order, oldest to newest.
    /// The 2015 edition
    Edition2015,
    /// The 2018 edition
    Edition2018,
    /// The 2021 ediiton
    Edition2021,
}

// Must be in order from oldest to newest.
pub const ALL_EDITIONS: &[Edition] =
    &[Edition::Edition2015, Edition::Edition2018, Edition::Edition2021];

pub const EDITION_NAME_LIST: &str = "2015|2018|2021";

pub const DEFAULT_EDITION: Edition = Edition::Edition2015;

pub const LATEST_STABLE_EDITION: Edition = Edition::Edition2018;

impl fmt::Display for Edition {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        let s = match *self {
            Edition::Edition2015 => "2015",
            Edition::Edition2018 => "2018",
            Edition::Edition2021 => "2021",
        };
        write!(f, "{}", s)
    }
}

impl Edition {
    pub fn lint_name(&self) -> &'static str {
        match *self {
            Edition::Edition2015 => "rust_2015_compatibility",
            Edition::Edition2018 => "rust_2018_compatibility",
            Edition::Edition2021 => "rust_2021_compatibility",
        }
    }

    pub fn feature_name(&self) -> Symbol {
        match *self {
            Edition::Edition2015 => sym::rust_2015_preview,
            Edition::Edition2018 => sym::rust_2018_preview,
            Edition::Edition2021 => sym::rust_2021_preview,
        }
    }

    pub fn is_stable(&self) -> bool {
        match *self {
            Edition::Edition2015 => true,
            Edition::Edition2018 => true,
            Edition::Edition2021 => false,
        }
    }
}

impl FromStr for Edition {
    type Err = ();
    fn from_str(s: &str) -> Result<Self, ()> {
        match s {
            "2015" => Ok(Edition::Edition2015),
            "2018" => Ok(Edition::Edition2018),
            "2021" => Ok(Edition::Edition2021),
            _ => Err(()),
        }
    }
}
<commit_msg>fix: wrong word<commit_after>use crate::symbol::{sym, Symbol};
use std::fmt;
use std::str::FromStr;

use rustc_macros::HashStable_Generic;

/// The edition of the compiler. (See [RFC 2052](https://github.com/rust-lang/rfcs/blob/master/text/2052-epochs.md).)
#[derive(Clone, Copy, Hash, PartialEq, PartialOrd, Debug, Encodable, Decodable, Eq)]
#[derive(HashStable_Generic)]
pub enum Edition {
    // When adding new editions, be sure to do the following:
    //
    // - update the `ALL_EDITIONS` const
    // - update the `EDITION_NAME_LIST` const
    // - add a `rust_####()` function to the session
    // - update the enum in Cargo's sources as well
    //
    // Editions *must* be kept in order, oldest to newest.
    /// The 2015 edition
    Edition2015,
    /// The 2018 edition
    Edition2018,
    /// The 2021 edition
    Edition2021,
}

// Must be in order from oldest to newest.
pub const ALL_EDITIONS: &[Edition] =
    &[Edition::Edition2015, Edition::Edition2018, Edition::Edition2021];

pub const EDITION_NAME_LIST: &str = "2015|2018|2021";

pub const DEFAULT_EDITION: Edition = Edition::Edition2015;

pub const LATEST_STABLE_EDITION: Edition = Edition::Edition2018;

impl fmt::Display for Edition {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        let s = match *self {
            Edition::Edition2015 => "2015",
            Edition::Edition2018 => "2018",
            Edition::Edition2021 => "2021",
        };
        write!(f, "{}", s)
    }
}

impl Edition {
    pub fn lint_name(&self) -> &'static str {
        match *self {
            Edition::Edition2015 => "rust_2015_compatibility",
            Edition::Edition2018 => "rust_2018_compatibility",
            Edition::Edition2021 => "rust_2021_compatibility",
        }
    }

    pub fn feature_name(&self) -> Symbol {
        match *self {
            Edition::Edition2015 => sym::rust_2015_preview,
            Edition::Edition2018 => sym::rust_2018_preview,
            Edition::Edition2021 => sym::rust_2021_preview,
        }
    }

    pub fn is_stable(&self) -> bool {
        match *self {
            Edition::Edition2015 => true,
            Edition::Edition2018 => true,
            Edition::Edition2021 => false,
        }
    }
}

impl FromStr for Edition {
    type Err = ();
    fn from_str(s: &str) -> Result<Self, ()> {
        match s {
            "2015" => Ok(Edition::Edition2015),
            "2018" => Ok(Edition::Edition2018),
            "2021" => Ok(Edition::Edition2021),
            _ => Err(()),
        }
    }
}
<|endoftext|>


<commit_before>use crate::error::Result;
use std::{fs::File,
          io::{BufReader,
               Read},
          path::Path,
          ptr};

const BUF_SIZE: usize = 1024;

/// Calculate the BLAKE2b hash of a file, return as a hex string
/// digest size = 32 BYTES
/// NOTE: the hashing is keyless
pub fn hash_file<P>(filename: P) -> Result<String>
    where P: AsRef<Path>
{
    let file = File::open(filename.as_ref())?;
    let mut reader = BufReader::new(file);
    hash_reader(&mut reader)
}

pub fn hash_string(data: &str) -> String {
    let mut out = [0u8; libsodium_sys::crypto_generichash_BYTES as usize];
    let mut st = vec![0u8; unsafe { libsodium_sys::crypto_generichash_statebytes() }];
    #[allow(clippy::cast_ptr_alignment)]
    let pst = st.as_mut_ptr() as *mut libsodium_sys::crypto_generichash_state;
    unsafe {
        libsodium_sys::crypto_generichash_init(pst, ptr::null_mut(), 0, out.len());
        libsodium_sys::crypto_generichash_update(pst, data[..].as_ptr(), data.len() as u64);
        libsodium_sys::crypto_generichash_final(pst, out.as_mut_ptr(), out.len());
    }
    hex::encode(out)
}

pub fn hash_bytes(data: &[u8]) -> String {
    let mut out = [0u8; libsodium_sys::crypto_generichash_BYTES as usize];
    let mut st = vec![0u8; unsafe { libsodium_sys::crypto_generichash_statebytes() }];
    #[allow(clippy::cast_ptr_alignment)]
    let pst = st.as_mut_ptr() as *mut libsodium_sys::crypto_generichash_state;
    unsafe {
        libsodium_sys::crypto_generichash_init(pst, ptr::null_mut(), 0, out.len());
        libsodium_sys::crypto_generichash_update(pst, data[..].as_ptr(), data.len() as u64);
        libsodium_sys::crypto_generichash_final(pst, out.as_mut_ptr(), out.len());
    }
    hex::encode(out)
}

pub fn hash_reader(reader: &mut BufReader<File>) -> Result<String> {
    let mut out = [0u8; libsodium_sys::crypto_generichash_BYTES as usize];
    let mut st = vec![0u8; unsafe { libsodium_sys::crypto_generichash_statebytes() }];
    #[allow(clippy::cast_ptr_alignment)]
    let pst = st.as_mut_ptr() as *mut libsodium_sys::crypto_generichash_state;
    unsafe {
        libsodium_sys::crypto_generichash_init(pst, ptr::null_mut(), 0, out.len());
    }
    let mut buf = [0u8; BUF_SIZE];
    loop {
        let bytes_read = reader.read(&mut buf)?;
        if bytes_read == 0 {
            break;
        }
        let chunk = &buf[0..bytes_read];
        unsafe {
            libsodium_sys::crypto_generichash_update(pst, chunk.as_ptr(), chunk.len() as u64);
        }
    }
    unsafe {
        libsodium_sys::crypto_generichash_final(pst, out.as_mut_ptr(), out.len());
    }
    Ok(hex::encode(out))
}

#[cfg(test)]
mod test {
    #[allow(unused_imports)]
    use std::fs::{self,
                  File};
    #[allow(unused_imports)]
    use std::io;
    use std::{env,
              path::PathBuf};

    use super::{super::test_support::*,
                *};
    #[cfg(feature = "functional")]
    use hyper::{header,
                Client,
                Url};

    #[allow(dead_code)]
    fn mk_local_tmpdir() -> PathBuf {
        let dir = env::current_exe().unwrap()
                                    .parent()
                                    .unwrap()
                                    .parent()
                                    .unwrap()
                                    .parent()
                                    .unwrap()
                                    .parent()
                                    .unwrap()
                                    .parent()
                                    .unwrap()
                                    .join("tmp");
        fs::create_dir_all(&dir).unwrap();
        dir
    }

    #[test]
    fn hash_file_working() {
        // The expected values were computed using the `b2sum` program from
        // https://github.com/dchest/b2sum using the the `-s=32` option. For example:
        //      b2sum -s=32 signme.dat

        let computed = hash_file(&fixture("signme.dat")).unwrap();
        let expected = "20590a52c4f00588c500328b16d466c982a26fabaa5fa4dcc83052dd0a84f233";
        assert_eq!(computed, expected);

        let computed = hash_file(&fixture("happyhumans-20160424223347.sig.key")).unwrap();
        let expected = "e966844bbc50b7a3a6d81e94dd38e27b92814b944095a8e55f1780921bfcfbe1";
        assert_eq!(computed, expected);

        let computed = hash_file(&fixture("happyhumans-20160424223347.pub")).unwrap();
        let expected = "b80c4f412f9a0a7727b6e6f115e1b5fa3bae79ad2fcf47f769ed4e42cfb12265";
        assert_eq!(computed, expected);
    }

    #[test]
    #[cfg(feature = "functional")]
    fn hash_file_large_binary() {
        let url = "http://ftp.kernel.org/pub/linux/kernel/v4.x/linux-4.3.tar.gz";
        let dst: PathBuf = {
            let file = mk_local_tmpdir().join(url.rsplit('/').next().unwrap());
            if !file.is_file() {
                let client = match env::var("http_proxy") {
                    Ok(url) => {
                        let url = Url::parse(&url).unwrap();
                        Client::with_http_proxy(url.host_str().unwrap().to_string(),
                                                url.port_or_known_default().unwrap())
                    }
                    _ => Client::new(),
                };
                let mut response = client.get(url)
                                         .header(header::Connection::close())
                                         .send()
                                         .unwrap();
                let mut f = File::create(&file).unwrap();
                io::copy(&mut response, &mut f).unwrap();
            }
            file
        };
        let computed = hash_file(&dst);
        let expected = "ba640dc063f0ed27e60b38dbb7cf19778cf7805d9fc91eb129fb68b409d46414";
        assert_eq!(computed, expected);
    }
}
<commit_msg>[REFACTOR] Loosen parameter typing on `hash_reader`<commit_after>use crate::error::Result;
use std::{fs::File,
          io::{BufReader,
               Read},
          path::Path,
          ptr};

const BUF_SIZE: usize = 1024;

/// Calculate the BLAKE2b hash of a file, return as a hex string
/// digest size = 32 BYTES
/// NOTE: the hashing is keyless
pub fn hash_file<P>(filename: P) -> Result<String>
    where P: AsRef<Path>
{
    let file = File::open(filename.as_ref())?;
    let mut reader = BufReader::new(file);
    hash_reader(&mut reader)
}

pub fn hash_string(data: &str) -> String {
    let mut out = [0u8; libsodium_sys::crypto_generichash_BYTES as usize];
    let mut st = vec![0u8; unsafe { libsodium_sys::crypto_generichash_statebytes() }];
    #[allow(clippy::cast_ptr_alignment)]
    let pst = st.as_mut_ptr() as *mut libsodium_sys::crypto_generichash_state;
    unsafe {
        libsodium_sys::crypto_generichash_init(pst, ptr::null_mut(), 0, out.len());
        libsodium_sys::crypto_generichash_update(pst, data[..].as_ptr(), data.len() as u64);
        libsodium_sys::crypto_generichash_final(pst, out.as_mut_ptr(), out.len());
    }
    hex::encode(out)
}

pub fn hash_bytes(data: &[u8]) -> String {
    let mut out = [0u8; libsodium_sys::crypto_generichash_BYTES as usize];
    let mut st = vec![0u8; unsafe { libsodium_sys::crypto_generichash_statebytes() }];
    #[allow(clippy::cast_ptr_alignment)]
    let pst = st.as_mut_ptr() as *mut libsodium_sys::crypto_generichash_state;
    unsafe {
        libsodium_sys::crypto_generichash_init(pst, ptr::null_mut(), 0, out.len());
        libsodium_sys::crypto_generichash_update(pst, data[..].as_ptr(), data.len() as u64);
        libsodium_sys::crypto_generichash_final(pst, out.as_mut_ptr(), out.len());
    }
    hex::encode(out)
}

pub fn hash_reader(reader: &mut dyn Read) -> Result<String> {
    let mut out = [0u8; libsodium_sys::crypto_generichash_BYTES as usize];
    let mut st = vec![0u8; unsafe { libsodium_sys::crypto_generichash_statebytes() }];
    #[allow(clippy::cast_ptr_alignment)]
    let pst = st.as_mut_ptr() as *mut libsodium_sys::crypto_generichash_state;
    unsafe {
        libsodium_sys::crypto_generichash_init(pst, ptr::null_mut(), 0, out.len());
    }
    let mut buf = [0u8; BUF_SIZE];
    loop {
        let bytes_read = reader.read(&mut buf)?;
        if bytes_read == 0 {
            break;
        }
        let chunk = &buf[0..bytes_read];
        unsafe {
            libsodium_sys::crypto_generichash_update(pst, chunk.as_ptr(), chunk.len() as u64);
        }
    }
    unsafe {
        libsodium_sys::crypto_generichash_final(pst, out.as_mut_ptr(), out.len());
    }
    Ok(hex::encode(out))
}

#[cfg(test)]
mod test {
    #[allow(unused_imports)]
    use std::fs::{self,
                  File};
    #[allow(unused_imports)]
    use std::io;
    use std::{env,
              path::PathBuf};

    use super::{super::test_support::*,
                *};
    #[cfg(feature = "functional")]
    use hyper::{header,
                Client,
                Url};

    #[allow(dead_code)]
    fn mk_local_tmpdir() -> PathBuf {
        let dir = env::current_exe().unwrap()
                                    .parent()
                                    .unwrap()
                                    .parent()
                                    .unwrap()
                                    .parent()
                                    .unwrap()
                                    .parent()
                                    .unwrap()
                                    .parent()
                                    .unwrap()
                                    .join("tmp");
        fs::create_dir_all(&dir).unwrap();
        dir
    }

    #[test]
    fn hash_file_working() {
        // The expected values were computed using the `b2sum` program from
        // https://github.com/dchest/b2sum using the the `-s=32` option. For example:
        //      b2sum -s=32 signme.dat

        let computed = hash_file(&fixture("signme.dat")).unwrap();
        let expected = "20590a52c4f00588c500328b16d466c982a26fabaa5fa4dcc83052dd0a84f233";
        assert_eq!(computed, expected);

        let computed = hash_file(&fixture("happyhumans-20160424223347.sig.key")).unwrap();
        let expected = "e966844bbc50b7a3a6d81e94dd38e27b92814b944095a8e55f1780921bfcfbe1";
        assert_eq!(computed, expected);

        let computed = hash_file(&fixture("happyhumans-20160424223347.pub")).unwrap();
        let expected = "b80c4f412f9a0a7727b6e6f115e1b5fa3bae79ad2fcf47f769ed4e42cfb12265";
        assert_eq!(computed, expected);
    }

    #[test]
    #[cfg(feature = "functional")]
    fn hash_file_large_binary() {
        let url = "http://ftp.kernel.org/pub/linux/kernel/v4.x/linux-4.3.tar.gz";
        let dst: PathBuf = {
            let file = mk_local_tmpdir().join(url.rsplit('/').next().unwrap());
            if !file.is_file() {
                let client = match env::var("http_proxy") {
                    Ok(url) => {
                        let url = Url::parse(&url).unwrap();
                        Client::with_http_proxy(url.host_str().unwrap().to_string(),
                                                url.port_or_known_default().unwrap())
                    }
                    _ => Client::new(),
                };
                let mut response = client.get(url)
                                         .header(header::Connection::close())
                                         .send()
                                         .unwrap();
                let mut f = File::create(&file).unwrap();
                io::copy(&mut response, &mut f).unwrap();
            }
            file
        };
        let computed = hash_file(&dst);
        let expected = "ba640dc063f0ed27e60b38dbb7cf19778cf7805d9fc91eb129fb68b409d46414";
        assert_eq!(computed, expected);
    }
}
<|endoftext|>


<commit_before>use crate::consts::{constant, Constant};
use crate::utils::usage::mutated_variables;
use crate::utils::{
    eq_expr_value, higher, match_def_path, multispan_sugg, paths, qpath_res, snippet, span_lint_and_then,
};

use if_chain::if_chain;
use rustc_ast::ast::LitKind;
use rustc_hir::def::Res;
use rustc_hir::intravisit::{walk_expr, NestedVisitorMap, Visitor};
use rustc_hir::BinOpKind;
use rustc_hir::{BorrowKind, Expr, ExprKind};
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::hir::map::Map;
use rustc_middle::ty;
use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::source_map::Spanned;
use rustc_span::Span;

declare_clippy_lint! {
    /// **What it does:**
    /// Suggests using `strip_{prefix,suffix}` over `str::{starts,ends}_with` and slicing using
    /// the pattern's length.
    ///
    /// **Why is this bad?**
    /// Using `str:strip_{prefix,suffix}` is safer and may have better performance as there is no
    /// slicing which may panic and the compiler does not need to insert this panic code. It is
    /// also sometimes more readable as it removes the need for duplicating or storing the pattern
    /// used by `str::{starts,ends}_with` and in the slicing.
    ///
    /// **Known problems:**
    /// None.
    ///
    /// **Example:**
    ///
    /// ```rust
    /// let s = "hello, world!";
    /// if s.starts_with("hello, ") {
    ///     assert_eq!(s["hello, ".len()..].to_uppercase(), "WORLD!");
    /// }
    /// ```
    /// Use instead:
    /// ```rust
    /// let s = "hello, world!";
    /// if let Some(end) = s.strip_prefix("hello, ") {
    ///     assert_eq!(end.to_uppercase(), "WORLD!");
    /// }
    /// ```
    pub MANUAL_STRIP,
    complexity,
    "suggests using `strip_{prefix,suffix}` over `str::{starts,ends}_with` and slicing"
}

declare_lint_pass!(ManualStrip => [MANUAL_STRIP]);

#[derive(Clone, Copy, Debug, Eq, PartialEq)]
enum StripKind {
    Prefix,
    Suffix,
}

impl<'tcx> LateLintPass<'tcx> for ManualStrip {
    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
        if_chain! {
            if let Some((cond, then, _)) = higher::if_block(&expr);
            if let ExprKind::MethodCall(_, _, [target_arg, pattern], _) = cond.kind;
            if let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(cond.hir_id);
            if let ExprKind::Path(target_path) = &target_arg.kind;
            then {
                let strip_kind = if match_def_path(cx, method_def_id, &paths::STR_STARTS_WITH) {
                    StripKind::Prefix
                } else if match_def_path(cx, method_def_id, &paths::STR_ENDS_WITH) {
                    StripKind::Suffix
                } else {
                    return;
                };
                let target_res = qpath_res(cx, &target_path, target_arg.hir_id);
                if target_res == Res::Err {
                    return;
                };

                if_chain! {
                    if let Res::Local(hir_id) = target_res;
                    if let Some(used_mutably) = mutated_variables(then, cx);
                    if used_mutably.contains(&hir_id);
                    then {
                        return;
                    }
                }

                let strippings = find_stripping(cx, strip_kind, target_res, pattern, then);
                if !strippings.is_empty() {

                    let kind_word = match strip_kind {
                        StripKind::Prefix => "prefix",
                        StripKind::Suffix => "suffix",
                    };

                    let test_span = expr.span.until(then.span);
                    span_lint_and_then(cx, MANUAL_STRIP, strippings[0], &format!("stripping a {} manually", kind_word), |diag| {
                        diag.span_note(test_span, &format!("the {} was tested here", kind_word));
                        multispan_sugg(
                            diag,
                            &format!("try using the `strip_{}` method", kind_word),
                            vec![(test_span,
                                  format!("if let Some(<stripped>) = {}.strip_{}({}) ",
                                          snippet(cx, target_arg.span, ".."),
                                          kind_word,
                                          snippet(cx, pattern.span, "..")))]
                            .into_iter().chain(strippings.into_iter().map(|span| (span, "<stripped>".into()))),
                        )
                    });
                }
            }
        }
    }
}

// Returns `Some(arg)` if `expr` matches `arg.len()` and `None` otherwise.
fn len_arg<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<&'tcx Expr<'tcx>> {
    if_chain! {
        if let ExprKind::MethodCall(_, _, [arg], _) = expr.kind;
        if let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id);
        if match_def_path(cx, method_def_id, &paths::STR_LEN);
        then {
            Some(arg)
        }
        else {
            None
        }
    }
}

// Returns the length of the `expr` if it's a constant string or char.
fn constant_length(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<u128> {
    let (value, _) = constant(cx, cx.typeck_results(), expr)?;
    match value {
        Constant::Str(value) => Some(value.len() as u128),
        Constant::Char(value) => Some(value.len_utf8() as u128),
        _ => None,
    }
}

// Tests if `expr` equals the length of the pattern.
fn eq_pattern_length<'tcx>(cx: &LateContext<'tcx>, pattern: &Expr<'_>, expr: &'tcx Expr<'_>) -> bool {
    if let ExprKind::Lit(Spanned {
        node: LitKind::Int(n, _),
        ..
    }) = expr.kind
    {
        constant_length(cx, pattern).map_or(false, |length| length == n)
    } else {
        len_arg(cx, expr).map_or(false, |arg| eq_expr_value(cx, pattern, arg))
    }
}

// Tests if `expr` is a `&str`.
fn is_ref_str(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
    match cx.typeck_results().expr_ty_adjusted(&expr).kind() {
        ty::Ref(_, ty, _) => ty.is_str(),
        _ => false,
    }
}

// Removes the outer `AddrOf` expression if needed.
fn peel_ref<'a>(expr: &'a Expr<'_>) -> &'a Expr<'a> {
    if let ExprKind::AddrOf(BorrowKind::Ref, _, unref) = &expr.kind {
        unref
    } else {
        expr
    }
}

// Find expressions where `target` is stripped using the length of `pattern`.
// We'll suggest replacing these expressions with the result of the `strip_{prefix,suffix}`
// method.
fn find_stripping<'tcx>(
    cx: &LateContext<'tcx>,
    strip_kind: StripKind,
    target: Res,
    pattern: &'tcx Expr<'_>,
    expr: &'tcx Expr<'_>,
) -> Vec<Span> {
    struct StrippingFinder<'a, 'tcx> {
        cx: &'a LateContext<'tcx>,
        strip_kind: StripKind,
        target: Res,
        pattern: &'tcx Expr<'tcx>,
        results: Vec<Span>,
    }

    impl<'a, 'tcx> Visitor<'tcx> for StrippingFinder<'a, 'tcx> {
        type Map = Map<'tcx>;
        fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
            NestedVisitorMap::None
        }

        fn visit_expr(&mut self, ex: &'tcx Expr<'_>) {
            if_chain! {
                if is_ref_str(self.cx, ex);
                let unref = peel_ref(ex);
                if let ExprKind::Index(indexed, index) = &unref.kind;
                if let Some(range) = higher::range(index);
                if let higher::Range { start, end, .. } = range;
                if let ExprKind::Path(path) = &indexed.kind;
                if qpath_res(self.cx, path, ex.hir_id) == self.target;
                then {
                    match (self.strip_kind, start, end) {
                        (StripKind::Prefix, Some(start), None) => {
                            if eq_pattern_length(self.cx, self.pattern, start) {
                                self.results.push(ex.span);
                                return;
                            }
                        },
                        (StripKind::Suffix, None, Some(end)) => {
                            if_chain! {
                                if let ExprKind::Binary(Spanned { node: BinOpKind::Sub, .. }, left, right) = end.kind;
                                if let Some(left_arg) = len_arg(self.cx, left);
                                if let ExprKind::Path(left_path) = &left_arg.kind;
                                if qpath_res(self.cx, left_path, left_arg.hir_id) == self.target;
                                if eq_pattern_length(self.cx, self.pattern, right);
                                then {
                                    self.results.push(ex.span);
                                    return;
                                }
                            }
                        },
                        _ => {}
                    }
                }
            }

            walk_expr(self, ex);
        }
    }

    let mut finder = StrippingFinder {
        cx,
        strip_kind,
        target,
        pattern,
        results: vec![],
    };
    walk_expr(&mut finder, expr);
    finder.results
}
<commit_msg>manual-strip: Fix formatting<commit_after>use crate::consts::{constant, Constant};
use crate::utils::usage::mutated_variables;
use crate::utils::{
    eq_expr_value, higher, match_def_path, multispan_sugg, paths, qpath_res, snippet, span_lint_and_then,
};

use if_chain::if_chain;
use rustc_ast::ast::LitKind;
use rustc_hir::def::Res;
use rustc_hir::intravisit::{walk_expr, NestedVisitorMap, Visitor};
use rustc_hir::BinOpKind;
use rustc_hir::{BorrowKind, Expr, ExprKind};
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::hir::map::Map;
use rustc_middle::ty;
use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::source_map::Spanned;
use rustc_span::Span;

declare_clippy_lint! {
    /// **What it does:**
    /// Suggests using `strip_{prefix,suffix}` over `str::{starts,ends}_with` and slicing using
    /// the pattern's length.
    ///
    /// **Why is this bad?**
    /// Using `str:strip_{prefix,suffix}` is safer and may have better performance as there is no
    /// slicing which may panic and the compiler does not need to insert this panic code. It is
    /// also sometimes more readable as it removes the need for duplicating or storing the pattern
    /// used by `str::{starts,ends}_with` and in the slicing.
    ///
    /// **Known problems:**
    /// None.
    ///
    /// **Example:**
    ///
    /// ```rust
    /// let s = "hello, world!";
    /// if s.starts_with("hello, ") {
    ///     assert_eq!(s["hello, ".len()..].to_uppercase(), "WORLD!");
    /// }
    /// ```
    /// Use instead:
    /// ```rust
    /// let s = "hello, world!";
    /// if let Some(end) = s.strip_prefix("hello, ") {
    ///     assert_eq!(end.to_uppercase(), "WORLD!");
    /// }
    /// ```
    pub MANUAL_STRIP,
    complexity,
    "suggests using `strip_{prefix,suffix}` over `str::{starts,ends}_with` and slicing"
}

declare_lint_pass!(ManualStrip => [MANUAL_STRIP]);

#[derive(Clone, Copy, Debug, Eq, PartialEq)]
enum StripKind {
    Prefix,
    Suffix,
}

impl<'tcx> LateLintPass<'tcx> for ManualStrip {
    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
        if_chain! {
            if let Some((cond, then, _)) = higher::if_block(&expr);
            if let ExprKind::MethodCall(_, _, [target_arg, pattern], _) = cond.kind;
            if let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(cond.hir_id);
            if let ExprKind::Path(target_path) = &target_arg.kind;
            then {
                let strip_kind = if match_def_path(cx, method_def_id, &paths::STR_STARTS_WITH) {
                    StripKind::Prefix
                } else if match_def_path(cx, method_def_id, &paths::STR_ENDS_WITH) {
                    StripKind::Suffix
                } else {
                    return;
                };
                let target_res = qpath_res(cx, &target_path, target_arg.hir_id);
                if target_res == Res::Err {
                    return;
                };

                if_chain! {
                    if let Res::Local(hir_id) = target_res;
                    if let Some(used_mutably) = mutated_variables(then, cx);
                    if used_mutably.contains(&hir_id);
                    then {
                        return;
                    }
                }

                let strippings = find_stripping(cx, strip_kind, target_res, pattern, then);
                if !strippings.is_empty() {

                    let kind_word = match strip_kind {
                        StripKind::Prefix => "prefix",
                        StripKind::Suffix => "suffix",
                    };

                    let test_span = expr.span.until(then.span);
                    span_lint_and_then(cx, MANUAL_STRIP, strippings[0], &format!("stripping a {} manually", kind_word), |diag| {
                        diag.span_note(test_span, &format!("the {} was tested here", kind_word));
                        multispan_sugg(
                            diag,
                            &format!("try using the `strip_{}` method", kind_word),
                            vec![(test_span,
                                  format!("if let Some(<stripped>) = {}.strip_{}({}) ",
                                          snippet(cx, target_arg.span, ".."),
                                          kind_word,
                                          snippet(cx, pattern.span, "..")))]
                            .into_iter().chain(strippings.into_iter().map(|span| (span, "<stripped>".into()))),
                        )
                    });
                }
            }
        }
    }
}

// Returns `Some(arg)` if `expr` matches `arg.len()` and `None` otherwise.
fn len_arg<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<&'tcx Expr<'tcx>> {
    if_chain! {
        if let ExprKind::MethodCall(_, _, [arg], _) = expr.kind;
        if let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id);
        if match_def_path(cx, method_def_id, &paths::STR_LEN);
        then {
            Some(arg)
        } else {
            None
        }
    }
}

// Returns the length of the `expr` if it's a constant string or char.
fn constant_length(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<u128> {
    let (value, _) = constant(cx, cx.typeck_results(), expr)?;
    match value {
        Constant::Str(value) => Some(value.len() as u128),
        Constant::Char(value) => Some(value.len_utf8() as u128),
        _ => None,
    }
}

// Tests if `expr` equals the length of the pattern.
fn eq_pattern_length<'tcx>(cx: &LateContext<'tcx>, pattern: &Expr<'_>, expr: &'tcx Expr<'_>) -> bool {
    if let ExprKind::Lit(Spanned {
        node: LitKind::Int(n, _),
        ..
    }) = expr.kind
    {
        constant_length(cx, pattern).map_or(false, |length| length == n)
    } else {
        len_arg(cx, expr).map_or(false, |arg| eq_expr_value(cx, pattern, arg))
    }
}

// Tests if `expr` is a `&str`.
fn is_ref_str(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
    match cx.typeck_results().expr_ty_adjusted(&expr).kind() {
        ty::Ref(_, ty, _) => ty.is_str(),
        _ => false,
    }
}

// Removes the outer `AddrOf` expression if needed.
fn peel_ref<'a>(expr: &'a Expr<'_>) -> &'a Expr<'a> {
    if let ExprKind::AddrOf(BorrowKind::Ref, _, unref) = &expr.kind {
        unref
    } else {
        expr
    }
}

// Find expressions where `target` is stripped using the length of `pattern`.
// We'll suggest replacing these expressions with the result of the `strip_{prefix,suffix}`
// method.
fn find_stripping<'tcx>(
    cx: &LateContext<'tcx>,
    strip_kind: StripKind,
    target: Res,
    pattern: &'tcx Expr<'_>,
    expr: &'tcx Expr<'_>,
) -> Vec<Span> {
    struct StrippingFinder<'a, 'tcx> {
        cx: &'a LateContext<'tcx>,
        strip_kind: StripKind,
        target: Res,
        pattern: &'tcx Expr<'tcx>,
        results: Vec<Span>,
    }

    impl<'a, 'tcx> Visitor<'tcx> for StrippingFinder<'a, 'tcx> {
        type Map = Map<'tcx>;
        fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
            NestedVisitorMap::None
        }

        fn visit_expr(&mut self, ex: &'tcx Expr<'_>) {
            if_chain! {
                if is_ref_str(self.cx, ex);
                let unref = peel_ref(ex);
                if let ExprKind::Index(indexed, index) = &unref.kind;
                if let Some(range) = higher::range(index);
                if let higher::Range { start, end, .. } = range;
                if let ExprKind::Path(path) = &indexed.kind;
                if qpath_res(self.cx, path, ex.hir_id) == self.target;
                then {
                    match (self.strip_kind, start, end) {
                        (StripKind::Prefix, Some(start), None) => {
                            if eq_pattern_length(self.cx, self.pattern, start) {
                                self.results.push(ex.span);
                                return;
                            }
                        },
                        (StripKind::Suffix, None, Some(end)) => {
                            if_chain! {
                                if let ExprKind::Binary(Spanned { node: BinOpKind::Sub, .. }, left, right) = end.kind;
                                if let Some(left_arg) = len_arg(self.cx, left);
                                if let ExprKind::Path(left_path) = &left_arg.kind;
                                if qpath_res(self.cx, left_path, left_arg.hir_id) == self.target;
                                if eq_pattern_length(self.cx, self.pattern, right);
                                then {
                                    self.results.push(ex.span);
                                    return;
                                }
                            }
                        },
                        _ => {}
                    }
                }
            }

            walk_expr(self, ex);
        }
    }

    let mut finder = StrippingFinder {
        cx,
        strip_kind,
        target,
        pattern,
        results: vec![],
    };
    walk_expr(&mut finder, expr);
    finder.results
}
<|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::str::CharIndices;

// support arguments like '4', 'ab', '4.0', '>=10.14', '*123'
fn acceptable_arg_character(c: char) -> bool {
    c.is_alphanumeric() || c == '.' || c == '-' || c == '<' || c == '>' || c == '=' || c == '*'
}

// A crappy parser for parsing strings like "translate(1, 3) blahblah"
// Returns a tuple with three components:
// - First component is the function name (e.g. "translate")
// - Second component is the list of arguments (e.g. vec!["1", "3"])
// - Third component is the rest of the string "blahblah"
pub fn parse_function(s: &str) -> (&str, Vec<&str>, &str) {
    // XXX: This is not particularly easy to read. Sorry.
    struct Parser<'a> {
        itr: CharIndices<'a>,
        start: usize,
        o: Option<(usize, char)>,
    }
    impl<'a> Parser<'a> {
        fn skip_whitespace(&mut self) {
            while let Some(k) = self.o {
                if !k.1.is_whitespace() {
                    break;
                }
                self.start = k.0 + k.1.len_utf8();
                self.o = self.itr.next();
            }
        }
    }
    let mut c = s.char_indices();
    let o = c.next();
    let mut p = Parser {
        itr: c,
        start: 0,
        o: o,
    };

    p.skip_whitespace();

    let mut end = p.start;
    while let Some(k) = p.o {
        if !k.1.is_alphabetic() && k.1 != '_' && k.1 != '-' {
            break;
        }
        end = k.0 + k.1.len_utf8();
        p.o = p.itr.next();
    }

    let name = &s[p.start .. end];
    let mut args = Vec::new();

    p.skip_whitespace();

    if let Some(k) = p.o {
        if k.1 != '(' {
            return (name, args, &s[p.start ..]);
        }
        p.start = k.0 + k.1.len_utf8();
        p.o = p.itr.next();
    }

    loop {
        p.skip_whitespace();

        let mut end = p.start;
        let mut bracket_count: i32 = 0;
        while let Some(k) = p.o {
            let prev_bracket_count = bracket_count;
            if k.1 == '[' {
                bracket_count = bracket_count + 1;
            } else if k.1 == ']' {
                bracket_count = bracket_count - 1;
            }

            if bracket_count < 0 {
                println!("Unexpected closing bracket");
                break;
            }

            let not_in_bracket = bracket_count == 0 && prev_bracket_count == 0;
            if !acceptable_arg_character(k.1) && not_in_bracket {
                break;
            }
            end = k.0 + k.1.len_utf8();
            p.o = p.itr.next();
        }

        args.push(&s[p.start .. end]);

        p.skip_whitespace();

        if let Some(k) = p.o {
            p.start = k.0 + k.1.len_utf8();
            p.o = p.itr.next();
            // unless we find a comma we're done
            if k.1 != ',' {
                if k.1 != ')' {
                    println!("Unexpected closing character: {}", k.1);
                }
                break;
            }
        } else {
            break;
        }
    }
    (name, args, &s[p.start ..])
}

#[test]
fn test() {
    assert_eq!(parse_function("rotate(40)").0, "rotate");
    assert_eq!(parse_function("  rotate(40)").0, "rotate");
    assert_eq!(parse_function("  rotate  (40)").0, "rotate");
    assert_eq!(parse_function("  rotate  (  40 )").1[0], "40");
    assert_eq!(parse_function("rotate(-40.0)").1[0], "-40.0");
    assert_eq!(parse_function("drop-shadow(0, [1, 2, 3, 4], 5)").1[0], "0");
    assert_eq!(parse_function("drop-shadow(0, [1, 2, 3, 4], 5)").1[1], "[1, 2, 3, 4]");
    assert_eq!(parse_function("drop-shadow(0, [1, 2, 3, 4], 5)").1[2], "5");
    assert_eq!(parse_function("drop-shadow(0, [1, 2, [3, 4]], 5)").1[1], "[1, 2, [3, 4]]");
}
<commit_msg>Bug 1637859 - support nesting in wrench's reftest function parser. r=jrmuizel<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::str::CharIndices;

// support arguments like '4', 'ab', '4.0', '>=10.14', '*123'
fn acceptable_arg_character(c: char) -> bool {
    c.is_alphanumeric() || c == '.' || c == '-' || c == '<' || c == '>' || c == '=' || c == '*'
}

// A crappy parser for parsing strings like "translate(1, 3) blahblah"
// Returns a tuple with three components:
// - First component is the function name (e.g. "translate")
// - Second component is the list of arguments (e.g. vec!["1", "3"])
// - Third component is the rest of the string "blahblah"
pub fn parse_function(s: &str) -> (&str, Vec<&str>, &str) {
    // XXX: This is not particularly easy to read. Sorry.
    struct Parser<'a> {
        itr: CharIndices<'a>,
        start: usize,
        o: Option<(usize, char)>,
    }
    impl<'a> Parser<'a> {
        fn skip_whitespace(&mut self) {
            while let Some(k) = self.o {
                if !k.1.is_whitespace() {
                    break;
                }
                self.start = k.0 + k.1.len_utf8();
                self.o = self.itr.next();
            }
        }
    }
    let mut c = s.char_indices();
    let o = c.next();
    let mut p = Parser {
        itr: c,
        start: 0,
        o: o,
    };

    p.skip_whitespace();

    let mut end = p.start;
    while let Some(k) = p.o {
        if !k.1.is_alphabetic() && k.1 != '_' && k.1 != '-' {
            break;
        }
        end = k.0 + k.1.len_utf8();
        p.o = p.itr.next();
    }

    let name = &s[p.start .. end];
    let mut args = Vec::new();

    p.skip_whitespace();

    if let Some(k) = p.o {
        if k.1 != '(' {
            return (name, args, &s[p.start ..]);
        }
        p.start = k.0 + k.1.len_utf8();
        p.o = p.itr.next();
    }

    loop {
        p.skip_whitespace();

        let mut end = p.start;
        let mut brackets: Vec<char> = Vec::new();
        while let Some(k) = p.o {
            let prev_bracket_count = brackets.len();
            match k.1 {
                '[' | '(' => brackets.push(k.1),
                ']' | ')' => {
                    let open_bracket = match k.1 {
                        ']' => '[',
                        ')' => '(',
                        _ => panic!(),
                    };
                    match brackets.pop() {
                        // Allow final closing ) for command invocation after args
                        None if k.1 == ')' => break,
                        Some(bracket) if bracket == open_bracket => {}
                        _ => panic!("Unexpected closing bracket {}", k.1),
                    }
                }
                _ => {}
            }

            let not_in_bracket = brackets.len() == 0 && prev_bracket_count == 0;
            if !acceptable_arg_character(k.1) && not_in_bracket {
                break;
            }
            end = k.0 + k.1.len_utf8();
            p.o = p.itr.next();
        }

        args.push(&s[p.start .. end]);

        p.skip_whitespace();

        if let Some(k) = p.o {
            p.start = k.0 + k.1.len_utf8();
            p.o = p.itr.next();
            // unless we find a comma we're done
            if k.1 != ',' {
                if k.1 != ')' {
                    panic!("Unexpected closing character: {}", k.1);
                }
                break;
            }
        } else {
            break;
        }
    }
    (name, args, &s[p.start ..])
}

#[test]
fn test() {
    assert_eq!(parse_function("rotate(40)").0, "rotate");
    assert_eq!(parse_function("  rotate(40)").0, "rotate");
    assert_eq!(parse_function("  rotate  (40)").0, "rotate");
    assert_eq!(parse_function("  rotate  (  40 )").1[0], "40");
    assert_eq!(parse_function("rotate(-40.0)").1[0], "-40.0");
    assert_eq!(parse_function("drop-shadow(0, [1, 2, 3, 4], 5)").1[0], "0");
    assert_eq!(parse_function("drop-shadow(0, [1, 2, 3, 4], 5)").1[1], "[1, 2, 3, 4]");
    assert_eq!(parse_function("drop-shadow(0, [1, 2, 3, 4], 5)").1[2], "5");
    assert_eq!(parse_function("drop-shadow(0, [1, 2, [3, 4]], 5)").1[1], "[1, 2, [3, 4]]");
    assert_eq!(parse_function("func(nest([1, 2]), [3, 4])").1[0], "nest([1, 2])");
    assert_eq!(parse_function("func(nest([1, 2]), [nest(3), nest(4)])").1[1], "[nest(3), nest(4)]");
}
<|endoftext|>


<commit_before>use crate::attr;
use proc_macro2::TokenStream;
use quote::{format_ident, quote, quote_spanned};
use syn::spanned::Spanned;
use syn::{
    Data, DataEnum, DataStruct, DeriveInput, Error, Field, Fields, Ident, Index, Member, Result,
    Type,
};

pub fn derive(input: &DeriveInput) -> Result<TokenStream> {
    match &input.data {
        Data::Struct(data) => impl_struct(input, data),
        Data::Enum(data) => impl_enum(input, data),
        Data::Union(_) => Err(Error::new_spanned(
            input,
            "union as errors are not supported",
        )),
    }
}

fn impl_struct(input: &DeriveInput, data: &DataStruct) -> Result<TokenStream> {
    let ty = &input.ident;
    let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();

    let source = source_member(&data.fields)?;
    let source_method = source.map(|source| {
        let member = quote_spanned!(source.span()=> self.#source);
        quote! {
            fn source(&self) -> std::option::Option<&(dyn std::error::Error + 'static)> {
                use thiserror::private::AsDynError;
                std::option::Option::Some(#member.as_dyn_error())
            }
        }
    });

    let backtrace = backtrace_member(&data.fields)?;
    let backtrace_method = backtrace.map(|backtrace| {
        quote! {
            fn backtrace(&self) -> std::option::Option<&std::backtrace::Backtrace> {
                std::option::Option::Some(&self.#backtrace)
            }
        }
    });

    let struct_attrs = attr::get(&input.attrs)?;
    let display = struct_attrs.display.map(|display| {
        let pat = match &data.fields {
            Fields::Named(fields) => {
                let var = fields.named.iter().map(|field| &field.ident);
                quote!(Self { #(#var),* })
            }
            Fields::Unnamed(fields) => {
                let var = (0..fields.unnamed.len()).map(|i| format_ident!("_{}", i));
                quote!(Self(#(#var),*))
            }
            Fields::Unit => quote!(_),
        };
        quote! {
            impl #impl_generics std::fmt::Display for #ty #ty_generics #where_clause {
                fn fmt(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
                    #[allow(unused_variables)]
                    let #pat = self;
                    #display
                }
            }
        }
    });

    Ok(quote! {
        impl #impl_generics std::error::Error for #ty #ty_generics #where_clause {
            #source_method
            #backtrace_method
        }
        #display
    })
}

fn impl_enum(input: &DeriveInput, data: &DataEnum) -> Result<TokenStream> {
    let ty = &input.ident;
    let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();

    let variant_fields: Vec<&Fields> = data
        .variants
        .iter()
        .map(|variant| &variant.fields)
        .collect();

    let sources: Vec<Option<Member>> = variant_fields
        .iter()
        .copied()
        .map(source_member)
        .collect::<Result<_>>()?;

    let backtraces: Vec<Option<Member>> = variant_fields
        .iter()
        .copied()
        .map(backtrace_member)
        .collect::<Result<_>>()?;

    let source_method = if sources.iter().any(Option::is_some) {
        let arms = data.variants.iter().zip(sources).map(|(variant, source)| {
            let ident = &variant.ident;
            match source {
                Some(source) => quote! {
                    #ty::#ident {#source: source, ..} => std::option::Option::Some(source.as_dyn_error()),
                },
                None => quote! {
                    #ty::#ident {..} => std::option::Option::None,
                },
            }
        });
        Some(quote! {
            fn source(&self) -> std::option::Option<&(dyn std::error::Error + 'static)> {
                use thiserror::private::AsDynError;
                match self {
                    #(#arms)*
                }
            }
        })
    } else {
        None
    };

    let backtrace_method = if backtraces.iter().any(Option::is_some) {
        let arms = data.variants.iter().zip(backtraces).map(|(variant, backtrace)| {
            let ident = &variant.ident;
            match backtrace {
                Some(backtrace) => quote! {
                    #ty::#ident {#backtrace: backtrace, ..} => std::option::Option::Some(backtrace),
                },
                None => quote! {
                    #ty::#ident {..} => std::option::Option::None,
                },
            }
        });
        Some(quote! {
            fn backtrace(&self) -> std::option::Option<&std::backtrace::Backtrace> {
                match self {
                    #(#arms)*
                }
            }
        })
    } else {
        None
    };

    let variant_attrs = data
        .variants
        .iter()
        .map(|variant| attr::get(&variant.attrs))
        .collect::<Result<Vec<_>>>()?;
    let display = if variant_attrs.iter().any(|attrs| attrs.display.is_some()) {
        let arms = data
            .variants
            .iter()
            .zip(variant_attrs)
            .map(|(variant, attrs)| {
                let display = attrs.display.ok_or_else(|| {
                    Error::new_spanned(variant, "missing #[error(\"...\")] display attribute")
                })?;
                let ident = &variant.ident;
                Ok(match &variant.fields {
                    Fields::Named(fields) => {
                        let var = fields.named.iter().map(|field| &field.ident);
                        quote!(#ty::#ident { #(#var),* } => #display)
                    }
                    Fields::Unnamed(fields) => {
                        let var = (0..fields.unnamed.len()).map(|i| format_ident!("_{}", i));
                        quote!(#ty::#ident(#(#var),*) => #display)
                    }
                    Fields::Unit => quote!(#ty::#ident => #display),
                })
            })
            .collect::<Result<Vec<_>>>()?;
        Some(quote! {
            impl #impl_generics std::fmt::Display for #ty #ty_generics #where_clause {
                fn fmt(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
                    #[allow(unused_variables)]
                    match self {
                        #(#arms,)*
                    }
                }
            }
        })
    } else {
        None
    };

    Ok(quote! {
        impl #impl_generics std::error::Error for #ty #ty_generics #where_clause {
            #source_method
            #backtrace_method
        }
        #display
    })
}

fn source_member<'a>(fields: impl IntoIterator<Item = &'a Field>) -> Result<Option<Member>> {
    for (i, field) in fields.into_iter().enumerate() {
        let attrs = attr::get(&field.attrs)?;
        if attrs.source {
            return Ok(Some(member(i, &field.ident)));
        }
    }
    Ok(None)
}

fn backtrace_member<'a>(fields: impl IntoIterator<Item = &'a Field>) -> Result<Option<Member>> {
    for (i, field) in fields.into_iter().enumerate() {
        if type_is_backtrace(&field.ty) {
            return Ok(Some(member(i, &field.ident)));
        }
    }
    Ok(None)
}

fn type_is_backtrace(ty: &Type) -> bool {
    let path = match ty {
        Type::Path(ty) => &ty.path,
        _ => return false,
    };

    let last = path.segments.last().unwrap();
    last.ident == "Backtrace" && last.arguments.is_empty()
}

fn member(i: usize, ident: &Option<Ident>) -> Member {
    match ident {
        Some(ident) => Member::Named(ident.clone()),
        None => Member::Unnamed(Index::from(i)),
    }
}
<commit_msg>Restore support for rustc <1.36<commit_after>use crate::attr;
use proc_macro2::TokenStream;
use quote::{format_ident, quote, quote_spanned};
use syn::spanned::Spanned;
use syn::{
    Data, DataEnum, DataStruct, DeriveInput, Error, Field, Fields, Ident, Index, Member, Result,
    Type,
};

pub fn derive(input: &DeriveInput) -> Result<TokenStream> {
    match &input.data {
        Data::Struct(data) => impl_struct(input, data),
        Data::Enum(data) => impl_enum(input, data),
        Data::Union(_) => Err(Error::new_spanned(
            input,
            "union as errors are not supported",
        )),
    }
}

fn impl_struct(input: &DeriveInput, data: &DataStruct) -> Result<TokenStream> {
    let ty = &input.ident;
    let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();

    let source = source_member(&data.fields)?;
    let source_method = source.map(|source| {
        let member = quote_spanned!(source.span()=> self.#source);
        quote! {
            fn source(&self) -> std::option::Option<&(dyn std::error::Error + 'static)> {
                use thiserror::private::AsDynError;
                std::option::Option::Some(#member.as_dyn_error())
            }
        }
    });

    let backtrace = backtrace_member(&data.fields)?;
    let backtrace_method = backtrace.map(|backtrace| {
        quote! {
            fn backtrace(&self) -> std::option::Option<&std::backtrace::Backtrace> {
                std::option::Option::Some(&self.#backtrace)
            }
        }
    });

    let struct_attrs = attr::get(&input.attrs)?;
    let display = struct_attrs.display.map(|display| {
        let pat = match &data.fields {
            Fields::Named(fields) => {
                let var = fields.named.iter().map(|field| &field.ident);
                quote!(Self { #(#var),* })
            }
            Fields::Unnamed(fields) => {
                let var = (0..fields.unnamed.len()).map(|i| format_ident!("_{}", i));
                quote!(Self(#(#var),*))
            }
            Fields::Unit => quote!(_),
        };
        quote! {
            impl #impl_generics std::fmt::Display for #ty #ty_generics #where_clause {
                fn fmt(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
                    #[allow(unused_variables)]
                    let #pat = self;
                    #display
                }
            }
        }
    });

    Ok(quote! {
        impl #impl_generics std::error::Error for #ty #ty_generics #where_clause {
            #source_method
            #backtrace_method
        }
        #display
    })
}

fn impl_enum(input: &DeriveInput, data: &DataEnum) -> Result<TokenStream> {
    let ty = &input.ident;
    let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();

    let variant_fields: Vec<&Fields> = data
        .variants
        .iter()
        .map(|variant| &variant.fields)
        .collect();

    let sources: Vec<Option<Member>> = variant_fields
        .iter()
        .cloned()
        .map(source_member)
        .collect::<Result<_>>()?;

    let backtraces: Vec<Option<Member>> = variant_fields
        .iter()
        .cloned()
        .map(backtrace_member)
        .collect::<Result<_>>()?;

    let source_method = if sources.iter().any(Option::is_some) {
        let arms = data.variants.iter().zip(sources).map(|(variant, source)| {
            let ident = &variant.ident;
            match source {
                Some(source) => quote! {
                    #ty::#ident {#source: source, ..} => std::option::Option::Some(source.as_dyn_error()),
                },
                None => quote! {
                    #ty::#ident {..} => std::option::Option::None,
                },
            }
        });
        Some(quote! {
            fn source(&self) -> std::option::Option<&(dyn std::error::Error + 'static)> {
                use thiserror::private::AsDynError;
                match self {
                    #(#arms)*
                }
            }
        })
    } else {
        None
    };

    let backtrace_method = if backtraces.iter().any(Option::is_some) {
        let arms = data.variants.iter().zip(backtraces).map(|(variant, backtrace)| {
            let ident = &variant.ident;
            match backtrace {
                Some(backtrace) => quote! {
                    #ty::#ident {#backtrace: backtrace, ..} => std::option::Option::Some(backtrace),
                },
                None => quote! {
                    #ty::#ident {..} => std::option::Option::None,
                },
            }
        });
        Some(quote! {
            fn backtrace(&self) -> std::option::Option<&std::backtrace::Backtrace> {
                match self {
                    #(#arms)*
                }
            }
        })
    } else {
        None
    };

    let variant_attrs = data
        .variants
        .iter()
        .map(|variant| attr::get(&variant.attrs))
        .collect::<Result<Vec<_>>>()?;
    let display = if variant_attrs.iter().any(|attrs| attrs.display.is_some()) {
        let arms = data
            .variants
            .iter()
            .zip(variant_attrs)
            .map(|(variant, attrs)| {
                let display = attrs.display.ok_or_else(|| {
                    Error::new_spanned(variant, "missing #[error(\"...\")] display attribute")
                })?;
                let ident = &variant.ident;
                Ok(match &variant.fields {
                    Fields::Named(fields) => {
                        let var = fields.named.iter().map(|field| &field.ident);
                        quote!(#ty::#ident { #(#var),* } => #display)
                    }
                    Fields::Unnamed(fields) => {
                        let var = (0..fields.unnamed.len()).map(|i| format_ident!("_{}", i));
                        quote!(#ty::#ident(#(#var),*) => #display)
                    }
                    Fields::Unit => quote!(#ty::#ident => #display),
                })
            })
            .collect::<Result<Vec<_>>>()?;
        Some(quote! {
            impl #impl_generics std::fmt::Display for #ty #ty_generics #where_clause {
                fn fmt(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
                    #[allow(unused_variables)]
                    match self {
                        #(#arms,)*
                    }
                }
            }
        })
    } else {
        None
    };

    Ok(quote! {
        impl #impl_generics std::error::Error for #ty #ty_generics #where_clause {
            #source_method
            #backtrace_method
        }
        #display
    })
}

fn source_member<'a>(fields: impl IntoIterator<Item = &'a Field>) -> Result<Option<Member>> {
    for (i, field) in fields.into_iter().enumerate() {
        let attrs = attr::get(&field.attrs)?;
        if attrs.source {
            return Ok(Some(member(i, &field.ident)));
        }
    }
    Ok(None)
}

fn backtrace_member<'a>(fields: impl IntoIterator<Item = &'a Field>) -> Result<Option<Member>> {
    for (i, field) in fields.into_iter().enumerate() {
        if type_is_backtrace(&field.ty) {
            return Ok(Some(member(i, &field.ident)));
        }
    }
    Ok(None)
}

fn type_is_backtrace(ty: &Type) -> bool {
    let path = match ty {
        Type::Path(ty) => &ty.path,
        _ => return false,
    };

    let last = path.segments.last().unwrap();
    last.ident == "Backtrace" && last.arguments.is_empty()
}

fn member(i: usize, ident: &Option<Ident>) -> Member {
    match ident {
        Some(ident) => Member::Named(ident.clone()),
        None => Member::Unnamed(Index::from(i)),
    }
}
<|endoftext|>


<commit_before>pub struct Export {
    pub name: String,
    pub func: String,
}

pub enum Expr {
    Add(Typ,Box<Expr>,Box<Expr>),
    Return(usize),
}

pub struct Function {
    pub name: String,
    pub params: Vec<Var>,
    pub result: Option<Typ>,
    pub locals: Vec<Var>,
    pub body: Vec<Expr>,
}

pub struct Import {
    pub func: String,
    pub module: String,
    pub name: String,
}

pub struct Memory {
    pub init: usize,
    pub max: Option<usize>,
    pub segments: Vec<Segment>,
}

pub struct Module {
    pub memory: Option<Memory>,
    pub functions: Vec<Function>,
}

pub struct Segment {
    pub addr: usize,
    pub data: String,
}

pub enum Typ {
    I32,
    I64,
    F32,
    F64,
}

pub struct Var {
    pub name: String,
    pub typ: Typ,
}
<commit_msg>Added imports/exports to ast Module.<commit_after>pub struct Export {
    pub name: String,
    pub func: String,
}

pub enum Expr {
    Add(Typ,Box<Expr>,Box<Expr>),
    Return(usize),
}

pub struct Function {
    pub name: String,
    pub params: Vec<Var>,
    pub result: Option<Typ>,
    pub locals: Vec<Var>,
    pub body: Vec<Expr>,
}

pub struct Import {
    pub func: String,
    pub module: String,
    pub name: String,
}

pub struct Memory {
    pub init: usize,
    pub max: Option<usize>,
    pub segments: Vec<Segment>,
}

pub struct Module {
    pub memory: Option<Memory>,
    pub imports: Vec<Import>,
    pub exports: Vec<Export>,
    pub functions: Vec<Function>,
}

pub struct Segment {
    pub addr: usize,
    pub data: String,
}

pub enum Typ {
    I32,
    I64,
    F32,
    F64,
}

pub struct Var {
    pub name: String,
    pub typ: Typ,
}
<|endoftext|>