<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/. */

//! Text layout.

use layout::box_::{Box, ScannedTextBox, ScannedTextBoxInfo, UnscannedTextBox};
use layout::context::LayoutContext;
use layout::flow::Flow;

use extra::arc::Arc;
use gfx::text::text_run::TextRun;
use gfx::text::util::{CompressWhitespaceNewline, transform_text, CompressNone};
use servo_util::range::Range;
use std::vec;
use style::computed_values::white_space;

/// A stack-allocated object for scanning an inline flow into `TextRun`-containing `TextBox`es.
pub struct TextRunScanner {
    clump: Range,
}

impl TextRunScanner {
    pub fn new() -> TextRunScanner {
        TextRunScanner {
            clump: Range::empty(),
        }
    }

    pub fn scan_for_runs(&mut self, ctx: &mut LayoutContext, flow: &mut Flow) {
        {
            let inline = flow.as_immutable_inline();
            debug!("TextRunScanner: scanning {:u} boxes for text runs...", inline.boxes.len());
        }

        let mut last_whitespace = true;
        let mut out_boxes = ~[];
        for box_i in range(0, flow.as_immutable_inline().boxes.len()) {
            debug!("TextRunScanner: considering box: {:u}", box_i);
            if box_i > 0 && !can_coalesce_text_nodes(flow.as_immutable_inline().boxes,
                                                     box_i - 1,
                                                     box_i) {
                last_whitespace = self.flush_clump_to_list(ctx, flow, last_whitespace, &mut out_boxes);
            }
            self.clump.extend_by(1);
        }
        // handle remaining clumps
        if self.clump.length() > 0 {
            self.flush_clump_to_list(ctx, flow, last_whitespace, &mut out_boxes);
        }

        debug!("TextRunScanner: swapping out boxes.");

        // Swap out the old and new box list of the flow.
        flow.as_inline().boxes = out_boxes;

        // A helper function.
        fn can_coalesce_text_nodes(boxes: &[Box], left_i: uint, right_i: uint) -> bool {
            assert!(left_i < boxes.len());
            assert!(right_i > 0 && right_i < boxes.len());
            assert!(left_i != right_i);
            boxes[left_i].can_merge_with_box(&boxes[right_i])
        }
    }

    /// A "clump" is a range of inline flow leaves that can be merged together into a single box.
    /// Adjacent text with the same style can be merged, and nothing else can.
    ///
    /// The flow keeps track of the boxes contained by all non-leaf DOM nodes. This is necessary
    /// for correct painting order. Since we compress several leaf boxes here, the mapping must be
    /// adjusted.
    ///
    /// FIXME(pcwalton): Stop cloning boxes. Instead we will need to consume the `in_box`es as we
    /// iterate over them.
    pub fn flush_clump_to_list(&mut self,
                               ctx: &mut LayoutContext,
                               flow: &mut Flow,
                               last_whitespace: bool,
                               out_boxes: &mut ~[Box])
                               -> bool {
        let inline = flow.as_inline();
        let in_boxes = &mut inline.boxes;

        assert!(self.clump.length() > 0);

        debug!("TextRunScanner: flushing boxes in range={}", self.clump);
        let is_singleton = self.clump.length() == 1;

        let is_text_clump = match in_boxes[self.clump.begin()].specific {
            UnscannedTextBox(_) => true,
            _ => false,
        };

        let mut new_whitespace = last_whitespace;
        match (is_singleton, is_text_clump) {
            (false, false) => {
                fail!(~"WAT: can't coalesce non-text nodes in flush_clump_to_list()!")
            }
            (true, false) => {
                // FIXME(pcwalton): Stop cloning boxes, as above.
                debug!("TextRunScanner: pushing single non-text box in range: {}", self.clump);
                out_boxes.push(in_boxes[self.clump.begin()].clone());
            },
            (true, true)  => {
                let old_box = &in_boxes[self.clump.begin()];
                let text = match old_box.specific {
                    UnscannedTextBox(ref text_box_info) => &text_box_info.text,
                    _ => fail!("Expected an unscanned text box!"),
                };

                let font_style = old_box.font_style();
                let decoration = old_box.text_decoration();

                // TODO(#115): Use the actual CSS `white-space` property of the relevant style.
                let compression = match old_box.white_space() {
                    white_space::normal => CompressWhitespaceNewline,
                    white_space::pre => CompressNone,
                };

                let mut new_line_pos = ~[];

                let (transformed_text, whitespace) = transform_text(*text,
                                                                    compression,
                                                                    last_whitespace,
                                                                    &mut new_line_pos);

                new_whitespace = whitespace;

                if transformed_text.len() > 0 {
                    // TODO(#177): Text run creation must account for the renderability of text by
                    // font group fonts. This is probably achieved by creating the font group above
                    // and then letting `FontGroup` decide which `Font` to stick into the text run.
                    let fontgroup = ctx.font_ctx.get_resolved_font_for_style(&font_style);
                    let run = ~fontgroup.borrow().with(|fg| fg.create_textrun(transformed_text.clone(), decoration));

                    debug!("TextRunScanner: pushing single text box in range: {} ({})",
                           self.clump,
                           *text);
                    let range = Range::new(0, run.char_len());
                    let new_metrics = run.metrics_for_range(&range);
                    let new_text_box_info = ScannedTextBoxInfo::new(Arc::new(run), range);
                    let mut new_box = old_box.transform(new_metrics.bounding_box.size,
                                                    ScannedTextBox(new_text_box_info));
                    new_box.new_line_pos = new_line_pos;
                    out_boxes.push(new_box)
                }
            },
            (false, true) => {
                // TODO(#177): Text run creation must account for the renderability of text by
                // font group fonts. This is probably achieved by creating the font group above
                // and then letting `FontGroup` decide which `Font` to stick into the text run.
                let in_box = &in_boxes[self.clump.begin()];
                let font_style = in_box.font_style();
                let fontgroup = ctx.font_ctx.get_resolved_font_for_style(&font_style);
                let decoration = in_box.text_decoration();

                // TODO(#115): Use the actual CSS `white-space` property of the relevant style.
                let compression = match in_box.white_space() {
                    white_space::normal => CompressWhitespaceNewline,
                    white_space::pre => CompressNone,
                };

                struct NewLinePositions {
                    new_line_pos: ~[uint],
                }

                let mut new_line_positions: ~[NewLinePositions] = ~[];

                // First, transform/compress text of all the nodes.
                let mut last_whitespace_in_clump = new_whitespace;
                let transformed_strs: ~[~str] = vec::from_fn(self.clump.length(), |i| {
                    // TODO(#113): We should be passing the compression context between calls to
                    // `transform_text`, so that boxes starting and/or ending with whitespace can
                    // be compressed correctly with respect to the text run.
                    let idx = i + self.clump.begin();
                    let in_box = match in_boxes[idx].specific {
                        UnscannedTextBox(ref text_box_info) => &text_box_info.text,
                        _ => fail!("Expected an unscanned text box!"),
                    };

                    let mut new_line_pos = ~[];

                    let (new_str, new_whitespace) = transform_text(*in_box,
                                                                   compression,
                                                                   last_whitespace_in_clump,
                                                                   &mut new_line_pos);
                    new_line_positions.push(NewLinePositions { new_line_pos: new_line_pos });

                    last_whitespace_in_clump = new_whitespace;
                    new_str
                });
                new_whitespace = last_whitespace_in_clump;

                // Next, concatenate all of the transformed strings together, saving the new
                // character indices.
                let mut run_str: ~str = ~"";
                let mut new_ranges: ~[Range] = ~[];
                let mut char_total = 0;
                for i in range(0, transformed_strs.len()) {
                    let added_chars = transformed_strs[i].char_len();
                    new_ranges.push(Range::new(char_total, added_chars));
                    run_str.push_str(transformed_strs[i]);
                    char_total += added_chars;
                }

                // Now create the run.
                // TextRuns contain a cycle which is usually resolved by the teardown
                // sequence. If no clump takes ownership, however, it will leak.
                let clump = self.clump;
                let run = if clump.length() != 0 && run_str.len() > 0 {
                    fontgroup.borrow().with(|fg| {
                        fg.fonts[0].borrow().with_mut(|font| {
                            Some(Arc::new(~TextRun::new(font, run_str.clone(), decoration)))
                        })
                    })
                } else {
                    None
                };

                // Make new boxes with the run and adjusted text indices.
                debug!("TextRunScanner: pushing box(es) in range: {}", self.clump);
                for i in clump.eachi() {
                    let range = new_ranges[i - self.clump.begin()];
                    if range.length() == 0 {
                        debug!("Elided an `UnscannedTextbox` because it was zero-length after \
                                compression; {:s}",
                               in_boxes[i].debug_str());
                        continue
                    }

                    let new_text_box_info = ScannedTextBoxInfo::new(run.get_ref().clone(), range);
                    let new_metrics = new_text_box_info.run.get().metrics_for_range(&range);
                    let mut new_box = in_boxes[i].transform(new_metrics.bounding_box.size,
                                                        ScannedTextBox(new_text_box_info));
                    new_box.new_line_pos = new_line_positions[i].new_line_pos.clone();
                    out_boxes.push(new_box)
                }
            }
        } // End of match.

        debug!("--- In boxes: ---");
        for (i, box_) in in_boxes.iter().enumerate() {
            debug!("{:u} --> {:s}", i, box_.debug_str());
        }
        debug!("------------------");

        debug!("--- Out boxes: ---");
        for (i, box_) in out_boxes.iter().enumerate() {
            debug!("{:u} --> {:s}", i, box_.debug_str());
        }
        debug!("------------------");

        debug!("--- Elem ranges: ---");
        for (i, nr) in inline.elems.eachi() {
            debug!("{:u}: {} --> {:?}", i, nr.range, nr.node.id()); ()
        }
        debug!("--------------------");

        let end = self.clump.end(); // FIXME: borrow checker workaround
        self.clump.reset(end, 0);

        new_whitespace
    } // End of `flush_clump_to_list`.
}
<commit_msg>auto merge of #1550 : jdm/servo/logicalrange, r=metajack<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/. */

