<commit_before>// Copyright 2012-2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

#![allow(non_camel_case_types)]

use rustc::hir::map as ast_map;

use rustc::hir::intravisit::{Visitor, IdRangeComputingVisitor, IdRange};

use cstore::CrateMetadata;
use decoder::DecodeContext;
use encoder::EncodeContext;

use middle::cstore::{InlinedItem, InlinedItemRef};
use rustc::ty::adjustment;
use rustc::hir::def;
use rustc::hir::def_id::DefId;
use rustc::ty::{self, TyCtxt};

use syntax::ast;

use rbml;
use rustc_serialize::{Decodable, Encodable};

// ______________________________________________________________________
// Top-level methods.

pub fn encode_inlined_item(ecx: &mut EncodeContext, ii: InlinedItemRef) {
    ecx.tag(::common::item_tag::ast, |ecx| {
        let mut visitor = IdRangeComputingVisitor::new();
        match ii {
            InlinedItemRef::Item(_, i) => visitor.visit_item(i),
            InlinedItemRef::TraitItem(_, ti) => visitor.visit_trait_item(ti),
            InlinedItemRef::ImplItem(_, ii) => visitor.visit_impl_item(ii)
        }
        visitor.result().encode(ecx).unwrap();

        ii.encode(ecx).unwrap();

        let mut visitor = SideTableEncodingIdVisitor {
            ecx: ecx
        };
        match ii {
            InlinedItemRef::Item(_, i) => visitor.visit_item(i),
            InlinedItemRef::TraitItem(_, ti) => visitor.visit_trait_item(ti),
            InlinedItemRef::ImplItem(_, ii) => visitor.visit_impl_item(ii)
        }
    });
}

/// Decodes an item from its AST in the cdata's metadata and adds it to the
/// ast-map.
pub fn decode_inlined_item<'a, 'tcx>(cdata: &CrateMetadata,
                                     tcx: TyCtxt<'a, 'tcx, 'tcx>,
                                     parent_def_path: ast_map::DefPath,
                                     parent_did: DefId,
                                     ast_doc: rbml::Doc,
                                     orig_did: DefId)
                                     -> &'tcx InlinedItem {
    debug!("> Decoding inlined fn: {:?}", tcx.item_path_str(orig_did));
    let dcx = &mut ast_doc.decoder();
    dcx.tcx = Some(tcx);
    dcx.cdata = Some(cdata);
    dcx.from_id_range = IdRange::decode(dcx).unwrap();
    let cnt = dcx.from_id_range.max.as_usize() - dcx.from_id_range.min.as_usize();
    dcx.to_id_range.min = tcx.sess.reserve_node_ids(cnt);
    dcx.to_id_range.max = ast::NodeId::new(dcx.to_id_range.min.as_usize() + cnt);
    let ii = InlinedItem::decode(dcx).unwrap();

    let ii = ast_map::map_decoded_item(&tcx.map,
                                       parent_def_path,
                                       parent_did,
                                       ii,
                                       tcx.sess.next_node_id());

    let item_node_id = match ii {
        &InlinedItem::Item(_, ref i) => i.id,
        &InlinedItem::TraitItem(_, ref ti) => ti.id,
        &InlinedItem::ImplItem(_, ref ii) => ii.id
    };
    let inlined_did = tcx.map.local_def_id(item_node_id);
    tcx.register_item_type(inlined_did, tcx.lookup_item_type(orig_did));

    decode_side_tables(dcx, ast_doc);

    ii
}

// ______________________________________________________________________
// Encoding and decoding the side tables

impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
    fn tag<F>(&mut self,
              tag_id: usize,
              f: F) where
        F: FnOnce(&mut Self),
    {
        self.start_tag(tag_id).unwrap();
        f(self);
        self.end_tag().unwrap();
    }

    fn entry(&mut self, table: Table, id: ast::NodeId) {
        table.encode(self).unwrap();
        id.encode(self).unwrap();
    }
}

struct SideTableEncodingIdVisitor<'a, 'b:'a, 'tcx:'b> {
    ecx: &'a mut EncodeContext<'b, 'tcx>,
}

impl<'a, 'b, 'tcx, 'v> Visitor<'v> for SideTableEncodingIdVisitor<'a, 'b, 'tcx> {
    fn visit_id(&mut self, id: ast::NodeId) {
        encode_side_tables_for_id(self.ecx, id)
    }
}

#[derive(RustcEncodable, RustcDecodable, Debug)]
enum Table {
    Def,
    NodeType,
    ItemSubsts,
    Freevars,
    MethodMap,
    Adjustment,
    UpvarCaptureMap,
    ConstQualif,
    CastKind
}

fn encode_side_tables_for_id(ecx: &mut EncodeContext, id: ast::NodeId) {
    let tcx = ecx.tcx;

    debug!("Encoding side tables for id {}", id);

    if let Some(def) = tcx.expect_def_or_none(id) {
        ecx.entry(Table::Def, id);
        def.encode(ecx).unwrap();
    }

    if let Some(ty) = tcx.node_types().get(&id) {
        ecx.entry(Table::NodeType, id);
        ty.encode(ecx).unwrap();
    }

    if let Some(item_substs) = tcx.tables.borrow().item_substs.get(&id) {
        ecx.entry(Table::ItemSubsts, id);
        item_substs.substs.encode(ecx).unwrap();
    }

    if let Some(fv) = tcx.freevars.borrow().get(&id) {
        ecx.entry(Table::Freevars, id);
        fv.encode(ecx).unwrap();

        for freevar in fv {
            ecx.entry(Table::UpvarCaptureMap, id);
            let def_id = freevar.def.def_id();
            let var_id = tcx.map.as_local_node_id(def_id).unwrap();
            let upvar_id = ty::UpvarId {
                var_id: var_id,
                closure_expr_id: id
            };
            let upvar_capture = tcx.tables
                                    .borrow()
                                    .upvar_capture_map
                                    .get(&upvar_id)
                                    .unwrap()
                                    .clone();
            var_id.encode(ecx).unwrap();
            upvar_capture.encode(ecx).unwrap();
        }
    }

    let method_call = ty::MethodCall::expr(id);
    if let Some(method) = tcx.tables.borrow().method_map.get(&method_call) {
        ecx.entry(Table::MethodMap, id);
        method_call.autoderef.encode(ecx).unwrap();
        method.encode(ecx).unwrap();
    }

    if let Some(adjustment) = tcx.tables.borrow().adjustments.get(&id) {
        match *adjustment {
            adjustment::AdjustDerefRef(ref adj) => {
                for autoderef in 0..adj.autoderefs {
                    let method_call = ty::MethodCall::autoderef(id, autoderef as u32);
                    if let Some(method) = tcx.tables.borrow().method_map.get(&method_call) {
                        ecx.entry(Table::MethodMap, id);
                        method_call.autoderef.encode(ecx).unwrap();
                        method.encode(ecx).unwrap();
                    }
                }
            }
            _ => {}
        }

        ecx.entry(Table::Adjustment, id);
        adjustment.encode(ecx).unwrap();
    }

    if let Some(cast_kind) = tcx.cast_kinds.borrow().get(&id) {
        ecx.entry(Table::CastKind, id);
        cast_kind.encode(ecx).unwrap();
    }

    if let Some(qualif) = tcx.const_qualif_map.borrow().get(&id) {
        ecx.entry(Table::ConstQualif, id);
        qualif.encode(ecx).unwrap();
    }
}

fn decode_side_tables(dcx: &mut DecodeContext, ast_doc: rbml::Doc) {
    while dcx.opaque.position() < ast_doc.end {
        let table = Decodable::decode(dcx).unwrap();
        let id = Decodable::decode(dcx).unwrap();
        debug!("decode_side_tables: entry for id={}, table={:?}", id, table);
        match table {
            Table::Def => {
                let def = Decodable::decode(dcx).unwrap();
                dcx.tcx().def_map.borrow_mut().insert(id, def::PathResolution::new(def));
            }
            Table::NodeType => {
                let ty = Decodable::decode(dcx).unwrap();
                dcx.tcx().node_type_insert(id, ty);
            }
            Table::ItemSubsts => {
                let item_substs = Decodable::decode(dcx).unwrap();
                dcx.tcx().tables.borrow_mut().item_substs.insert(id, item_substs);
            }
            Table::Freevars => {
                let fv_info = Decodable::decode(dcx).unwrap();
                dcx.tcx().freevars.borrow_mut().insert(id, fv_info);
            }
            Table::UpvarCaptureMap => {
                let upvar_id = ty::UpvarId {
                    var_id: Decodable::decode(dcx).unwrap(),
                    closure_expr_id: id
                };
                let ub = Decodable::decode(dcx).unwrap();
                dcx.tcx().tables.borrow_mut().upvar_capture_map.insert(upvar_id, ub);
            }
            Table::MethodMap => {
                let method_call = ty::MethodCall {
                    expr_id: id,
                    autoderef: Decodable::decode(dcx).unwrap()
                };
                let method = Decodable::decode(dcx).unwrap();
                dcx.tcx().tables.borrow_mut().method_map.insert(method_call, method);
            }
            Table::Adjustment => {
                let adj = Decodable::decode(dcx).unwrap();
                dcx.tcx().tables.borrow_mut().adjustments.insert(id, adj);
            }
            Table::CastKind => {
                let cast_kind = Decodable::decode(dcx).unwrap();
                dcx.tcx().cast_kinds.borrow_mut().insert(id, cast_kind);
            }
            Table::ConstQualif => {
                let qualif = Decodable::decode(dcx).unwrap();
                dcx.tcx().const_qualif_map.borrow_mut().insert(id, qualif);
            }
        }
    }
}
<commit_msg>rustc_metadata: remove all unnecessary tables from astencode.<commit_after>// Copyright 2012-2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

#![allow(non_camel_case_types)]

use rustc::hir::map as ast_map;

use rustc::hir::intravisit::{Visitor, IdRangeComputingVisitor, IdRange};

use cstore::CrateMetadata;
use decoder::DecodeContext;
use encoder::EncodeContext;

use middle::cstore::{InlinedItem, InlinedItemRef};
use rustc::hir::def;
use rustc::hir::def_id::DefId;
use rustc::ty::TyCtxt;

use syntax::ast;

use rbml;
use rustc_serialize::{Decodable, Encodable};

// ______________________________________________________________________
// Top-level methods.

pub fn encode_inlined_item(ecx: &mut EncodeContext, ii: InlinedItemRef) {
    ecx.tag(::common::item_tag::ast, |ecx| {
        let mut visitor = IdRangeComputingVisitor::new();
        match ii {
            InlinedItemRef::Item(_, i) => visitor.visit_item(i),
            InlinedItemRef::TraitItem(_, ti) => visitor.visit_trait_item(ti),
            InlinedItemRef::ImplItem(_, ii) => visitor.visit_impl_item(ii)
        }
        visitor.result().encode(ecx).unwrap();

        ii.encode(ecx).unwrap();

        let mut visitor = SideTableEncodingIdVisitor {
            ecx: ecx
        };
        match ii {
            InlinedItemRef::Item(_, i) => visitor.visit_item(i),
            InlinedItemRef::TraitItem(_, ti) => visitor.visit_trait_item(ti),
            InlinedItemRef::ImplItem(_, ii) => visitor.visit_impl_item(ii)
        }
    });
}

/// Decodes an item from its AST in the cdata's metadata and adds it to the
/// ast-map.
pub fn decode_inlined_item<'a, 'tcx>(cdata: &CrateMetadata,
                                     tcx: TyCtxt<'a, 'tcx, 'tcx>,
                                     parent_def_path: ast_map::DefPath,
                                     parent_did: DefId,
                                     ast_doc: rbml::Doc,
                                     orig_did: DefId)
                                     -> &'tcx InlinedItem {
    debug!("> Decoding inlined fn: {:?}", tcx.item_path_str(orig_did));
    let dcx = &mut ast_doc.decoder();
    dcx.tcx = Some(tcx);
    dcx.cdata = Some(cdata);
    dcx.from_id_range = IdRange::decode(dcx).unwrap();
    let cnt = dcx.from_id_range.max.as_usize() - dcx.from_id_range.min.as_usize();
    dcx.to_id_range.min = tcx.sess.reserve_node_ids(cnt);
    dcx.to_id_range.max = ast::NodeId::new(dcx.to_id_range.min.as_usize() + cnt);
    let ii = InlinedItem::decode(dcx).unwrap();

    let ii = ast_map::map_decoded_item(&tcx.map,
                                       parent_def_path,
                                       parent_did,
                                       ii,
                                       tcx.sess.next_node_id());

    let item_node_id = match ii {
        &InlinedItem::Item(_, ref i) => i.id,
        &InlinedItem::TraitItem(_, ref ti) => ti.id,
        &InlinedItem::ImplItem(_, ref ii) => ii.id
    };
    let inlined_did = tcx.map.local_def_id(item_node_id);
    tcx.register_item_type(inlined_did, tcx.lookup_item_type(orig_did));

    decode_side_tables(dcx, ast_doc);

    ii
}

