<commit_before>use codespan::{CodeMap, LineIndex, RawIndex};
use std::io;
use termcolor::{Color, ColorSpec, WriteColor};

use crate::{Diagnostic, LabelStyle, Severity};

#[derive(Clone, Debug)]
pub struct Config {
    pub bug_color: Color,
    pub error_color: Color,
    pub warning_color: Color,
    pub note_color: Color,
    pub help_color: Color,
    pub secondary_color: Color,
    pub gutter_color: Color,
    pub primary_mark: char,
    pub secondary_mark: char,
}

impl Default for Config {
    fn default() -> Config {
        // Blue is really difficult to see on the standard windows command line
        #[cfg(windows)]
        const BLUE: Color = Color::Cyan;
        #[cfg(not(windows))]
        const BLUE: Color = Color::Blue;

        Config {
            bug_color: Color::Red,
            error_color: Color::Red,
            warning_color: Color::Yellow,
            note_color: Color::Green,
            help_color: Color::Cyan,
            secondary_color: BLUE,
            gutter_color: BLUE,
            primary_mark: '^',
            secondary_mark: '-',
        }
    }
}

impl Config {
    /// The color used to mark a given severity.
    pub fn severity_color(&self, severity: Severity) -> Color {
        match severity {
            Severity::Bug => self.bug_color,
            Severity::Error => self.error_color,
            Severity::Warning => self.warning_color,
            Severity::Note => self.note_color,
            Severity::Help => self.help_color,
        }
    }

    /// The style used to mark the labelled section of code.
    pub fn label_color(&self, severity: Severity, label_style: LabelStyle) -> Color {
        match label_style {
            LabelStyle::Primary => self.severity_color(severity),
            LabelStyle::Secondary => self.secondary_color,
        }
    }

    /// The character used for the underlined section of code.
    fn underline_char(&self, label_style: LabelStyle) -> char {
        match label_style {
            LabelStyle::Primary => self.primary_mark,
            LabelStyle::Secondary => self.secondary_mark,
        }
    }
}

pub fn emit(
    mut writer: impl WriteColor,
    config: &Config,
    codemap: &CodeMap<impl AsRef<str>>,
    diagnostic: &Diagnostic,
) -> io::Result<()> {
    Header::new(diagnostic).emit(&mut writer, config)?;

    for label in &diagnostic.labels {
        match codemap.find_file(label.span.start()) {
            None => {
                if !label.message.is_empty() {
                    write!(writer, "- {}", label.message)?;
                    write!(writer, "\n")?;
                }
            },
            Some(file) => {
                let gutter_spec = ColorSpec::new().set_fg(Some(config.gutter_color)).clone();
                let (start_line, start_column) =
                    file.location(label.span.start()).expect("location_start");
                let (end_line, _) = file.location(label.span.end()).expect("location_end");
                let start_line_span = file.line_span(start_line).expect("line_span");
                let end_line_span = file.line_span(end_line).expect("line_span");

                // Use the length of the last line number as the gutter padding
                let gutter_padding = format!("{}", end_line.number()).len();

                // File name
                //
                // ```
                // ┌╴ <test>:2:9
                // ```

                // Write gutter
                writer.set_color(&gutter_spec)?;
                write!(writer, "{: >width$} ┌╴ ", "", width = gutter_padding)?;
                writer.reset()?;

                // Write file name
                write!(
                    writer,
                    "{file}:{line}:{column}",
                    file = file.name(),
                    line = start_line.number(),
                    column = start_column.number(),
                )?;
                write!(writer, "\n")?;

                // Source code snippet
                //
                // ```
                //   │
                // 2 │ (+ test "")
                //   │         ^^ Expected integer but got string
                //   ╵
                // ```

                // Write line number and gutter
                writer.set_color(&gutter_spec)?;
                write!(writer, "{: >width$} │ ", "", width = gutter_padding)?;
                write!(writer, "\n")?;
                write!(
                    writer,
                    "{: >width$} │ ",
                    start_line.number(),
                    width = gutter_padding,
                )?;
                writer.reset()?;

                // Write source prefix before marked section
                let source_prefix = file
                    .src_slice(start_line_span.with_end(label.span.start()))
                    .expect("prefix");
                write!(writer, "{}", source_prefix)?;

                let label_color = config.label_color(diagnostic.severity, label.style);
                let label_spec = ColorSpec::new().set_fg(Some(label_color)).clone();

                // Write marked section
                let mark_len = if start_line == end_line {
                    // Single line

                    // Write marked source section
                    let marked_source = file.src_slice(label.span).expect("marked_source");
                    writer.set_color(&label_spec)?;
                    write!(writer, "{}", marked_source)?;
                    writer.reset()?;
                    marked_source.len()
                } else {
                    // Multiple lines

                    // Write marked source section
                    let marked_source = file
                        .src_slice(start_line_span.with_start(label.span.start()))
                        .expect("start_of_marked");
                    writer.set_color(&label_spec)?;
                    write!(writer, "{}", marked_source)?;

                    for line_index in ((start_line.to_usize() + 1)..end_line.to_usize())
                        .map(|i| LineIndex::from(i as RawIndex))
                    {
                        // Write line number and gutter
                        writer.set_color(&gutter_spec)?;
                        write!(
                            writer,
                            "{: >width$} │ ",
                            line_index.number(),
                            width = gutter_padding,
                        )?;

                        // Write marked source section
                        let line_span = file.line_span(line_index).expect("marked_line_span");
                        let marked_source = file
                            .src_slice(line_span)
                            .expect("marked_source")
                            .trim_end_matches(|ch: char| ch == '\r' || ch == '\n');
                        writer.set_color(&label_spec)?;
                        write!(writer, "{}", marked_source)?;
                        write!(writer, "\n")?;
                    }

                    // Write line number and gutter
                    writer.set_color(&gutter_spec)?;
                    write!(
                        writer,
                        "{: >width$} │ ",
                        end_line.number(),
                        width = gutter_padding,
                    )?;

                    // Write marked source section
                    let marked_source = file
                        .src_slice(end_line_span.with_end(label.span.end()))
                        .expect("marked_source");
                    writer.set_color(&label_spec)?;
                    write!(writer, "{}", marked_source)?;
                    writer.reset()?;
                    marked_source.len()
                };

                // Write source suffix after marked section
                let source = file
                    .src_slice(end_line_span.with_start(label.span.end()))
                    .expect("suffix")
                    .trim_end_matches(|ch: char| ch == '\r' || ch == '\n');
                write!(writer, "{}", source)?;
                write!(writer, "\n")?;

                // Write underline gutter
                writer.set_color(&gutter_spec)?;
                write!(writer, "{: >width$} │ ", "", width = gutter_padding)?;
                writer.reset()?;

                // Write underline and label
                writer.set_color(&label_spec)?;
                write!(writer, "{: >width$}", "", width = source_prefix.len())?;
                for _ in 0..mark_len {
                    write!(writer, "{}", config.underline_char(label.style))?;
                }
                if !label.message.is_empty() {
                    write!(writer, " {}", label.message)?;
                    write!(writer, "\n")?;
                }
                writer.reset()?;

                // Write final gutter
                writer.set_color(&gutter_spec)?;
                write!(writer, "{: >width$} ╵", "", width = gutter_padding)?;
                write!(writer, "\n")?;
                writer.reset()?;
            },
        }
    }
    Ok(())
}

/// Diagnostic header
///
/// ```
/// error[E0001]: Unexpected type in `+` application
/// ```
#[derive(Copy, Clone, Debug)]
struct Header<'doc> {
    severity: Severity,
    code: Option<&'doc str>,
    message: &'doc str,
}

impl<'doc> Header<'doc> {
    fn new(diagnostic: &'doc Diagnostic) -> Header<'doc> {
        Header {
            severity: diagnostic.severity,
            code: diagnostic.code.as_ref().map(String::as_str),
            message: &diagnostic.message,
        }
    }

    fn severity_name(&self) -> &'static str {
        match self.severity {
            Severity::Bug => "bug",
            Severity::Error => "error",
            Severity::Warning => "warning",
            Severity::Help => "help",
            Severity::Note => "note",
        }
    }

    fn emit(&self, writer: &mut impl WriteColor, config: &Config) -> io::Result<()> {
        let message_spec = ColorSpec::new().set_bold(true).set_intense(true).clone();
        let primary_spec = ColorSpec::new()
            .set_bold(true)
            .set_intense(true)
            .set_fg(Some(config.severity_color(self.severity)))
            .clone();

        // Write severity name
        //
        // ```
        // error
        // ```
        writer.set_color(&primary_spec)?;
        write!(writer, "{}", self.severity_name())?;
        if let Some(code) = &self.code {
            // Write error code
            //
            // ```
            // [E0001]
            // ```
            write!(writer, "[{}]", code)?;
        }

        // Write diagnostic message
        //
        // ```
        // : Unexpected type in `+` application
        // ```
        writer.set_color(&message_spec)?;
        write!(writer, ": {}", self.message)?;
        write!(writer, "\n")?;
        writer.reset()?;

        Ok(())
    }
}
<commit_msg>Split up message body into components<commit_after>use codespan::{ByteSpan, CodeMap, FileMap, LineIndex, RawIndex};
use std::io;
use termcolor::{Color, ColorSpec, WriteColor};

use crate::{Diagnostic, Label, LabelStyle, Severity};

#[derive(Clone, Debug)]
pub struct Config {
    pub bug_color: Color,
    pub error_color: Color,
    pub warning_color: Color,
    pub note_color: Color,
    pub help_color: Color,
    pub secondary_color: Color,
    pub gutter_color: Color,
    pub primary_mark: char,
    pub secondary_mark: char,
}

impl Default for Config {
    fn default() -> Config {
        // Blue is really difficult to see on the standard windows command line
        #[cfg(windows)]
        const BLUE: Color = Color::Cyan;
        #[cfg(not(windows))]
        const BLUE: Color = Color::Blue;

        Config {
            bug_color: Color::Red,
            error_color: Color::Red,
            warning_color: Color::Yellow,
            note_color: Color::Green,
            help_color: Color::Cyan,
            secondary_color: BLUE,
            gutter_color: BLUE,
            primary_mark: '^',
            secondary_mark: '-',
        }
    }
}

impl Config {
    /// The color used to mark a given severity.
    pub fn severity_color(&self, severity: Severity) -> Color {
        match severity {
            Severity::Bug => self.bug_color,
            Severity::Error => self.error_color,
            Severity::Warning => self.warning_color,
            Severity::Note => self.note_color,
            Severity::Help => self.help_color,
        }
    }
}