//! Text layout.

use layout::box_::{Box, ScannedTextBox, ScannedTextBoxInfo, UnscannedTextBox};
use layout::context::LayoutContext;
use layout::flow::Flow;

use extra::arc::Arc;
use gfx::text::text_run::TextRun;
use gfx::text::util::{CompressWhitespaceNewline, transform_text, CompressNone};
use servo_util::range::Range;
use std::vec;
use style::computed_values::white_space;

/// A stack-allocated object for scanning an inline flow into `TextRun`-containing `TextBox`es.
pub struct TextRunScanner {
    clump: Range,
}

impl TextRunScanner {
    pub fn new() -> TextRunScanner {
        TextRunScanner {
            clump: Range::empty(),
        }
    }

    pub fn scan_for_runs(&mut self, ctx: &mut LayoutContext, flow: &mut Flow) {
        {
            let inline = flow.as_immutable_inline();
            debug!("TextRunScanner: scanning {:u} boxes for text runs...", inline.boxes.len());
        }

        let mut last_whitespace = true;
        let mut out_boxes = ~[];
        for box_i in range(0, flow.as_immutable_inline().boxes.len()) {
            debug!("TextRunScanner: considering box: {:u}", box_i);
            if box_i > 0 && !can_coalesce_text_nodes(flow.as_immutable_inline().boxes,
                                                     box_i - 1,
                                                     box_i) {
                last_whitespace = self.flush_clump_to_list(ctx, flow, last_whitespace, &mut out_boxes);
            }
            self.clump.extend_by(1);
        }
        // handle remaining clumps
        if self.clump.length() > 0 {
            self.flush_clump_to_list(ctx, flow, last_whitespace, &mut out_boxes);
        }

        debug!("TextRunScanner: swapping out boxes.");

        // Swap out the old and new box list of the flow.
        flow.as_inline().boxes = out_boxes;

        // A helper function.
        fn can_coalesce_text_nodes(boxes: &[Box], left_i: uint, right_i: uint) -> bool {
            assert!(left_i < boxes.len());
            assert!(right_i > 0 && right_i < boxes.len());
            assert!(left_i != right_i);
            boxes[left_i].can_merge_with_box(&boxes[right_i])
        }
    }