// ______________________________________________________________________
// Encoding and decoding the side tables

impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
    fn tag<F>(&mut self,
              tag_id: usize,
              f: F) where
        F: FnOnce(&mut Self),
    {
        self.start_tag(tag_id).unwrap();
        f(self);
        self.end_tag().unwrap();
    }

    fn entry(&mut self, table: Table, id: ast::NodeId) {
        table.encode(self).unwrap();
        id.encode(self).unwrap();
    }
}

struct SideTableEncodingIdVisitor<'a, 'b:'a, 'tcx:'b> {
    ecx: &'a mut EncodeContext<'b, 'tcx>,
}

impl<'a, 'b, 'tcx, 'v> Visitor<'v> for SideTableEncodingIdVisitor<'a, 'b, 'tcx> {
    fn visit_id(&mut self, id: ast::NodeId) {
        encode_side_tables_for_id(self.ecx, id)
    }
}

#[derive(RustcEncodable, RustcDecodable, Debug)]
enum Table {
    Def,
    NodeType,
    ItemSubsts,
    Adjustment,
    ConstQualif
}

fn encode_side_tables_for_id(ecx: &mut EncodeContext, id: ast::NodeId) {
    let tcx = ecx.tcx;

    debug!("Encoding side tables for id {}", id);

    if let Some(def) = tcx.expect_def_or_none(id) {
        ecx.entry(Table::Def, id);
        def.encode(ecx).unwrap();
    }

    if let Some(ty) = tcx.node_types().get(&id) {
        ecx.entry(Table::NodeType, id);
        ty.encode(ecx).unwrap();
    }

    if let Some(item_substs) = tcx.tables.borrow().item_substs.get(&id) {
        ecx.entry(Table::ItemSubsts, id);
        item_substs.substs.encode(ecx).unwrap();
    }

    if let Some(adjustment) = tcx.tables.borrow().adjustments.get(&id) {
        ecx.entry(Table::Adjustment, id);
        adjustment.encode(ecx).unwrap();
    }

    if let Some(qualif) = tcx.const_qualif_map.borrow().get(&id) {
        ecx.entry(Table::ConstQualif, id);
        qualif.encode(ecx).unwrap();
    }
}

fn decode_side_tables(dcx: &mut DecodeContext, ast_doc: rbml::Doc) {
    while dcx.opaque.position() < ast_doc.end {
        let table = Decodable::decode(dcx).unwrap();
        let id = Decodable::decode(dcx).unwrap();
        debug!("decode_side_tables: entry for id={}, table={:?}", id, table);
        match table {
            Table::Def => {
                let def = Decodable::decode(dcx).unwrap();
                dcx.tcx().def_map.borrow_mut().insert(id, def::PathResolution::new(def));
            }
            Table::NodeType => {
                let ty = Decodable::decode(dcx).unwrap();
                dcx.tcx().node_type_insert(id, ty);
            }
            Table::ItemSubsts => {
                let item_substs = Decodable::decode(dcx).unwrap();
                dcx.tcx().tables.borrow_mut().item_substs.insert(id, item_substs);
            }
            Table::Adjustment => {
                let adj = Decodable::decode(dcx).unwrap();
                dcx.tcx().tables.borrow_mut().adjustments.insert(id, adj);
            }
            Table::ConstQualif => {
                let qualif = Decodable::decode(dcx).unwrap();
                dcx.tcx().const_qualif_map.borrow_mut().insert(id, qualif);
            }
        }
    }
}
<|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/. */

//! Specified values for font properties

#[cfg(feature = "gecko")]
use Atom;
use app_units::Au;
use cssparser::Parser;
use properties::longhands::system_font::SystemFont;
use std::fmt;
use style_traits::{ToCss, ParseError};
use values::computed::{font as computed, Context, NonNegativeLength, ToComputedValue};
use values::specified::{LengthOrPercentage, NoCalcLength};
use values::specified::length::FontBaseSize;

#[derive(Clone, Debug, PartialEq)]
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
#[cfg_attr(feature = "gecko", derive(MallocSizeOf))]
/// A specified font-size value
pub enum FontSize {
    /// A length; e.g. 10px.
    Length(LengthOrPercentage),
    /// A keyword value, along with a ratio and absolute offset.
    /// The ratio in any specified keyword value
    /// will be 1 (with offset 0), but we cascade keywordness even
    /// after font-relative (percent and em) values
    /// have been applied, which is where the ratio
    /// comes in. The offset comes in if we cascaded a calc value,
    /// where the font-relative portion (em and percentage) will
    /// go into the ratio, and the remaining units all computed together
    /// will go into the offset.
    /// See bug 1355707.
    Keyword(computed::KeywordInfo),
    /// font-size: smaller
    Smaller,
    /// font-size: larger
    Larger,
    /// Derived from a specified system font.
    System(SystemFont)
}

impl ToCss for FontSize {
    fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
        match *self {
            FontSize::Length(ref lop) => lop.to_css(dest),
            FontSize::Keyword(info) => info.kw.to_css(dest),
            FontSize::Smaller => dest.write_str("smaller"),
            FontSize::Larger => dest.write_str("larger"),
            FontSize::System(sys) => sys.to_css(dest),
        }
    }
}

impl From<LengthOrPercentage> for FontSize {
    fn from(other: LengthOrPercentage) -> Self {
        FontSize::Length(other)
    }
}

/// CSS font keywords
#[derive(Animate, ComputeSquaredDistance, ToAnimatedValue, ToAnimatedZero)]
#[derive(Clone, Copy, Debug, PartialEq)]
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
#[cfg_attr(feature = "gecko", derive(MallocSizeOf))]
#[allow(missing_docs)]
pub enum KeywordSize {
    XXSmall = 1, // This is to enable the NonZero optimization
                 // which simplifies the representation of Option<KeywordSize>
                 // in bindgen
    XSmall,
    Small,
    Medium,
    Large,
    XLarge,
    XXLarge,
    // This is not a real font keyword and will not parse
    // HTML font-size 7 corresponds to this value
    XXXLarge,
}

impl KeywordSize {
    /// Parse a keyword size
    pub fn parse<'i, 't>(input: &mut Parser<'i, 't>) -> Result<Self, ParseError<'i>> {
        try_match_ident_ignore_ascii_case! { input.expect_ident()?,
            "xx-small" => Ok(KeywordSize::XXSmall),
            "x-small" => Ok(KeywordSize::XSmall),
            "small" => Ok(KeywordSize::Small),
            "medium" => Ok(KeywordSize::Medium),
            "large" => Ok(KeywordSize::Large),
            "x-large" => Ok(KeywordSize::XLarge),
            "xx-large" => Ok(KeywordSize::XXLarge),
        }
    }

    /// Convert to an HTML <font size> value
    pub fn html_size(&self) -> u8 {
        match *self {
            KeywordSize::XXSmall => 0,
            KeywordSize::XSmall => 1,
            KeywordSize::Small => 2,
            KeywordSize::Medium => 3,
            KeywordSize::Large => 4,
            KeywordSize::XLarge => 5,
            KeywordSize::XXLarge => 6,
            KeywordSize::XXXLarge => 7,
        }
    }
}

impl Default for KeywordSize {
    fn default() -> Self {
        KeywordSize::Medium
    }
}

impl ToCss for KeywordSize {
    fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
        dest.write_str(match *self {
            KeywordSize::XXSmall => "xx-small",
            KeywordSize::XSmall => "x-small",
            KeywordSize::Small => "small",
            KeywordSize::Medium => "medium",
            KeywordSize::Large => "large",
            KeywordSize::XLarge => "x-large",
            KeywordSize::XXLarge => "xx-large",
            KeywordSize::XXXLarge => unreachable!("We should never serialize \
                                      specified values set via
                                      HTML presentation attributes"),
        })
    }
}

/// This is the ratio applied for font-size: larger
/// and smaller by both Firefox and Chrome
const LARGER_FONT_SIZE_RATIO: f32 = 1.2;

/// The default font size.
pub const FONT_MEDIUM_PX: i32 = 16;

#[cfg(feature = "servo")]
impl ToComputedValue for KeywordSize {
    type ComputedValue = NonNegativeLength;
    #[inline]
    fn to_computed_value(&self, _: &Context) -> NonNegativeLength {
        // https://drafts.csswg.org/css-fonts-3/#font-size-prop
        match *self {
            KeywordSize::XXSmall => Au::from_px(FONT_MEDIUM_PX) * 3 / 5,
            KeywordSize::XSmall => Au::from_px(FONT_MEDIUM_PX) * 3 / 4,
            KeywordSize::Small => Au::from_px(FONT_MEDIUM_PX) * 8 / 9,
            KeywordSize::Medium => Au::from_px(FONT_MEDIUM_PX),
            KeywordSize::Large => Au::from_px(FONT_MEDIUM_PX) * 6 / 5,
            KeywordSize::XLarge => Au::from_px(FONT_MEDIUM_PX) * 3 / 2,
            KeywordSize::XXLarge => Au::from_px(FONT_MEDIUM_PX) * 2,
            KeywordSize::XXXLarge => Au::from_px(FONT_MEDIUM_PX) * 3,
        }.into()
    }

    #[inline]
    fn from_computed_value(_: &NonNegativeLength) -> Self {
        unreachable!()
    }
}

#[cfg(feature = "gecko")]
impl ToComputedValue for KeywordSize {
    type ComputedValue = NonNegativeLength;
    #[inline]
    fn to_computed_value(&self, cx: &Context) -> NonNegativeLength {
        use values::specified::length::au_to_int_px;
        // Data from nsRuleNode.cpp in Gecko
        // Mapping from base size and HTML size to pixels
        // The first index is (base_size - 9), the second is the
        // HTML size. "0" is CSS keyword xx-small, not HTML size 0,
        // since HTML size 0 is the same as 1.
        //
        //  xxs   xs      s      m     l      xl     xxl   -
        //  -     0/1     2      3     4      5      6     7
        static FONT_SIZE_MAPPING: [[i32; 8]; 8] = [
            [9,    9,     9,     9,    11,    14,    18,    27],
            [9,    9,     9,    10,    12,    15,    20,    30],
            [9,    9,    10,    11,    13,    17,    22,    33],
            [9,    9,    10,    12,    14,    18,    24,    36],
            [9,   10,    12,    13,    16,    20,    26,    39],
            [9,   10,    12,    14,    17,    21,    28,    42],
            [9,   10,    13,    15,    18,    23,    30,    45],
            [9,   10,    13,    16,    18,    24,    32,    48]
        ];

        static FONT_SIZE_FACTORS: [i32; 8] = [60, 75, 89, 100, 120, 150, 200, 300];

        // XXXManishearth handle quirks mode (bug 1401322)

        let ref gecko_font = cx.style().get_font().gecko();
        let base_size = unsafe { Atom::with(gecko_font.mLanguage.mRawPtr, |atom| {
            cx.font_metrics_provider.get_size(atom, gecko_font.mGenericID).0
        }) };

        let base_size_px = au_to_int_px(base_size as f32);
        let html_size = self.html_size() as usize;
        if base_size_px >= 9 && base_size_px <= 16 {
            Au::from_px(FONT_SIZE_MAPPING[(base_size_px - 9) as usize][html_size]).into()
        } else {
            Au(FONT_SIZE_FACTORS[html_size] * base_size / 100).into()
        }
    }

    #[inline]
    fn from_computed_value(_: &NonNegativeLength) -> Self {
        unreachable!()
    }
}

impl FontSize {
    /// https://html.spec.whatwg.org/multipage/#rules-for-parsing-a-legacy-font-size
    pub fn from_html_size(size: u8) -> Self {
        FontSize::Keyword(match size {
            // If value is less than 1, let it be 1.
            0 | 1 => KeywordSize::XSmall,
            2 => KeywordSize::Small,
            3 => KeywordSize::Medium,
            4 => KeywordSize::Large,
            5 => KeywordSize::XLarge,
            6 => KeywordSize::XXLarge,
            // If value is greater than 7, let it be 7.
            _ => KeywordSize::XXXLarge,
        }.into())
    }