pub fn emit(
    mut writer: impl WriteColor,
    config: &Config,
    codemap: &CodeMap<impl AsRef<str>>,
    diagnostic: &Diagnostic,
) -> io::Result<()> {
    Header::new(diagnostic).emit(&mut writer, config)?;

    for label in &diagnostic.labels {
        match codemap.find_file(label.span.start()) {
            None => SimpleMessage::new(&label.message).emit(&mut writer, config)?,
            Some(file) => {
                MarkedSource::new(file, &diagnostic, &label).emit(&mut writer, config)?
            },
        }
    }

    Ok(())
}

/// Diagnostic header
///
/// ```text
/// error[E0001]: Unexpected type in `+` application
/// ```
#[derive(Copy, Clone, Debug)]
struct Header<'a> {
    severity: Severity,
    code: Option<&'a str>,
    message: &'a str,
}

impl<'a> Header<'a> {
    fn new(diagnostic: &'a Diagnostic) -> Header<'a> {
        Header {
            severity: diagnostic.severity,
            code: diagnostic.code.as_ref().map(String::as_str),
            message: &diagnostic.message,
        }
    }

    fn severity_name(&self) -> &'static str {
        match self.severity {
            Severity::Bug => "bug",
            Severity::Error => "error",
            Severity::Warning => "warning",
            Severity::Help => "help",
            Severity::Note => "note",
        }
    }

    fn emit(&self, writer: &mut impl WriteColor, config: &Config) -> io::Result<()> {
        let message_spec = ColorSpec::new().set_bold(true).set_intense(true).clone();
        let primary_spec = ColorSpec::new()
            .set_bold(true)
            .set_intense(true)
            .set_fg(Some(config.severity_color(self.severity)))
            .clone();

        // Write severity name
        //
        // ```
        // error
        // ```
        writer.set_color(&primary_spec)?;
        write!(writer, "{}", self.severity_name())?;
        if let Some(code) = &self.code {
            // Write error code
            //
            // ```
            // [E0001]
            // ```
            write!(writer, "[{}]", code)?;
        }

        // Write diagnostic message
        //
        // ```
        // : Unexpected type in `+` application
        // ```
        writer.set_color(&message_spec)?;
        write!(writer, ": {}", self.message)?;
        write!(writer, "\n")?;
        writer.reset()?;

        Ok(())
    }
}

/// A simple message
///
/// ```text
/// - Expected integer but got string
/// ```
struct SimpleMessage<'a> {
    message: &'a str,
}

impl<'a> SimpleMessage<'a> {
    fn new(message: &'a str) -> SimpleMessage<'a> {
        SimpleMessage { message }
    }

    fn emit(&self, writer: &mut impl WriteColor, _config: &Config) -> io::Result<()> {
        if !self.message.is_empty() {
            write!(writer, "- {}", self.message)?;
            write!(writer, "\n")?;
        }

        Ok(())
    }
}

/// A marked section of source code
///
/// ```text
///   ┌╴ <test>:2:9
///   │
/// 2 │ (+ test "")
///   │         ^^ Expected integer but got string
///   ╵
/// ```
struct MarkedSource<'a, S: AsRef<str>> {
    file: &'a FileMap<S>,
    span: ByteSpan,
    message: &'a str,
    severity: Option<Severity>,
}

impl<'a, S: AsRef<str>> MarkedSource<'a, S> {
    fn new(
        file: &'a FileMap<S>,
        diagnostic: &Diagnostic,
        label: &'a Label,
    ) -> MarkedSource<'a, S> {
        MarkedSource {
            file,
            span: label.span,
            message: &label.message,
            severity: match label.style {
                LabelStyle::Primary => Some(diagnostic.severity),
                LabelStyle::Secondary => None,
            },
        }
    }

    fn label_color(&self, config: &Config) -> Color {
        match self.severity {
            None => config.secondary_color,
            Some(severity) => config.severity_color(severity),
        }
    }

    fn underline_char(&self, config: &Config) -> char {
        match self.severity {
            Some(_) => config.primary_mark,
            None => config.secondary_mark,
        }
    }

    fn emit(&self, writer: &mut impl WriteColor, config: &Config) -> io::Result<()> {
        let gutter_spec = ColorSpec::new().set_fg(Some(config.gutter_color)).clone();
        let label_color = self.label_color(config);
        let label_spec = ColorSpec::new().set_fg(Some(label_color)).clone();

        let (start_line, start_column) = self
            .file
            .location(self.span.start())
            .expect("location_start");
        let (end_line, _) = self.file.location(self.span.end()).expect("location_end");
        let start_line_span = self.file.line_span(start_line).expect("line_span");
        let end_line_span = self.file.line_span(end_line).expect("line_span");

        // Use the length of the last line number as the gutter padding
        let gutter_padding = format!("{}", end_line.number()).len();

        // File name
        //
        // ```
        // ┌╴ <test>:2:9
        // ```

        // Write gutter
        writer.set_color(&gutter_spec)?;
        write!(writer, "{: >width$} ┌╴ ", "", width = gutter_padding)?;
        writer.reset()?;

        // Write file name
        write!(
            writer,
            "{file}:{line}:{column}",
            file = self.file.name(),
            line = start_line.number(),
            column = start_column.number(),
        )?;
        write!(writer, "\n")?;

        // Source code snippet
        //
        // ```
        //   │
        // 2 │ (+ test "")
        //   │         ^^ Expected integer but got string
        //   ╵
        // ```

        // Write line number and gutter
        writer.set_color(&gutter_spec)?;
        write!(writer, "{: >width$} │ ", "", width = gutter_padding)?;
        write!(writer, "\n")?;
        write!(
            writer,
            "{: >width$} │ ",
            start_line.number(),
            width = gutter_padding,
        )?;
        writer.reset()?;

        // Write source prefix before marked section
        let source_prefix = self
            .file
            .src_slice(start_line_span.with_end(self.span.start()))
            .expect("prefix");
        write!(writer, "{}", source_prefix)?;

        // Write marked section
        let mark_len = if start_line == end_line {
            // Single line

            // Write marked source section
            let marked_source = self.file.src_slice(self.span).expect("marked_source");
            writer.set_color(&label_spec)?;
            write!(writer, "{}", marked_source)?;
            writer.reset()?;
            marked_source.len()
        } else {
            // Multiple lines

            // Write marked source section
            let marked_source = self
                .file
                .src_slice(start_line_span.with_start(self.span.start()))
                .expect("start_of_marked");
            writer.set_color(&label_spec)?;
            write!(writer, "{}", marked_source)?;

            for line_index in ((start_line.to_usize() + 1)..end_line.to_usize())
                .map(|i| LineIndex::from(i as RawIndex))
            {
                // Write line number and gutter
                writer.set_color(&gutter_spec)?;
                write!(
                    writer,
                    "{: >width$} │ ",
                    line_index.number(),
                    width = gutter_padding,
                )?;

                // Write marked source section
                let line_span = self.file.line_span(line_index).expect("marked_line_span");
                let marked_source = self
                    .file
                    .src_slice(line_span)
                    .expect("marked_source")
                    .trim_end_matches(|ch: char| ch == '\r' || ch == '\n');
                writer.set_color(&label_spec)?;
                write!(writer, "{}", marked_source)?;
                write!(writer, "\n")?;
            }

            // Write line number and gutter
            writer.set_color(&gutter_spec)?;
            write!(
                writer,
                "{: >width$} │ ",
                end_line.number(),
                width = gutter_padding,
            )?;

            // Write marked source section
            let marked_source = self
                .file
                .src_slice(end_line_span.with_end(self.span.end()))
                .expect("marked_source");
            writer.set_color(&label_spec)?;
            write!(writer, "{}", marked_source)?;
            writer.reset()?;
            marked_source.len()
        };

        // Write source suffix after marked section
        let source = self
            .file
            .src_slice(end_line_span.with_start(self.span.end()))
            .expect("suffix")
            .trim_end_matches(|ch: char| ch == '\r' || ch == '\n');
        write!(writer, "{}", source)?;
        write!(writer, "\n")?;

        // Write underline gutter
        writer.set_color(&gutter_spec)?;
        write!(writer, "{: >width$} │ ", "", width = gutter_padding)?;
        writer.reset()?;

        // Write underline and label
        writer.set_color(&label_spec)?;
        write!(writer, "{: >width$}", "", width = source_prefix.len())?;
        for _ in 0..mark_len {
            write!(writer, "{}", self.underline_char(config))?;
        }
        if !self.message.is_empty() {
            write!(writer, " {}", self.message)?;
            write!(writer, "\n")?;
        }
        writer.reset()?;

        // Write final gutter
        writer.set_color(&gutter_spec)?;
        write!(writer, "{: >width$} ╵", "", width = gutter_padding)?;
        write!(writer, "\n")?;
        writer.reset()?;

        Ok(())
    }
}
<|endoftext|>


<commit_before>//! Helpers to build and aggregate diagnostics.

use std::cell::RefCell;
use std::rc::Rc;
use position::Span;

/// Reporter aggregates and allows building diagnostics.
///
/// Note that reporters created by cloning will share diagnostic list with the
/// original reporter.
#[derive(Default, Debug, Clone)]
pub struct Reporter {
    diagnostics: Rc<RefCell<Vec<Diagnostic>>>,
}

impl Reporter {
    /// Create a new reporter with no errors.
    pub fn new() -> Reporter {
        Default::default()
    }

    /// Returns if the reporter has any errors.
    ///
    /// # Examples
    ///
    /// ```rust
    /// use plank_errors::reporter::Reporter;
    ///
    /// let mut reporter = Reporter::new();
    /// // empty reporter should not have any errors
    /// assert!(!reporter.have_errors());
    /// ```
    pub fn have_errors(&self) -> bool {
        self.diagnostics.borrow().iter().any(|d| {
            d.severity == Severity::Error
        })
    }

    /// Return the list of diagnostics collected with this reporter.
    ///
    /// The diagnosics are returned in arbitrary order. Depending on how they
    /// will be displayed, you might want to sort them.
    pub fn get_diagnostics(&self) -> Vec<Diagnostic> {
        self.diagnostics.borrow().clone()
    }

    /// Create a new error without associated span.
    ///
    /// # Examples
    ///
    /// ```rust
    /// use plank_errors::reporter::Reporter;
    ///
    /// let mut reporter = Reporter::new();
    /// reporter.global_error("`main` function is missing");
    /// ```
    pub fn global_error<T: Into<String>>(&self, msg: T) {
        let diagnostic = Diagnostic {
            message: msg.into(),
            primary_span: None,
            notes: Vec::new(),
            severity: Severity::Error,
        };
        self.diagnostics.borrow_mut().push(diagnostic);
    }