    /// A "clump" is a range of inline flow leaves that can be merged together into a single box.
    /// Adjacent text with the same style can be merged, and nothing else can.
    ///
    /// The flow keeps track of the boxes contained by all non-leaf DOM nodes. This is necessary
    /// for correct painting order. Since we compress several leaf boxes here, the mapping must be
    /// adjusted.
    ///
    /// FIXME(pcwalton): Stop cloning boxes. Instead we will need to consume the `in_box`es as we
    /// iterate over them.
    pub fn flush_clump_to_list(&mut self,
                               ctx: &mut LayoutContext,
                               flow: &mut Flow,
                               last_whitespace: bool,
                               out_boxes: &mut ~[Box])
                               -> bool {
        let inline = flow.as_inline();
        let in_boxes = &mut inline.boxes;

        assert!(self.clump.length() > 0);

        debug!("TextRunScanner: flushing boxes in range={}", self.clump);
        let is_singleton = self.clump.length() == 1;

        let is_text_clump = match in_boxes[self.clump.begin()].specific {
            UnscannedTextBox(_) => true,
            _ => false,
        };

        let mut new_whitespace = last_whitespace;
        match (is_singleton, is_text_clump) {
            (false, false) => {
                fail!(~"WAT: can't coalesce non-text nodes in flush_clump_to_list()!")
            }
            (true, false) => {
                // FIXME(pcwalton): Stop cloning boxes, as above.
                debug!("TextRunScanner: pushing single non-text box in range: {}", self.clump);
                out_boxes.push(in_boxes[self.clump.begin()].clone());
            },
            (true, true)  => {
                let old_box = &in_boxes[self.clump.begin()];
                let text = match old_box.specific {
                    UnscannedTextBox(ref text_box_info) => &text_box_info.text,
                    _ => fail!("Expected an unscanned text box!"),
                };

                let font_style = old_box.font_style();
                let decoration = old_box.text_decoration();

                // TODO(#115): Use the actual CSS `white-space` property of the relevant style.
                let compression = match old_box.white_space() {
                    white_space::normal => CompressWhitespaceNewline,
                    white_space::pre => CompressNone,
                };

                let mut new_line_pos = ~[];

                let (transformed_text, whitespace) = transform_text(*text,
                                                                    compression,
                                                                    last_whitespace,
                                                                    &mut new_line_pos);

                new_whitespace = whitespace;

                if transformed_text.len() > 0 {
                    // TODO(#177): Text run creation must account for the renderability of text by
                    // font group fonts. This is probably achieved by creating the font group above
                    // and then letting `FontGroup` decide which `Font` to stick into the text run.
                    let fontgroup = ctx.font_ctx.get_resolved_font_for_style(&font_style);
                    let run = ~fontgroup.borrow().with(|fg| fg.create_textrun(transformed_text.clone(), decoration));

                    debug!("TextRunScanner: pushing single text box in range: {} ({})",
                           self.clump,
                           *text);
                    let range = Range::new(0, run.char_len());
                    let new_metrics = run.metrics_for_range(&range);
                    let new_text_box_info = ScannedTextBoxInfo::new(Arc::new(run), range);
                    let mut new_box = old_box.transform(new_metrics.bounding_box.size,
                                                    ScannedTextBox(new_text_box_info));
                    new_box.new_line_pos = new_line_pos;
                    out_boxes.push(new_box)
                }
            },
            (false, true) => {
                // TODO(#177): Text run creation must account for the renderability of text by
                // font group fonts. This is probably achieved by creating the font group above
                // and then letting `FontGroup` decide which `Font` to stick into the text run.
                let in_box = &in_boxes[self.clump.begin()];
                let font_style = in_box.font_style();
                let fontgroup = ctx.font_ctx.get_resolved_font_for_style(&font_style);
                let decoration = in_box.text_decoration();

                // TODO(#115): Use the actual CSS `white-space` property of the relevant style.
                let compression = match in_box.white_space() {
                    white_space::normal => CompressWhitespaceNewline,
                    white_space::pre => CompressNone,
                };

                struct NewLinePositions {
                    new_line_pos: ~[uint],
                }

                let mut new_line_positions: ~[NewLinePositions] = ~[];

                // First, transform/compress text of all the nodes.
                let mut last_whitespace_in_clump = new_whitespace;
                let transformed_strs: ~[~str] = vec::from_fn(self.clump.length(), |i| {
                    // TODO(#113): We should be passing the compression context between calls to
                    // `transform_text`, so that boxes starting and/or ending with whitespace can
                    // be compressed correctly with respect to the text run.
                    let idx = i + self.clump.begin();
                    let in_box = match in_boxes[idx].specific {
                        UnscannedTextBox(ref text_box_info) => &text_box_info.text,
                        _ => fail!("Expected an unscanned text box!"),
                    };

                    let mut new_line_pos = ~[];

                    let (new_str, new_whitespace) = transform_text(*in_box,
                                                                   compression,
                                                                   last_whitespace_in_clump,
                                                                   &mut new_line_pos);
                    new_line_positions.push(NewLinePositions { new_line_pos: new_line_pos });

                    last_whitespace_in_clump = new_whitespace;
                    new_str
                });
                new_whitespace = last_whitespace_in_clump;

                // Next, concatenate all of the transformed strings together, saving the new
                // character indices.
                let mut run_str: ~str = ~"";
                let mut new_ranges: ~[Range] = ~[];
                let mut char_total = 0;
                for i in range(0, transformed_strs.len()) {
                    let added_chars = transformed_strs[i].char_len();
                    new_ranges.push(Range::new(char_total, added_chars));
                    run_str.push_str(transformed_strs[i]);
                    char_total += added_chars;
                }

                // Now create the run.
                // TextRuns contain a cycle which is usually resolved by the teardown
                // sequence. If no clump takes ownership, however, it will leak.
                let clump = self.clump;
                let run = if clump.length() != 0 && run_str.len() > 0 {
                    fontgroup.borrow().with(|fg| {
                        fg.fonts[0].borrow().with_mut(|font| {
                            Some(Arc::new(~TextRun::new(font, run_str.clone(), decoration)))
                        })
                    })
                } else {
                    None
                };

                // Make new boxes with the run and adjusted text indices.
                debug!("TextRunScanner: pushing box(es) in range: {}", self.clump);
                for i in clump.eachi() {
                    let logical_offset = i - self.clump.begin();
                    let range = new_ranges[logical_offset];
                    if range.length() == 0 {
                        debug!("Elided an `UnscannedTextbox` because it was zero-length after \
                                compression; {:s}",
                               in_boxes[i].debug_str());
                        continue
                    }

                    let new_text_box_info = ScannedTextBoxInfo::new(run.get_ref().clone(), range);
                    let new_metrics = new_text_box_info.run.get().metrics_for_range(&range);
                    let mut new_box = in_boxes[i].transform(new_metrics.bounding_box.size,
                                                        ScannedTextBox(new_text_box_info));
                    new_box.new_line_pos = new_line_positions[logical_offset].new_line_pos.clone();
                    out_boxes.push(new_box)
                }
            }
        } // End of match.

        debug!("--- In boxes: ---");
        for (i, box_) in in_boxes.iter().enumerate() {
            debug!("{:u} --> {:s}", i, box_.debug_str());
        }
        debug!("------------------");

        debug!("--- Out boxes: ---");
        for (i, box_) in out_boxes.iter().enumerate() {
            debug!("{:u} --> {:s}", i, box_.debug_str());
        }
        debug!("------------------");

        debug!("--- Elem ranges: ---");
        for (i, nr) in inline.elems.eachi() {
            debug!("{:u}: {} --> {:?}", i, nr.range, nr.node.id()); ()
        }
        debug!("--------------------");

        let end = self.clump.end(); // FIXME: borrow checker workaround
        self.clump.reset(end, 0);

        new_whitespace
    } // End of `flush_clump_to_list`.
}
<|endoftext|>


<commit_before><commit_msg>Comment out the vanity failure.<commit_after><|endoftext|>


<commit_before><commit_msg>Implements `*.x` matches `x` for subdomains and full-star match<commit_after><|endoftext|>


<commit_before>use {Parcel, Error, Settings};
use std::io::prelude::*;

use uuid::Uuid;

impl Parcel for Uuid
{
    fn read(read: &mut Read,
            settings: &Settings,
            _: &mut hint::Hints)
        -> Result<Self, Error> {
        let bytes: Result<Vec<u8>, _> = read.bytes().take(16).collect();
        let bytes = bytes?;

        Ok(Uuid::from_bytes(&bytes)?)
    }

    fn write(&self, write: &mut Write) -> Result<(), Error> {
        write.write(self.as_bytes())?;
        Ok(())
    }
}

<commit_msg>Fix the UUID Parcel implementation<commit_after>use {Parcel, Error, Settings};
use hint;
use std::io::prelude::*;

use uuid::Uuid;

impl Parcel for Uuid
{
    const TYPE_NAME: &'static str = "Uuid";

    fn read(read: &mut Read,
            _: &Settings,
            _: &mut hint::Hints)
        -> Result<Self, Error> {
        let bytes: Result<Vec<u8>, _> = read.bytes().take(16).collect();
        let bytes = bytes?;

        Ok(Uuid::from_bytes(&bytes)?)
    }

    fn write(&self, write: &mut Write,
             _: &Settings) -> Result<(), Error> {
        write.write(self.as_bytes())?;
        Ok(())
    }
}

<|endoftext|>


<commit_before><commit_msg>Try to recover some of the coverage lost (due to reducing the number of lines overall by reducing the set of covered lines).<commit_after><|endoftext|>