    /// Compute it against a given base font size
    pub fn to_computed_value_against(
        &self,
        context: &Context,
        base_size: FontBaseSize,
    ) -> computed::FontSize {
        use values::specified::length::FontRelativeLength;

        let compose_keyword = |factor| {
            context.style().get_parent_font()
                   .clone_font_size().keyword_info
                   .map(|i| i.compose(factor, Au(0).into()))
        };
        let mut info = None;
        let size = match *self {
            FontSize::Length(LengthOrPercentage::Length(
                    NoCalcLength::FontRelative(value))) => {
                if let FontRelativeLength::Em(em) = value {
                    // If the parent font was keyword-derived, this is too.
                    // Tack the em unit onto the factor
                    info = compose_keyword(em);
                }
                value.to_computed_value(context, base_size).into()
            }
            FontSize::Length(LengthOrPercentage::Length(
                    NoCalcLength::ServoCharacterWidth(value))) => {
                value.to_computed_value(base_size.resolve(context)).into()
            }
            FontSize::Length(LengthOrPercentage::Length(
                    NoCalcLength::Absolute(ref l))) => {
                context.maybe_zoom_text(l.to_computed_value(context).into())
            }
            FontSize::Length(LengthOrPercentage::Length(ref l)) => {
                l.to_computed_value(context).into()
            }
            FontSize::Length(LengthOrPercentage::Percentage(pc)) => {
                // If the parent font was keyword-derived, this is too.
                // Tack the % onto the factor
                info = compose_keyword(pc.0);
                base_size.resolve(context).scale_by(pc.0).into()
            }
            FontSize::Length(LengthOrPercentage::Calc(ref calc)) => {
                let parent = context.style().get_parent_font().clone_font_size();
                // if we contain em/% units and the parent was keyword derived, this is too
                // Extract the ratio/offset and compose it
                if (calc.em.is_some() || calc.percentage.is_some()) && parent.keyword_info.is_some() {
                    let ratio = calc.em.unwrap_or(0.) + calc.percentage.map_or(0., |pc| pc.0);
                    // Compute it, but shave off the font-relative part (em, %)
                    // This will mean that other font-relative units like ex and ch will be computed against
                    // the old font even when the font changes. There's no particular "right answer" for what
                    // to do here -- Gecko recascades as if the font had changed, we instead track the changes
                    // and reapply, which means that we carry over old computed ex/ch values whilst Gecko
                    // recomputes new ones. This is enough of an edge case to not really matter.
                    let abs = calc.to_computed_value_zoomed(context, FontBaseSize::Custom(Au(0).into()))
                                  .length_component().into();
                    info = parent.keyword_info.map(|i| i.compose(ratio, abs));
                }
                let calc = calc.to_computed_value_zoomed(context, base_size);
                calc.to_used_value(Some(base_size.resolve(context))).unwrap().into()
            }
            FontSize::Keyword(i) => {
                // As a specified keyword, this is keyword derived
                info = Some(i);
                context.maybe_zoom_text(i.kw.to_computed_value(context).scale_by(i.factor) + i.offset)
            }
            FontSize::Smaller => {
                info = compose_keyword(1. / LARGER_FONT_SIZE_RATIO);
                FontRelativeLength::Em(1. / LARGER_FONT_SIZE_RATIO)
                    .to_computed_value(context, base_size).into()
            }
            FontSize::Larger => {
                info = compose_keyword(LARGER_FONT_SIZE_RATIO);
                FontRelativeLength::Em(LARGER_FONT_SIZE_RATIO)
                    .to_computed_value(context, base_size).into()
            }

            FontSize::System(_) => {
                #[cfg(feature = "servo")] {
                    unreachable!()
                }
                #[cfg(feature = "gecko")] {
                    context.cached_system_font.as_ref().unwrap().font_size.size
                }
            }
        };
        computed::FontSize {
            size: size,
            keyword_info: info,
        }
    }
}

impl ToComputedValue for FontSize {
    type ComputedValue = computed::FontSize;

    #[inline]
    fn to_computed_value(&self, context: &Context) -> computed::FontSize {
        self.to_computed_value_against(context, FontBaseSize::InheritedStyle)
    }

    #[inline]
    fn from_computed_value(computed: &computed::FontSize) -> Self {
        FontSize::Length(LengthOrPercentage::Length(
            ToComputedValue::from_computed_value(&computed.size.0)
        ))
    }
}

impl FontSize {
    /// Construct a system font value.
    pub fn system_font(f: SystemFont) -> Self {
        FontSize::System(f)
    }

    /// Obtain the system font, if any
    pub fn get_system(&self) -> Option<SystemFont> {
        if let FontSize::System(s) = *self {
            Some(s)
        } else {
            None
        }
    }
}
<commit_msg>stylo: Handle quirks mode on font base sizes<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/. */

//! Specified values for font properties

#[cfg(feature = "gecko")]
use Atom;
use app_units::Au;
use cssparser::Parser;
use properties::longhands::system_font::SystemFont;
use std::fmt;
use style_traits::{ToCss, ParseError};
use values::computed::{font as computed, Context, NonNegativeLength, ToComputedValue};
use values::specified::{LengthOrPercentage, NoCalcLength};
use values::specified::length::FontBaseSize;

#[derive(Clone, Debug, PartialEq)]
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
#[cfg_attr(feature = "gecko", derive(MallocSizeOf))]
/// A specified font-size value
pub enum FontSize {
    /// A length; e.g. 10px.
    Length(LengthOrPercentage),
    /// A keyword value, along with a ratio and absolute offset.
    /// The ratio in any specified keyword value
    /// will be 1 (with offset 0), but we cascade keywordness even
    /// after font-relative (percent and em) values
    /// have been applied, which is where the ratio
    /// comes in. The offset comes in if we cascaded a calc value,
    /// where the font-relative portion (em and percentage) will
    /// go into the ratio, and the remaining units all computed together
    /// will go into the offset.
    /// See bug 1355707.
    Keyword(computed::KeywordInfo),
    /// font-size: smaller
    Smaller,
    /// font-size: larger
    Larger,
    /// Derived from a specified system font.
    System(SystemFont)
}

impl ToCss for FontSize {
    fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
        match *self {
            FontSize::Length(ref lop) => lop.to_css(dest),
            FontSize::Keyword(info) => info.kw.to_css(dest),
            FontSize::Smaller => dest.write_str("smaller"),
            FontSize::Larger => dest.write_str("larger"),
            FontSize::System(sys) => sys.to_css(dest),
        }
    }
}

impl From<LengthOrPercentage> for FontSize {
    fn from(other: LengthOrPercentage) -> Self {
        FontSize::Length(other)
    }
}

/// CSS font keywords
#[derive(Animate, ComputeSquaredDistance, ToAnimatedValue, ToAnimatedZero)]
#[derive(Clone, Copy, Debug, PartialEq)]
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
#[cfg_attr(feature = "gecko", derive(MallocSizeOf))]
#[allow(missing_docs)]
pub enum KeywordSize {
    XXSmall = 1, // This is to enable the NonZero optimization
                 // which simplifies the representation of Option<KeywordSize>
                 // in bindgen
    XSmall,
    Small,
    Medium,
    Large,
    XLarge,
    XXLarge,
    // This is not a real font keyword and will not parse
    // HTML font-size 7 corresponds to this value
    XXXLarge,
}

impl KeywordSize {
    /// Parse a keyword size
    pub fn parse<'i, 't>(input: &mut Parser<'i, 't>) -> Result<Self, ParseError<'i>> {
        try_match_ident_ignore_ascii_case! { input.expect_ident()?,
            "xx-small" => Ok(KeywordSize::XXSmall),
            "x-small" => Ok(KeywordSize::XSmall),
            "small" => Ok(KeywordSize::Small),
            "medium" => Ok(KeywordSize::Medium),
            "large" => Ok(KeywordSize::Large),
            "x-large" => Ok(KeywordSize::XLarge),
            "xx-large" => Ok(KeywordSize::XXLarge),
        }
    }

    /// Convert to an HTML <font size> value
    pub fn html_size(&self) -> u8 {
        match *self {
            KeywordSize::XXSmall => 0,
            KeywordSize::XSmall => 1,
            KeywordSize::Small => 2,
            KeywordSize::Medium => 3,
            KeywordSize::Large => 4,
            KeywordSize::XLarge => 5,
            KeywordSize::XXLarge => 6,
            KeywordSize::XXXLarge => 7,
        }
    }
}

impl Default for KeywordSize {
    fn default() -> Self {
        KeywordSize::Medium
    }
}

impl ToCss for KeywordSize {
    fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
        dest.write_str(match *self {
            KeywordSize::XXSmall => "xx-small",
            KeywordSize::XSmall => "x-small",
            KeywordSize::Small => "small",
            KeywordSize::Medium => "medium",
            KeywordSize::Large => "large",
            KeywordSize::XLarge => "x-large",
            KeywordSize::XXLarge => "xx-large",
            KeywordSize::XXXLarge => unreachable!("We should never serialize \
                                      specified values set via
                                      HTML presentation attributes"),
        })
    }
}

/// This is the ratio applied for font-size: larger
/// and smaller by both Firefox and Chrome
const LARGER_FONT_SIZE_RATIO: f32 = 1.2;

/// The default font size.
pub const FONT_MEDIUM_PX: i32 = 16;

#[cfg(feature = "servo")]
impl ToComputedValue for KeywordSize {
    type ComputedValue = NonNegativeLength;
    #[inline]
    fn to_computed_value(&self, _: &Context) -> NonNegativeLength {
        // https://drafts.csswg.org/css-fonts-3/#font-size-prop
        match *self {
            KeywordSize::XXSmall => Au::from_px(FONT_MEDIUM_PX) * 3 / 5,
            KeywordSize::XSmall => Au::from_px(FONT_MEDIUM_PX) * 3 / 4,
            KeywordSize::Small => Au::from_px(FONT_MEDIUM_PX) * 8 / 9,
            KeywordSize::Medium => Au::from_px(FONT_MEDIUM_PX),
            KeywordSize::Large => Au::from_px(FONT_MEDIUM_PX) * 6 / 5,
            KeywordSize::XLarge => Au::from_px(FONT_MEDIUM_PX) * 3 / 2,
            KeywordSize::XXLarge => Au::from_px(FONT_MEDIUM_PX) * 2,
            KeywordSize::XXXLarge => Au::from_px(FONT_MEDIUM_PX) * 3,
        }.into()
    }

    #[inline]
    fn from_computed_value(_: &NonNegativeLength) -> Self {
        unreachable!()
    }
}

#[cfg(feature = "gecko")]
impl ToComputedValue for KeywordSize {
    type ComputedValue = NonNegativeLength;
    #[inline]
    fn to_computed_value(&self, cx: &Context) -> NonNegativeLength {
        use context::QuirksMode;
        use values::specified::length::au_to_int_px;
        // Data from nsRuleNode.cpp in Gecko
        // Mapping from base size and HTML size to pixels
        // The first index is (base_size - 9), the second is the
        // HTML size. "0" is CSS keyword xx-small, not HTML size 0,
        // since HTML size 0 is the same as 1.
        //
        //  xxs   xs      s      m     l      xl     xxl   -
        //  -     0/1     2      3     4      5      6     7
        static FONT_SIZE_MAPPING: [[i32; 8]; 8] = [
            [9,    9,     9,     9,    11,    14,    18,    27],
            [9,    9,     9,    10,    12,    15,    20,    30],
            [9,    9,    10,    11,    13,    17,    22,    33],
            [9,    9,    10,    12,    14,    18,    24,    36],
            [9,   10,    12,    13,    16,    20,    26,    39],
            [9,   10,    12,    14,    17,    21,    28,    42],
            [9,   10,    13,    15,    18,    23,    30,    45],
            [9,   10,    13,    16,    18,    24,    32,    48]
        ];

        // Data from nsRuleNode.cpp in Gecko
        // (https://dxr.mozilla.org/mozilla-central/rev/35fbf14b9/layout/style/nsRuleNode.cpp#3303)
        //
        // This table gives us compatibility with WinNav4 for the default fonts only.
        // In WinNav4, the default fonts were:
        //
        //     Times/12pt ==   Times/16px at 96ppi
        //   Courier/10pt == Courier/13px at 96ppi
        //
        // xxs   xs     s      m      l     xl     xxl    -
        // -     1      2      3      4     5      6      7
        static QUIRKS_FONT_SIZE_MAPPING: [[i32; 8]; 8] = [
            [9,    9,     9,     9,    11,    14,    18,    28],
            [9,    9,     9,    10,    12,    15,    20,    31],
            [9,    9,     9,    11,    13,    17,    22,    34],
            [9,    9,    10,    12,    14,    18,    24,    37],
            [9,    9,    10,    13,    16,    20,    26,    40],
            [9,    9,    11,    14,    17,    21,    28,    42],
            [9,   10,    12,    15,    17,    23,    30,    45],
            [9,   10,    13,    16,    18,    24,    32,    48]
        ];

        static FONT_SIZE_FACTORS: [i32; 8] = [60, 75, 89, 100, 120, 150, 200, 300];

        let ref gecko_font = cx.style().get_font().gecko();
        let base_size = unsafe { Atom::with(gecko_font.mLanguage.mRawPtr, |atom| {
            cx.font_metrics_provider.get_size(atom, gecko_font.mGenericID).0
        }) };

        let base_size_px = au_to_int_px(base_size as f32);
        let html_size = self.html_size() as usize;
        if base_size_px >= 9 && base_size_px <= 16 {
            let mapping = if cx.quirks_mode == QuirksMode::Quirks {
                QUIRKS_FONT_SIZE_MAPPING
            } else {
                FONT_SIZE_MAPPING
            };
            Au::from_px(mapping[(base_size_px - 9) as usize][html_size]).into()
        } else {
            Au(FONT_SIZE_FACTORS[html_size] * base_size / 100).into()
        }
    }