    /// Create a builder for a new error.
    ///
    /// # Examples
    ///
    /// ```rust
    /// use plank_errors::reporter::Reporter;
    /// use plank_errors::position::{Position, Span};
    ///
    /// let mut reporter = Reporter::new();
    /// # let error_span = Span::new(Position::new(1, 1), Position::new(1, 1));
    /// # let help_span = error_span;
    /// reporter
    ///     .error("error message", error_span)
    ///     .span_note("shorter message", error_span)
    ///     .span_note("helper note", help_span)
    ///     .build();
    /// ```
    pub fn error<T>(&self, msg: T, span: Span) -> Builder
        where T: Into<String>
    {
        self.diagnostic(Severity::Error, msg, span)
    }

    /// Create a builder for a new warning.
    ///
    /// # Examples
    ///
    /// ```rust
    /// use plank_errors::reporter::Reporter;
    /// use plank_errors::position::{Position, Span};
    ///
    /// let mut reporter = Reporter::new();
    /// # let warning_span = Span::new(Position::new(1, 1), Position::new(1, 1));
    /// # let help_span = warning_span;
    /// reporter
    ///     .warning("warning message", warning_span)
    ///     .span_note("shorter message", warning_span)
    ///     .span_note("helper note", help_span)
    ///     .build();
    /// ```
    pub fn warning<T>(&self, msg: T, span: Span) -> Builder
        where T: Into<String>
    {
        self.diagnostic(Severity::Warning, msg, span)
    }

    /// Create a builder for a new diagnostic.
    ///
    /// # Examples
    ///
    /// ```rust
    /// use plank_errors::reporter::{Reporter, Severity};
    /// use plank_errors::position::{Position, Span};
    /// let mut reporter = Reporter::new();
    /// # let error_span = Span::new(Position::new(1, 1), Position::new(1, 1));
    /// reporter
    ///     .diagnostic(Severity::Error, "error message", error_span)
    ///     .span(error_span)
    ///     .build();
    /// ```
    pub fn diagnostic<T>(&self, severity: Severity, msg: T, span: Span) -> Builder
        where T: Into<String>
    {
        Builder::new(self, severity, msg.into(), span)
    }
}

#[allow(missing_docs)]
#[derive(PartialEq, PartialOrd, Eq, Ord, Hash, Debug, Copy, Clone)]
pub enum Severity {
    Error,
    Warning,
}

#[allow(missing_docs)]
#[derive(Debug, Clone)]
pub struct Diagnostic {
    pub message: String,
    pub primary_span: Option<Span>,
    pub severity: Severity,
    pub notes: Vec<Note>,
}

#[allow(missing_docs)]
#[derive(Debug, Clone)]
pub struct Note {
    pub span: Span,
    pub message: Option<String>,
}

/// A helper for building a diagnostic.
#[must_use]
pub struct Builder {
    reporter: Reporter,
    diagnostic: Diagnostic,
}

impl Builder {
    fn new(reporter: &Reporter, severity: Severity, msg: String, primary_span: Span) -> Self {
        Builder {
            reporter: reporter.clone(),
            diagnostic: Diagnostic {
                message: msg,
                severity,
                primary_span: Some(primary_span),
                notes: Vec::new(),
            },
        }
    }

    /// Complete current diagnostic.
    ///
    /// # Panics
    ///
    /// Panics if current diagnostic has no notes. If you want an error without
    /// any notes, use [`Reporter::global_error`]
    /// (struct.Reporter.html#method.global_error) instead.
    pub fn build(self) {
        assert!(!self.diagnostic.notes.is_empty(), "built a diagnostic without any notes");
        self.reporter.diagnostics.borrow_mut().push(self.diagnostic);
    }

    /// Add a new note that has only a span.
    pub fn span(self, span: Span) -> Self {
        self.note(None, span)
    }

    /// Add a new note that has a message and a span.
    pub fn span_note<T>(self, msg: T, span: Span) -> Self
        where T: Into<String>
    {
        self.note(Some(msg.into()), span)
    }

    fn note(mut self, msg: Option<String>, span: Span) -> Self {
        self.diagnostic.notes.push(Note {
            span,
            message: msg,
        });
        self
    }
}
<commit_msg>"have_errors" to "has_errors"<commit_after>//! Helpers to build and aggregate diagnostics.

use std::cell::RefCell;
use std::rc::Rc;
use position::Span;

/// Reporter aggregates and allows building diagnostics.
///
/// Note that reporters created by cloning will share diagnostic list with the
/// original reporter.
#[derive(Default, Debug, Clone)]
pub struct Reporter {
    diagnostics: Rc<RefCell<Vec<Diagnostic>>>,
}

impl Reporter {
    /// Create a new reporter with no errors.
    pub fn new() -> Reporter {
        Default::default()
    }

    /// Returns if the reporter has any errors.
    ///
    /// This function will return false even if reporter has any warnings.
    ///
    /// # Examples
    ///
    /// ```rust
    /// use plank_errors::reporter::Reporter;
    ///
    /// let mut reporter = Reporter::new();
    /// // empty reporter should not have any errors
    /// assert!(!reporter.has_errors());
    /// ```
    pub fn has_errors(&self) -> bool {
        self.diagnostics.borrow().iter().any(|d| {
            d.severity == Severity::Error
        })
    }

    /// Return the list of diagnostics collected with this reporter.
    ///
    /// The diagnosics are returned in arbitrary order. Depending on how they
    /// will be displayed, you might want to sort them.
    pub fn get_diagnostics(&self) -> Vec<Diagnostic> {
        self.diagnostics.borrow().clone()
    }

    /// Create a new error without associated span.
    ///
    /// # Examples
    ///
    /// ```rust
    /// use plank_errors::reporter::Reporter;
    ///
    /// let mut reporter = Reporter::new();
    /// reporter.global_error("`main` function is missing");
    /// ```
    pub fn global_error<T: Into<String>>(&self, msg: T) {
        let diagnostic = Diagnostic {
            message: msg.into(),
            primary_span: None,
            notes: Vec::new(),
            severity: Severity::Error,
        };
        self.diagnostics.borrow_mut().push(diagnostic);
    }

    /// Create a builder for a new error.
    ///
    /// # Examples
    ///
    /// ```rust
    /// use plank_errors::reporter::Reporter;
    /// use plank_errors::position::{Position, Span};
    ///
    /// let mut reporter = Reporter::new();
    /// # let error_span = Span::new(Position::new(1, 1), Position::new(1, 1));
    /// # let help_span = error_span;
    /// reporter
    ///     .error("error message", error_span)
    ///     .span_note("shorter message", error_span)
    ///     .span_note("helper note", help_span)
    ///     .build();
    /// ```
    pub fn error<T>(&self, msg: T, span: Span) -> Builder
        where T: Into<String>
    {
        self.diagnostic(Severity::Error, msg, span)
    }

    /// Create a builder for a new warning.
    ///
    /// # Examples
    ///
    /// ```rust
    /// use plank_errors::reporter::Reporter;
    /// use plank_errors::position::{Position, Span};
    ///
    /// let mut reporter = Reporter::new();
    /// # let warning_span = Span::new(Position::new(1, 1), Position::new(1, 1));
    /// # let help_span = warning_span;
    /// reporter
    ///     .warning("warning message", warning_span)
    ///     .span_note("shorter message", warning_span)
    ///     .span_note("helper note", help_span)
    ///     .build();
    /// ```
    pub fn warning<T>(&self, msg: T, span: Span) -> Builder
        where T: Into<String>
    {
        self.diagnostic(Severity::Warning, msg, span)
    }

    /// Create a builder for a new diagnostic.
    ///
    /// # Examples
    ///
    /// ```rust
    /// use plank_errors::reporter::{Reporter, Severity};
    /// use plank_errors::position::{Position, Span};
    /// let mut reporter = Reporter::new();
    /// # let error_span = Span::new(Position::new(1, 1), Position::new(1, 1));
    /// reporter
    ///     .diagnostic(Severity::Error, "error message", error_span)
    ///     .span(error_span)
    ///     .build();
    /// ```
    pub fn diagnostic<T>(&self, severity: Severity, msg: T, span: Span) -> Builder
        where T: Into<String>
    {
        Builder::new(self, severity, msg.into(), span)
    }
}

/// Diagnostics severity.
#[derive(PartialEq, PartialOrd, Eq, Ord, Hash, Debug, Copy, Clone)]
pub enum Severity {
    /// Represents a fatal error.
    Error,
    /// Represents a non-fatal error.
    Warning,
}

#[allow(missing_docs)]
#[derive(Debug, Clone)]
pub struct Diagnostic {
    pub message: String,
    pub primary_span: Option<Span>,
    pub severity: Severity,
    pub notes: Vec<Note>,
}

#[allow(missing_docs)]
#[derive(Debug, Clone)]
pub struct Note {
    pub span: Span,
    pub message: Option<String>,
}

/// A helper for building a diagnostic.
#[must_use]
pub struct Builder {
    reporter: Reporter,
    diagnostic: Diagnostic,
}

impl Builder {
    fn new(reporter: &Reporter, severity: Severity, msg: String, primary_span: Span) -> Self {
        Builder {
            reporter: reporter.clone(),
            diagnostic: Diagnostic {
                message: msg,
                severity,
                primary_span: Some(primary_span),
                notes: Vec::new(),
            },
        }
    }

    /// Complete current diagnostic.
    ///
    /// # Panics
    ///
    /// Panics if current diagnostic has no notes. If you want an error without
    /// any notes, use [`Reporter::global_error`]
    /// (struct.Reporter.html#method.global_error) instead.
    pub fn build(self) {
        assert!(!self.diagnostic.notes.is_empty(), "built a diagnostic without any notes");
        self.reporter.diagnostics.borrow_mut().push(self.diagnostic);
    }

    /// Add a new note that has only a span.
    pub fn span(self, span: Span) -> Self {
        self.note(None, span)
    }

    /// Add a new note that has a message and a span.
    pub fn span_note<T>(self, msg: T, span: Span) -> Self
        where T: Into<String>
    {
        self.note(Some(msg.into()), span)
    }

    fn note(mut self, msg: Option<String>, span: Span) -> Self {
        self.diagnostic.notes.push(Note {
            span,
            message: msg,
        });
        self
    }
}
<|endoftext|>


<commit_before>extern crate iron;