<commit_before>#[macro_export]
macro_rules! define_proc_macros {
    (
        $(
            $( #[$attr:meta] )*
            pub fn $func:ident($input:ident: &str) -> String
            $body:block
        )+
    ) => {
        $(
            $( #[$attr] )*
            #[proc_macro_derive($func)]
            pub fn $func(input: ::proc_macro::TokenStream) -> ::proc_macro::TokenStream {
                // Use another function to hide this one’s local variables from $block
                #[inline]
                fn implementation($input: &str) -> String $body

                let input = input.to_string();
                let mut normalized = String::with_capacity(input.len());
                for piece in input.split_whitespace() {
                    normalized.push_str(piece);
                    normalized.push(' ');
                }

                let prefix = "#[allow(unused)] enum Dummy { Input = (0, stringify!(";
                let suffix = ")).0, } ";
                assert!(normalized.starts_with(prefix), "Unexpected proc_macro_derive input {:?}", input);
                assert!(normalized.ends_with(suffix), "Unexpected proc_macro_derive input {:?}", input);

                let start = prefix.len();
                let end = normalized.len() - suffix.len();
                let output = implementation(&normalized[start..end]);
                output.parse().unwrap()
            }
        )+
    }
}

#[macro_export]
macro_rules! define_invoke_proc_macro {
    ($macro_name: ident) => {
        #[macro_export]
        macro_rules! $macro_name {
            ($proc_macro_name: ident ! $paren: tt) => {
                #[derive($proc_macro_name)]
                #[allow(unused)]
                enum Dummy {
                    Input = (0, stringify! $paren ).0
                }
            }
        }
    }
}
<commit_msg>Document procedural-masquarade<commit_after>//! # Custom `derive` pretending to be functional procedural macros on Rust 1.15
//!
//! This crate enables creating function-like macros (invoked as `foo!(...)`)
//! with a procedural component,
//! based on both custom `derive` (a.k.a. *Macros 1.1*) and `macro_rules!`.
//!
//! This convoluted mechanism enables such macros to run on stable Rust 1.15,
//! even though functional procedural macros (a.k.a. *Macros 2.0*) are not available yet.
//!
//! A library defining such a macro needs two crates: a “normal” one, and a `proc-macro` one.
//! In the example below we’ll call them `libfoo` and `libfoo-macros`, respectively.
//!
//! # Credits
//!
//! The trick that makes this crate work
//! is based on an idea from [David Tolnay](https://github.com/dtolnay).
//! Many thanks!
//!
//! # Example
//!
//! As a simple example, we’re going to re-implement the `stringify!` macro.
//! This is useless since `stringify!` already exists in the standard library,
//! and a bit absurd since this crate uses `stringify!` internally.
//!
//! Nevertheless, it serves as a simple example to demonstrate the use of this crate.
//!
//! ## The `proc-macro` crate
//!
//! The minimal `Cargo.toml` file is typical for Macros 1.1:
//!
//! ```toml
//! [package]
//! name = "libfoo-macros"
//! version = "1.0.0"
//!
//! [lib]
//! proc-macro = true
//! ```
//!
//! In the code, we define the procedural part of our macro in a function.
//! This function will not be used directly by end users,
//! but it still needs to be re-exported to them
//! (because of limitations in `macro_rules!`).
//!
//! To avoid name collisions, we and a long and explicit prefix in the function’s name.
//!
//! The function takes a string containing arbitrary Rust tokens,
//! and returns a string that is parsed as *items*.
//! The returned string can contain constants, statics, functions, `impl`s, etc.,
//! but not expressions directly.
//!
//! ```rust
//! #[macro_use] extern crate procedural_masquarade;
//! extern crate proc_macro;
//!
//! define_proc_macros! {
//!     #[allow(non_snake_case)]
//!     pub fn foo_internal__stringify_const(input: &str) -> String {
//!         format!("const STRINGIFIED: &'static str = {:?};", input)
//!     }
//! }
//! ```
//!
//! A less trivial macro would probably use
//! the [`syn`](https://github.com/dtolnay/syn/) crate to parse its input
//! and the [`quote`](https://github.com/dtolnay/quote) crate to generate its output.
//!
//! ## The library crate
//!
//! ```toml
//! [package]
//! name = "libfoo"
//! version = "1.0.0"
//!
//! [dependencies]
//! cssparser-macros = {path = "./macros", version = "1.0"}
//! ```
//!
//! ```rust
//! #[macro_use] extern crate libfoo_macros;  // (1)
//!
//! pub use libfoo_macros::*;  // (2)
//!
//! define_invoke_proc_macro!(libfoo__invoke_proc_macro);  // (3)
//!
//! #[macro_export]
//! macro_rules! foo_stringify {  // (4)
//!     ( $( $tts: tt ) ) => {
//!         {  // (5)
//!             libfoo__invoke_proc_macro! {  // (6)
//!                 foo_internal__stringify_const!( $( $tts ) )  // (7)
//!             }
//!             STRINGIFIED  // (8)
//!         }
//!     }
//! }
//! ```
//!
//! Let’s go trough the numbered lines one by one:
//!
//! 1. `libfoo` depends on the other `libfoo-macros`, and imports its macros.
//! 2. Everything exported by `libfoo-macros` (which is one custom `derive`)
//!    is re-exported to users of `libfoo`.
//!    They’re not expected to use it directly,
//!    but expansion of the `foo_stringify` macro needs it.
//! 3. This macro invocation defines yet another macro, called `libfoo__invoke_proc_macro`,
//!    which is also exported.
//!    This indirection is necessary
//!    because re-exporting `macro_rules!` macros doesn’t work currently,
//!    and once again it is used by the expansion of `foo_stringify`.
//!    Again, we use a long prefix to avoid name collisions.
//! 4. Finally, we define the macro that we really want.
//!    This one has a name that users will use.
//! 5. The expansion of this macro will define some items,
//!    whose names are not hygienic in `macro_rules`.
//!    So we wrap everything in an extra `{…}` block to prevent these names for leaking.
//! 6. Here we use the macro defined in (3),
//!    which allows us to write something that look like invoking a functional procedural macro,
//!    but really uses a custom `derive`.
//!    This will define a type called `ProceduralMasquaradeDummyType`,
//!    as a placeholder to use `derive`.
//!    If `libfoo__invoke_proc_macro!` is to be used more than once,
//!    each use needs to be nested in another block
//!    so that the names of multiple dummy types don’t collide.
//! 7. In addition to the dummy type,
//!    the items returned by our procedural component are inserted here.
//!    (In this case the `STRINGIFIED` constant.)
//! 8. Finally, we write the expression that we want the macro to evaluate to.
//!    This expression can use parts of `foo_stringify`’s input,
//!    it can contain control-flow statements like `return` or `continue`,
//!    and of course refer to procedurally-defined items.
//!
//! This macro can be used in an expression context.
//! It expands to a block-expression that contains some items (as an implementation detail)
//! and ends with another expression.
//!
//! ## For users
//!
//! Users of `libfoo` don’t need to worry about any of these implementation details.
//! They can use the `foo_stringify` macro as if it were a simle `macro_rules` macro:
//!
//! ```rust
//! #[macro_use] extern crate libfoo;
//!
//! fn main() {
//!     do_something(foo_stringify!(1 + 2));
//! }
//!
//! fn do_something(_: &str) { /* ... */ }
//! ```
//!
//! # More
//!
//! To see a more complex example, look at
//! [`cssparser`’s `src/macros.rs](https://github.com/servo/rust-cssparser/blob/master/src/macros.rs)
//! and
//! [`cssparser-macros`’s `macros/lib.rs](https://github.com/servo/rust-cssparser/blob/master/macros/lib.rs).

/////
///// This indirection is necessary because re-exporting `macro_rules!` macros doesn’t work.
///// Without it, when a library `libfoo` defines a macro whose expansion uses directly
///// a macro

/// This macro wraps `&str -> String` functions
/// in custom `derive` implementations with `#[proc_macro_derive]`.
///
/// See crate documentation for details.
#[macro_export]
macro_rules! define_proc_macros {
    (
        $(
            $( #[$attr:meta] )*
            pub fn $proc_macro_name: ident ($input: ident : &str) -> String
            $body: block
        )+
    ) => {
        $(
            $( #[$attr] )*
            #[proc_macro_derive($proc_macro_name)]
            pub fn $proc_macro_name(input: ::proc_macro::TokenStream) -> ::proc_macro::TokenStream {
                // Use another function to hide this one’s local variables from $block
                #[inline]
                fn implementation($input: &str) -> String $body

                let input = input.to_string();
                let mut normalized = String::with_capacity(input.len());
                for piece in input.split_whitespace() {
                    normalized.push_str(piece);
                    normalized.push(' ');
                }

                let prefix = "#[allow(unused)] enum ProceduralMasqueradeDummyType { Input = (0, stringify!(";
                let suffix = ")).0, } ";
                assert!(normalized.starts_with(prefix), "expected prefix not found in {:?}", input);
                assert!(normalized.ends_with(suffix), "expected suffix not found in {:?}", input);

                let start = prefix.len();
                let end = normalized.len() - suffix.len();
                let output = implementation(&normalized[start..end]);
                output.parse().unwrap()
            }
        )+
    }
}

/// This macro expands to the definition of another macro (whose name is given as a parameter).
///
/// See crate documentation for details.
#[macro_export]
macro_rules! define_invoke_proc_macro {
    ($macro_name: ident) => {
        #[macro_export]
        macro_rules! $macro_name {
            ($proc_macro_name: ident ! $paren: tt) => {
                #[derive($proc_macro_name)]
                #[allow(unused)]
                enum ProceduralMasquaradeDummyType {
                    // The magic happens here.
                    //
                    // We use an `enum` with an explicit discriminant
                    // because that is the only case where a type definition
                    // can contain a (const) expression.
                    //
                    // `(0, "foo").0` evalutes to 0, with the `"foo"` part ignored.
                    //
                    // By the time the `#[proc_macro_derive]` function
                    // implementing `#[derive($proc_macro_name)]` is called,
                    // `$paren` has already been replaced with the input of this inner macro,
                    // but `stringify!` has not been expanded yet.
                    //
                    // This how arbitrary tokens can be inserted
                    // in the input to the `#[proc_macro_derive]` function.
                    //
                    // Later, `stringify!(...)` is expanded into a string literal
                    // which is then ignored.
                    // Using `stringify!` enables passing arbitrary tokens
                    // rather than only what can be parsed as a const expression.
                    Input = (0, stringify! $paren ).0
                }
            }
        }
    }
}
<|endoftext|>


<commit_before>use rustc_errors::Applicability;
use rustc_hir::def::{CtorKind, CtorOf, DefKind, Res};
use rustc_hir::{Body, Expr, ExprKind, LangItem, MatchSource, QPath};
use rustc_lint::{LateContext, LateLintPass, LintContext};
use rustc_middle::ty::DefIdTree;
use rustc_semver::RustcVersion;
use rustc_session::{declare_tool_lint, impl_lint_pass};
use rustc_span::sym;

use crate::utils;
use if_chain::if_chain;

declare_clippy_lint! {
    /// **What it does:**
    /// Suggests alternatives for useless applications of `?` in terminating expressions
    ///
    /// **Why is this bad?** There's no reason to use ? to short-circuit when execution of the body will end there anyway.
    ///
    /// **Known problems:** None.
    ///
    /// **Example:**
    ///
    /// ```rust
    /// struct TO {
    ///     magic: Option<usize>,
    /// }
    ///
    /// fn f(to: TO) -> Option<usize> {
    ///     Some(to.magic?)
    /// }
    ///
    /// struct TR {
    ///     magic: Result<usize, bool>,
    /// }
    ///
    /// fn g(tr: Result<TR, bool>) -> Result<usize, bool> {
    ///     tr.and_then(|t| Ok(t.magic?))
    /// }
    ///
    /// ```
    /// Use instead:
    /// ```rust
    /// struct TO {
    ///     magic: Option<usize>,
    /// }
    ///
    /// fn f(to: TO) -> Option<usize> {
    ///    to.magic
    /// }
    ///
    /// struct TR {
    ///     magic: Result<usize, bool>,
    /// }
    ///
    /// fn g(tr: Result<TR, bool>) -> Result<usize, bool> {
    ///     tr.and_then(|t| t.magic)
    /// }
    /// ```
    pub NEEDLESS_QUESTION_MARK,
    complexity,
    "Suggest value.inner_option instead of Some(value.inner_option?). The same goes for Result<T, E>."
}

const NEEDLESS_QUESTION_MARK_RESULT_MSRV: RustcVersion = RustcVersion::new(1, 13, 0);
const NEEDLESS_QUESTION_MARK_OPTION_MSRV: RustcVersion = RustcVersion::new(1, 22, 0);

pub struct NeedlessQuestionMark {
    msrv: Option<RustcVersion>,
}

impl NeedlessQuestionMark {
    #[must_use]
    pub fn new(msrv: Option<RustcVersion>) -> Self {
        Self { msrv }
    }
}

impl_lint_pass!(NeedlessQuestionMark => [NEEDLESS_QUESTION_MARK]);

#[derive(Debug)]
enum SomeOkCall<'a> {
    SomeCall(&'a Expr<'a>, &'a Expr<'a>),
    OkCall(&'a Expr<'a>, &'a Expr<'a>),
}

impl LateLintPass<'_> for NeedlessQuestionMark {
    /*
     * The question mark operator is compatible with both Result<T, E> and Option<T>,
     * from Rust 1.13 and 1.22 respectively.
     */

    /*
     * What do we match:
     * Expressions that look like this:
     * Some(option?), Ok(result?)
     *
     * Where do we match:
     *      Last expression of a body
     *      Return statement
     *      A body's value (single line closure)
     *
     * What do we not match:
     *      Implicit calls to `from(..)` on the error value
     */

    fn check_expr(&mut self, cx: &LateContext<'_>, expr: &'_ Expr<'_>) {
        let e = match &expr.kind {
            ExprKind::Ret(Some(e)) => e,
            _ => return,
        };

        if let Some(ok_some_call) = is_some_or_ok_call(self, cx, e) {
            emit_lint(cx, &ok_some_call);
        }
    }

    fn check_body(&mut self, cx: &LateContext<'_>, body: &'_ Body<'_>) {
        // Function / Closure block
        let expr_opt = if let ExprKind::Block(block, _) = &body.value.kind {
            block.expr
        } else {
            // Single line closure
            Some(&body.value)
        };

        if_chain! {
            if let Some(expr) = expr_opt;
            if let Some(ok_some_call) = is_some_or_ok_call(self, cx, expr);
            then {
                emit_lint(cx, &ok_some_call);
            }
        };
    }

    extract_msrv_attr!(LateContext);
}

fn emit_lint(cx: &LateContext<'_>, expr: &SomeOkCall<'_>) {
    let (entire_expr, inner_expr) = match expr {
        SomeOkCall::OkCall(outer, inner) | SomeOkCall::SomeCall(outer, inner) => (outer, inner),
    };

    utils::span_lint_and_sugg(
        cx,
        NEEDLESS_QUESTION_MARK,
        entire_expr.span,
        "Question mark operator is useless here",
        "try",
        format!("{}", utils::snippet(cx, inner_expr.span, r#""...""#)),
        Applicability::MachineApplicable,
    );
}

fn is_some_or_ok_call<'a>(
    nqml: &NeedlessQuestionMark,
    cx: &'a LateContext<'_>,
    expr: &'a Expr<'_>,
) -> Option<SomeOkCall<'a>> {
    if_chain! {
        // Check outer expression matches CALL_IDENT(ARGUMENT) format
        if let ExprKind::Call(path, args) = &expr.kind;
        if let ExprKind::Path(QPath::Resolved(None, path)) = &path.kind;
        if is_some_ctor(cx, path.res) || is_ok_ctor(cx, path.res);

        // Extract inner expression from ARGUMENT
        if let ExprKind::Match(inner_expr_with_q, _, MatchSource::TryDesugar) = &args[0].kind;
        if let ExprKind::Call(called, args) = &inner_expr_with_q.kind;
        if args.len() == 1;

        if let ExprKind::Path(QPath::LangItem(LangItem::TryIntoResult, _)) = &called.kind;
        then {
            // Extract inner expr type from match argument generated by
            // question mark operator
            let inner_expr = &args[0];

            let inner_ty = cx.typeck_results().expr_ty(inner_expr);
            let outer_ty = cx.typeck_results().expr_ty(expr);

            // Check if outer and inner type are Option
            let outer_is_some = utils::is_type_diagnostic_item(cx, outer_ty, sym::option_type);
            let inner_is_some = utils::is_type_diagnostic_item(cx, inner_ty, sym::option_type);

            // Check for Option MSRV
            let meets_option_msrv = utils::meets_msrv(nqml.msrv.as_ref(), &NEEDLESS_QUESTION_MARK_OPTION_MSRV);
            if outer_is_some && inner_is_some && meets_option_msrv {
                return Some(SomeOkCall::SomeCall(expr, inner_expr));
            }

            // Check if outer and inner type are Result
            let outer_is_result = utils::is_type_diagnostic_item(cx, outer_ty, sym::result_type);
            let inner_is_result = utils::is_type_diagnostic_item(cx, inner_ty, sym::result_type);

            // Additional check: if the error type of the Result can be converted
            // via the From trait, then don't match
            let does_not_call_from = !has_implicit_error_from(cx, expr, inner_expr);

            // Must meet Result MSRV
            let meets_result_msrv = utils::meets_msrv(nqml.msrv.as_ref(), &NEEDLESS_QUESTION_MARK_RESULT_MSRV);
            if outer_is_result && inner_is_result && does_not_call_from && meets_result_msrv {
                return Some(SomeOkCall::OkCall(expr, inner_expr));
            }
        }
    }

    None
}

fn has_implicit_error_from(cx: &LateContext<'_>, entire_expr: &Expr<'_>, inner_result_expr: &Expr<'_>) -> bool {
    return cx.typeck_results().expr_ty(entire_expr) != cx.typeck_results().expr_ty(inner_result_expr);
}

fn is_ok_ctor(cx: &LateContext<'_>, res: Res) -> bool {
    if let Some(ok_id) = cx.tcx.lang_items().result_ok_variant() {
        if let Res::Def(DefKind::Ctor(CtorOf::Variant, CtorKind::Fn), id) = res {
            if let Some(variant_id) = cx.tcx.parent(id) {
                return variant_id == ok_id;
            }
        }
    }
    false
}

fn is_some_ctor(cx: &LateContext<'_>, res: Res) -> bool {
    if let Some(some_id) = cx.tcx.lang_items().option_some_variant() {
        if let Res::Def(DefKind::Ctor(CtorOf::Variant, CtorKind::Fn), id) = res {
            if let Some(variant_id) = cx.tcx.parent(id) {
                return variant_id == some_id;
            }
        }
    }
    false
}
<commit_msg>Small fixes of doc in `needless_question_mark`<commit_after>use rustc_errors::Applicability;
use rustc_hir::def::{CtorKind, CtorOf, DefKind, Res};
use rustc_hir::{Body, Expr, ExprKind, LangItem, MatchSource, QPath};
use rustc_lint::{LateContext, LateLintPass, LintContext};
use rustc_middle::ty::DefIdTree;
use rustc_semver::RustcVersion;
use rustc_session::{declare_tool_lint, impl_lint_pass};
use rustc_span::sym;

use crate::utils;
use if_chain::if_chain;

declare_clippy_lint! {
    /// **What it does:**
    /// Suggests alternatives for useless applications of `?` in terminating expressions
    ///
    /// **Why is this bad?** There's no reason to use `?` to short-circuit when execution of the body will end there anyway.
    ///
    /// **Known problems:** None.
    ///
    /// **Example:**
    ///
    /// ```rust
    /// struct TO {
    ///     magic: Option<usize>,
    /// }
    ///
    /// fn f(to: TO) -> Option<usize> {
    ///     Some(to.magic?)
    /// }
    ///
    /// struct TR {
    ///     magic: Result<usize, bool>,
    /// }
    ///
    /// fn g(tr: Result<TR, bool>) -> Result<usize, bool> {
    ///     tr.and_then(|t| Ok(t.magic?))
    /// }
    ///
    /// ```
    /// Use instead:
    /// ```rust
    /// struct TO {
    ///     magic: Option<usize>,
    /// }
    ///
    /// fn f(to: TO) -> Option<usize> {
    ///    to.magic
    /// }
    ///
    /// struct TR {
    ///     magic: Result<usize, bool>,
    /// }
    ///
    /// fn g(tr: Result<TR, bool>) -> Result<usize, bool> {
    ///     tr.and_then(|t| t.magic)
    /// }
    /// ```
    pub NEEDLESS_QUESTION_MARK,
    complexity,
    "Suggest `value.inner_option` instead of `Some(value.inner_option?)`. The same goes for `Result<T, E>`."
}

const NEEDLESS_QUESTION_MARK_RESULT_MSRV: RustcVersion = RustcVersion::new(1, 13, 0);
const NEEDLESS_QUESTION_MARK_OPTION_MSRV: RustcVersion = RustcVersion::new(1, 22, 0);

pub struct NeedlessQuestionMark {
    msrv: Option<RustcVersion>,
}

impl NeedlessQuestionMark {
    #[must_use]
    pub fn new(msrv: Option<RustcVersion>) -> Self {
        Self { msrv }
    }
}

impl_lint_pass!(NeedlessQuestionMark => [NEEDLESS_QUESTION_MARK]);

#[derive(Debug)]
enum SomeOkCall<'a> {
    SomeCall(&'a Expr<'a>, &'a Expr<'a>),
    OkCall(&'a Expr<'a>, &'a Expr<'a>),
}

impl LateLintPass<'_> for NeedlessQuestionMark {
    /*
     * The question mark operator is compatible with both Result<T, E> and Option<T>,
     * from Rust 1.13 and 1.22 respectively.
     */

    /*
     * What do we match:
     * Expressions that look like this:
     * Some(option?), Ok(result?)
     *
     * Where do we match:
     *      Last expression of a body
     *      Return statement
     *      A body's value (single line closure)
     *
     * What do we not match:
     *      Implicit calls to `from(..)` on the error value
     */

    fn check_expr(&mut self, cx: &LateContext<'_>, expr: &'_ Expr<'_>) {
        let e = match &expr.kind {
            ExprKind::Ret(Some(e)) => e,
            _ => return,
        };

        if let Some(ok_some_call) = is_some_or_ok_call(self, cx, e) {
            emit_lint(cx, &ok_some_call);
        }
    }

    fn check_body(&mut self, cx: &LateContext<'_>, body: &'_ Body<'_>) {
        // Function / Closure block
        let expr_opt = if let ExprKind::Block(block, _) = &body.value.kind {
            block.expr
        } else {
            // Single line closure
            Some(&body.value)
        };

        if_chain! {
            if let Some(expr) = expr_opt;
            if let Some(ok_some_call) = is_some_or_ok_call(self, cx, expr);
            then {
                emit_lint(cx, &ok_some_call);
            }
        };
    }

    extract_msrv_attr!(LateContext);
}