    #[inline]
    fn from_computed_value(_: &NonNegativeLength) -> Self {
        unreachable!()
    }
}

impl FontSize {
    /// https://html.spec.whatwg.org/multipage/#rules-for-parsing-a-legacy-font-size
    pub fn from_html_size(size: u8) -> Self {
        FontSize::Keyword(match size {
            // If value is less than 1, let it be 1.
            0 | 1 => KeywordSize::XSmall,
            2 => KeywordSize::Small,
            3 => KeywordSize::Medium,
            4 => KeywordSize::Large,
            5 => KeywordSize::XLarge,
            6 => KeywordSize::XXLarge,
            // If value is greater than 7, let it be 7.
            _ => KeywordSize::XXXLarge,
        }.into())
    }

    /// Compute it against a given base font size
    pub fn to_computed_value_against(
        &self,
        context: &Context,
        base_size: FontBaseSize,
    ) -> computed::FontSize {
        use values::specified::length::FontRelativeLength;

        let compose_keyword = |factor| {
            context.style().get_parent_font()
                   .clone_font_size().keyword_info
                   .map(|i| i.compose(factor, Au(0).into()))
        };
        let mut info = None;
        let size = match *self {
            FontSize::Length(LengthOrPercentage::Length(
                    NoCalcLength::FontRelative(value))) => {
                if let FontRelativeLength::Em(em) = value {
                    // If the parent font was keyword-derived, this is too.
                    // Tack the em unit onto the factor
                    info = compose_keyword(em);
                }
                value.to_computed_value(context, base_size).into()
            }
            FontSize::Length(LengthOrPercentage::Length(
                    NoCalcLength::ServoCharacterWidth(value))) => {
                value.to_computed_value(base_size.resolve(context)).into()
            }
            FontSize::Length(LengthOrPercentage::Length(
                    NoCalcLength::Absolute(ref l))) => {
                context.maybe_zoom_text(l.to_computed_value(context).into())
            }
            FontSize::Length(LengthOrPercentage::Length(ref l)) => {
                l.to_computed_value(context).into()
            }
            FontSize::Length(LengthOrPercentage::Percentage(pc)) => {
                // If the parent font was keyword-derived, this is too.
                // Tack the % onto the factor
                info = compose_keyword(pc.0);
                base_size.resolve(context).scale_by(pc.0).into()
            }
            FontSize::Length(LengthOrPercentage::Calc(ref calc)) => {
                let parent = context.style().get_parent_font().clone_font_size();
                // if we contain em/% units and the parent was keyword derived, this is too
                // Extract the ratio/offset and compose it
                if (calc.em.is_some() || calc.percentage.is_some()) && parent.keyword_info.is_some() {
                    let ratio = calc.em.unwrap_or(0.) + calc.percentage.map_or(0., |pc| pc.0);
                    // Compute it, but shave off the font-relative part (em, %)
                    // This will mean that other font-relative units like ex and ch will be computed against
                    // the old font even when the font changes. There's no particular "right answer" for what
                    // to do here -- Gecko recascades as if the font had changed, we instead track the changes
                    // and reapply, which means that we carry over old computed ex/ch values whilst Gecko
                    // recomputes new ones. This is enough of an edge case to not really matter.
                    let abs = calc.to_computed_value_zoomed(context, FontBaseSize::Custom(Au(0).into()))
                                  .length_component().into();
                    info = parent.keyword_info.map(|i| i.compose(ratio, abs));
                }
                let calc = calc.to_computed_value_zoomed(context, base_size);
                calc.to_used_value(Some(base_size.resolve(context))).unwrap().into()
            }
            FontSize::Keyword(i) => {
                // As a specified keyword, this is keyword derived
                info = Some(i);
                context.maybe_zoom_text(i.kw.to_computed_value(context).scale_by(i.factor) + i.offset)
            }
            FontSize::Smaller => {
                info = compose_keyword(1. / LARGER_FONT_SIZE_RATIO);
                FontRelativeLength::Em(1. / LARGER_FONT_SIZE_RATIO)
                    .to_computed_value(context, base_size).into()
            }
            FontSize::Larger => {
                info = compose_keyword(LARGER_FONT_SIZE_RATIO);
                FontRelativeLength::Em(LARGER_FONT_SIZE_RATIO)
                    .to_computed_value(context, base_size).into()
            }

            FontSize::System(_) => {
                #[cfg(feature = "servo")] {
                    unreachable!()
                }
                #[cfg(feature = "gecko")] {
                    context.cached_system_font.as_ref().unwrap().font_size.size
                }
            }
        };
        computed::FontSize {
            size: size,
            keyword_info: info,
        }
    }
}

impl ToComputedValue for FontSize {
    type ComputedValue = computed::FontSize;

    #[inline]
    fn to_computed_value(&self, context: &Context) -> computed::FontSize {
        self.to_computed_value_against(context, FontBaseSize::InheritedStyle)
    }

    #[inline]
    fn from_computed_value(computed: &computed::FontSize) -> Self {
        FontSize::Length(LengthOrPercentage::Length(
            ToComputedValue::from_computed_value(&computed.size.0)
        ))
    }
}

impl FontSize {
    /// Construct a system font value.
    pub fn system_font(f: SystemFont) -> Self {
        FontSize::System(f)
    }

    /// Obtain the system font, if any
    pub fn get_system(&self) -> Option<SystemFont> {
        if let FontSize::System(s) = *self {
            Some(s)
        } else {
            None
        }
    }
}
<|endoftext|>


<commit_before>use itertools::Itertools;
use ra_syntax::{
    SourceFile, TextRange, TextUnit, AstNode, SyntaxNode,
    SyntaxKind::{self, WHITESPACE, COMMA, R_CURLY, R_PAREN, R_BRACK},
    algo::find_covering_node,
    ast,
};
use ra_fmt::{
    compute_ws, extract_trivial_expression
};
use crate::{
    LocalEdit, TextEditBuilder,
};

pub fn join_lines(file: &SourceFile, range: TextRange) -> LocalEdit {
    let range = if range.is_empty() {
        let syntax = file.syntax();
        let text = syntax.text().slice(range.start()..);
        let pos = match text.find('\n') {
            None => {
                return LocalEdit {
                    label: "join lines".to_string(),
                    edit: TextEditBuilder::default().finish(),
                    cursor_position: None,
                };
            }
            Some(pos) => pos,
        };
        TextRange::offset_len(range.start() + pos, TextUnit::of_char('\n'))
    } else {
        range
    };

    let node = find_covering_node(file.syntax(), range);
    let mut edit = TextEditBuilder::default();
    for node in node.descendants() {
        let text = match node.leaf_text() {
            Some(text) => text,
            None => continue,
        };
        let range = match range.intersection(&node.range()) {
            Some(range) => range,
            None => continue,
        } - node.range().start();
        for (pos, _) in text[range].bytes().enumerate().filter(|&(_, b)| b == b'\n') {
            let pos: TextUnit = (pos as u32).into();
            let off = node.range().start() + range.start() + pos;
            if !edit.invalidates_offset(off) {
                remove_newline(&mut edit, node, text.as_str(), off);
            }
        }
    }

    LocalEdit { label: "join lines".to_string(), edit: edit.finish(), cursor_position: None }
}

fn remove_newline(
    edit: &mut TextEditBuilder,
    node: &SyntaxNode,
    node_text: &str,
    offset: TextUnit,
) {
    if node.kind() != WHITESPACE || node_text.bytes().filter(|&b| b == b'\n').count() != 1 {
        // The node is either the first or the last in the file
        let suff = &node_text[TextRange::from_to(
            offset - node.range().start() + TextUnit::of_char('\n'),
            TextUnit::of_str(node_text),
        )];
        let spaces = suff.bytes().take_while(|&b| b == b' ').count();

        edit.replace(TextRange::offset_len(offset, ((spaces + 1) as u32).into()), " ".to_string());
        return;
    }

    // Special case that turns something like:
    //
    // ```
    // my_function({<|>
    //    <some-expr>
    // })
    // ```
    //
    // into `my_function(<some-expr>)`
    if join_single_expr_block(edit, node).is_some() {
        return;
    }
    // ditto for
    //
    // ```
    // use foo::{<|>
    //    bar
    // };
    // ```
    if join_single_use_tree(edit, node).is_some() {
        return;
    }

    // The node is between two other nodes
    let prev = node.prev_sibling().unwrap();
    let next = node.next_sibling().unwrap();
    if is_trailing_comma(prev.kind(), next.kind()) {
        // Removes: trailing comma, newline (incl. surrounding whitespace)
        edit.delete(TextRange::from_to(prev.range().start(), node.range().end()));
    } else if prev.kind() == COMMA && next.kind() == R_CURLY {
        // Removes: comma, newline (incl. surrounding whitespace)
        let space = if let Some(left) = prev.prev_sibling() { compute_ws(left, next) } else { " " };
        edit.replace(
            TextRange::from_to(prev.range().start(), node.range().end()),
            space.to_string(),
        );
    } else if let (Some(_), Some(next)) = (ast::Comment::cast(prev), ast::Comment::cast(next)) {
        // Removes: newline (incl. surrounding whitespace), start of the next comment
        edit.delete(TextRange::from_to(
            node.range().start(),
            next.syntax().range().start() + TextUnit::of_str(next.prefix()),
        ));
    } else {
        // Remove newline but add a computed amount of whitespace characters
        edit.replace(node.range(), compute_ws(prev, next).to_string());
    }
}

/// fixes a comma after the given expression and optionally inserts a new trailing comma
/// if no comma was found and `comma_offset` is provided
fn fix_comma_after(edit: &mut TextEditBuilder, node: &SyntaxNode, comma_offset: Option<TextUnit>) {
    let next = node.next_sibling();
    let nnext = node.next_sibling().and_then(|n| n.next_sibling());

    match (next, nnext) {
        // Whitespace followed by a comma
        // remove the whitespace
        (Some(ws), Some(comma)) if ws.kind() == WHITESPACE && comma.kind() == COMMA => {
            edit.delete(ws.range());
        }

        // if we are not a comma and if comma_offset was provided,
        // insert trailing comma after the block
        (Some(n), _) if n.kind() != COMMA => {
            if let Some(comma_offset) = comma_offset {
                edit.insert(comma_offset, ",".to_owned());
            }
        }

        _ => {}
    }
}

fn join_single_expr_block(edit: &mut TextEditBuilder, node: &SyntaxNode) -> Option<()> {
    let block = ast::Block::cast(node.parent()?)?;
    let block_expr = ast::BlockExpr::cast(block.syntax().parent()?)?;
    let expr = extract_trivial_expression(block)?;

    let block_range = block_expr.syntax().range();
    edit.replace(block_range, expr.syntax().text().to_string());

    // Match block needs to have a comma after the block
    // otherwise we'll maintain a comma after the block if such existed
    // but we remove excess whitespace between the expression and the comma.
    if let Some(match_arm) = block_expr.syntax().parent().and_then(ast::MatchArm::cast) {
        fix_comma_after(edit, match_arm.syntax(), Some(block_range.end()));
    } else {
        fix_comma_after(edit, block_expr.syntax(), None);
    }
    Some(())
}