use std::io::net::ip::Ipv4Addr;

use iron::{Ingot, Iron, Alloy, Request, Response, ServerT};
use iron::ingot::{Status, Continue};

#[deriving(Clone)]
struct HelloWorld;

impl Ingot for HelloWorld {
    fn enter(&mut self,
             _request: &mut Request,
             response: &mut Response,
             _alloy: &mut Alloy) -> Status {
        let _ = response.write(bytes!("Hello World!"));
        Continue
    }
}

fn main() {
    let mut server: ServerT = Iron::new();
    server.smelt(HelloWorld);
    server.listen(Ipv4Addr(127, 0, 0, 1), 3000);
}

<commit_msg>(refactor) Updated example to use Middleware.<commit_after>extern crate iron;

use std::io::net::ip::Ipv4Addr;

use iron::{Iron, Alloy, Middleware, Request, Response, ServerT};
use iron::middleware::{Status, Continue};

#[deriving(Clone)]
struct HelloWorld;

impl Middleware for HelloWorld {
    fn enter(&mut self,
             _request: &mut Request,
             response: &mut Response,
             _alloy: &mut Alloy) -> Status {
        let _ = response.write(bytes!("Hello World!"));
        Continue
    }
}

fn main() {
    let mut server: ServerT = Iron::new();
    server.smelt(HelloWorld);
    server.listen(Ipv4Addr(127, 0, 0, 1), 3000);
}

<|endoftext|>


<commit_before>#![doc(html_root_url="https://drakulix.github.io/zwreec/",
    html_logo_url="https://dl.dropboxusercontent.com/u/70410095/zwreec/logo.png")]
//! Twee-to-Zcode compile library.
//!
//! Zwreec is a compiler for [interactive fiction](http://en.wikipedia.org/wiki/Interactive_fiction)
//! in the Twee format (created by the [Twine software](http://en.wikipedia.org/wiki/Twine_(software)))
//! to [Z-Machine](http://en.wikipedia.org/wiki/Z-machine) instructions (Zcode)
//! which can be run with interpreters like [frotz](http://frotz.sourceforge.net).
//!
//! This library was developed as part of the course "Softwareprojekt
//! Übersetzerbau" in 2015.
//!
//! # Requirements and Usage
//!
//! The library uses a [fork of rustlex](https://github.com/Drakulix/rustlex)
//! to do lexical analysis.
//! You can install Rust stable or nightly as you like via the
//! [provided binaries](http://www.rust-lang.org/install.html)
//! or the rustup script:
//!
//! ```sh
//! $ curl -s https://static.rust-lang.org/rustup.sh | sh -s -- --channel=nightly --date=2015-06-02
//! ```
//!
//! Or install the Ubuntu/Debian Packages for
//! [Rust](http://ppa.launchpad.net/hansjorg/rust/ubuntu/pool/main/r/rust-nightly/) and
//! [Cargo](http://ppa.launchpad.net/hansjorg/rust/ubuntu/pool/main/c/cargo-nightly/).
//!
//! To use zwreec in your project you can add it as a dependency to your `Cargo.toml`.
//!
//! ```toml
//! [dependencies.zwreec]
//! git = "https:://github.com/Drakulix/zwreec"
//! ```
//!
//! Then you can use it in your crate root.
//!
//! ```rust
//! extern crate zwreec;
//! ```
//!
//! # Features
//!
//! Only rudimentary Twee features are supported right now. This is about to change in the upcoming weeks.
//! Check the github issues for more information on the currently supported features.
//!
//! # Example
//!
//! The following example is itself a full working command line compiler. It is a simpler
//! version of the [Reference Binary Implementation](#reference-binary-implementation).
//!
//! ```no_run
//! extern crate zwreec;
//!
//! use std::env;
//! use std::error::Error;
//! use std::fs::File;
//! use std::path::Path;
//!
//! fn main() {
//!     let mut args: Vec<String> = env::args().collect();
//!
//!     if args.len() != 2 { panic!("Need exactly one input file!"); }
//!
//!     let cfg = zwreec::config::Config::default_config();
//!     let mut input = match File::open(Path::new(&args[1])) {
//!         Ok(file) => file,
//!         Err(why) => { panic!("Couldn't open input: {}", Error::description(&why)); }
//!     };
//!     let mut output = match File::create(Path::new("a.z8")) {
//!         Ok(file) => file,
//!         Err(why) => { panic!("Couldn't open output: {}", Error::description(&why)); }
//!     };
//!
//!     zwreec::compile(cfg, &mut input, &mut output);
//! }
//! ```
//!
//! # Reference Binary Implementation
//!
//! Zwreecs [Github-Repository](https://github.com/Drakulix/zwreec) contains a
//! reference binary implementation that uses this library and provides a simple
//! command line interface to compile Twee files.
//!
//! To build the binary, you need the rust version as outlined in [Requirements and
//! Usage](#requirements-and-usage). Then you can build the binary using
//!
//! ```sh
//! $ cargo build
//! ```
//!
//! The resulting binary can be found at `target/debug/zwreec`.
//!
//! # Logging
//!
//! Zwreec uses the logging facade provided by
//! [log](../log/index.html). The reference binary implementation of zwreec also includes an
//! implementation of the `Log` trait.

extern crate rustlex_codegen as rustlex;
#[macro_use] extern crate log;
extern crate time;
extern crate term;
extern crate getopts;

#[macro_use] pub mod utils;
pub mod config;
pub mod frontend;
pub mod backend;

use config::{Config,TestCase};
use std::error::Error;
use std::io::{BufReader,Cursor,Read,Write};

/// Compiles a Twee Input to Zcode
///
/// This is the main call into the Zwreec compiler. It will process `input: Read`
/// by calling the different parts of the compiler-chain, notably `frontend::lexer`
/// to generate a tokenstream, `frontend::parser` and `frontend::ast` to generate
/// the Abstract Syntax Tree and lastly `frontend::codegen` to generate the Zcode.
///
/// # Example
///
/// ```no_run
/// # use std::env;
/// # use std::fs::File;
/// # use std::path::Path;
/// # let mut args: Vec<String> = env::args().collect();
/// #
/// # if args.len() != 2 { panic!("Need exactly one input file!"); }
/// #
/// let cfg = zwreec::config::Config::default_config();
/// let mut input = File::open(Path::new(&args[1])).unwrap();
/// let mut output = File::create(Path::new("a.z8")).unwrap();
///
/// zwreec::compile(cfg, &mut input, &mut output);
/// ```
#[allow(unused_variables)]
pub fn compile<R: Read, W: Write>(cfg: Config, input: &mut R, output: &mut W) {

    // check the data if it has a bom
    let mut cursor = handle_bom_encoding(input);

    // tokenize
    let tokens = frontend::lexer::lex(&cfg, &mut cursor);

    // create parser
    let parser = frontend::parser::Parser::new(&cfg);

    // create ast builder
    let ast_builder = frontend::ast::ASTBuilder::new(&cfg);

    // build up ast from tokens
    let ast = ast_builder.build(parser.parse(tokens.inspect(|ref token| {
        debug!("{:?}", token);
    })));
    debug!("{:?}", ast);

    // create code
    frontend::codegen::generate_zcode(&cfg, ast, output);
}

/// checks the input if there is an bom, if true it will delete it
fn handle_bom_encoding<'a, R: Read>(input: &'a mut R) -> Cursor<Vec<u8>> {
    let mut reader = BufReader::new(input);
    let mut content = String::new();
    match reader.read_to_string(&mut content) {
        Err(why) => error!("Couldn't read {}", Error::description(&why)),
        Ok(_) => (),
    };

    let mut v: Vec<u8> = content.bytes().collect();
    if v.len() < 5 {
        error!("The file is to short for a valid twee File");
    }
    let has_bom = if &v[0..3] == [0xef, 0xbb, 0xbf] {
        true
    } else {
        false
    };
    if has_bom {
        debug!("File has Byte order mark");
        v.remove(0);
        v.remove(0);
        v.remove(0);
    }

    let cursor: Cursor<Vec<u8>> = Cursor::new(v);

    cursor
}

/// Run internal library tests.
///
/// This function is used to circumvent certain parts of the compiler toolchain.
/// It currently only processes `TestCase::ZcodeBackend` which creates a Zcode
/// file using all available OP-Codes.
///
/// **Warning:** This function should be considered unstable and might be removed
/// in later versions.
///
/// # Example
///
/// ```
/// # use std::env;
/// # use std::fs::File;
/// # use std::path::Path;
/// let mut cfg = zwreec::config::Config::default_config();
/// cfg.test_cases.push(zwreec::config::TestCase::ZcodeBackend);
///
/// let mut input: Option<File> = None;
/// let mut output = Some(File::create(Path::new("a.z8")).unwrap());
///
/// zwreec::test_library(cfg, &mut input, &mut output);
/// ```
#[allow(unused_variables)]
pub fn test_library<R: Read, W: Write>(cfg: Config, input: &mut Option<R>, output: &mut Option<W>) {
    for case in cfg.test_cases {
        match case {
            TestCase::ZcodeBackend => {
                match output.as_mut() {
                     Some(o) => backend::zcode::temp_create_zcode_example(o),
                     None => error!("TestCase::ZcodeBackend requires output!"),
                }
            }
        }
    }
}
<commit_msg>Update comments<commit_after>#![doc(html_root_url="https://drakulix.github.io/zwreec/",
    html_logo_url="https://dl.dropboxusercontent.com/u/70410095/zwreec/logo.png")]