fn emit_lint(cx: &LateContext<'_>, expr: &SomeOkCall<'_>) {
    let (entire_expr, inner_expr) = match expr {
        SomeOkCall::OkCall(outer, inner) | SomeOkCall::SomeCall(outer, inner) => (outer, inner),
    };

    utils::span_lint_and_sugg(
        cx,
        NEEDLESS_QUESTION_MARK,
        entire_expr.span,
        "Question mark operator is useless here",
        "try",
        format!("{}", utils::snippet(cx, inner_expr.span, r#""...""#)),
        Applicability::MachineApplicable,
    );
}

fn is_some_or_ok_call<'a>(
    nqml: &NeedlessQuestionMark,
    cx: &'a LateContext<'_>,
    expr: &'a Expr<'_>,
) -> Option<SomeOkCall<'a>> {
    if_chain! {
        // Check outer expression matches CALL_IDENT(ARGUMENT) format
        if let ExprKind::Call(path, args) = &expr.kind;
        if let ExprKind::Path(QPath::Resolved(None, path)) = &path.kind;
        if is_some_ctor(cx, path.res) || is_ok_ctor(cx, path.res);

        // Extract inner expression from ARGUMENT
        if let ExprKind::Match(inner_expr_with_q, _, MatchSource::TryDesugar) = &args[0].kind;
        if let ExprKind::Call(called, args) = &inner_expr_with_q.kind;
        if args.len() == 1;

        if let ExprKind::Path(QPath::LangItem(LangItem::TryIntoResult, _)) = &called.kind;
        then {
            // Extract inner expr type from match argument generated by
            // question mark operator
            let inner_expr = &args[0];

            let inner_ty = cx.typeck_results().expr_ty(inner_expr);
            let outer_ty = cx.typeck_results().expr_ty(expr);

            // Check if outer and inner type are Option
            let outer_is_some = utils::is_type_diagnostic_item(cx, outer_ty, sym::option_type);
            let inner_is_some = utils::is_type_diagnostic_item(cx, inner_ty, sym::option_type);

            // Check for Option MSRV
            let meets_option_msrv = utils::meets_msrv(nqml.msrv.as_ref(), &NEEDLESS_QUESTION_MARK_OPTION_MSRV);
            if outer_is_some && inner_is_some && meets_option_msrv {
                return Some(SomeOkCall::SomeCall(expr, inner_expr));
            }

            // Check if outer and inner type are Result
            let outer_is_result = utils::is_type_diagnostic_item(cx, outer_ty, sym::result_type);
            let inner_is_result = utils::is_type_diagnostic_item(cx, inner_ty, sym::result_type);

            // Additional check: if the error type of the Result can be converted
            // via the From trait, then don't match
            let does_not_call_from = !has_implicit_error_from(cx, expr, inner_expr);

            // Must meet Result MSRV
            let meets_result_msrv = utils::meets_msrv(nqml.msrv.as_ref(), &NEEDLESS_QUESTION_MARK_RESULT_MSRV);
            if outer_is_result && inner_is_result && does_not_call_from && meets_result_msrv {
                return Some(SomeOkCall::OkCall(expr, inner_expr));
            }
        }
    }

    None
}