fn join_single_use_tree(edit: &mut TextEditBuilder, node: &SyntaxNode) -> Option<()> {
    let use_tree_list = ast::UseTreeList::cast(node.parent()?)?;
    let (tree,) = use_tree_list.use_trees().collect_tuple()?;
    edit.replace(use_tree_list.syntax().range(), tree.syntax().text().to_string());
    Some(())
}

fn is_trailing_comma(left: SyntaxKind, right: SyntaxKind) -> bool {
    match (left, right) {
        (COMMA, R_PAREN) | (COMMA, R_BRACK) => true,
        _ => false,
    }
}

#[cfg(test)]
mod tests {
    use crate::test_utils::{assert_eq_text, check_action, extract_range};

    use super::*;

    fn check_join_lines(before: &str, after: &str) {
        check_action(before, after, |file, offset| {
            let range = TextRange::offset_len(offset, 0.into());
            let res = join_lines(file, range);
            Some(res)
        })
    }

    #[test]
    fn test_join_lines_comma() {
        check_join_lines(
            r"
fn foo() {
    <|>foo(1,
    )
}
",
            r"
fn foo() {
    <|>foo(1)
}
",
        );
    }

    #[test]
    fn test_join_lines_lambda_block() {
        check_join_lines(
            r"
pub fn reparse(&self, edit: &AtomTextEdit) -> File {
    <|>self.incremental_reparse(edit).unwrap_or_else(|| {
        self.full_reparse(edit)
    })
}
",
            r"
pub fn reparse(&self, edit: &AtomTextEdit) -> File {
    <|>self.incremental_reparse(edit).unwrap_or_else(|| self.full_reparse(edit))
}
",
        );
    }

    #[test]
    fn test_join_lines_block() {
        check_join_lines(
            r"
fn foo() {
    foo(<|>{
        92
    })
}",
            r"
fn foo() {
    foo(<|>92)
}",
        );
    }

    #[test]
    fn join_lines_adds_comma_for_block_in_match_arm() {
        check_join_lines(
            r"
fn foo(e: Result<U, V>) {
    match e {
        Ok(u) => <|>{
            u.foo()
        }
        Err(v) => v,
    }
}",
            r"
fn foo(e: Result<U, V>) {
    match e {
        Ok(u) => <|>u.foo(),
        Err(v) => v,
    }
}",
        );
    }

    #[test]
    fn join_lines_keeps_comma_for_block_in_match_arm() {
        // We already have a comma
        check_join_lines(
            r"
fn foo(e: Result<U, V>) {
    match e {
        Ok(u) => <|>{
            u.foo()
        },
        Err(v) => v,
    }
}",
            r"
fn foo(e: Result<U, V>) {
    match e {
        Ok(u) => <|>u.foo(),
        Err(v) => v,
    }
}",
        );

        // comma with whitespace between brace and ,
        check_join_lines(
            r"
fn foo(e: Result<U, V>) {
    match e {
        Ok(u) => <|>{
            u.foo()
        }    ,
        Err(v) => v,
    }
}",
            r"
fn foo(e: Result<U, V>) {
    match e {
        Ok(u) => <|>u.foo(),
        Err(v) => v,
    }
}",
        );

        // comma with newline between brace and ,
        check_join_lines(
            r"
fn foo(e: Result<U, V>) {
    match e {
        Ok(u) => <|>{
            u.foo()
        }
        ,
        Err(v) => v,
    }
}",
            r"
fn foo(e: Result<U, V>) {
    match e {
        Ok(u) => <|>u.foo(),
        Err(v) => v,
    }
}",
        );
    }

    #[test]
    fn join_lines_keeps_comma_with_single_arg_tuple() {
        // A single arg tuple
        check_join_lines(
            r"
fn foo() {
    let x = (<|>{
       4
    },);
}",
            r"
fn foo() {
    let x = (<|>4,);
}",
        );

        // single arg tuple with whitespace between brace and comma
        check_join_lines(
            r"
fn foo() {
    let x = (<|>{
       4
    }   ,);
}",
            r"
fn foo() {
    let x = (<|>4,);
}",
        );

        // single arg tuple with newline between brace and comma
        check_join_lines(
            r"
fn foo() {
    let x = (<|>{
       4
    }
    ,);
}",
            r"
fn foo() {
    let x = (<|>4,);
}",
        );
    }

    #[test]
    fn test_join_lines_use_items_left() {
        // No space after the '{'
        check_join_lines(
            r"
<|>use ra_syntax::{
    TextUnit, TextRange,
};",
            r"
<|>use ra_syntax::{TextUnit, TextRange,
};",
        );
    }

    #[test]
    fn test_join_lines_use_items_right() {
        // No space after the '}'
        check_join_lines(
            r"
use ra_syntax::{
<|>    TextUnit, TextRange
};",
            r"
use ra_syntax::{
<|>    TextUnit, TextRange};",
        );
    }

    #[test]
    fn test_join_lines_use_items_right_comma() {
        // No space after the '}'
        check_join_lines(
            r"
use ra_syntax::{
<|>    TextUnit, TextRange,
};",
            r"
use ra_syntax::{
<|>    TextUnit, TextRange};",
        );
    }

    #[test]
    fn test_join_lines_use_tree() {
        check_join_lines(
            r"
use ra_syntax::{
    algo::<|>{
        find_leaf_at_offset,
    },
    ast,
};",
            r"
use ra_syntax::{
    algo::<|>find_leaf_at_offset,
    ast,
};",
        );
    }

    #[test]
    fn test_join_lines_normal_comments() {
        check_join_lines(
            r"
fn foo() {
    // Hello<|>
    // world!
}
",
            r"
fn foo() {
    // Hello<|> world!
}
",
        );
    }

    #[test]
    fn test_join_lines_doc_comments() {
        check_join_lines(
            r"
fn foo() {
    /// Hello<|>
    /// world!
}
",
            r"
fn foo() {
    /// Hello<|> world!
}
",
        );
    }

    #[test]
    fn test_join_lines_mod_comments() {
        check_join_lines(
            r"
fn foo() {
    //! Hello<|>
    //! world!
}
",
            r"
fn foo() {
    //! Hello<|> world!
}
",
        );
    }

    #[test]
    fn test_join_lines_multiline_comments_1() {
        check_join_lines(
            r"
fn foo() {
    // Hello<|>
    /* world! */
}
",
            r"
fn foo() {
    // Hello<|> world! */
}
",
        );
    }

    #[test]
    fn test_join_lines_multiline_comments_2() {
        check_join_lines(
            r"
fn foo() {
    // The<|>
    /* quick
    brown
    fox! */
}
",
            r"
fn foo() {
    // The<|> quick
    brown
    fox! */
}
",
        );
    }

    fn check_join_lines_sel(before: &str, after: &str) {
        let (sel, before) = extract_range(before);
        let file = SourceFile::parse(&before);
        let result = join_lines(&file, sel);
        let actual = result.edit.apply(&before);
        assert_eq_text!(after, &actual);
    }

    #[test]
    fn test_join_lines_selection_fn_args() {
        check_join_lines_sel(
            r"
fn foo() {
    <|>foo(1,
        2,
        3,
    <|>)
}
    ",
            r"
fn foo() {
    foo(1, 2, 3)
}
    ",
        );
    }

    #[test]
    fn test_join_lines_selection_struct() {
        check_join_lines_sel(
            r"
struct Foo <|>{
    f: u32,
}<|>
    ",
            r"
struct Foo { f: u32 }
    ",
        );
    }

    #[test]
    fn test_join_lines_selection_dot_chain() {
        check_join_lines_sel(
            r"
fn foo() {
    join(<|>type_params.type_params()
            .filter_map(|it| it.name())
            .map(|it| it.text())<|>)
}",
            r"
fn foo() {
    join(type_params.type_params().filter_map(|it| it.name()).map(|it| it.text()))
}",
        );
    }

    #[test]
    fn test_join_lines_selection_lambda_block_body() {
        check_join_lines_sel(
            r"
pub fn handle_find_matching_brace() {
    params.offsets
        .map(|offset| <|>{
            world.analysis().matching_brace(&file, offset).unwrap_or(offset)
        }<|>)
        .collect();
}",
            r"
pub fn handle_find_matching_brace() {
    params.offsets
        .map(|offset| world.analysis().matching_brace(&file, offset).unwrap_or(offset))
        .collect();
}",
        );
    }
}
<commit_msg>Simplify adding a comma after match arm<commit_after>use itertools::Itertools;
use ra_syntax::{
    SourceFile, TextRange, TextUnit, AstNode, SyntaxNode,
    SyntaxKind::{self, WHITESPACE, COMMA, R_CURLY, R_PAREN, R_BRACK},
    algo::find_covering_node,
    ast,
};
use ra_fmt::{
    compute_ws, extract_trivial_expression
};
use crate::{
    LocalEdit, TextEditBuilder,
};

pub fn join_lines(file: &SourceFile, range: TextRange) -> LocalEdit {
    let range = if range.is_empty() {
        let syntax = file.syntax();
        let text = syntax.text().slice(range.start()..);
        let pos = match text.find('\n') {
            None => {
                return LocalEdit {
                    label: "join lines".to_string(),
                    edit: TextEditBuilder::default().finish(),
                    cursor_position: None,
                };
            }
            Some(pos) => pos,
        };
        TextRange::offset_len(range.start() + pos, TextUnit::of_char('\n'))
    } else {
        range
    };

    let node = find_covering_node(file.syntax(), range);
    let mut edit = TextEditBuilder::default();
    for node in node.descendants() {
        let text = match node.leaf_text() {
            Some(text) => text,
            None => continue,
        };
        let range = match range.intersection(&node.range()) {
            Some(range) => range,
            None => continue,
        } - node.range().start();
        for (pos, _) in text[range].bytes().enumerate().filter(|&(_, b)| b == b'\n') {
            let pos: TextUnit = (pos as u32).into();
            let off = node.range().start() + range.start() + pos;
            if !edit.invalidates_offset(off) {
                remove_newline(&mut edit, node, text.as_str(), off);
            }
        }
    }

    LocalEdit { label: "join lines".to_string(), edit: edit.finish(), cursor_position: None }
}

fn remove_newline(
    edit: &mut TextEditBuilder,
    node: &SyntaxNode,
    node_text: &str,
    offset: TextUnit,
) {
    if node.kind() != WHITESPACE || node_text.bytes().filter(|&b| b == b'\n').count() != 1 {
        // The node is either the first or the last in the file
        let suff = &node_text[TextRange::from_to(
            offset - node.range().start() + TextUnit::of_char('\n'),
            TextUnit::of_str(node_text),
        )];
        let spaces = suff.bytes().take_while(|&b| b == b' ').count();

        edit.replace(TextRange::offset_len(offset, ((spaces + 1) as u32).into()), " ".to_string());
        return;
    }

    // Special case that turns something like:
    //
    // ```
    // my_function({<|>
    //    <some-expr>
    // })
    // ```
    //
    // into `my_function(<some-expr>)`
    if join_single_expr_block(edit, node).is_some() {
        return;
    }
    // ditto for
    //
    // ```
    // use foo::{<|>
    //    bar
    // };
    // ```
    if join_single_use_tree(edit, node).is_some() {
        return;
    }

    // The node is between two other nodes
    let prev = node.prev_sibling().unwrap();
    let next = node.next_sibling().unwrap();
    if is_trailing_comma(prev.kind(), next.kind()) {
        // Removes: trailing comma, newline (incl. surrounding whitespace)
        edit.delete(TextRange::from_to(prev.range().start(), node.range().end()));
    } else if prev.kind() == COMMA && next.kind() == R_CURLY {
        // Removes: comma, newline (incl. surrounding whitespace)
        let space = if let Some(left) = prev.prev_sibling() { compute_ws(left, next) } else { " " };
        edit.replace(
            TextRange::from_to(prev.range().start(), node.range().end()),
            space.to_string(),
        );
    } else if let (Some(_), Some(next)) = (ast::Comment::cast(prev), ast::Comment::cast(next)) {
        // Removes: newline (incl. surrounding whitespace), start of the next comment
        edit.delete(TextRange::from_to(
            node.range().start(),
            next.syntax().range().start() + TextUnit::of_str(next.prefix()),
        ));
    } else {
        // Remove newline but add a computed amount of whitespace characters
        edit.replace(node.range(), compute_ws(prev, next).to_string());
    }
}