//! Twee-to-Zcode compile library.
//!
//! Zwreec is a compiler for [interactive fiction](http://en.wikipedia.org/wiki/Interactive_fiction)
//! in the Twee format (created by the [Twine software](http://en.wikipedia.org/wiki/Twine_(software)))
//! to [Z-Machine](http://en.wikipedia.org/wiki/Z-machine) instructions (Zcode)
//! which can be run with interpreters like [frotz](http://frotz.sourceforge.net).
//!
//! This library was developed as part of the course "Softwareprojekt
//! Übersetzerbau" in 2015.
//!
//! # Requirements and Usage
//!
//! The library uses a [fork of rustlex](https://github.com/Drakulix/rustlex)
//! to do lexical analysis.
//! You can install Rust stable or nightly as you like via the
//! [provided binaries](http://www.rust-lang.org/install.html)
//! or the rustup script:
//!
//! ```sh
//! $ curl -s https://static.rust-lang.org/rustup.sh | sh -s -- --channel=nightly --date=2015-06-02
//! ```
//!
//! Or install the Ubuntu/Debian Packages for
//! [Rust](http://ppa.launchpad.net/hansjorg/rust/ubuntu/pool/main/r/rust-nightly/) and
//! [Cargo](http://ppa.launchpad.net/hansjorg/rust/ubuntu/pool/main/c/cargo-nightly/).
//!
//! To use zwreec in your project you can add it as a dependency to your `Cargo.toml`.
//!
//! ```toml
//! [dependencies.zwreec]
//! git = "https:://github.com/Drakulix/zwreec"
//! ```
//!
//! Then you can use it in your crate root.
//!
//! ```rust
//! extern crate zwreec;
//! ```
//!
//! # Features
//!
//! Only rudimentary Twee features are supported right now. This is about to change in the upcoming weeks.
//! Check the github issues for more information on the currently supported features.
//!
//! # Example
//!
//! The following example is itself a full working command line compiler. It is a simpler
//! version of the [Reference Binary Implementation](#reference-binary-implementation).
//!
//! ```no_run
//! extern crate zwreec;
//!
//! use std::env;
//! use std::error::Error;
//! use std::fs::File;
//! use std::path::Path;
//!
//! fn main() {
//!     let mut args: Vec<String> = env::args().collect();
//!
//!     if args.len() != 2 { panic!("Need exactly one input file!"); }
//!
//!     let cfg = zwreec::config::Config::default_config();
//!     let mut input = match File::open(Path::new(&args[1])) {
//!         Ok(file) => file,
//!         Err(why) => { panic!("Couldn't open input: {}", Error::description(&why)); }
//!     };
//!     let mut output = match File::create(Path::new("a.z8")) {
//!         Ok(file) => file,
//!         Err(why) => { panic!("Couldn't open output: {}", Error::description(&why)); }
//!     };
//!
//!     zwreec::compile(cfg, &mut input, &mut output);
//! }
//! ```
//!
//! # Reference Binary Implementation
//!
//! Zwreecs [Github-Repository](https://github.com/Drakulix/zwreec) contains a
//! reference binary implementation that uses this library and provides a simple
//! command line interface to compile Twee files.
//!
//! To build the binary, you need the rust version as outlined in [Requirements and
//! Usage](#requirements-and-usage). Then you can build the binary using
//!
//! ```sh
//! $ cargo build
//! ```
//!
//! The resulting binary can be found at `target/debug/zwreec`.
//!
//! # Logging
//!
//! Zwreec uses the logging facade provided by
//! [log](../log/index.html). The reference binary implementation of zwreec also includes an
//! implementation of the `Log` trait.

extern crate rustlex_codegen as rustlex;
#[macro_use] extern crate log;
extern crate time;
extern crate term;
extern crate getopts;

#[macro_use] pub mod utils;
pub mod config;
pub mod frontend;
pub mod backend;

use config::{Config,TestCase};
use std::error::Error;
use std::io::{BufReader,Cursor,Read,Write};

/// Compiles a Twee Input to Zcode
///
/// This is the main call into the Zwreec compiler. It will process `input: Read`
/// by calling the different parts of the compiler-chain, notably `frontend::lexer`
/// to generate a tokenstream, `frontend::parser` and `frontend::ast` to generate
/// the Abstract Syntax Tree and lastly `frontend::codegen` to generate the Zcode.
///
/// # Example
///
/// ```no_run
/// # use std::env;
/// # use std::fs::File;
/// # use std::path::Path;
/// # let mut args: Vec<String> = env::args().collect();
/// #
/// # if args.len() != 2 { panic!("Need exactly one input file!"); }
/// #
/// let cfg = zwreec::config::Config::default_config();
/// let mut input = File::open(Path::new(&args[1])).unwrap();
/// let mut output = File::create(Path::new("a.z8")).unwrap();
///
/// zwreec::compile(cfg, &mut input, &mut output);
/// ```
#[allow(unused_variables)]
pub fn compile<R: Read, W: Write>(cfg: Config, input: &mut R, output: &mut W) {

    // check the data if it has a bom
    let mut cursor = handle_bom_encoding(input);

    // tokenize
    let tokens = frontend::lexer::lex(&cfg, &mut cursor);

    // create parser
    let parser = frontend::parser::Parser::new(&cfg);

    // create ast builder
    let ast_builder = frontend::ast::ASTBuilder::new(&cfg);

    // build up ast from tokens
    let ast = ast_builder.build(parser.parse(tokens.inspect(|ref token| {
        debug!("{:?}", token);
    })));
    debug!("{:?}", ast);

    // create code
    frontend::codegen::generate_zcode(&cfg, ast, output);
}

/// checks the input if there is an BOM, if true it will delete it
fn handle_bom_encoding<'a, R: Read>(input: &'a mut R) -> Cursor<Vec<u8>> {
    let mut reader = BufReader::new(input);
    let mut content = String::new();
    match reader.read_to_string(&mut content) {
        Err(why) => error!("Couldn't read {}", Error::description(&why)),
        Ok(_) => (),
    };

    let mut v: Vec<u8> = content.bytes().collect();
    if v.len() < 5 {
        error!("The file is too short for a valid twee File");
    }
    let has_bom = if &v[0..3] == [0xef, 0xbb, 0xbf] {
        true
    } else {
        false
    };
    if has_bom {
        debug!("File has Byte order mark. Removing...");
        v.remove(0);
        v.remove(0);
        v.remove(0);
    }

    let cursor: Cursor<Vec<u8>> = Cursor::new(v);

    cursor
}

/// Run internal library tests.
///
/// This function is used to circumvent certain parts of the compiler toolchain.
/// It currently only processes `TestCase::ZcodeBackend` which creates a Zcode
/// file using all available OP-Codes.
///
/// **Warning:** This function should be considered unstable and might be removed
/// in later versions.
///
/// # Example
///
/// ```
/// # use std::env;
/// # use std::fs::File;
/// # use std::path::Path;
/// let mut cfg = zwreec::config::Config::default_config();
/// cfg.test_cases.push(zwreec::config::TestCase::ZcodeBackend);
///
/// let mut input: Option<File> = None;
/// let mut output = Some(File::create(Path::new("a.z8")).unwrap());
///
/// zwreec::test_library(cfg, &mut input, &mut output);
/// ```
#[allow(unused_variables)]
pub fn test_library<R: Read, W: Write>(cfg: Config, input: &mut Option<R>, output: &mut Option<W>) {
    for case in cfg.test_cases {
        match case {
            TestCase::ZcodeBackend => {
                match output.as_mut() {
                     Some(o) => backend::zcode::temp_create_zcode_example(o),
                     None => error!("TestCase::ZcodeBackend requires output!"),
                }
            }
        }
    }
}
<|endoftext|>


<commit_before>use clap::{crate_version, value_t};

use std::env;
use std::ffi::OsStr;
use std::io;
use std::process::{self, Command, ExitStatus};
use std::sync::{Arc, Condvar, Mutex};
use std::thread;
use std::time::{Duration, Instant};

#[path = "../common.rs"]
mod common;

fn main() {
    if !common::archive_file_path().exists() {
        let version = common::afl_rustc_version();
        eprintln!(
            "AFL LLVM runtime is not built with Rust {}, run `cargo \
             install --force afl` to build it.",
            version
        );
        process::exit(1);
    }

    let app_matches = clap_app().get_matches();
    // This unwrap is okay because we set SubcommandRequiredElseHelp at the top level, and afl is
    // the only subcommand
    let afl_matches = app_matches.subcommand_matches("afl").unwrap();

    match afl_matches.subcommand() {
        ("analyze", Some(sub_matches)) => {
            let args = sub_matches
                .values_of_os("afl-analyze args")
                .unwrap_or_default();
            run_afl(args, "afl-analyze", None);
        }
        ("cmin", Some(sub_matches)) => {
            let args = sub_matches
                .values_of_os("afl-cmin args")
                .unwrap_or_default();
            run_afl(args, "afl-cmin", None);
        }
        ("fuzz", Some(sub_matches)) => {
            let args = sub_matches
                .values_of_os("afl-fuzz args")
                .unwrap_or_default();
            let timeout = sub_matches
                .value_of("max_total_time")
                .map(|_| value_t!(sub_matches, "max_total_time", u64).unwrap_or_else(|e| e.exit()));
            run_afl(args, "afl-fuzz", timeout);
        }
        ("gotcpu", Some(sub_matches)) => {
            let args = sub_matches
                .values_of_os("afl-gotcpu args")
                .unwrap_or_default();
            run_afl(args, "afl-gotcpu", None);
        }
        ("plot", Some(sub_matches)) => {
            let args = sub_matches
                .values_of_os("afl-plot args")
                .unwrap_or_default();
            run_afl(args, "afl-plot", None);
        }
        ("showmap", Some(sub_matches)) => {
            let args = sub_matches
                .values_of_os("afl-showmap args")
                .unwrap_or_default();
            run_afl(args, "afl-showmap", None);
        }
        ("tmin", Some(sub_matches)) => {
            let args = sub_matches
                .values_of_os("afl-tmin args")
                .unwrap_or_default();
            run_afl(args, "afl-tmin", None);
        }
        ("whatsup", Some(sub_matches)) => {
            let args = sub_matches
                .values_of_os("afl-whatsup args")
                .unwrap_or_default();
            run_afl(args, "afl-whatsup", None);
        }
        (subcommand, Some(sub_matches)) => {
            let args = sub_matches.values_of_os("").unwrap_or_default();
            run_cargo(subcommand, args);
        }
        // unreachable due to SubcommandRequiredElseHelp on "afl" subcommand
        (_, None) => unreachable!(),
    }
}