fn has_implicit_error_from(cx: &LateContext<'_>, entire_expr: &Expr<'_>, inner_result_expr: &Expr<'_>) -> bool {
    return cx.typeck_results().expr_ty(entire_expr) != cx.typeck_results().expr_ty(inner_result_expr);
}

fn is_ok_ctor(cx: &LateContext<'_>, res: Res) -> bool {
    if let Some(ok_id) = cx.tcx.lang_items().result_ok_variant() {
        if let Res::Def(DefKind::Ctor(CtorOf::Variant, CtorKind::Fn), id) = res {
            if let Some(variant_id) = cx.tcx.parent(id) {
                return variant_id == ok_id;
            }
        }
    }
    false
}

fn is_some_ctor(cx: &LateContext<'_>, res: Res) -> bool {
    if let Some(some_id) = cx.tcx.lang_items().option_some_variant() {
        if let Res::Def(DefKind::Ctor(CtorOf::Variant, CtorKind::Fn), id) = res {
            if let Some(variant_id) = cx.tcx.parent(id) {
                return variant_id == some_id;
            }
        }
    }
    false
}
<|endoftext|>


<commit_before>//! Simple script to verify the coding style of this library
//!
//! ## How to run
//!
//! The first argument to this script is the directory to run on, so running
//! this script should be as simple as:
//!
//! ```notrust
//! rustc ci/style.rs
//! ./style src
//! ```
//!
//! ## Guidelines
//!
//! The current style is:
//!
//! * No trailing whitespace
//! * No tabs
//! * 80-character lines
//! * Specific module layout:
//!     1. use directives
//!     2. typedefs
//!     3. structs
//!     4. constants
//!     5. f! { ... } functions
//!     6. extern functions
//!     7. modules + pub use
//!
//! Things not verified:
//!
//! * alignment
//! * 4-space tabs
//! * leading colons on paths