fn has_comma_after(node: &SyntaxNode) -> bool {
    let next = node.next_sibling();
    let nnext = node.next_sibling().and_then(|n| n.next_sibling());

    match (next, nnext) {
        // Whitespace followed by a comma is fine
        (Some(ws), Some(comma)) if ws.kind() == WHITESPACE && comma.kind() == COMMA => true,
        (Some(n), _) => n.kind() == COMMA,
        _ => false,
    }
}

fn join_single_expr_block(edit: &mut TextEditBuilder, node: &SyntaxNode) -> Option<()> {
    let block = ast::Block::cast(node.parent()?)?;
    let block_expr = ast::BlockExpr::cast(block.syntax().parent()?)?;
    let expr = extract_trivial_expression(block)?;

    let block_range = block_expr.syntax().range();
    let mut buf = expr.syntax().text().to_string();

    // Match block needs to have a comma after the block
    if let Some(match_arm) = block_expr.syntax().parent().and_then(ast::MatchArm::cast) {
        if !has_comma_after(match_arm.syntax()) {
            buf.push(',');
        }
    }

    edit.replace(block_range, buf);

    Some(())
}

fn join_single_use_tree(edit: &mut TextEditBuilder, node: &SyntaxNode) -> Option<()> {
    let use_tree_list = ast::UseTreeList::cast(node.parent()?)?;
    let (tree,) = use_tree_list.use_trees().collect_tuple()?;
    edit.replace(use_tree_list.syntax().range(), tree.syntax().text().to_string());
    Some(())
}

fn is_trailing_comma(left: SyntaxKind, right: SyntaxKind) -> bool {
    match (left, right) {
        (COMMA, R_PAREN) | (COMMA, R_BRACK) => true,
        _ => false,
    }
}

#[cfg(test)]
mod tests {
    use crate::test_utils::{assert_eq_text, check_action, extract_range};

    use super::*;

    fn check_join_lines(before: &str, after: &str) {
        check_action(before, after, |file, offset| {
            let range = TextRange::offset_len(offset, 0.into());
            let res = join_lines(file, range);
            Some(res)
        })
    }

    #[test]
    fn test_join_lines_comma() {
        check_join_lines(
            r"
fn foo() {
    <|>foo(1,
    )
}
",
            r"
fn foo() {
    <|>foo(1)
}
",
        );
    }

    #[test]
    fn test_join_lines_lambda_block() {
        check_join_lines(
            r"
pub fn reparse(&self, edit: &AtomTextEdit) -> File {
    <|>self.incremental_reparse(edit).unwrap_or_else(|| {
        self.full_reparse(edit)
    })
}
",
            r"
pub fn reparse(&self, edit: &AtomTextEdit) -> File {
    <|>self.incremental_reparse(edit).unwrap_or_else(|| self.full_reparse(edit))
}
",
        );
    }

    #[test]
    fn test_join_lines_block() {
        check_join_lines(
            r"
fn foo() {
    foo(<|>{
        92
    })
}",
            r"
fn foo() {
    foo(<|>92)
}",
        );
    }

    #[test]
    fn join_lines_adds_comma_for_block_in_match_arm() {
        check_join_lines(
            r"
fn foo(e: Result<U, V>) {
    match e {
        Ok(u) => <|>{
            u.foo()
        }
        Err(v) => v,
    }
}",
            r"
fn foo(e: Result<U, V>) {
    match e {
        Ok(u) => <|>u.foo(),
        Err(v) => v,
    }
}",
        );
    }

    #[test]
    fn join_lines_keeps_comma_for_block_in_match_arm() {
        // We already have a comma
        check_join_lines(
            r"
fn foo(e: Result<U, V>) {
    match e {
        Ok(u) => <|>{
            u.foo()
        },
        Err(v) => v,
    }
}",
            r"
fn foo(e: Result<U, V>) {
    match e {
        Ok(u) => <|>u.foo(),
        Err(v) => v,
    }
}",
        );

        // comma with whitespace between brace and ,
        check_join_lines(
            r"
fn foo(e: Result<U, V>) {
    match e {
        Ok(u) => <|>{
            u.foo()
        }    ,
        Err(v) => v,
    }
}",
            r"
fn foo(e: Result<U, V>) {
    match e {
        Ok(u) => <|>u.foo()    ,
        Err(v) => v,
    }
}",
        );

        // comma with newline between brace and ,
        check_join_lines(
            r"
fn foo(e: Result<U, V>) {
    match e {
        Ok(u) => <|>{
            u.foo()
        }
        ,
        Err(v) => v,
    }
}",
            r"
fn foo(e: Result<U, V>) {
    match e {
        Ok(u) => <|>u.foo()
        ,
        Err(v) => v,
    }
}",
        );
    }

    #[test]
    fn join_lines_keeps_comma_with_single_arg_tuple() {
        // A single arg tuple
        check_join_lines(
            r"
fn foo() {
    let x = (<|>{
       4
    },);
}",
            r"
fn foo() {
    let x = (<|>4,);
}",
        );

        // single arg tuple with whitespace between brace and comma
        check_join_lines(
            r"
fn foo() {
    let x = (<|>{
       4
    }   ,);
}",
            r"
fn foo() {
    let x = (<|>4   ,);
}",
        );

        // single arg tuple with newline between brace and comma
        check_join_lines(
            r"
fn foo() {
    let x = (<|>{
       4
    }
    ,);
}",
            r"
fn foo() {
    let x = (<|>4
    ,);
}",
        );
    }

    #[test]
    fn test_join_lines_use_items_left() {
        // No space after the '{'
        check_join_lines(
            r"
<|>use ra_syntax::{
    TextUnit, TextRange,
};",
            r"
<|>use ra_syntax::{TextUnit, TextRange,
};",
        );
    }

    #[test]
    fn test_join_lines_use_items_right() {
        // No space after the '}'
        check_join_lines(
            r"
use ra_syntax::{
<|>    TextUnit, TextRange
};",
            r"
use ra_syntax::{
<|>    TextUnit, TextRange};",
        );
    }

    #[test]
    fn test_join_lines_use_items_right_comma() {
        // No space after the '}'
        check_join_lines(
            r"
use ra_syntax::{
<|>    TextUnit, TextRange,
};",
            r"
use ra_syntax::{
<|>    TextUnit, TextRange};",
        );
    }

    #[test]
    fn test_join_lines_use_tree() {
        check_join_lines(
            r"
use ra_syntax::{
    algo::<|>{
        find_leaf_at_offset,
    },
    ast,
};",
            r"
use ra_syntax::{
    algo::<|>find_leaf_at_offset,
    ast,
};",
        );
    }

    #[test]
    fn test_join_lines_normal_comments() {
        check_join_lines(
            r"
fn foo() {
    // Hello<|>
    // world!
}
",
            r"
fn foo() {
    // Hello<|> world!
}
",
        );
    }

    #[test]
    fn test_join_lines_doc_comments() {
        check_join_lines(
            r"
fn foo() {
    /// Hello<|>
    /// world!
}
",
            r"
fn foo() {
    /// Hello<|> world!
}
",
        );
    }

    #[test]
    fn test_join_lines_mod_comments() {
        check_join_lines(
            r"
fn foo() {
    //! Hello<|>
    //! world!
}
",
            r"
fn foo() {
    //! Hello<|> world!
}
",
        );
    }

    #[test]
    fn test_join_lines_multiline_comments_1() {
        check_join_lines(
            r"
fn foo() {
    // Hello<|>
    /* world! */
}
",
            r"
fn foo() {
    // Hello<|> world! */
}
",
        );
    }

    #[test]
    fn test_join_lines_multiline_comments_2() {
        check_join_lines(
            r"
fn foo() {
    // The<|>
    /* quick
    brown
    fox! */
}
",
            r"
fn foo() {
    // The<|> quick
    brown
    fox! */
}
",
        );
    }

    fn check_join_lines_sel(before: &str, after: &str) {
        let (sel, before) = extract_range(before);
        let file = SourceFile::parse(&before);
        let result = join_lines(&file, sel);
        let actual = result.edit.apply(&before);
        assert_eq_text!(after, &actual);
    }

    #[test]
    fn test_join_lines_selection_fn_args() {
        check_join_lines_sel(
            r"
fn foo() {
    <|>foo(1,
        2,
        3,
    <|>)
}
    ",
            r"
fn foo() {
    foo(1, 2, 3)
}
    ",
        );
    }

    #[test]
    fn test_join_lines_selection_struct() {
        check_join_lines_sel(
            r"
struct Foo <|>{
    f: u32,
}<|>
    ",
            r"
struct Foo { f: u32 }
    ",
        );
    }

    #[test]
    fn test_join_lines_selection_dot_chain() {
        check_join_lines_sel(
            r"
fn foo() {
    join(<|>type_params.type_params()
            .filter_map(|it| it.name())
            .map(|it| it.text())<|>)
}",
            r"
fn foo() {
    join(type_params.type_params().filter_map(|it| it.name()).map(|it| it.text()))
}",
        );
    }

    #[test]
    fn test_join_lines_selection_lambda_block_body() {
        check_join_lines_sel(
            r"
pub fn handle_find_matching_brace() {
    params.offsets
        .map(|offset| <|>{
            world.analysis().matching_brace(&file, offset).unwrap_or(offset)
        }<|>)
        .collect();
}",
            r"
pub fn handle_find_matching_brace() {
    params.offsets
        .map(|offset| world.analysis().matching_brace(&file, offset).unwrap_or(offset))
        .collect();
}",
        );
    }
}
<|endoftext|>


<commit_before>//! FIXME: write short doc here

pub mod not_bash;
pub mod install;
pub mod pre_commit;

pub mod codegen;
mod ast_src;

use anyhow::Context;
use std::{
    env, fs,
    io::Write,
    path::{Path, PathBuf},
    process::{Command, Stdio},
};

use crate::{
    codegen::Mode,
    not_bash::{pushd, run},
};

pub use anyhow::Result;

const TOOLCHAIN: &str = "stable";

pub fn project_root() -> PathBuf {
    Path::new(
        &env::var("CARGO_MANIFEST_DIR").unwrap_or_else(|_| env!("CARGO_MANIFEST_DIR").to_owned()),
    )
    .ancestors()
    .nth(1)
    .unwrap()
    .to_path_buf()
}

pub fn run_rustfmt(mode: Mode) -> Result<()> {
    ensure_rustfmt()?;

    if mode == Mode::Verify {
        run!("rustup run {} -- cargo fmt -- --check", TOOLCHAIN)?;
    } else {
        run!("rustup run {} -- cargo fmt", TOOLCHAIN)?;
    }
    Ok(())
}

fn reformat(text: impl std::fmt::Display) -> Result<String> {
    ensure_rustfmt()?;
    let mut rustfmt = Command::new("rustup")
        .args(&["run", TOOLCHAIN, "--", "rustfmt", "--config-path"])
        .arg(project_root().join("rustfmt.toml"))
        .stdin(Stdio::piped())
        .stdout(Stdio::piped())
        .spawn()?;
    write!(rustfmt.stdin.take().unwrap(), "{}", text)?;
    let output = rustfmt.wait_with_output()?;
    let stdout = String::from_utf8(output.stdout)?;
    let preamble = "Generated file, do not edit by hand, see `xtask/src/codegen`";
    Ok(format!("//! {}\n\n{}", preamble, stdout))
}

fn ensure_rustfmt() -> Result<()> {
    match Command::new("rustup")
        .args(&["run", TOOLCHAIN, "--", "cargo", "fmt", "--version"])
        .stderr(Stdio::null())
        .stdout(Stdio::null())
        .status()
    {
        Ok(status) if status.success() => return Ok(()),
        _ => (),
    };
    run!("rustup toolchain install {}", TOOLCHAIN)?;
    run!("rustup component add rustfmt --toolchain {}", TOOLCHAIN)?;
    Ok(())
}

pub fn run_clippy() -> Result<()> {
    match Command::new("rustup")
        .args(&["run", TOOLCHAIN, "--", "cargo", "clippy", "--version"])
        .stderr(Stdio::null())
        .stdout(Stdio::null())
        .status()
    {
        Ok(status) if status.success() => (),
        _ => install_clippy().context("install clippy")?,
    };

    let allowed_lints = [
        "clippy::collapsible_if",
        "clippy::map_clone", // FIXME: remove when Iterator::copied stabilizes (1.36.0)
        "clippy::needless_pass_by_value",
        "clippy::nonminimal_bool",
        "clippy::redundant_pattern_matching",
    ];
    run!(
        "rustup run {} -- cargo clippy --all-features --all-targets -- -A {}",
        TOOLCHAIN,
        allowed_lints.join(" -A ")
    )?;
    Ok(())
}