fn clap_app() -> clap::App<'static, 'static> {
    use clap::{
        App,
        AppSettings::{
            AllowExternalSubcommands, AllowLeadingHyphen, DisableHelpFlags, DisableHelpSubcommand,
            DisableVersion, SubcommandRequiredElseHelp,
        },
        Arg, SubCommand,
    };

    App::new("cargo afl")
        .bin_name("cargo")
        .setting(SubcommandRequiredElseHelp)
        .subcommand(
            SubCommand::with_name("afl")
                .version(crate_version!())
                .setting(SubcommandRequiredElseHelp)
                .setting(AllowExternalSubcommands)
                .usage("cargo afl [SUBCOMMAND or Cargo SUBCOMMAND]")
                .after_help(
                    "In addition to the subcommands above, Cargo subcommands are also \
                 supported (see `cargo help` for a list of all Cargo subcommands).",
                )
                .subcommand(
                    SubCommand::with_name("analyze")
                        .about("Invoke afl-analyze")
                        .setting(AllowLeadingHyphen)
                        .setting(DisableHelpSubcommand)
                        .setting(DisableHelpFlags)
                        .setting(DisableVersion)
                        .arg(Arg::with_name("afl-analyze args").multiple(true)),
                )
                .subcommand(
                    SubCommand::with_name("cmin")
                        .about("Invoke afl-cmin")
                        .setting(AllowLeadingHyphen)
                        .setting(DisableHelpSubcommand)
                        .setting(DisableHelpFlags)
                        .setting(DisableVersion)
                        .arg(Arg::with_name("afl-cmin args").multiple(true)),
                )
                .subcommand(
                    SubCommand::with_name("fuzz")
                        .about("Invoke afl-fuzz")
                        .setting(AllowLeadingHyphen)
                        .setting(DisableHelpSubcommand)
                        .setting(DisableHelpFlags)
                        .setting(DisableVersion)
                        .arg(
                            Arg::with_name("max_total_time")
                                .long("max_total_time")
                                .takes_value(true)
                                .help("Maximum amount of time to run the fuzzer"),
                        )
                        .arg(Arg::with_name("afl-fuzz args").multiple(true)),
                )
                .subcommand(
                    SubCommand::with_name("gotcpu")
                        .about("Invoke afl-gotcpu")
                        .setting(AllowLeadingHyphen)
                        .setting(DisableHelpSubcommand)
                        .setting(DisableHelpFlags)
                        .setting(DisableVersion)
                        .arg(Arg::with_name("afl-gotcpu args").multiple(true)),
                )
                .subcommand(
                    SubCommand::with_name("plot")
                        .about("Invoke afl-plot")
                        .setting(AllowLeadingHyphen)
                        .setting(DisableHelpSubcommand)
                        .setting(DisableHelpFlags)
                        .setting(DisableVersion)
                        .arg(Arg::with_name("afl-plot args").multiple(true)),
                )
                .subcommand(
                    SubCommand::with_name("showmap")
                        .about("Invoke afl-showmap")
                        .setting(AllowLeadingHyphen)
                        .setting(DisableHelpSubcommand)
                        .setting(DisableHelpFlags)
                        .setting(DisableVersion)
                        .arg(Arg::with_name("afl-showmap args").multiple(true)),
                )
                .subcommand(
                    SubCommand::with_name("tmin")
                        .about("Invoke afl-tmin")
                        .setting(AllowLeadingHyphen)
                        .setting(DisableHelpSubcommand)
                        .setting(DisableHelpFlags)
                        .setting(DisableVersion)
                        .arg(Arg::with_name("afl-tmin args").multiple(true)),
                )
                .subcommand(
                    SubCommand::with_name("whatsup")
                        .about("Invoke afl-whatsup")
                        .setting(AllowLeadingHyphen)
                        .setting(DisableHelpSubcommand)
                        .setting(DisableHelpFlags)
                        .setting(DisableVersion)
                        .arg(Arg::with_name("afl-whatsup args").multiple(true)),
                ),
        )
}

fn run_timeout_terminate(mut cmd: Command, timeout: Option<u64>) -> Result<ExitStatus, io::Error> {
    let timeout = match timeout {
        Some(timeout) => Duration::from_secs(timeout),
        None => return cmd.status(),
    };
    let start_time = Instant::now();

    let mut child = cmd.spawn()?;
    let pid = child.id();

    let pair = Arc::new((Mutex::new(false), Condvar::new()));
    let (stop_mutex, condvar) = &*pair;
    let thread_handle = {
        let pair = pair.clone();
        thread::spawn(move || -> Result<(), io::Error> {
            // This thread will wait until the child process has exited, or the
            // timeout has elapsed, whichever comes first. If the timeout
            // elapses, and the process is still running, it will send SIGTERM
            // to the child process.

            let (stop_mutex, condvar) = &*pair;
            let mut stop = stop_mutex.lock().unwrap();
            loop {
                let elapsed = Instant::now() - start_time;
                if elapsed >= timeout {
                    break;
                }

                let dur = timeout - elapsed;
                let results = condvar.wait_timeout(stop, dur).unwrap();
                stop = results.0;
                if *stop {
                    // Blocking waitid call on the main thread has returned,
                    // thus the child process has terminated
                    return Ok(());
                }
                if results.1.timed_out() {
                    break;
                }
            }

            // Since the waitid call on the main thread is using WNOWAIT, the
            // child process won't be cleaned up (until after this thread
            // exits and the main thread calls wait) and thus its PID won't be
            // reused by another, unrelated process.
            unsafe {
                let ret = libc::kill(pid as i32, libc::SIGTERM);
                if ret == -1 {
                    Err(io::Error::last_os_error())?
                }
            }

            Ok(())
        })
    };

    unsafe {
        // Block until the child process terminates, but leave it in a waitable
        // state still
        let ret = libc::waitid(
            libc::P_PID,
            pid,
            std::ptr::null_mut(),
            libc::WEXITED | libc::WNOWAIT,
        );
        if ret == -1 {
            Err(io::Error::last_os_error())?
        }
    }
    {
        let mut stop = stop_mutex.lock().unwrap();
        *stop = true;
    }
    // Tell the timeout thread to stop, wake it, and wait for it to exit
    condvar.notify_one();
    thread_handle.join().unwrap()?;

    // Clean up zombie and get exit status (this won't block, because the child
    // process has terminated and is still waitable)
    child.wait()
}

fn run_afl<I, S>(args: I, cmd: &str, timeout: Option<u64>)
where
    I: IntoIterator<Item = S>,
    S: AsRef<OsStr>,
{
    let cmd_path = common::afl_dir().join("bin").join(cmd);
    let mut cmd = Command::new(cmd_path);
    cmd.args(args);
    let status = run_timeout_terminate(cmd, timeout).unwrap();
    process::exit(status.code().unwrap_or(1));
}

fn run_cargo<I, S>(subcommand: &str, args: I)
where
    I: IntoIterator<Item = S>,
    S: AsRef<OsStr>,
{
    let cargo_path = env::var("CARGO").expect("Could not determine `cargo` path");

    // add some flags to sanitizers to make them work with Rust code
    let asan_options = env::var("ASAN_OPTIONS").unwrap_or_default();
    let asan_options = format!("detect_odr_violation=0:abort_on_error=1:symbolize=0:{}", asan_options);

    let tsan_options = env::var("TSAN_OPTIONS").unwrap_or_default();
    let tsan_options = format!("report_signal_unsafe=0:{}", tsan_options);

    let mut rustflags = format!(
        "--cfg fuzzing \
         -C debug-assertions \
         -C overflow_checks \
         -C passes=sancov \
         -C llvm-args=-sanitizer-coverage-level=3 \
         -C llvm-args=-sanitizer-coverage-trace-pc-guard \
         -C llvm-args=-sanitizer-coverage-prune-blocks=0 \
         -C opt-level=3 \
         -C target-cpu=native \
         -C debuginfo=0 \
         -l afl-llvm-rt \
         -L {} ",
        common::afl_llvm_rt_dir().display()
    );

    // RUSTFLAGS are not used by rustdoc, instead RUSTDOCFLAGS are used. Since
    // doctests will try to link against afl-llvm-rt, set up RUSTDOCFLAGS to
    // have doctests built the same as other code to avoid issues with doctests.
    let mut rustdocflags = format!(
        "--cfg fuzzing \
         -C debug-assertions \
         -C overflow_checks \
         -C passes=sancov \
         -C llvm-args=-sanitizer-coverage-level=3 \
         -C llvm-args=-sanitizer-coverage-trace-pc-guard \
         -C llvm-args=-sanitizer-coverage-prune-blocks=0 \
         -C opt-level=3 \
         -C target-cpu=native \
         -C debuginfo=0 \
         -L {} ",
        common::afl_llvm_rt_dir().display()
    );

    if cfg!(target_os = "linux") {
        // work around https://github.com/rust-fuzz/afl.rs/issues/141 /
        // https://github.com/rust-lang/rust/issues/53945, can be removed once
        // those are fixed.
        rustflags.push_str("-Clink-arg=-fuse-ld=gold ");
        rustdocflags.push_str("-Clink-arg=-fuse-ld=gold ");
    }

    // add user provided flags
    rustflags.push_str(&env::var("RUSTFLAGS").unwrap_or_default());
    rustdocflags.push_str(&env::var("RUSTDOCFLAGS").unwrap_or_default());

    let status = Command::new(cargo_path)
        .arg(subcommand)
        .args(args)
        .env("RUSTFLAGS", &rustflags)
        .env("RUSTDOCFLAGS", &rustdocflags)
        .env("ASAN_OPTIONS", asan_options)
        .env("TSAN_OPTIONS", tsan_options)
        .status()
        .unwrap();
    process::exit(status.code().unwrap_or(1));
}
<commit_msg>Update for new LLVM pass manager<commit_after>use clap::{crate_version, value_t};

use std::env;
use std::ffi::OsStr;
use std::io;
use std::process::{self, Command, ExitStatus};
use std::sync::{Arc, Condvar, Mutex};
use std::thread;
use std::time::{Duration, Instant};

#[path = "../common.rs"]
mod common;

fn main() {
    if !common::archive_file_path().exists() {
        let version = common::afl_rustc_version();
        eprintln!(
            "AFL LLVM runtime is not built with Rust {}, run `cargo \
             install --force afl` to build it.",
            version
        );
        process::exit(1);
    }

    let app_matches = clap_app().get_matches();
    // This unwrap is okay because we set SubcommandRequiredElseHelp at the top level, and afl is
    // the only subcommand
    let afl_matches = app_matches.subcommand_matches("afl").unwrap();

    match afl_matches.subcommand() {
        ("analyze", Some(sub_matches)) => {
            let args = sub_matches
                .values_of_os("afl-analyze args")
                .unwrap_or_default();
            run_afl(args, "afl-analyze", None);
        }
        ("cmin", Some(sub_matches)) => {
            let args = sub_matches
                .values_of_os("afl-cmin args")
                .unwrap_or_default();
            run_afl(args, "afl-cmin", None);
        }
        ("fuzz", Some(sub_matches)) => {
            let args = sub_matches
                .values_of_os("afl-fuzz args")
                .unwrap_or_default();
            let timeout = sub_matches
                .value_of("max_total_time")
                .map(|_| value_t!(sub_matches, "max_total_time", u64).unwrap_or_else(|e| e.exit()));
            run_afl(args, "afl-fuzz", timeout);
        }
        ("gotcpu", Some(sub_matches)) => {
            let args = sub_matches
                .values_of_os("afl-gotcpu args")
                .unwrap_or_default();
            run_afl(args, "afl-gotcpu", None);
        }
        ("plot", Some(sub_matches)) => {
            let args = sub_matches
                .values_of_os("afl-plot args")
                .unwrap_or_default();
            run_afl(args, "afl-plot", None);
        }
        ("showmap", Some(sub_matches)) => {
            let args = sub_matches
                .values_of_os("afl-showmap args")
                .unwrap_or_default();
            run_afl(args, "afl-showmap", None);
        }
        ("tmin", Some(sub_matches)) => {
            let args = sub_matches
                .values_of_os("afl-tmin args")
                .unwrap_or_default();
            run_afl(args, "afl-tmin", None);
        }
        ("whatsup", Some(sub_matches)) => {
            let args = sub_matches
                .values_of_os("afl-whatsup args")
                .unwrap_or_default();
            run_afl(args, "afl-whatsup", None);
        }
        (subcommand, Some(sub_matches)) => {
            let args = sub_matches.values_of_os("").unwrap_or_default();
            run_cargo(subcommand, args);
        }
        // unreachable due to SubcommandRequiredElseHelp on "afl" subcommand
        (_, None) => unreachable!(),
    }
}