use std::env;
use std::fs;
use std::io::prelude::*;
use std::path::Path;

macro_rules! t {
    ($e:expr) => (match $e {
        Ok(e) => e,
        Err(e) => panic!("{} failed with {}", stringify!($e), e),
    })
}

fn main() {
    let arg = env::args().skip(1).next().unwrap_or(".".to_string());

    let mut errors = Errors { errs: false };
    walk(Path::new(&arg), &mut errors);

    if errors.errs {
        panic!("found some lint errors");
    } else {
        println!("good style!");
    }
}

fn walk(path: &Path, err: &mut Errors) {
    for entry in t!(path.read_dir()).map(|e| t!(e)) {
        let path = entry.path();
        if t!(entry.file_type()).is_dir() {
            walk(&path, err);
            continue
        }

        let name = entry.file_name().into_string().unwrap();
        match &name[..] {
            n if !n.ends_with(".rs") => continue,

            "dox.rs" |
            "lib.rs" |
            "ctypes.rs" |
            "libc.rs" |
            "macros.rs" => continue,

            _ => {}
        }

        let mut contents = String::new();
        t!(t!(fs::File::open(&path)).read_to_string(&mut contents));

        check_style(&contents, &path, err);
    }
}

struct Errors {
    errs: bool,
}

#[derive(Clone, Copy, PartialEq)]
enum State {
    Start,
    Imports,
    Typedefs,
    Structs,
    Constants,
    FunctionDefinitions,
    Functions,
    Modules,
}