fn install_clippy() -> Result<()> {
    run!("rustup toolchain install {}", TOOLCHAIN)?;
    run!("rustup component add clippy --toolchain {}", TOOLCHAIN)?;
    Ok(())
}

pub fn run_fuzzer() -> Result<()> {
    let _d = pushd("./crates/ra_syntax");
    match run!("cargo fuzz --help") {
        Ok(_) => (),
        _ => {
            run!("cargo install cargo-fuzz")?;
        }
    };

    run!("rustup run nightly -- cargo fuzz run parser")?;
    Ok(())
}

/// Cleans the `./target` dir after the build such that only
/// dependencies are cached on CI.
pub fn run_pre_cache() -> Result<()> {
    let slow_tests_cookie = Path::new("./target/.slow_tests_cookie");
    if !slow_tests_cookie.exists() {
        panic!("slow tests were skipped on CI!")
    }
    rm_rf(slow_tests_cookie)?;

    for entry in Path::new("./target/debug").read_dir()? {
        let entry = entry?;
        if entry.file_type().map(|it| it.is_file()).ok() == Some(true) {
            // Can't delete yourself on windows :-(
            if !entry.path().ends_with("xtask.exe") {
                rm_rf(&entry.path())?
            }
        }
    }

    fs::remove_file("./target/.rustc_info.json")?;
    let to_delete = ["ra_", "heavy_test"];
    for &dir in ["./target/debug/deps", "target/debug/.fingerprint"].iter() {
        for entry in Path::new(dir).read_dir()? {
            let entry = entry?;
            if to_delete.iter().any(|&it| entry.path().display().to_string().contains(it)) {
                rm_rf(&entry.path())?
            }
        }
    }

    Ok(())
}

fn rm_rf(path: &Path) -> Result<()> {
    if path.is_file() { fs::remove_file(path) } else { fs::remove_dir_all(path) }
        .with_context(|| format!("failed to remove {:?}", path))
}

pub fn run_release(dry_run: bool) -> Result<()> {
    if !dry_run {
        run!("git switch release")?;
        run!("git fetch upstream")?;
        run!("git reset --hard upstream/master")?;
        run!("git push")?;
    }

    let changelog_dir = project_root().join("../rust-analyzer.github.io/thisweek/_posts");

    let today = run!("date --iso")?;
    let commit = run!("git rev-parse HEAD")?;
    let changelog_n = fs::read_dir(changelog_dir.as_path())?.count();

    let contents = format!(
        "\
= Changelog #{}
:sectanchors:
:page-layout: post

Commit: commit:{}[] +
Release: release:{}[]

== New Features

* pr:[] .

== Fixes

== Internal Improvements
",
        changelog_n, commit, today
    );

    let path = changelog_dir.join(format!("{}-changelog-{}.adoc", today, changelog_n));
    fs::write(&path, &contents)?;

    Ok(())
}
<commit_msg>Update the manual on release<commit_after>//! FIXME: write short doc here

pub mod not_bash;
pub mod install;
pub mod pre_commit;

pub mod codegen;
mod ast_src;

use anyhow::Context;
use std::{
    env, fs,
    io::Write,
    path::{Path, PathBuf},
    process::{Command, Stdio},
};

use crate::{
    codegen::Mode,
    not_bash::{pushd, run},
};

pub use anyhow::Result;

const TOOLCHAIN: &str = "stable";

pub fn project_root() -> PathBuf {
    Path::new(
        &env::var("CARGO_MANIFEST_DIR").unwrap_or_else(|_| env!("CARGO_MANIFEST_DIR").to_owned()),
    )
    .ancestors()
    .nth(1)
    .unwrap()
    .to_path_buf()
}

pub fn run_rustfmt(mode: Mode) -> Result<()> {
    ensure_rustfmt()?;

    if mode == Mode::Verify {
        run!("rustup run {} -- cargo fmt -- --check", TOOLCHAIN)?;
    } else {
        run!("rustup run {} -- cargo fmt", TOOLCHAIN)?;
    }
    Ok(())
}

fn reformat(text: impl std::fmt::Display) -> Result<String> {
    ensure_rustfmt()?;
    let mut rustfmt = Command::new("rustup")
        .args(&["run", TOOLCHAIN, "--", "rustfmt", "--config-path"])
        .arg(project_root().join("rustfmt.toml"))
        .stdin(Stdio::piped())
        .stdout(Stdio::piped())
        .spawn()?;
    write!(rustfmt.stdin.take().unwrap(), "{}", text)?;
    let output = rustfmt.wait_with_output()?;
    let stdout = String::from_utf8(output.stdout)?;
    let preamble = "Generated file, do not edit by hand, see `xtask/src/codegen`";
    Ok(format!("//! {}\n\n{}", preamble, stdout))
}

fn ensure_rustfmt() -> Result<()> {
    match Command::new("rustup")
        .args(&["run", TOOLCHAIN, "--", "cargo", "fmt", "--version"])
        .stderr(Stdio::null())
        .stdout(Stdio::null())
        .status()
    {
        Ok(status) if status.success() => return Ok(()),
        _ => (),
    };
    run!("rustup toolchain install {}", TOOLCHAIN)?;
    run!("rustup component add rustfmt --toolchain {}", TOOLCHAIN)?;
    Ok(())
}

pub fn run_clippy() -> Result<()> {
    match Command::new("rustup")
        .args(&["run", TOOLCHAIN, "--", "cargo", "clippy", "--version"])
        .stderr(Stdio::null())
        .stdout(Stdio::null())
        .status()
    {
        Ok(status) if status.success() => (),
        _ => install_clippy().context("install clippy")?,
    };

    let allowed_lints = [
        "clippy::collapsible_if",
        "clippy::map_clone", // FIXME: remove when Iterator::copied stabilizes (1.36.0)
        "clippy::needless_pass_by_value",
        "clippy::nonminimal_bool",
        "clippy::redundant_pattern_matching",
    ];
    run!(
        "rustup run {} -- cargo clippy --all-features --all-targets -- -A {}",
        TOOLCHAIN,
        allowed_lints.join(" -A ")
    )?;
    Ok(())
}

fn install_clippy() -> Result<()> {
    run!("rustup toolchain install {}", TOOLCHAIN)?;
    run!("rustup component add clippy --toolchain {}", TOOLCHAIN)?;
    Ok(())
}

pub fn run_fuzzer() -> Result<()> {
    let _d = pushd("./crates/ra_syntax");
    match run!("cargo fuzz --help") {
        Ok(_) => (),
        _ => {
            run!("cargo install cargo-fuzz")?;
        }
    };

    run!("rustup run nightly -- cargo fuzz run parser")?;
    Ok(())
}

/// Cleans the `./target` dir after the build such that only
/// dependencies are cached on CI.
pub fn run_pre_cache() -> Result<()> {
    let slow_tests_cookie = Path::new("./target/.slow_tests_cookie");
    if !slow_tests_cookie.exists() {
        panic!("slow tests were skipped on CI!")
    }
    rm_rf(slow_tests_cookie)?;

    for entry in Path::new("./target/debug").read_dir()? {
        let entry = entry?;
        if entry.file_type().map(|it| it.is_file()).ok() == Some(true) {
            // Can't delete yourself on windows :-(
            if !entry.path().ends_with("xtask.exe") {
                rm_rf(&entry.path())?
            }
        }
    }

    fs::remove_file("./target/.rustc_info.json")?;
    let to_delete = ["ra_", "heavy_test"];
    for &dir in ["./target/debug/deps", "target/debug/.fingerprint"].iter() {
        for entry in Path::new(dir).read_dir()? {
            let entry = entry?;
            if to_delete.iter().any(|&it| entry.path().display().to_string().contains(it)) {
                rm_rf(&entry.path())?
            }
        }
    }

    Ok(())
}

fn rm_rf(path: &Path) -> Result<()> {
    if path.is_file() { fs::remove_file(path) } else { fs::remove_dir_all(path) }
        .with_context(|| format!("failed to remove {:?}", path))
}

pub fn run_release(dry_run: bool) -> Result<()> {
    if !dry_run {
        run!("git switch release")?;
        run!("git fetch upstream")?;
        run!("git reset --hard upstream/master")?;
        run!("git push")?;
    }

    let website_root = project_root().join("../rust-analyzer.github.io");
    let changelog_dir = website_root.join("/thisweek/_posts");

    let today = run!("date --iso")?;
    let commit = run!("git rev-parse HEAD")?;
    let changelog_n = fs::read_dir(changelog_dir.as_path())?.count();

    let contents = format!(
        "\
= Changelog #{}
:sectanchors:
:page-layout: post

Commit: commit:{}[] +
Release: release:{}[]

== New Features

* pr:[] .

== Fixes

== Internal Improvements
",
        changelog_n, commit, today
    );

    let path = changelog_dir.join(format!("{}-changelog-{}.adoc", today, changelog_n));
    fs::write(&path, &contents)?;

    fs::copy(project_root().join("./docs/user/readme.adoc"), website_root.join("manual.adoc"))?;

    Ok(())
}
<|endoftext|>


<commit_before>use super::*;