fn clap_app() -> clap::App<'static, 'static> {
    use clap::{
        App,
        AppSettings::{
            AllowExternalSubcommands, AllowLeadingHyphen, DisableHelpFlags, DisableHelpSubcommand,
            DisableVersion, SubcommandRequiredElseHelp,
        },
        Arg, SubCommand,
    };

    App::new("cargo afl")
        .bin_name("cargo")
        .setting(SubcommandRequiredElseHelp)
        .subcommand(
            SubCommand::with_name("afl")
                .version(crate_version!())
                .setting(SubcommandRequiredElseHelp)
                .setting(AllowExternalSubcommands)
                .usage("cargo afl [SUBCOMMAND or Cargo SUBCOMMAND]")
                .after_help(
                    "In addition to the subcommands above, Cargo subcommands are also \
                 supported (see `cargo help` for a list of all Cargo subcommands).",
                )
                .subcommand(
                    SubCommand::with_name("analyze")
                        .about("Invoke afl-analyze")
                        .setting(AllowLeadingHyphen)
                        .setting(DisableHelpSubcommand)
                        .setting(DisableHelpFlags)
                        .setting(DisableVersion)
                        .arg(Arg::with_name("afl-analyze args").multiple(true)),
                )
                .subcommand(
                    SubCommand::with_name("cmin")
                        .about("Invoke afl-cmin")
                        .setting(AllowLeadingHyphen)
                        .setting(DisableHelpSubcommand)
                        .setting(DisableHelpFlags)
                        .setting(DisableVersion)
                        .arg(Arg::with_name("afl-cmin args").multiple(true)),
                )
                .subcommand(
                    SubCommand::with_name("fuzz")
                        .about("Invoke afl-fuzz")
                        .setting(AllowLeadingHyphen)
                        .setting(DisableHelpSubcommand)
                        .setting(DisableHelpFlags)
                        .setting(DisableVersion)
                        .arg(
                            Arg::with_name("max_total_time")
                                .long("max_total_time")
                                .takes_value(true)
                                .help("Maximum amount of time to run the fuzzer"),
                        )
                        .arg(Arg::with_name("afl-fuzz args").multiple(true)),
                )
                .subcommand(
                    SubCommand::with_name("gotcpu")
                        .about("Invoke afl-gotcpu")
                        .setting(AllowLeadingHyphen)
                        .setting(DisableHelpSubcommand)
                        .setting(DisableHelpFlags)
                        .setting(DisableVersion)
                        .arg(Arg::with_name("afl-gotcpu args").multiple(true)),
                )
                .subcommand(
                    SubCommand::with_name("plot")
                        .about("Invoke afl-plot")
                        .setting(AllowLeadingHyphen)
                        .setting(DisableHelpSubcommand)
                        .setting(DisableHelpFlags)
                        .setting(DisableVersion)
                        .arg(Arg::with_name("afl-plot args").multiple(true)),
                )
                .subcommand(
                    SubCommand::with_name("showmap")
                        .about("Invoke afl-showmap")
                        .setting(AllowLeadingHyphen)
                        .setting(DisableHelpSubcommand)
                        .setting(DisableHelpFlags)
                        .setting(DisableVersion)
                        .arg(Arg::with_name("afl-showmap args").multiple(true)),
                )
                .subcommand(
                    SubCommand::with_name("tmin")
                        .about("Invoke afl-tmin")
                        .setting(AllowLeadingHyphen)
                        .setting(DisableHelpSubcommand)
                        .setting(DisableHelpFlags)
                        .setting(DisableVersion)
                        .arg(Arg::with_name("afl-tmin args").multiple(true)),
                )
                .subcommand(
                    SubCommand::with_name("whatsup")
                        .about("Invoke afl-whatsup")
                        .setting(AllowLeadingHyphen)
                        .setting(DisableHelpSubcommand)
                        .setting(DisableHelpFlags)
                        .setting(DisableVersion)
                        .arg(Arg::with_name("afl-whatsup args").multiple(true)),
                ),
        )
}

fn run_timeout_terminate(mut cmd: Command, timeout: Option<u64>) -> Result<ExitStatus, io::Error> {
    let timeout = match timeout {
        Some(timeout) => Duration::from_secs(timeout),
        None => return cmd.status(),
    };
    let start_time = Instant::now();

    let mut child = cmd.spawn()?;
    let pid = child.id();

    let pair = Arc::new((Mutex::new(false), Condvar::new()));
    let (stop_mutex, condvar) = &*pair;
    let thread_handle = {
        let pair = pair.clone();
        thread::spawn(move || -> Result<(), io::Error> {
            // This thread will wait until the child process has exited, or the
            // timeout has elapsed, whichever comes first. If the timeout
            // elapses, and the process is still running, it will send SIGTERM
            // to the child process.

            let (stop_mutex, condvar) = &*pair;
            let mut stop = stop_mutex.lock().unwrap();
            loop {
                let elapsed = Instant::now() - start_time;
                if elapsed >= timeout {
                    break;
                }

                let dur = timeout - elapsed;
                let results = condvar.wait_timeout(stop, dur).unwrap();
                stop = results.0;
                if *stop {
                    // Blocking waitid call on the main thread has returned,
                    // thus the child process has terminated
                    return Ok(());
                }
                if results.1.timed_out() {
                    break;
                }
            }

            // Since the waitid call on the main thread is using WNOWAIT, the
            // child process won't be cleaned up (until after this thread
            // exits and the main thread calls wait) and thus its PID won't be
            // reused by another, unrelated process.
            unsafe {
                let ret = libc::kill(pid as i32, libc::SIGTERM);
                if ret == -1 {
                    Err(io::Error::last_os_error())?
                }
            }

            Ok(())
        })
    };

    unsafe {
        // Block until the child process terminates, but leave it in a waitable
        // state still
        let ret = libc::waitid(
            libc::P_PID,
            pid,
            std::ptr::null_mut(),
            libc::WEXITED | libc::WNOWAIT,
        );
        if ret == -1 {
            Err(io::Error::last_os_error())?
        }
    }
    {
        let mut stop = stop_mutex.lock().unwrap();
        *stop = true;
    }
    // Tell the timeout thread to stop, wake it, and wait for it to exit
    condvar.notify_one();
    thread_handle.join().unwrap()?;

    // Clean up zombie and get exit status (this won't block, because the child
    // process has terminated and is still waitable)
    child.wait()
}

fn run_afl<I, S>(args: I, cmd: &str, timeout: Option<u64>)
where
    I: IntoIterator<Item = S>,
    S: AsRef<OsStr>,
{
    let cmd_path = common::afl_dir().join("bin").join(cmd);
    let mut cmd = Command::new(cmd_path);
    cmd.args(args);
    let status = run_timeout_terminate(cmd, timeout).unwrap();
    process::exit(status.code().unwrap_or(1));
}

fn run_cargo<I, S>(subcommand: &str, args: I)
where
    I: IntoIterator<Item = S>,
    S: AsRef<OsStr>,
{
    let cargo_path = env::var("CARGO").expect("Could not determine `cargo` path");

    // add some flags to sanitizers to make them work with Rust code
    let asan_options = env::var("ASAN_OPTIONS").unwrap_or_default();
    let asan_options = format!(
        "detect_odr_violation=0:abort_on_error=1:symbolize=0:{}",
        asan_options
    );

    let tsan_options = env::var("TSAN_OPTIONS").unwrap_or_default();
    let tsan_options = format!("report_signal_unsafe=0:{}", tsan_options);

    let version_meta = rustc_version::version_meta().unwrap();
    let passes = if version_meta.semver.minor >= 57
        && version_meta.llvm_version.map_or(true, |v| v.major >= 13)
    {
        // New LLVM pass manager is enabled when Rust 1.57+ and LLVM 13+
        // https://github.com/rust-lang/rust/pull/88243
        "sancov-module"
    } else {
        "sancov"
    };

    let mut rustflags = format!(
        "--cfg fuzzing \
         -C debug-assertions \
         -C overflow_checks \
         -C passes={} \
         -C llvm-args=-sanitizer-coverage-level=3 \
         -C llvm-args=-sanitizer-coverage-trace-pc-guard \
         -C llvm-args=-sanitizer-coverage-prune-blocks=0 \
         -C opt-level=3 \
         -C target-cpu=native \
         -C debuginfo=0 \
         -l afl-llvm-rt \
         -L {} ",
        passes,
        common::afl_llvm_rt_dir().display()
    );

    // RUSTFLAGS are not used by rustdoc, instead RUSTDOCFLAGS are used. Since
    // doctests will try to link against afl-llvm-rt, set up RUSTDOCFLAGS to
    // have doctests built the same as other code to avoid issues with doctests.
    let mut rustdocflags = format!(
        "--cfg fuzzing \
         -C debug-assertions \
         -C overflow_checks \
         -C passes={} \
         -C llvm-args=-sanitizer-coverage-level=3 \
         -C llvm-args=-sanitizer-coverage-trace-pc-guard \
         -C llvm-args=-sanitizer-coverage-prune-blocks=0 \
         -C opt-level=3 \
         -C target-cpu=native \
         -C debuginfo=0 \
         -L {} ",
        passes,
        common::afl_llvm_rt_dir().display()
    );

    if cfg!(target_os = "linux") {
        // work around https://github.com/rust-fuzz/afl.rs/issues/141 /
        // https://github.com/rust-lang/rust/issues/53945, can be removed once
        // those are fixed.
        rustflags.push_str("-Clink-arg=-fuse-ld=gold ");
        rustdocflags.push_str("-Clink-arg=-fuse-ld=gold ");
    }

    // add user provided flags
    rustflags.push_str(&env::var("RUSTFLAGS").unwrap_or_default());
    rustdocflags.push_str(&env::var("RUSTDOCFLAGS").unwrap_or_default());

    let status = Command::new(cargo_path)
        .arg(subcommand)
        .args(args)
        .env("RUSTFLAGS", &rustflags)
        .env("RUSTDOCFLAGS", &rustdocflags)
        .env("ASAN_OPTIONS", asan_options)
        .env("TSAN_OPTIONS", tsan_options)
        .status()
        .unwrap();
    process::exit(status.code().unwrap_or(1));
}
<|endoftext|>


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