fn check_style(file: &str, path: &Path, err: &mut Errors) {
    let mut state = State::Start;
    let mut s_macros = 0;
    let mut f_macros = 0;
    let mut prev_blank = false;

    for (i, line) in file.lines().enumerate() {
        if line == "" {
            if prev_blank {
                err.error(path, i, "double blank line");
            }
            prev_blank = true;
        } else {
            prev_blank = false;
        }
        if line != line.trim_end() {
            err.error(path, i, "trailing whitespace");
        }
        if line.contains("\t") {
            err.error(path, i, "tab character");
        }
        if line.len() > 80 {
            err.error(path, i, "line longer than 80 chars");
        }
        if line.contains("#[cfg(") && !line.contains(" if ")
            && !(line.contains("target_endian") ||
                 line.contains("target_arch"))
        {
            if state != State::Structs {
                err.error(path, i, "use cfg_if! and submodules \
                                    instead of #[cfg]");
            }
        }

        let line = line.trim_start();
        let is_pub = line.starts_with("pub ");
        let line = if is_pub {&line[4..]} else {line};

        let line_state = if line.starts_with("use ") {
            if line.contains("c_void") {
                continue;
            }
            if is_pub {
                State::Modules
            } else {
                State::Imports
            }
        } else if line.starts_with("const ") {
            State::Constants
        } else if line.starts_with("type ") {
            State::Typedefs
        } else if line.starts_with("s! {") {
            s_macros += 1;
            State::Structs
        } else if line.starts_with("f! {") {
            f_macros += 1;
            State::FunctionDefinitions
        } else if line.starts_with("extern ") {
            State::Functions
        } else if line.starts_with("mod ") {
            State::Modules
        } else {
            continue
        };

        if state as usize > line_state as usize {
            err.error(path, i, &format!("{} found after {} when \
                                         it belongs before",
                                        line_state.desc(), state.desc()));
        }

        if f_macros == 2 {
            f_macros += 1;
            err.error(path, i, "multiple f! macros in one module");
        }
        if s_macros == 2 {
            s_macros += 1;
            err.error(path, i, "multiple s! macros in one module");
        }

        state = line_state;
    }
}

impl State {
    fn desc(&self) -> &str {
        match *self {
            State::Start => "start",
            State::Imports => "import",
            State::Typedefs => "typedef",
            State::Structs => "struct",
            State::Constants => "constant",
            State::FunctionDefinitions => "function definition",
            State::Functions => "extern function",
            State::Modules => "module",
        }
    }
}

impl Errors {
    fn error(&mut self, path: &Path, line: usize, msg: &str) {
        self.errs = true;
        println!("{}:{} - {}", path.display(), line + 1, msg);
    }
}
<commit_msg>ci/style.rs: Catch derives of Copy and Clone<commit_after>//! Simple script to verify the coding style of this library
//!
//! ## How to run
//!
//! The first argument to this script is the directory to run on, so running
//! this script should be as simple as:
//!
//! ```notrust
//! rustc ci/style.rs
//! ./style src
//! ```
//!
//! ## Guidelines
//!
//! The current style is:
//!
//! * No trailing whitespace
//! * No tabs
//! * 80-character lines
//! * Specific module layout:
//!     1. use directives
//!     2. typedefs
//!     3. structs
//!     4. constants
//!     5. f! { ... } functions
//!     6. extern functions
//!     7. modules + pub use
//!
//! Things not verified:
//!
//! * alignment
//! * 4-space tabs
//! * leading colons on paths

use std::env;
use std::fs;
use std::io::prelude::*;
use std::path::Path;

macro_rules! t {
    ($e:expr) => (match $e {
        Ok(e) => e,
        Err(e) => panic!("{} failed with {}", stringify!($e), e),
    })
}

fn main() {
    let arg = env::args().skip(1).next().unwrap_or(".".to_string());

    let mut errors = Errors { errs: false };
    walk(Path::new(&arg), &mut errors);

    if errors.errs {
        panic!("found some lint errors");
    } else {
        println!("good style!");
    }
}

fn walk(path: &Path, err: &mut Errors) {
    for entry in t!(path.read_dir()).map(|e| t!(e)) {
        let path = entry.path();
        if t!(entry.file_type()).is_dir() {
            walk(&path, err);
            continue
        }

        let name = entry.file_name().into_string().unwrap();
        match &name[..] {
            n if !n.ends_with(".rs") => continue,

            "dox.rs" |
            "lib.rs" |
            "ctypes.rs" |
            "libc.rs" |
            "macros.rs" => continue,

            _ => {}
        }

        let mut contents = String::new();
        t!(t!(fs::File::open(&path)).read_to_string(&mut contents));

        check_style(&contents, &path, err);
    }
}

struct Errors {
    errs: bool,
}

#[derive(Clone, Copy, PartialEq)]
enum State {
    Start,
    Imports,
    Typedefs,
    Structs,
    Constants,
    FunctionDefinitions,
    Functions,
    Modules,
}

fn check_style(file: &str, path: &Path, err: &mut Errors) {
    let mut state = State::Start;
    let mut s_macros = 0;
    let mut f_macros = 0;
    let mut prev_blank = false;

    for (i, line) in file.lines().enumerate() {
        if line == "" {
            if prev_blank {
                err.error(path, i, "double blank line");
            }
            prev_blank = true;
        } else {
            prev_blank = false;
        }
        if line != line.trim_end() {
            err.error(path, i, "trailing whitespace");
        }
        if line.contains("\t") {
            err.error(path, i, "tab character");
        }
        if line.len() > 80 {
            err.error(path, i, "line longer than 80 chars");
        }
        if line.contains("#[cfg(") && !line.contains(" if ")
            && !(line.contains("target_endian") ||
                 line.contains("target_arch"))
        {
            if state != State::Structs {
                err.error(path, i, "use cfg_if! and submodules \
                                    instead of #[cfg]");
            }
        }
        if line.contains("#[derive(") && (line.contains("Copy") || line.contains("Clone")) {
            err.error(path, i, "impl ::Copy and ::Clone manually");
        }

        let line = line.trim_start();
        let is_pub = line.starts_with("pub ");
        let line = if is_pub {&line[4..]} else {line};

        let line_state = if line.starts_with("use ") {
            if line.contains("c_void") {
                continue;
            }
            if is_pub {
                State::Modules
            } else {
                State::Imports
            }
        } else if line.starts_with("const ") {
            State::Constants
        } else if line.starts_with("type ") {
            State::Typedefs
        } else if line.starts_with("s! {") {
            s_macros += 1;
            State::Structs
        } else if line.starts_with("f! {") {
            f_macros += 1;
            State::FunctionDefinitions
        } else if line.starts_with("extern ") {
            State::Functions
        } else if line.starts_with("mod ") {
            State::Modules
        } else {
            continue
        };

        if state as usize > line_state as usize {
            err.error(path, i, &format!("{} found after {} when \
                                         it belongs before",
                                        line_state.desc(), state.desc()));
        }

        if f_macros == 2 {
            f_macros += 1;
            err.error(path, i, "multiple f! macros in one module");
        }
        if s_macros == 2 {
            s_macros += 1;
            err.error(path, i, "multiple s! macros in one module");
        }

        state = line_state;
    }
}

impl State {
    fn desc(&self) -> &str {
        match *self {
            State::Start => "start",
            State::Imports => "import",
            State::Typedefs => "typedef",
            State::Structs => "struct",
            State::Constants => "constant",
            State::FunctionDefinitions => "function definition",
            State::Functions => "extern function",
            State::Modules => "module",
        }
    }
}

impl Errors {
    fn error(&mut self, path: &Path, line: usize, msg: &str) {
        self.errs = true;
        println!("{}:{} - {}", path.display(), line + 1, msg);
    }
}
<|endoftext|>