type Element = (usize, &'static str);

fn test_map() -> Vec<Element> {
    let mut data = vec![(3, "three-a"), (0, "zero"), (3, "three-b"), (22, "twenty-two")];
    data.sort_by_key(get_key)
}

fn get_key(data: &Element) -> usize {
    data.0
}

#[test]
fn binary_search_slice() {
    let map = test_map();
    assert_eq!(binary_search_slice(&map, get_key, &0), &[(0, "zero")]);
    assert_eq!(binary_search_slice(&map, get_key, &1), &[]);
    assert_eq!(binary_search_slice(&map, get_key, &3), &[(3, "three-a"), (3, "three-b")]);
    assert_eq!(binary_search_slice(&map, get_key, &22), &[(22, "twenty-two")]);
    assert_eq!(binary_search_slice(&map, get_key, &23), &[]);
}
<commit_msg>fix silly bugs in binary_search_util test<commit_after>use super::*;

type Element = (usize, &'static str);

fn test_map() -> Vec<Element> {
    let mut data = vec![(3, "three-a"), (0, "zero"), (3, "three-b"), (22, "twenty-two")];
    data.sort_by_key(get_key);
    data
}

fn get_key(data: &Element) -> usize {
    data.0
}

#[test]
fn binary_search_slice_test() {
    let map = test_map();
    assert_eq!(binary_search_slice(&map, get_key, &0), &[(0, "zero")]);
    assert_eq!(binary_search_slice(&map, get_key, &1), &[]);
    assert_eq!(binary_search_slice(&map, get_key, &3), &[(3, "three-a"), (3, "three-b")]);
    assert_eq!(binary_search_slice(&map, get_key, &22), &[(22, "twenty-two")]);
    assert_eq!(binary_search_slice(&map, get_key, &23), &[]);
}
<|endoftext|>


<commit_before>//! Abstraction around the object writing crate

use std::convert::{TryFrom, TryInto};

use rustc_data_structures::fx::FxHashMap;
use rustc_session::Session;

use cranelift_module::FuncId;

use object::write::*;
use object::{RelocationEncoding, RelocationKind, SectionKind, SymbolFlags};

use cranelift_object::{ObjectBuilder, ObjectModule, ObjectProduct};

use gimli::SectionId;

use crate::debuginfo::{DebugReloc, DebugRelocName};

pub(crate) trait WriteMetadata {
    fn add_rustc_section(&mut self, symbol_name: String, data: Vec<u8>, is_like_osx: bool);
}

impl WriteMetadata for object::write::Object {
    fn add_rustc_section(&mut self, symbol_name: String, data: Vec<u8>, _is_like_osx: bool) {
        let segment = self
            .segment_name(object::write::StandardSegment::Data)
            .to_vec();
        let section_id = self.add_section(segment, b".rustc".to_vec(), object::SectionKind::Data);
        let offset = self.append_section_data(section_id, &data, 1);
        // For MachO and probably PE this is necessary to prevent the linker from throwing away the
        // .rustc section. For ELF this isn't necessary, but it also doesn't harm.
        self.add_symbol(object::write::Symbol {
            name: symbol_name.into_bytes(),
            value: offset,
            size: data.len() as u64,
            kind: object::SymbolKind::Data,
            scope: object::SymbolScope::Dynamic,
            weak: false,
            section: SymbolSection::Section(section_id),
            flags: SymbolFlags::None,
        });
    }
}

pub(crate) trait WriteDebugInfo {
    type SectionId: Copy;

    fn add_debug_section(&mut self, name: SectionId, data: Vec<u8>) -> Self::SectionId;
    fn add_debug_reloc(
        &mut self,
        section_map: &FxHashMap<SectionId, Self::SectionId>,
        from: &Self::SectionId,
        reloc: &DebugReloc,
    );
}

impl WriteDebugInfo for ObjectProduct {
    type SectionId = (object::write::SectionId, object::write::SymbolId);

    fn add_debug_section(
        &mut self,
        id: SectionId,
        data: Vec<u8>,
    ) -> (object::write::SectionId, object::write::SymbolId) {
        let name = if self.object.format() == object::BinaryFormat::MachO {
            id.name().replace('.', "__") // machO expects __debug_info instead of .debug_info
        } else {
            id.name().to_string()
        }
        .into_bytes();

        let segment = self.object.segment_name(StandardSegment::Debug).to_vec();
        // FIXME use SHT_X86_64_UNWIND for .eh_frame
        let section_id = self.object.add_section(
            segment,
            name.clone(),
            if id == SectionId::EhFrame {
                SectionKind::ReadOnlyData
            } else {
                SectionKind::Debug
            },
        );
        self.object
            .section_mut(section_id)
            .set_data(data, if id == SectionId::EhFrame { 8 } else { 1 });
        let symbol_id = self.object.section_symbol(section_id);
        (section_id, symbol_id)
    }

    fn add_debug_reloc(
        &mut self,
        section_map: &FxHashMap<SectionId, Self::SectionId>,
        from: &Self::SectionId,
        reloc: &DebugReloc,
    ) {
        let (symbol, symbol_offset) = match reloc.name {
            DebugRelocName::Section(id) => (section_map.get(&id).unwrap().1, 0),
            DebugRelocName::Symbol(id) => {
                let symbol_id = self.function_symbol(FuncId::from_u32(id.try_into().unwrap()));
                self.object
                    .symbol_section_and_offset(symbol_id)
                    .expect("Debug reloc for undef sym???")
            }
        };
        self.object
            .add_relocation(
                from.0,
                Relocation {
                    offset: u64::from(reloc.offset),
                    symbol,
                    kind: reloc.kind,
                    encoding: RelocationEncoding::Generic,
                    size: reloc.size * 8,
                    addend: i64::try_from(symbol_offset).unwrap() + reloc.addend,
                },
            )
            .unwrap();
    }
}

// FIXME remove once atomic instructions are implemented in Cranelift.
pub(crate) trait AddConstructor {
    fn add_constructor(&mut self, func_id: FuncId);
}

impl AddConstructor for ObjectProduct {
    fn add_constructor(&mut self, func_id: FuncId) {
        let symbol = self.function_symbol(func_id);
        let segment = self
            .object
            .segment_name(object::write::StandardSegment::Data);
        let init_array_section =
            self.object
                .add_section(segment.to_vec(), b".init_array".to_vec(), SectionKind::Data);
        self.object.append_section_data(
            init_array_section,
            &std::iter::repeat(0)
                .take(8 /*FIXME pointer size*/)
                .collect::<Vec<u8>>(),
            8,
        );
        self.object
            .add_relocation(
                init_array_section,
                object::write::Relocation {
                    offset: 0,
                    size: 64, // FIXME pointer size
                    kind: RelocationKind::Absolute,
                    encoding: RelocationEncoding::Generic,
                    symbol,
                    addend: 0,
                },
            )
            .unwrap();
    }
}

pub(crate) fn with_object(sess: &Session, name: &str, f: impl FnOnce(&mut Object)) -> Vec<u8> {
    let triple = crate::build_isa(sess, true).triple().clone();

    let binary_format = match triple.binary_format {
        target_lexicon::BinaryFormat::Elf => object::BinaryFormat::Elf,
        target_lexicon::BinaryFormat::Coff => object::BinaryFormat::Coff,
        target_lexicon::BinaryFormat::Macho => object::BinaryFormat::MachO,
        binary_format => sess.fatal(&format!("binary format {} is unsupported", binary_format)),
    };
    let architecture = match triple.architecture {
        target_lexicon::Architecture::X86_32(_) => object::Architecture::I386,
        target_lexicon::Architecture::X86_64 => object::Architecture::X86_64,
        target_lexicon::Architecture::Arm(_) => object::Architecture::Arm,
        target_lexicon::Architecture::Aarch64(_) => object::Architecture::Aarch64,
        architecture => sess.fatal(&format!(
            "target architecture {:?} is unsupported",
            architecture,
        )),
    };
    let endian = match triple.endianness().unwrap() {
        target_lexicon::Endianness::Little => object::Endianness::Little,
        target_lexicon::Endianness::Big => object::Endianness::Big,
    };

    let mut metadata_object = object::write::Object::new(binary_format, architecture, endian);
    metadata_object.add_file_symbol(name.as_bytes().to_vec());
    f(&mut metadata_object);
    metadata_object.write().unwrap()
}

pub(crate) fn make_module(sess: &Session, name: String) -> ObjectModule {
    let mut builder = ObjectBuilder::new(
        crate::build_isa(sess, true),
        name + ".o",
        cranelift_module::default_libcall_names(),
    )
    .unwrap();
    if std::env::var("CG_CLIF_FUNCTION_SECTIONS").is_ok() {
        builder.per_function_section(true);
    }
    let module = ObjectModule::new(builder);
    module
}
<commit_msg>Use architecture pointer size for AddConstructor (#1094)<commit_after>//! Abstraction around the object writing crate

use std::convert::{TryFrom, TryInto};

use rustc_data_structures::fx::FxHashMap;
use rustc_session::Session;

use cranelift_module::FuncId;

use object::write::*;
use object::{RelocationEncoding, RelocationKind, SectionKind, SymbolFlags};

use cranelift_object::{ObjectBuilder, ObjectModule, ObjectProduct};

use gimli::SectionId;

use crate::debuginfo::{DebugReloc, DebugRelocName};

pub(crate) trait WriteMetadata {
    fn add_rustc_section(&mut self, symbol_name: String, data: Vec<u8>, is_like_osx: bool);
}

impl WriteMetadata for object::write::Object {
    fn add_rustc_section(&mut self, symbol_name: String, data: Vec<u8>, _is_like_osx: bool) {
        let segment = self
            .segment_name(object::write::StandardSegment::Data)
            .to_vec();
        let section_id = self.add_section(segment, b".rustc".to_vec(), object::SectionKind::Data);
        let offset = self.append_section_data(section_id, &data, 1);
        // For MachO and probably PE this is necessary to prevent the linker from throwing away the
        // .rustc section. For ELF this isn't necessary, but it also doesn't harm.
        self.add_symbol(object::write::Symbol {
            name: symbol_name.into_bytes(),
            value: offset,
            size: data.len() as u64,
            kind: object::SymbolKind::Data,
            scope: object::SymbolScope::Dynamic,
            weak: false,
            section: SymbolSection::Section(section_id),
            flags: SymbolFlags::None,
        });
    }
}

pub(crate) trait WriteDebugInfo {
    type SectionId: Copy;

    fn add_debug_section(&mut self, name: SectionId, data: Vec<u8>) -> Self::SectionId;
    fn add_debug_reloc(
        &mut self,
        section_map: &FxHashMap<SectionId, Self::SectionId>,
        from: &Self::SectionId,
        reloc: &DebugReloc,
    );
}

impl WriteDebugInfo for ObjectProduct {
    type SectionId = (object::write::SectionId, object::write::SymbolId);

    fn add_debug_section(
        &mut self,
        id: SectionId,
        data: Vec<u8>,
    ) -> (object::write::SectionId, object::write::SymbolId) {
        let name = if self.object.format() == object::BinaryFormat::MachO {
            id.name().replace('.', "__") // machO expects __debug_info instead of .debug_info
        } else {
            id.name().to_string()
        }
        .into_bytes();

        let segment = self.object.segment_name(StandardSegment::Debug).to_vec();
        // FIXME use SHT_X86_64_UNWIND for .eh_frame
        let section_id = self.object.add_section(
            segment,
            name.clone(),
            if id == SectionId::EhFrame {
                SectionKind::ReadOnlyData
            } else {
                SectionKind::Debug
            },
        );
        self.object
            .section_mut(section_id)
            .set_data(data, if id == SectionId::EhFrame { 8 } else { 1 });
        let symbol_id = self.object.section_symbol(section_id);
        (section_id, symbol_id)
    }

    fn add_debug_reloc(
        &mut self,
        section_map: &FxHashMap<SectionId, Self::SectionId>,
        from: &Self::SectionId,
        reloc: &DebugReloc,
    ) {
        let (symbol, symbol_offset) = match reloc.name {
            DebugRelocName::Section(id) => (section_map.get(&id).unwrap().1, 0),
            DebugRelocName::Symbol(id) => {
                let symbol_id = self.function_symbol(FuncId::from_u32(id.try_into().unwrap()));
                self.object
                    .symbol_section_and_offset(symbol_id)
                    .expect("Debug reloc for undef sym???")
            }
        };
        self.object
            .add_relocation(
                from.0,
                Relocation {
                    offset: u64::from(reloc.offset),
                    symbol,
                    kind: reloc.kind,
                    encoding: RelocationEncoding::Generic,
                    size: reloc.size * 8,
                    addend: i64::try_from(symbol_offset).unwrap() + reloc.addend,
                },
            )
            .unwrap();
    }
}

// FIXME remove once atomic instructions are implemented in Cranelift.
pub(crate) trait AddConstructor {
    fn add_constructor(&mut self, func_id: FuncId);
}

impl AddConstructor for ObjectProduct {
    fn add_constructor(&mut self, func_id: FuncId) {
        let symbol = self.function_symbol(func_id);
        let segment = self
            .object
            .segment_name(object::write::StandardSegment::Data);
        let init_array_section =
            self.object
                .add_section(segment.to_vec(), b".init_array".to_vec(), SectionKind::Data);
        let address_size = self
            .object
            .architecture()
            .address_size()
            .expect("address_size must be known")
            .bytes();
        self.object.append_section_data(
            init_array_section,
            &std::iter::repeat(0)
                .take(address_size.into())
                .collect::<Vec<u8>>(),
            8,
        );
        self.object
            .add_relocation(
                init_array_section,
                object::write::Relocation {
                    offset: 0,
                    size: address_size * 8,
                    kind: RelocationKind::Absolute,
                    encoding: RelocationEncoding::Generic,
                    symbol,
                    addend: 0,
                },
            )
            .unwrap();
    }
}

pub(crate) fn with_object(sess: &Session, name: &str, f: impl FnOnce(&mut Object)) -> Vec<u8> {
    let triple = crate::build_isa(sess, true).triple().clone();

    let binary_format = match triple.binary_format {
        target_lexicon::BinaryFormat::Elf => object::BinaryFormat::Elf,
        target_lexicon::BinaryFormat::Coff => object::BinaryFormat::Coff,
        target_lexicon::BinaryFormat::Macho => object::BinaryFormat::MachO,
        binary_format => sess.fatal(&format!("binary format {} is unsupported", binary_format)),
    };
    let architecture = match triple.architecture {
        target_lexicon::Architecture::X86_32(_) => object::Architecture::I386,
        target_lexicon::Architecture::X86_64 => object::Architecture::X86_64,
        target_lexicon::Architecture::Arm(_) => object::Architecture::Arm,
        target_lexicon::Architecture::Aarch64(_) => object::Architecture::Aarch64,
        architecture => sess.fatal(&format!(
            "target architecture {:?} is unsupported",
            architecture,
        )),
    };
    let endian = match triple.endianness().unwrap() {
        target_lexicon::Endianness::Little => object::Endianness::Little,
        target_lexicon::Endianness::Big => object::Endianness::Big,
    };

    let mut metadata_object = object::write::Object::new(binary_format, architecture, endian);
    metadata_object.add_file_symbol(name.as_bytes().to_vec());
    f(&mut metadata_object);
    metadata_object.write().unwrap()
}

pub(crate) fn make_module(sess: &Session, name: String) -> ObjectModule {
    let mut builder = ObjectBuilder::new(
        crate::build_isa(sess, true),
        name + ".o",
        cranelift_module::default_libcall_names(),
    )
    .unwrap();
    if std::env::var("CG_CLIF_FUNCTION_SECTIONS").is_ok() {
        builder.per_function_section(true);
    }
    let module = ObjectModule::new(builder);
    module
}
<|endoftext|>