use cg::{self, WhereClause};
use darling::util::Override;
use quote::Tokens;
use syn::{self, Data};
use synstructure::{Structure, VariantInfo};

pub fn derive(input: syn::DeriveInput) -> Tokens {
    let name = &input.ident;
    let trait_path = parse_quote!(::style_traits::ToCss);
    let (impl_generics, ty_generics, mut where_clause) =
        cg::trait_parts(&input, &trait_path);

    let input_attrs = cg::parse_input_attrs::<CssInputAttrs>(&input);
    let s = Structure::new(&input);

    let match_body = s.each_variant(|variant| {
        derive_variant_arm(variant, &mut where_clause)
    });

    let mut impls = quote! {
        impl #impl_generics ::style_traits::ToCss for #name #ty_generics #where_clause {
            #[allow(unused_variables)]
            #[inline]
            fn to_css<W>(
                &self,
                dest: &mut ::style_traits::CssWriter<W>,
            ) -> ::std::fmt::Result
            where
                W: ::std::fmt::Write
            {
                match *self {
                    #match_body
                }
            }
        }
    };

    if input_attrs.derive_debug {
        impls.append_all(quote! {
            impl #impl_generics ::std::fmt::Debug for #name #ty_generics #where_clause {
                fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
                    ::style_traits::ToCss::to_css(
                        self,
                        &mut ::style_traits::CssWriter::new(f),
                    )
                }
            }
        });
    }

    impls
}

fn derive_variant_arm(
    variant: &VariantInfo,
    where_clause: &mut WhereClause,
) -> Tokens {
    let bindings = variant.bindings();
    let identifier = cg::to_css_identifier(variant.ast().ident.as_ref());
    let ast = variant.ast();
    let variant_attrs = cg::parse_variant_attrs::<CssVariantAttrs>(&ast);
    let separator = if variant_attrs.comma { ", " } else { " " };

    if variant_attrs.dimension {
        assert_eq!(bindings.len(), 1);
        assert!(
            variant_attrs.function.is_none() && variant_attrs.keyword.is_none(),
            "That makes no sense"
        );
    }

    let mut expr = if let Some(keyword) = variant_attrs.keyword {
        assert!(bindings.is_empty());
        let keyword = keyword.to_string();
        quote! {
            ::std::fmt::Write::write_str(dest, #keyword)
        }
    } else if !bindings.is_empty() {
        let mut expr = quote! {};
        if variant_attrs.iterable {
            assert_eq!(bindings.len(), 1);
            let binding = &bindings[0];
            expr = quote! {
                #expr

                for item in #binding.iter() {
                    writer.item(&item)?;
                }
            };
        } else {
            for binding in bindings {
                let attrs = cg::parse_field_attrs::<CssFieldAttrs>(&binding.ast());
                if attrs.skip {
                    continue;
                }
                if !attrs.ignore_bound {
                    where_clause.add_trait_bound(&binding.ast().ty);
                }
                expr = quote! {
                    #expr
                    writer.item(#binding)?;
                };
            }
        }

        quote! {{
            let mut writer = ::style_traits::values::SequenceWriter::new(dest, #separator);
            #expr
            Ok(())
        }}
    } else {
        quote! {
            ::std::fmt::Write::write_str(dest, #identifier)
        }
    };

    if variant_attrs.dimension {
        expr = quote! {
            #expr?;
            ::std::fmt::Write::write_str(dest, #identifier)
        }
    } else if let Some(function) = variant_attrs.function {
        let mut identifier = function.explicit().map_or(identifier, |name| name);
        identifier.push_str("(");
        expr = quote! {
            ::std::fmt::Write::write_str(dest, #identifier)?;
            #expr?;
            ::std::fmt::Write::write_str(dest, ")")
        }
    }
    expr
}

#[darling(attributes(css), default)]
#[derive(Default, FromDeriveInput)]
struct CssInputAttrs {
    derive_debug: bool,
    // Here because structs variants are also their whole type definition.
    function: Option<Override<String>>,
    // Here because structs variants are also their whole type definition.
    comma: bool,
    // Here because structs variants are also their whole type definition.
    iterable: bool,
}

#[darling(attributes(css), default)]
#[derive(Default, FromVariant)]
pub struct CssVariantAttrs {
    pub function: Option<Override<String>>,
    pub iterable: bool,
    pub comma: bool,
    pub dimension: bool,
    pub keyword: Option<String>,
    pub aliases: Option<String>,
}

#[darling(attributes(css), default)]
#[derive(Default, FromField)]
struct CssFieldAttrs {
    ignore_bound: bool,
    skip: bool,
}
<commit_msg>Add some assertions for #[derive(ToCss)] on enums<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use cg::{self, WhereClause};
use darling::util::Override;
use quote::Tokens;
use syn::{self, Data};
use synstructure::{Structure, VariantInfo};

pub fn derive(input: syn::DeriveInput) -> Tokens {
    let name = &input.ident;
    let trait_path = parse_quote!(::style_traits::ToCss);
    let (impl_generics, ty_generics, mut where_clause) =
        cg::trait_parts(&input, &trait_path);

    let input_attrs = cg::parse_input_attrs::<CssInputAttrs>(&input);
    if let Data::Enum(_) = input.data {
        assert!(input_attrs.function.is_none(), "#[css(function)] is not allowed on enums");
        assert!(!input_attrs.comma, "#[css(comma)] is not allowed on enums");
        assert!(!input_attrs.iterable, "#[css(iterable)] is not allowed on enums");
    }
    let s = Structure::new(&input);

    let match_body = s.each_variant(|variant| {
        derive_variant_arm(variant, &mut where_clause)
    });

    let mut impls = quote! {
        impl #impl_generics ::style_traits::ToCss for #name #ty_generics #where_clause {
            #[allow(unused_variables)]
            #[inline]
            fn to_css<W>(
                &self,
                dest: &mut ::style_traits::CssWriter<W>,
            ) -> ::std::fmt::Result
            where
                W: ::std::fmt::Write
            {
                match *self {
                    #match_body
                }
            }
        }
    };

    if input_attrs.derive_debug {
        impls.append_all(quote! {
            impl #impl_generics ::std::fmt::Debug for #name #ty_generics #where_clause {
                fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
                    ::style_traits::ToCss::to_css(
                        self,
                        &mut ::style_traits::CssWriter::new(f),
                    )
                }
            }
        });
    }

    impls
}

fn derive_variant_arm(
    variant: &VariantInfo,
    where_clause: &mut WhereClause,
) -> Tokens {
    let bindings = variant.bindings();
    let identifier = cg::to_css_identifier(variant.ast().ident.as_ref());
    let ast = variant.ast();
    let variant_attrs = cg::parse_variant_attrs::<CssVariantAttrs>(&ast);
    let separator = if variant_attrs.comma { ", " } else { " " };

    if variant_attrs.dimension {
        assert_eq!(bindings.len(), 1);
        assert!(
            variant_attrs.function.is_none() && variant_attrs.keyword.is_none(),
            "That makes no sense"
        );
    }

    let mut expr = if let Some(keyword) = variant_attrs.keyword {
        assert!(bindings.is_empty());
        let keyword = keyword.to_string();
        quote! {
            ::std::fmt::Write::write_str(dest, #keyword)
        }
    } else if !bindings.is_empty() {
        let mut expr = quote! {};
        if variant_attrs.iterable {
            assert_eq!(bindings.len(), 1);
            let binding = &bindings[0];
            expr = quote! {
                #expr

                for item in #binding.iter() {
                    writer.item(&item)?;
                }
            };
        } else {
            for binding in bindings {
                let attrs = cg::parse_field_attrs::<CssFieldAttrs>(&binding.ast());
                if attrs.skip {
                    continue;
                }
                if !attrs.ignore_bound {
                    where_clause.add_trait_bound(&binding.ast().ty);
                }
                expr = quote! {
                    #expr
                    writer.item(#binding)?;
                };
            }
        }

        quote! {{
            let mut writer = ::style_traits::values::SequenceWriter::new(dest, #separator);
            #expr
            Ok(())
        }}
    } else {
        quote! {
            ::std::fmt::Write::write_str(dest, #identifier)
        }
    };

    if variant_attrs.dimension {
        expr = quote! {
            #expr?;
            ::std::fmt::Write::write_str(dest, #identifier)
        }
    } else if let Some(function) = variant_attrs.function {
        let mut identifier = function.explicit().map_or(identifier, |name| name);
        identifier.push_str("(");
        expr = quote! {
            ::std::fmt::Write::write_str(dest, #identifier)?;
            #expr?;
            ::std::fmt::Write::write_str(dest, ")")
        }
    }
    expr
}

#[darling(attributes(css), default)]
#[derive(Default, FromDeriveInput)]
struct CssInputAttrs {
    derive_debug: bool,
    // Here because structs variants are also their whole type definition.
    function: Option<Override<String>>,
    // Here because structs variants are also their whole type definition.
    comma: bool,
    // Here because structs variants are also their whole type definition.
    iterable: bool,
}

#[darling(attributes(css), default)]
#[derive(Default, FromVariant)]
pub struct CssVariantAttrs {
    pub function: Option<Override<String>>,
    pub iterable: bool,
    pub comma: bool,
    pub dimension: bool,
    pub keyword: Option<String>,
    pub aliases: Option<String>,
}

#[darling(attributes(css), default)]
#[derive(Default, FromField)]
struct CssFieldAttrs {
    ignore_bound: bool,
    skip: bool,
}
<|endoftext|>


<commit_before>mod is_initialized_is_always_true;
<commit_msg>Fix warning<commit_after>#![cfg(test)]

mod is_initialized_is_always_true;
<|endoftext|>