<commit_before>//! Checks for useless borrowed references.
//!
//! This lint is **warn** by default

use crate::utils::{snippet_with_applicability, span_lint_and_then};
use if_chain::if_chain;
use rustc_errors::Applicability;
use rustc_hir::{BindingAnnotation, Mutability, Node, Pat, PatKind};
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_lint_pass, declare_tool_lint};

declare_clippy_lint! {
    /// **What it does:** Checks for useless borrowed references.
    ///
    /// **Why is this bad?** It is mostly useless and make the code look more
    /// complex than it
    /// actually is.
    ///
    /// **Known problems:** It seems that the `&ref` pattern is sometimes useful.
    /// For instance in the following snippet:
    /// ```rust,ignore
    /// enum Animal {
    ///     Cat(u64),
    ///     Dog(u64),
    /// }
    ///
    /// fn foo(a: &Animal, b: &Animal) {
    ///     match (a, b) {
    ///         (&Animal::Cat(v), k) | (k, &Animal::Cat(v)) => (), // lifetime mismatch error
    ///         (&Animal::Dog(ref c), &Animal::Dog(_)) => ()
    ///     }
    /// }
    /// ```
    /// There is a lifetime mismatch error for `k` (indeed a and b have distinct
    /// lifetime).
    /// This can be fixed by using the `&ref` pattern.
    /// However, the code can also be fixed by much cleaner ways
    ///
    /// **Example:**
    /// ```rust
    /// let mut v = Vec::<String>::new();
    /// let _ = v.iter_mut().filter(|&ref a| a.is_empty());
    /// ```
    /// This closure takes a reference on something that has been matched as a
    /// reference and
    /// de-referenced.
    /// As such, it could just be |a| a.is_empty()
    pub NEEDLESS_BORROWED_REFERENCE,
    complexity,
    "taking a needless borrowed reference"
}

declare_lint_pass!(NeedlessBorrowedRef => [NEEDLESS_BORROWED_REFERENCE]);

impl<'tcx> LateLintPass<'tcx> for NeedlessBorrowedRef {
    fn check_pat(&mut self, cx: &LateContext<'tcx>, pat: &'tcx Pat<'_>) {
        if pat.span.from_expansion() {
            // OK, simple enough, lints doesn't check in macro.
            return;
        }

        if_chain! {
            // Only lint immutable refs, because `&mut ref T` may be useful.
            if let PatKind::Ref(ref sub_pat, Mutability::Not) = pat.kind;

            // Check sub_pat got a `ref` keyword (excluding `ref mut`).
            if let PatKind::Binding(BindingAnnotation::Ref, .., spanned_name, _) = sub_pat.kind;
            let parent_id = cx.tcx.hir().get_parent_node(pat.hir_id);
            if let Some(parent_node) = cx.tcx.hir().find(parent_id);
            then {
                // do not recurse within patterns, as they may have other references
                // XXXManishearth we can relax this constraint if we only check patterns
                // with a single ref pattern inside them
                if let Node::Pat(_) = parent_node {
                    return;
                }
                let mut applicability = Applicability::MachineApplicable;
                span_lint_and_then(cx, NEEDLESS_BORROWED_REFERENCE, pat.span,
                                   "this pattern takes a reference on something that is being de-referenced",
                                   |diag| {
                                       let hint = snippet_with_applicability(cx, spanned_name.span, "..", &mut applicability).into_owned();
                                       diag.span_suggestion(
                                           pat.span,
                                           "try removing the `&ref` part and just keep",
                                           hint,
                                           applicability,
                                       );
                                   });
            }
        }
    }
}
<commit_msg>Improve needless_borrowed_ref docs<commit_after>use crate::utils::{snippet_with_applicability, span_lint_and_then};
use if_chain::if_chain;
use rustc_errors::Applicability;
use rustc_hir::{BindingAnnotation, Mutability, Node, Pat, PatKind};
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_lint_pass, declare_tool_lint};

declare_clippy_lint! {
    /// **What it does:** Checks for bindings that destructure a reference and borrow the inner
    /// value with `&ref`.
    ///
    /// **Why is this bad?** This pattern has no effect in almost all cases.
    ///
    /// **Known problems:** In some cases, `&ref` is needed to avoid a lifetime mismatch error.
    /// Example:
    /// ```rust
    /// fn foo(a: &Option<String>, b: &Option<String>) {
    ///     match (a, b) {
    ///         (None, &ref c) | (&ref c, None) => (),
    ///         (&Some(ref c), _) => (),
    ///     };
    /// }
    /// ```
    ///
    /// **Example:**
    /// Bad:
    /// ```rust
    /// let mut v = Vec::<String>::new();
    /// let _ = v.iter_mut().filter(|&ref a| a.is_empty());
    /// ```
    ///
    /// Good:
    /// ```rust
    /// let mut v = Vec::<String>::new();
    /// let _ = v.iter_mut().filter(|a| a.is_empty());
    /// ```
    pub NEEDLESS_BORROWED_REFERENCE,
    complexity,
    "destructuring a reference and borrowing the inner value"
}

declare_lint_pass!(NeedlessBorrowedRef => [NEEDLESS_BORROWED_REFERENCE]);

impl<'tcx> LateLintPass<'tcx> for NeedlessBorrowedRef {
    fn check_pat(&mut self, cx: &LateContext<'tcx>, pat: &'tcx Pat<'_>) {
        if pat.span.from_expansion() {
            // OK, simple enough, lints doesn't check in macro.
            return;
        }

        if_chain! {
            // Only lint immutable refs, because `&mut ref T` may be useful.
            if let PatKind::Ref(ref sub_pat, Mutability::Not) = pat.kind;

            // Check sub_pat got a `ref` keyword (excluding `ref mut`).
            if let PatKind::Binding(BindingAnnotation::Ref, .., spanned_name, _) = sub_pat.kind;
            let parent_id = cx.tcx.hir().get_parent_node(pat.hir_id);
            if let Some(parent_node) = cx.tcx.hir().find(parent_id);
            then {
                // do not recurse within patterns, as they may have other references
                // XXXManishearth we can relax this constraint if we only check patterns
                // with a single ref pattern inside them
                if let Node::Pat(_) = parent_node {
                    return;
                }
                let mut applicability = Applicability::MachineApplicable;
                span_lint_and_then(cx, NEEDLESS_BORROWED_REFERENCE, pat.span,
                                   "this pattern takes a reference on something that is being de-referenced",
                                   |diag| {
                                       let hint = snippet_with_applicability(cx, spanned_name.span, "..", &mut applicability).into_owned();
                                       diag.span_suggestion(
                                           pat.span,
                                           "try removing the `&ref` part and just keep",
                                           hint,
                                           applicability,
                                       );
                                   });
            }
        }
    }
}
<|endoftext|>


<commit_before>// A Regex Operation
//
// Used on the stack to distinguish an operator
// (defined as an opcode), from a parsed
// regular expression.

pub enum OpCode {
  OpConcatenation,
  OpAlternation,
  OpKleine,
  OpZeroOrOne,
  OpOneOrMore,
  OpLineStart,
  OpLineEnd,
  OpLeftParen,
  OpRightParen
}

// Regexp Literal
//
struct RegexpLiteral {
  value: ~str
}

impl RegexpLiteral {
  pub fn new(s: &str) -> RegexpLiteral {
    RegexpLiteral { value: s.clone().to_owned() }
  }
}

struct Regexp {
  op: OpCode,
  state0: Option<~RegexpStack>,
  state1: Option<~RegexpStack>
}

impl Regexp {
  fn new(op: OpCode, state0: Option<~RegexpStack>, state1:
         Option<~RegexpStack>) -> Regexp {
    Regexp { op: op, state0: state0, state1: state1 }
  }
}

// Representations of regexp on
// the stack
enum RegexpStack {
  ReOp(OpCode),
  ReLiteral(RegexpLiteral),
  ReExpression(Regexp)
}

//
pub struct RegexpState {
  priv stack: ~[RegexpStack],
  priv nparen: uint
}

impl RegexpState {
  pub fn new() -> RegexpState {
    RegexpState { stack: ~[], nparen: 0 }
  }
}

impl RegexpState {
  pub fn pushLiteral(&mut self, s: &str) -> () {
    match self.stack.pop_opt() {
      Some(ReLiteral(l)) => {
        self.stack.push(ReLiteral(RegexpLiteral::new(l.value + s)));
      }
      Some(r) => {
        self.stack.push(r);
        self.stack.push(ReLiteral(RegexpLiteral::new(s)));
      }
      None => {
        self.stack.push(ReLiteral(RegexpLiteral::new(s)));
      }
    }
  }
  pub fn pushOperation(&mut self, op: OpCode) -> () {
    self.stack.push(ReOp(op));
  }
  pub fn pushExpression(&mut self, r: Regexp) -> () {
    self.stack.push(ReExpression(r));
  }
}

impl RegexpState {
  pub fn pushAlternation(&mut self) -> () {
    self.pushOperation(OpAlternation);
  }
  pub fn pushLeftParen(&mut self) -> () {
    self.pushOperation(OpLeftParen);
  }
}

impl RegexpState {
  pub fn doAlternation(&mut self) -> Result<bool, &'static str> {
    // try to pop off two items from the stack.
    // these should be branches that you can take.
    //     -> state0
    // s |
    //     -> state1
    //
    // with alternation occuring sequentially, we can
    // make an equivalent model by having state0 or state1
    // also be a regexp with a alternation operand applied to it.
    //     -> state0
    // s |              -> 'state0
    //     -> state1  |
    //                  -> 'state1
    //
    // the operand should be pushed onto the stack before we start parsing
    // the right hand side of the alternation, so the stack should look something
    // like this before doAlternation() is called:
    //
    // state0
    // OpCode(OpAlternation)
    // state1
    let branch1 = match self.stack.pop_opt() {
      Some(s) => s,
      None => return Err("Nothing to alternate")
    };
    match self.stack.pop_opt() {
      Some(ReOp(OpAlternation)) => { },
      _ => return Err("No alternation operand on the stack, but expected one")
    };
    let branch2 = match self.stack.pop_opt() {
      Some(s) => s,
      None => return Err("Nothing to alternate")
    };
    let r = Regexp::new(OpAlternation, Some(~branch1), Some(~branch2));

    self.pushExpression(r);

    Ok(true)
  }
  pub fn doConcatenation(&mut self) -> Result<bool, &'static str> {
    // try to take two items off the stack to
    // concatenate.
    //
    // state0 -> state1
    let branch1 = match self.stack.pop_opt() {
      Some(s) => s,
      None => return Err("Nothing to concatenate")
    };
    let branch2 = match self.stack.pop_opt() {
      Some(s) => s,
      None => return Err("Nothing to concatenate")
    };
    let r = Regexp::new(OpConcatenation, Some(~branch1), Some(~branch2));

    self.pushExpression(r);

    Ok(true)
  }
  pub fn tryConcatenation(&mut self) {
    if (self.stack.len() > 1) {
      self.doConcatenation();
    }
  }
  pub fn doLeftParen(&mut self) {

  }
  pub fn doRightParen(&mut self) {

  }
  pub fn doKleine(&mut self) -> Result<bool, &'static str> {
    match self.stack.pop_opt() {
      Some(r) => {
        let mut r = r;
        // check to see if the expr on the top
        // of the stack has some repition
        // op applied to it.
        let opcode = match &r {
          &ReExpression(ref e) => {
            match e.op {
              OpKleine      => Some(OpKleine),
              OpOneOrMore   => Some(OpOneOrMore),
              OpZeroOrMore  => Some(OpZeroOrMore)
            }
          },
          _ => {
            None
          }
        };
        // if we found that the expr already had
        // a repition op, try to condense (by collapsing
        // overlapping cases...i.e, *?, *+, **, can really
        // just be *).
        //
        // note: in the match block below,
        // the (_) case will cover cases
        // Some(OpOneOrMore), Some(OpZeroOrMore)
        //
        // otherwise, we can make a new expression.
        match opcode {
          Some(OpKleine) => {
            self.stack.push(r);
          }
          None => {
            let expr = Regexp::new(OpKleine, Some(~r), None);
            self.pushExpression(expr);
          }
          _ => {
            match r {
              ReExpression(ref mut e) => {
                e.op = OpKleine;
              }
              _ => { }
            }
            self.stack.push(r);
          }
        }
      }
      _ => {
        return Err("'*' not applied to any state.")
      }
    }
    Ok(true)
  }
}

impl RegexpState {
  pub fn trace(&mut self) {
    println("--STACK--");
    for e in self.stack.iter() {
      println(fmt!("%?", e));
    }
  }
}
<commit_msg>added comments<commit_after>// A Regex Operation
//
// Used on the stack to distinguish an operator
// (defined as an opcode), from a parsed
// regular expression.

pub enum OpCode {
  OpConcatenation,
  OpAlternation,
  OpKleine,
  OpZeroOrOne,
  OpOneOrMore,
  OpLineStart,
  OpLineEnd,
  OpLeftParen,
  OpRightParen
}

// Regexp Literal
//
struct RegexpLiteral {
  value: ~str
}

impl RegexpLiteral {
  pub fn new(s: &str) -> RegexpLiteral {
    RegexpLiteral { value: s.clone().to_owned() }
  }
}

struct Regexp {
  op: OpCode,
  state0: Option<~RegexpStack>,
  state1: Option<~RegexpStack>
}

impl Regexp {
  fn new(op: OpCode, state0: Option<~RegexpStack>, state1:
         Option<~RegexpStack>) -> Regexp {
    Regexp { op: op, state0: state0, state1: state1 }
  }
}

// Representations of regexp on
// the stack
enum RegexpStack {
  ReOp(OpCode),
  ReLiteral(RegexpLiteral),
  ReExpression(Regexp)
}

//
pub struct RegexpState {
  priv stack: ~[RegexpStack],
  priv nparen: uint
}

impl RegexpState {
  pub fn new() -> RegexpState {
    RegexpState { stack: ~[], nparen: 0 }
  }
}

impl RegexpState {
  pub fn pushLiteral(&mut self, s: &str) -> () {
    match self.stack.pop_opt() {
      Some(ReLiteral(l)) => {
        self.stack.push(ReLiteral(RegexpLiteral::new(l.value + s)));
      }
      Some(r) => {
        self.stack.push(r);
        self.stack.push(ReLiteral(RegexpLiteral::new(s)));
      }
      None => {
        self.stack.push(ReLiteral(RegexpLiteral::new(s)));
      }
    }
  }
  pub fn pushOperation(&mut self, op: OpCode) -> () {
    self.stack.push(ReOp(op));
  }
  pub fn pushExpression(&mut self, r: Regexp) -> () {
    self.stack.push(ReExpression(r));
  }
}

impl RegexpState {
  pub fn pushAlternation(&mut self) -> () {
    self.pushOperation(OpAlternation);
  }
  pub fn pushLeftParen(&mut self) -> () {
    self.pushOperation(OpLeftParen);
  }
}

impl RegexpState {
  pub fn doAlternation(&mut self) -> Result<bool, &'static str> {
    // try to pop off two items from the stack.
    // these should be branches that you can take.
    //     -> state0
    // s |
    //     -> state1
    //
    // with alternation occuring sequentially, we can
    // make an equivalent model by having state0 or state1
    // also be a regexp with a alternation operand applied to it.
    //     -> state0
    // s |              -> 'state0
    //     -> state1  |
    //                  -> 'state1
    //
    // the operand should be pushed onto the stack before we start parsing
    // the right hand side of the alternation, so the stack should look something
    // like this before doAlternation() is called:
    //
    // state0
    // OpCode(OpAlternation)
    // state1
    let branch1 = match self.stack.pop_opt() {
      Some(s) => s,
      None => return Err("Nothing to alternate")
    };
    match self.stack.pop_opt() {
      Some(ReOp(OpAlternation)) => { },
      _ => return Err("No alternation operand on the stack, but expected one")
    };
    let branch2 = match self.stack.pop_opt() {
      Some(s) => s,
      None => return Err("Nothing to alternate")
    };
    let r = Regexp::new(OpAlternation, Some(~branch1), Some(~branch2));

    self.pushExpression(r);

    Ok(true)
  }
  pub fn doConcatenation(&mut self) -> Result<bool, &'static str> {
    // try to take two items off the stack to
    // concatenate.
    //
    // state0 -> state1
    let branch1 = match self.stack.pop_opt() {
      Some(s) => s,
      None => return Err("Nothing to concatenate")
    };
    let branch2 = match self.stack.pop_opt() {
      Some(s) => s,
      None => return Err("Nothing to concatenate")
    };
    let r = Regexp::new(OpConcatenation, Some(~branch1), Some(~branch2));

    self.pushExpression(r);

    Ok(true)
  }
  pub fn tryConcatenation(&mut self) {
    // tries to perform a concatenation. this simply
    // means performing a concatenation operation
    // if two or more items are on the stack.
    if (self.stack.len() > 1) {
      self.doConcatenation();
    }
  }
  pub fn doLeftParen(&mut self) {

  }
  pub fn doRightParen(&mut self) {

  }
  pub fn doKleine(&mut self) -> Result<bool, &'static str> {
    match self.stack.pop_opt() {
      Some(r) => {
        let mut r = r;
        // check to see if the expr on the top
        // of the stack has some repition
        // op applied to it.
        let opcode = match &r {
          &ReExpression(ref e) => {
            match e.op {
              OpKleine      => Some(OpKleine),
              OpOneOrMore   => Some(OpOneOrMore),
              OpZeroOrMore  => Some(OpZeroOrMore)
            }
          },
          _ => {
            None
          }
        };
        // if we found that the expr already had
        // a repition op, try to condense (by collapsing
        // overlapping cases...i.e, *?, *+, **, can really
        // just be *).
        //
        // note: in the match block below,
        // the (_) case will cover cases
        // Some(OpOneOrMore), Some(OpZeroOrMore)
        //
        // otherwise, we can make a new expression.
        match opcode {
          Some(OpKleine) => {
            self.stack.push(r);
          }
          None => {
            let expr = Regexp::new(OpKleine, Some(~r), None);
            self.pushExpression(expr);
          }
          _ => {
            match r {
              ReExpression(ref mut e) => {
                e.op = OpKleine;
              }
              _ => { }
            }
            self.stack.push(r);
          }
        }
      }
      _ => {
        return Err("'*' not applied to any state.")
      }
    }
    Ok(true)
  }
}

impl RegexpState {
  pub fn trace(&mut self) {
    println("--STACK--");
    for e in self.stack.iter() {
      println(fmt!("%?", e));
    }
  }
}
<|endoftext|>


<commit_before>/// Parser

use std::char;

use charclass::CharClass;
use charclass::ascii;

/// A regular expression AST.
#[deriving(ToStr)]
pub enum Expr {
    Empty,
    Range(char, char),
    Concatenate(~[Expr]),
    Alternate(~[Expr]),
    Repeat(~Expr, u32, Option<u32>, Greedy),
    Capture(~Expr)
}

#[deriving(ToStr)]
pub enum Greedy {
    NonGreedy,
    Greedy
}

///
/// The maximum number of repetitions.  Any number larger than this will
/// cause a syntax error.  By honoring this limit, we prevent integer
/// overflow bugs in the library.
///
/// The value (100000) is taken from Ruby.
///
static REPEAT_MAX: u32 = 100000;

/// Parse a regular expression into an AST.  Fails on invalid syntax.
pub fn parse(input: &str) -> Expr {
    let mut s = State::new(input);
    let e = p_alternate(&mut s);
    if s.has_input() {
        // p_alternate() only terminates on an empty string or an extra
        // paren.  Since the string isn't empty, we infer the latter.
        fail!("unbalanced parenthesis")
    } else {
        e
    }
}

/// The parser state.  This tracks the position in the input string.
#[deriving(Clone)]
struct State<'a> {
    input: &'a str,
    prev: Option<&'a str>  // See `State::retreat`
}

impl<'a> State<'a> {
    fn new<'a>(input: &'a str) -> State<'a> {
        State {
            input: input,
            prev: None
        }
    }

    /// Consume and return the next character in the input, returning
    /// `None` if empty.
    fn advance(&mut self) -> Option<char> {
        self.prev = Some(self.input);
        if self.has_input() {
            let (c, input_) = self.input.slice_shift_char();
            self.input = input_;
            Some(c)
        } else {
            None
        }
    }

    /// Push the previously read character back onto the input.  This
    /// can only be called immediately after `advance`.
    fn retreat(&mut self) {
        self.input = self.prev.expect("nowhere to retreat");
        self.prev = None;
    }

    /// Return `true` if there is input remaining.
    fn has_input(&self) -> bool {
        self.input.len() > 0
    }
}

///
/// Parse alternation, e.g. `ducks|geese|swans`.
///
/// An alternation consists of zero or more concatenations, separated by
/// vertical bars `|`.
///
fn p_alternate(s: &mut State) -> Expr {
    let mut items: ~[Expr] = ~[];

    loop {
        items.push(p_concatenate(s));
        match s.advance() {
            Some(c) => {
                match c {
                    ')' => { s.retreat(); break },
                    '|' => continue,
                    _ => fail!("something bad happened; it's really bad")
                }
            },
            None => break
        }
    }

    match items {
        [] => Empty,
        [e] => e,
        _ => Alternate(items)
    }
}

/// Parse concatenation, e.g. `abc`.
fn p_concatenate(s: &mut State) -> Expr {
    let mut items: ~[Expr] = ~[];

    loop {
        match s.advance() {
            Some(c) => match c {
                '|' | ')' => { s.retreat(); break },
                '(' => push_ignore_empty(&mut items, p_group(s)),
                '.' => items.push(Range('\0', char::MAX)),
                '\\' => items.push(cc_to_expr(p_escape(s))),
                '[' => items.push(cc_to_expr(p_charclass(s))),
                '?' => {
                    let e = pop_expr(&mut items);
                    items.push(match e {
                        Repeat(_, _, _, NonGreedy) => fail!("multiple repeat"),
                        Repeat(inner, min, max, Greedy) =>
                            Repeat(inner, min, max, NonGreedy),
                        _ => Repeat(~e, 0, Some(1), Greedy)
                    });
                },
                '+' => add_repeat(&mut items, 1, None),
                '*' => add_repeat(&mut items, 0, None),
                '{' => {
                    let (min, max) = p_repetition(s);
                    add_repeat(&mut items, min, max);
                },
                _ => items.push(Range(c, c))
            },
            None => break
        }
    }

    match items {
        [] => Empty,
        [e] => e,
        _ => Concatenate(items)
    }
}

#[inline]
fn push_ignore_empty(items: &mut ~[Expr], e: Expr) {
    match e {
        Empty => {},
        _ => items.push(e)
    }
}

#[inline]
fn pop_expr(items: &mut ~[Expr]) -> Expr {
    items.pop_opt().expect("nothing to repeat")
}

#[inline]
fn add_repeat(items: &mut ~[Expr], min: u32, max: Option<u32>) {
    let e = pop_expr(items);
    items.push(match e {
        Repeat(..) => fail!("multiple repeat"),
        _ => Repeat(~e, min, max, Greedy)
    })
}

///
/// Parse a counted repetition (e.g. `a{2,3}`), sans the opening brace.
///
/// The following syntaxes are accepted:
///
/// * `{N}` – match exactly N repetitions;
/// * `{M,}` – at least M;
/// * `{,N}` – at most N;
/// * `{M,N}` – from M to N inclusive;
/// * `{,}` – zero or more (synonymous with `*`).
///
fn p_repetition(s: &mut State) -> (u32, Option<u32>) {
    let min = p_number(s);
    match s.advance() {
        Some(',') => {
            let max = p_number(s);
            match s.advance() {
                // {} or {M,} or {,N} or {M,N}
                Some('}') => {
                    let min_ = min.unwrap_or(0);
                    if check_repeat(min_, max) {
                        (min_, max)
                    } else {
                        fail!("bad repeat interval")
                    }
                },
                _ => fail!("invalid repeat")
            }
        },
        Some('}') => match min {
            // {N}
            Some(n) => (n, Some(n)),
            _ => fail!("invalid repeat")
        },
        _ => fail!("invalid repeat")
    }
}

#[inline]
fn check_repeat(min: u32, max: Option<u32>) -> bool {
    match max {
        Some(max_) => min <= max_,
        None => true
    }
}

///
/// Parse a non-negative integer, and return it as a `u32`.
///
/// This returns `None` if no number could be parsed, but fails directly
/// if the number is greater than `REPEAT_MAX`.
///
fn p_number(s: &mut State) -> Option<u32> {
    let mut acc = None;
    loop {
        match s.advance() {
            Some(c) if '0' <= c && c <= '9' => {
                let digit = c as u32 - '0' as u32;
                acc = Some(match acc {
                    Some(n) => {
                        let acc_ = 10 * n + digit;
                        if acc_ <= REPEAT_MAX {
                            acc_
                        } else {
                            fail!(format!("repeat must be <= {}", REPEAT_MAX))
                        }
                    },
                    None => digit
                });
            },
            _ => { s.retreat(); break }
        }
    }
    acc
}

/// Parse a group (e.g. `(hello)`), sans the opening parenthesis.
fn p_group(s: &mut State) -> Expr {
    let result = match s.advance() {
        Some('?') => match s.advance() {
            Some(c) => match c {
                ':' => p_alternate(s),
                '#' => p_comment(s),
                _ => fail!(format!("unknown extension: ?{}", c))
            },
            None => fail!("unexpected end of pattern")
        },
        _ => { s.retreat(); Capture(~p_alternate(s)) }
    };

    // Match the closing paren
    match s.advance() {
        Some(')') => result,
        _ => fail!("mismatched parenthesis")
    }
}

/// Consume all input up to the first closing parenthesis, and return
/// `Empty`.
fn p_comment(s: &mut State) -> Expr {
    loop {
        match s.advance() {
            Some(c) if c != ')' => continue,
            _ => { s.retreat(); break }
        }
    }
    Empty
}

/// Parse an escape sequence (e.g. `\d`), sans the leading backslash.
fn p_escape(s: &mut State) -> CharClass {
    match s.advance() {
        Some(c) => match c {
            'n' => CharClass::from_char('\n'),
            'r' => CharClass::from_char('\r'),
            't' => CharClass::from_char('\t'),

            'd' => ascii::digit,
            's' => ascii::space,
            'w' => ascii::word,

            'D' => ascii::digit.negate(),
            'S' => ascii::space.negate(),
            'W' => ascii::word.negate(),

            'x' => p_hex_escape(s, 2),
            'u' => p_hex_escape(s, 4),
            'U' => p_hex_escape(s, 8),

            _ if ascii::punct.includes(c) => CharClass::from_char(c),

            _ => fail!("invalid escape")
        },
        None => fail!("invalid escape")
    }
}

fn p_hex_escape(s: &mut State, n_digits: uint) -> CharClass {
    let mut acc = 0u32;
    for _ in range(0, n_digits) {
        acc = 16 * acc + match s.advance() {
            Some(c) => c.to_digit(16).expect("invalid escape") as u32,
            _ => fail!("invalid escape")
        };
    }
    let c = char::from_u32(acc).expect("character out of range");
    CharClass::from_char(c)
}

/// Parse a character class (e.g. `[a-z]`), sans the opening bracket.
fn p_charclass(s: &mut State) -> CharClass {
    let mut classes: ~[CharClass] = ~[];

    let negate = match s.advance() {
        Some('^') => true,
        _ => { s.retreat(); false }
    };

    loop {
        match s.advance() {
            Some(c) => match c {
                ']' => break,
                '-' => match p_charclass_token(s) {
                    Some(cc_hi) => match classes.pop_opt() {
                        Some(cc_lo) => {
                            // [a-z]
                            let lo = cc_lo.to_char().expect("bad character range");
                            let hi = cc_hi.to_char().expect("bad character range");
                            classes.push(CharClass::from_range(lo, hi));
                        },
                        None => classes.push(cc_hi)  // [-z]
                    },
                    None => classes.push(CharClass::from_char('-'))  // [a-]
                },
                _ => {
                    s.retreat();
                    classes.push(p_charclass_token(s).expect("invalid char class"));
                }
            },
            None => fail!("unexpected end of char class")
        }
    }

    let cc = CharClass::combine(classes);
    if negate {
        cc.negate()
    } else {
        cc
    }
}

fn p_charclass_token(s: &mut State) -> Option<CharClass> {
    match s.advance() {
        Some(c) => match c {
            ']' => { s.retreat(); None },
            '[' => Some(p_charclass(s)),
            '\\' => Some(p_escape(s)),
            _ => Some(CharClass::from_char(c))
        },
        None => None
    }
}

/// Reify a character class as an `Expr`.
fn cc_to_expr(cc: CharClass) -> Expr {
    Alternate(cc.ranges().iter().map(|&(lo, hi)| Range(lo, hi)).collect())
}
<commit_msg>Cosmetic fixes<commit_after>/// Parser

use std::char;

use charclass::CharClass;
use charclass::ascii;

/// A regular expression AST.
#[deriving(ToStr)]
pub enum Expr {
    Empty,
    Range(char, char),
    Concatenate(~[Expr]),
    Alternate(~[Expr]),
    Repeat(~Expr, u32, Option<u32>, Greedy),
    Capture(~Expr)
}

#[deriving(ToStr)]
pub enum Greedy {
    NonGreedy,
    Greedy
}

///
/// The maximum number of repetitions.  Any number larger than this will
/// cause a syntax error.  By honoring this limit, we prevent integer
/// overflow bugs in the library.
///
/// The value (100000) is taken from Ruby.
///
static REPEAT_MAX: u32 = 100000;

/// Parse a regular expression into an AST.  Fails on invalid syntax.
pub fn parse(input: &str) -> Expr {
    let mut s = State::new(input);
    let e = p_alternate(&mut s);
    if s.has_input() {
        // p_alternate() only terminates on an empty string or an extra
        // paren.  Since the string isn't empty, we infer the latter.
        fail!("unbalanced parenthesis")
    } else {
        e
    }
}

/// The parser state.  This tracks the position in the input string.
#[deriving(Clone)]
struct State<'a> {
    input: &'a str,
    prev: Option<&'a str>  // See `State::retreat`
}

impl<'a> State<'a> {
    fn new<'a>(input: &'a str) -> State<'a> {
        State {
            input: input,
            prev: None
        }
    }

    /// Consume and return the next character in the input, returning
    /// `None` if empty.
    fn advance(&mut self) -> Option<char> {
        self.prev = Some(self.input);
        if self.has_input() {
            let (c, input_) = self.input.slice_shift_char();
            self.input = input_;
            Some(c)
        } else {
            None
        }
    }

    /// Push the previously read character back onto the input.  This
    /// can only be called immediately after `advance`.
    fn retreat(&mut self) {
        self.input = self.prev.expect("nowhere to retreat");
        self.prev = None;
    }

    /// Return `true` if there is input remaining.
    fn has_input(&self) -> bool {
        self.input.len() > 0
    }
}

///
/// Parse alternation, e.g. `ducks|geese|swans`.
///
/// An alternation consists of zero or more concatenations, separated by
/// vertical bars `|`.
///
fn p_alternate(s: &mut State) -> Expr {
    let mut items: ~[Expr] = ~[];

    loop {
        items.push(p_concatenate(s));
        match s.advance() {
            Some(c) => {
                match c {
                    ')' => { s.retreat(); break },
                    '|' => continue,
                    _ => fail!("something bad happened; it's really bad")
                }
            },
            None => break
        }
    }

    match items {
        [] => Empty,
        [e] => e,
        _ => Alternate(items)
    }
}

/// Parse concatenation, e.g. `abc`.
fn p_concatenate(s: &mut State) -> Expr {
    let mut items: ~[Expr] = ~[];

    loop {
        match s.advance() {
            Some(c) => match c {
                '|' | ')' => { s.retreat(); break },
                '(' => push_ignore_empty(&mut items, p_group(s)),
                '.' => items.push(Range('\0', char::MAX)),
                '\\' => items.push(cc_to_expr(p_escape(s))),
                '[' => items.push(cc_to_expr(p_charclass(s))),
                '?' => {
                    let e = pop_expr(&mut items);
                    items.push(match e {
                        Repeat(_, _, _, NonGreedy) => fail!("multiple repeat"),
                        Repeat(inner, min, max, Greedy) =>
                            Repeat(inner, min, max, NonGreedy),
                        _ => Repeat(~e, 0, Some(1), Greedy)
                    });
                },
                '+' => add_repeat(&mut items, 1, None),
                '*' => add_repeat(&mut items, 0, None),
                '{' => {
                    let (min, max) = p_repetition(s);
                    add_repeat(&mut items, min, max);
                },
                _ => items.push(Range(c, c))
            },
            None => break
        }
    }

    match items {
        [] => Empty,
        [e] => e,
        _ => Concatenate(items)
    }
}

#[inline]
fn push_ignore_empty(items: &mut ~[Expr], e: Expr) {
    match e {
        Empty => (),
        _ => items.push(e)
    }
}

#[inline]
fn pop_expr(items: &mut ~[Expr]) -> Expr {
    items.pop_opt().expect("nothing to repeat")
}

#[inline]
fn add_repeat(items: &mut ~[Expr], min: u32, max: Option<u32>) {
    let e = pop_expr(items);
    items.push(match e {
        Repeat(..) => fail!("multiple repeat"),
        _ => Repeat(~e, min, max, Greedy)
    })
}

///
/// Parse a counted repetition (e.g. `a{2,3}`), sans the opening brace.
///
/// The following syntaxes are accepted:
///
/// * `{N}` – match exactly N repetitions;
/// * `{M,}` – at least M;
/// * `{,N}` – at most N;
/// * `{M,N}` – from M to N inclusive;
/// * `{,}` – zero or more (synonymous with `*`).
///
fn p_repetition(s: &mut State) -> (u32, Option<u32>) {
    let min = p_number(s);
    match s.advance() {
        Some(',') => {
            let max = p_number(s);
            match s.advance() {
                // {} or {M,} or {,N} or {M,N}
                Some('}') => {
                    let min_ = min.unwrap_or(0);
                    if check_repeat(min_, max) {
                        (min_, max)
                    } else {
                        fail!("bad repeat interval")
                    }
                },
                _ => fail!("invalid repeat")
            }
        },
        Some('}') => match min {
            // {N}
            Some(n) => (n, Some(n)),
            _ => fail!("invalid repeat")
        },
        _ => fail!("invalid repeat")
    }
}

#[inline]
fn check_repeat(min: u32, max: Option<u32>) -> bool {
    match max {
        Some(max_) => min <= max_,
        None => true
    }
}

///
/// Parse a non-negative integer, and return it as a `u32`.
///
/// This returns `None` if no number could be parsed, but fails directly
/// if the number is greater than `REPEAT_MAX`.
///
fn p_number(s: &mut State) -> Option<u32> {
    let mut acc = None;
    loop {
        match s.advance() {
            Some(c) if '0' <= c && c <= '9' => {
                let digit = c as u32 - '0' as u32;
                acc = Some(match acc {
                    Some(n) => {
                        let acc_ = 10 * n + digit;
                        if acc_ <= REPEAT_MAX {
                            acc_
                        } else {
                            fail!(format!("repeat must be <= {}", REPEAT_MAX))
                        }
                    },
                    None => digit
                });
            },
            _ => { s.retreat(); break }
        }
    }
    acc
}

/// Parse a group (e.g. `(hello)`), sans the opening parenthesis.
fn p_group(s: &mut State) -> Expr {
    let result = match s.advance() {
        Some('?') => match s.advance() {
            Some(c) => match c {
                ':' => p_alternate(s),
                '#' => p_comment(s),
                _ => fail!(format!("unknown extension: ?{}", c))
            },
            None => fail!("unexpected end of pattern")
        },
        _ => { s.retreat(); Capture(~p_alternate(s)) }
    };

    // Match the closing paren
    match s.advance() {
        Some(')') => result,
        _ => fail!("mismatched parenthesis")
    }
}

/// Consume all input up to the first closing parenthesis, and return
/// `Empty`.
fn p_comment(s: &mut State) -> Expr {
    loop {
        match s.advance() {
            Some(c) if c != ')' => continue,
            _ => { s.retreat(); break }
        }
    }
    Empty
}

/// Parse an escape sequence (e.g. `\d`), sans the leading backslash.
fn p_escape(s: &mut State) -> CharClass {
    match s.advance() {
        Some(c) => match c {
            'n' => CharClass::from_char('\n'),
            'r' => CharClass::from_char('\r'),
            't' => CharClass::from_char('\t'),

            'd' => ascii::digit,
            's' => ascii::space,
            'w' => ascii::word,

            'D' => ascii::digit.negate(),
            'S' => ascii::space.negate(),
            'W' => ascii::word.negate(),

            'x' => p_hex_escape(s, 2),
            'u' => p_hex_escape(s, 4),
            'U' => p_hex_escape(s, 8),

            _ if ascii::punct.includes(c) => CharClass::from_char(c),

            _ => fail!("invalid escape")
        },
        None => fail!("invalid escape")
    }
}

fn p_hex_escape(s: &mut State, n_digits: uint) -> CharClass {
    let mut acc = 0u32;
    for _ in range(0, n_digits) {
        acc = 16 * acc + match s.advance() {
            Some(c) => c.to_digit(16).expect("invalid escape") as u32,
            _ => fail!("invalid escape")
        };
    }
    let c = char::from_u32(acc).expect("character out of range");
    CharClass::from_char(c)
}

/// Parse a character class (e.g. `[a-z]`), sans the opening bracket.
fn p_charclass(s: &mut State) -> CharClass {
    let mut classes: ~[CharClass] = ~[];

    let negate = match s.advance() {
        Some('^') => true,
        _ => { s.retreat(); false }
    };

    loop {
        match s.advance() {
            Some(c) => match c {
                ']' => break,
                '-' => match p_charclass_token(s) {
                    Some(cc_hi) => match classes.pop_opt() {
                        Some(cc_lo) => {
                            // [a-z]
                            let lo = cc_lo.to_char().expect("bad character range");
                            let hi = cc_hi.to_char().expect("bad character range");
                            classes.push(CharClass::from_range(lo, hi));
                        },
                        None => classes.push(cc_hi)  // [-z]
                    },
                    None => classes.push(CharClass::from_char('-'))  // [a-]
                },
                _ => {
                    s.retreat();
                    classes.push(p_charclass_token(s).expect("invalid char class"));
                }
            },
            None => fail!("unexpected end of char class")
        }
    }

    let cc = CharClass::combine(classes);
    if negate {
        cc.negate()
    } else {
        cc
    }
}

fn p_charclass_token(s: &mut State) -> Option<CharClass> {
    match s.advance() {
        Some(c) => match c {
            ']' => { s.retreat(); None },
            '[' => Some(p_charclass(s)),
            '\\' => Some(p_escape(s)),
            _ => Some(CharClass::from_char(c))
        },
        None => None
    }
}

/// Reify a character class as an `Expr`.
fn cc_to_expr(cc: CharClass) -> Expr {
    Alternate(cc.ranges().iter().map(|&(lo, hi)| Range(lo, hi)).collect())
}
<|endoftext|>


<commit_before>/*
 * Copyright (c) Facebook, Inc. and its affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

#![deny(warnings)]
#![deny(rust_2018_idioms)]
#![deny(clippy::all)]
#![allow(clippy::large_enum_variant)]

mod lexer;
mod node;
mod parser;
mod source;
mod syntax_error;
mod utils;

pub use lexer::TokenKind;
pub use node::*;
pub use parser::ParserFeatures;
pub use source::GraphQLSource;
pub use syntax_error::SyntaxError;
pub use utils::*;

use crate::parser::Parser;
use common::{DiagnosticsResult, SourceLocationKey, WithDiagnostics};

/// Parses a GraphQL document that might contain type system and executable
/// definitions.
pub fn parse_document(
    source: &str,
    source_location: SourceLocationKey,
) -> DiagnosticsResult<Document> {
    let features = ParserFeatures::default();
    let parser = Parser::new(source, source_location, features);
    parser.parse_document()
}

/// Parses a GraphQL document that might contain type system and executable
/// definitions with custom feature flags passed as `features`.
pub fn parse_document_with_features(
    source: &str,
    source_location: SourceLocationKey,
    features: ParserFeatures,
) -> DiagnosticsResult<Document> {
    let parser = Parser::new(source, source_location, features);
    parser.parse_document()
}

/// Parses a GraphQL document that's restricted to executable definitions.
pub fn parse_executable(
    source: &str,
    source_location: SourceLocationKey,
) -> DiagnosticsResult<ExecutableDocument> {
    let features = ParserFeatures::default();
    let parser = Parser::new(source, source_location, features);
    parser.parse_executable_document().into()
}

/// Parses a GraphQL document that's restricted to executable definitions,
/// with error recovery.
pub fn parse_executable_with_error_recovery(
    source: &str,
    source_location: SourceLocationKey,
) -> WithDiagnostics<ExecutableDocument> {
    let features = ParserFeatures::default();
    let parser = Parser::new(source, source_location, features);
    parser.parse_executable_document()
}

/// Parses a GraphQL document that's restricted to executable executable
/// definitions with custom feature flags passed as `features`.
pub fn parse_executable_with_features(
    source: &str,
    source_location: SourceLocationKey,
    features: ParserFeatures,
) -> DiagnosticsResult<ExecutableDocument> {
    let parser = Parser::new(source, source_location, features);
    parser.parse_executable_document().into()
}

/// Parses a GraphQL document that's restricted to type system definitions
/// including schema definition, type definitions and type system extensions.
pub fn parse_schema_document(
    source: &str,
    source_location: SourceLocationKey,
) -> DiagnosticsResult<SchemaDocument> {
    let features = ParserFeatures::default();
    let parser = Parser::new(source, source_location, features);
    parser.parse_schema_document()
}

/// Parses a GraphQL type, such as `ID` or `[User!]!`.
pub fn parse_type(
    source: &str,
    source_location: SourceLocationKey,
) -> DiagnosticsResult<TypeAnnotation> {
    let features = ParserFeatures::default();
    let parser = Parser::new(source, source_location, features);
    parser.parse_type()
}

/// Parses a GraphQL document that's restricted to type system definitions
/// including schema definition, type definitions and type system extensions.
pub fn parse_directive(
    source: &str,
    source_location: SourceLocationKey,
) -> DiagnosticsResult<Directive> {
    let features = ParserFeatures::default();
    let parser = Parser::new(source, source_location, features);
    parser.parse_directive()
}
<commit_msg>Fixing parser-features in parse_executable_with_error_recovery<commit_after>/*
 * Copyright (c) Facebook, Inc. and its affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

#![deny(warnings)]
#![deny(rust_2018_idioms)]
#![deny(clippy::all)]
#![allow(clippy::large_enum_variant)]

mod lexer;
mod node;
mod parser;
mod source;
mod syntax_error;
mod utils;

pub use lexer::TokenKind;
pub use node::*;
pub use parser::ParserFeatures;
pub use source::GraphQLSource;
pub use syntax_error::SyntaxError;
pub use utils::*;

use crate::parser::Parser;
use common::{DiagnosticsResult, SourceLocationKey, WithDiagnostics};

/// Parses a GraphQL document that might contain type system and executable
/// definitions.
pub fn parse_document(
    source: &str,
    source_location: SourceLocationKey,
) -> DiagnosticsResult<Document> {
    let features = ParserFeatures::default();
    let parser = Parser::new(source, source_location, features);
    parser.parse_document()
}

/// Parses a GraphQL document that might contain type system and executable
/// definitions with custom feature flags passed as `features`.
pub fn parse_document_with_features(
    source: &str,
    source_location: SourceLocationKey,
    features: ParserFeatures,
) -> DiagnosticsResult<Document> {
    let parser = Parser::new(source, source_location, features);
    parser.parse_document()
}

/// Parses a GraphQL document that's restricted to executable definitions.
pub fn parse_executable(
    source: &str,
    source_location: SourceLocationKey,
) -> DiagnosticsResult<ExecutableDocument> {
    parse_executable_with_error_recovery(source, source_location).into()
}

/// Parses a GraphQL document that's restricted to executable definitions,
/// with error recovery.
pub fn parse_executable_with_error_recovery(
    source: &str,
    source_location: SourceLocationKey,
) -> WithDiagnostics<ExecutableDocument> {
    let features = ParserFeatures::default();
    let parser = Parser::new(source, source_location, features);
    parser.parse_executable_document()
}

/// Parses a GraphQL document that's restricted to executable definitions,
/// with error recovery and customed ParserFeatures.
pub fn parse_executable_with_error_recovery_and_parser_features(
    source: &str,
    source_location: SourceLocationKey,
    features: ParserFeatures,
) -> WithDiagnostics<ExecutableDocument> {
    let parser = Parser::new(source, source_location, features);
    parser.parse_executable_document()
}

/// Parses a GraphQL document that's restricted to executable executable
/// definitions with custom feature flags passed as `features`.
pub fn parse_executable_with_features(
    source: &str,
    source_location: SourceLocationKey,
    features: ParserFeatures,
) -> DiagnosticsResult<ExecutableDocument> {
    parse_executable_with_error_recovery_and_parser_features(source, source_location, features)
        .into()
}

/// Parses a GraphQL document that's restricted to type system definitions
/// including schema definition, type definitions and type system extensions.
pub fn parse_schema_document(
    source: &str,
    source_location: SourceLocationKey,
) -> DiagnosticsResult<SchemaDocument> {
    let features = ParserFeatures::default();
    let parser = Parser::new(source, source_location, features);
    parser.parse_schema_document()
}

/// Parses a GraphQL type, such as `ID` or `[User!]!`.
pub fn parse_type(
    source: &str,
    source_location: SourceLocationKey,
) -> DiagnosticsResult<TypeAnnotation> {
    let features = ParserFeatures::default();
    let parser = Parser::new(source, source_location, features);
    parser.parse_type()
}

/// Parses a GraphQL document that's restricted to type system definitions
/// including schema definition, type definitions and type system extensions.
pub fn parse_directive(
    source: &str,
    source_location: SourceLocationKey,
) -> DiagnosticsResult<Directive> {
    let features = ParserFeatures::default();
    let parser = Parser::new(source, source_location, features);
    parser.parse_directive()
}
<|endoftext|>


<commit_before>//! This module contains `HashStable` implementations for various data types
//! from `rustc_middle::ty` in no particular order.

use crate::ich::{NodeIdHashingMode, StableHashingContext};
use crate::middle::region;
use crate::mir;
use crate::ty;
use rustc_data_structures::fingerprint::Fingerprint;
use rustc_data_structures::fx::FxHashMap;
use rustc_data_structures::stable_hasher::{HashStable, StableHasher, ToStableHashKey};
use std::cell::RefCell;
use std::mem;

impl<'a, 'tcx, T> HashStable<StableHashingContext<'a>> for &'tcx ty::List<T>
where
    T: HashStable<StableHashingContext<'a>>,
{
    fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) {
        thread_local! {
            static CACHE: RefCell<FxHashMap<(usize, usize), Fingerprint>> =
                RefCell::new(Default::default());
        }

        let hash = CACHE.with(|cache| {
            let key = (self.as_ptr() as usize, self.len());
            if let Some(&hash) = cache.borrow().get(&key) {
                return hash;
            }

            let mut hasher = StableHasher::new();
            (&self[..]).hash_stable(hcx, &mut hasher);

            let hash: Fingerprint = hasher.finish();
            cache.borrow_mut().insert(key, hash);
            hash
        });

        hash.hash_stable(hcx, hasher);
    }
}

impl<'a, 'tcx, T> ToStableHashKey<StableHashingContext<'a>> for &'tcx ty::List<T>
where
    T: HashStable<StableHashingContext<'a>>,
{
    type KeyType = Fingerprint;

    #[inline]
    fn to_stable_hash_key(&self, hcx: &StableHashingContext<'a>) -> Fingerprint {
        let mut hasher = StableHasher::new();
        let mut hcx: StableHashingContext<'a> = hcx.clone();
        self.hash_stable(&mut hcx, &mut hasher);
        hasher.finish()
    }
}

impl<'a, 'tcx> HashStable<StableHashingContext<'a>> for ty::subst::GenericArg<'tcx> {
    fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) {
        self.unpack().hash_stable(hcx, hasher);
    }
}

impl<'a> HashStable<StableHashingContext<'a>> for ty::RegionKind {
    fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) {
        mem::discriminant(self).hash_stable(hcx, hasher);
        match *self {
            ty::ReErased | ty::ReStatic => {
                // No variant fields to hash for these ...
            }
            ty::ReEmpty(universe) => {
                universe.hash_stable(hcx, hasher);
            }
            ty::ReLateBound(db, ty::BoundRegion { kind: ty::BrAnon(i), .. }) => {
                db.hash_stable(hcx, hasher);
                i.hash_stable(hcx, hasher);
            }
            ty::ReLateBound(db, ty::BoundRegion { kind: ty::BrNamed(def_id, name), .. }) => {
                db.hash_stable(hcx, hasher);
                def_id.hash_stable(hcx, hasher);
                name.hash_stable(hcx, hasher);
            }
            ty::ReLateBound(db, ty::BoundRegion { kind: ty::BrEnv, .. }) => {
                db.hash_stable(hcx, hasher);
            }
            ty::ReEarlyBound(ty::EarlyBoundRegion { def_id, index, name }) => {
                def_id.hash_stable(hcx, hasher);
                index.hash_stable(hcx, hasher);
                name.hash_stable(hcx, hasher);
            }
            ty::ReFree(ref free_region) => {
                free_region.hash_stable(hcx, hasher);
            }
            ty::ReVar(..) | ty::RePlaceholder(..) => {
                bug!("StableHasher: unexpected region {:?}", *self)
            }
        }
    }
}

impl<'a> HashStable<StableHashingContext<'a>> for ty::RegionVid {
    #[inline]
    fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) {
        self.index().hash_stable(hcx, hasher);
    }
}

impl<'a, 'tcx> HashStable<StableHashingContext<'a>> for ty::ConstVid<'tcx> {
    #[inline]
    fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) {
        self.index.hash_stable(hcx, hasher);
    }
}

impl<'tcx> HashStable<StableHashingContext<'tcx>> for ty::BoundVar {
    #[inline]
    fn hash_stable(&self, hcx: &mut StableHashingContext<'tcx>, hasher: &mut StableHasher) {
        self.index().hash_stable(hcx, hasher);
    }
}

impl<'a, 'tcx, T> HashStable<StableHashingContext<'a>> for ty::Binder<'tcx, T>
where
    T: HashStable<StableHashingContext<'a>>,
{
    fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) {
        self.as_ref().skip_binder().hash_stable(hcx, hasher);
        self.bound_vars().hash_stable(hcx, hasher);
    }
}

// AllocIds get resolved to whatever they point to (to be stable)
impl<'a> HashStable<StableHashingContext<'a>> for mir::interpret::AllocId {
    fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) {
        ty::tls::with_opt(|tcx| {
            trace!("hashing {:?}", *self);
            let tcx = tcx.expect("can't hash AllocIds during hir lowering");
            tcx.get_global_alloc(*self).hash_stable(hcx, hasher);
        });
    }
}

// `Relocations` with default type parameters is a sorted map.
impl<'a, Tag> HashStable<StableHashingContext<'a>> for mir::interpret::Relocations<Tag>
where
    Tag: HashStable<StableHashingContext<'a>>,
{
    fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) {
        self.len().hash_stable(hcx, hasher);
        for reloc in self.iter() {
            reloc.hash_stable(hcx, hasher);
        }
    }
}

impl<'a> ToStableHashKey<StableHashingContext<'a>> for region::Scope {
    type KeyType = region::Scope;

    #[inline]
    fn to_stable_hash_key(&self, _: &StableHashingContext<'a>) -> region::Scope {
        *self
    }
}

impl<'a> HashStable<StableHashingContext<'a>> for ty::TyVid {
    fn hash_stable(&self, _hcx: &mut StableHashingContext<'a>, _hasher: &mut StableHasher) {
        // `TyVid` values are confined to an inference context and hence
        // should not be hashed.
        bug!("ty::TyKind::hash_stable() - can't hash a TyVid {:?}.", *self)
    }
}

impl<'a> HashStable<StableHashingContext<'a>> for ty::IntVid {
    fn hash_stable(&self, _hcx: &mut StableHashingContext<'a>, _hasher: &mut StableHasher) {
        // `IntVid` values are confined to an inference context and hence
        // should not be hashed.
        bug!("ty::TyKind::hash_stable() - can't hash an IntVid {:?}.", *self)
    }
}

impl<'a> HashStable<StableHashingContext<'a>> for ty::FloatVid {
    fn hash_stable(&self, _hcx: &mut StableHashingContext<'a>, _hasher: &mut StableHasher) {
        // `FloatVid` values are confined to an inference context and hence
        // should not be hashed.
        bug!("ty::TyKind::hash_stable() - can't hash a FloatVid {:?}.", *self)
    }
}

impl<'a> HashStable<StableHashingContext<'a>> for crate::middle::privacy::AccessLevels {
    fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) {
        hcx.with_node_id_hashing_mode(NodeIdHashingMode::HashDefPath, |hcx| {
            let crate::middle::privacy::AccessLevels { ref map } = *self;

            map.hash_stable(hcx, hasher);
        });
    }
}
<commit_msg>Fix match for placeholder region<commit_after>//! This module contains `HashStable` implementations for various data types
//! from `rustc_middle::ty` in no particular order.

use crate::ich::{NodeIdHashingMode, StableHashingContext};
use crate::middle::region;
use crate::mir;
use crate::ty;
use rustc_data_structures::fingerprint::Fingerprint;
use rustc_data_structures::fx::FxHashMap;
use rustc_data_structures::stable_hasher::{HashStable, StableHasher, ToStableHashKey};
use std::cell::RefCell;
use std::mem;

impl<'a, 'tcx, T> HashStable<StableHashingContext<'a>> for &'tcx ty::List<T>
where
    T: HashStable<StableHashingContext<'a>>,
{
    fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) {
        thread_local! {
            static CACHE: RefCell<FxHashMap<(usize, usize), Fingerprint>> =
                RefCell::new(Default::default());
        }

        let hash = CACHE.with(|cache| {
            let key = (self.as_ptr() as usize, self.len());
            if let Some(&hash) = cache.borrow().get(&key) {
                return hash;
            }

            let mut hasher = StableHasher::new();
            (&self[..]).hash_stable(hcx, &mut hasher);

            let hash: Fingerprint = hasher.finish();
            cache.borrow_mut().insert(key, hash);
            hash
        });

        hash.hash_stable(hcx, hasher);
    }
}

impl<'a, 'tcx, T> ToStableHashKey<StableHashingContext<'a>> for &'tcx ty::List<T>
where
    T: HashStable<StableHashingContext<'a>>,
{
    type KeyType = Fingerprint;

    #[inline]
    fn to_stable_hash_key(&self, hcx: &StableHashingContext<'a>) -> Fingerprint {
        let mut hasher = StableHasher::new();
        let mut hcx: StableHashingContext<'a> = hcx.clone();
        self.hash_stable(&mut hcx, &mut hasher);
        hasher.finish()
    }
}

impl<'a, 'tcx> HashStable<StableHashingContext<'a>> for ty::subst::GenericArg<'tcx> {
    fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) {
        self.unpack().hash_stable(hcx, hasher);
    }
}

impl<'a> HashStable<StableHashingContext<'a>> for ty::RegionKind {
    fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) {
        mem::discriminant(self).hash_stable(hcx, hasher);
        match *self {
            ty::ReErased | ty::ReStatic => {
                // No variant fields to hash for these ...
            }
            ty::ReEmpty(universe) => {
                universe.hash_stable(hcx, hasher);
            }
            ty::ReLateBound(db, ty::BoundRegion { kind: ty::BrAnon(i), .. }) => {
                db.hash_stable(hcx, hasher);
                i.hash_stable(hcx, hasher);
            }
            ty::ReLateBound(db, ty::BoundRegion { kind: ty::BrNamed(def_id, name), .. }) => {
                db.hash_stable(hcx, hasher);
                def_id.hash_stable(hcx, hasher);
                name.hash_stable(hcx, hasher);
            }
            ty::ReLateBound(db, ty::BoundRegion { kind: ty::BrEnv, .. }) => {
                db.hash_stable(hcx, hasher);
            }
            ty::ReEarlyBound(ty::EarlyBoundRegion { def_id, index, name }) => {
                def_id.hash_stable(hcx, hasher);
                index.hash_stable(hcx, hasher);
                name.hash_stable(hcx, hasher);
            }
            ty::ReFree(ref free_region) => {
                free_region.hash_stable(hcx, hasher);
            }
            ty::RePlaceholder(p) => {
                p.hash_stable(hcx, hasher);
            }
            ty::ReVar(..) => {
                bug!("StableHasher: unexpected region {:?}", *self)
            }
        }
    }
}

impl<'a> HashStable<StableHashingContext<'a>> for ty::RegionVid {
    #[inline]
    fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) {
        self.index().hash_stable(hcx, hasher);
    }
}

impl<'a, 'tcx> HashStable<StableHashingContext<'a>> for ty::ConstVid<'tcx> {
    #[inline]
    fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) {
        self.index.hash_stable(hcx, hasher);
    }
}

impl<'tcx> HashStable<StableHashingContext<'tcx>> for ty::BoundVar {
    #[inline]
    fn hash_stable(&self, hcx: &mut StableHashingContext<'tcx>, hasher: &mut StableHasher) {
        self.index().hash_stable(hcx, hasher);
    }
}

impl<'a, 'tcx, T> HashStable<StableHashingContext<'a>> for ty::Binder<'tcx, T>
where
    T: HashStable<StableHashingContext<'a>>,
{
    fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) {
        self.as_ref().skip_binder().hash_stable(hcx, hasher);
        self.bound_vars().hash_stable(hcx, hasher);
    }
}

// AllocIds get resolved to whatever they point to (to be stable)
impl<'a> HashStable<StableHashingContext<'a>> for mir::interpret::AllocId {
    fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) {
        ty::tls::with_opt(|tcx| {
            trace!("hashing {:?}", *self);
            let tcx = tcx.expect("can't hash AllocIds during hir lowering");
            tcx.get_global_alloc(*self).hash_stable(hcx, hasher);
        });
    }
}

// `Relocations` with default type parameters is a sorted map.
impl<'a, Tag> HashStable<StableHashingContext<'a>> for mir::interpret::Relocations<Tag>
where
    Tag: HashStable<StableHashingContext<'a>>,
{
    fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) {
        self.len().hash_stable(hcx, hasher);
        for reloc in self.iter() {
            reloc.hash_stable(hcx, hasher);
        }
    }
}

impl<'a> ToStableHashKey<StableHashingContext<'a>> for region::Scope {
    type KeyType = region::Scope;

    #[inline]
    fn to_stable_hash_key(&self, _: &StableHashingContext<'a>) -> region::Scope {
        *self
    }
}

impl<'a> HashStable<StableHashingContext<'a>> for ty::TyVid {
    fn hash_stable(&self, _hcx: &mut StableHashingContext<'a>, _hasher: &mut StableHasher) {
        // `TyVid` values are confined to an inference context and hence
        // should not be hashed.
        bug!("ty::TyKind::hash_stable() - can't hash a TyVid {:?}.", *self)
    }
}

impl<'a> HashStable<StableHashingContext<'a>> for ty::IntVid {
    fn hash_stable(&self, _hcx: &mut StableHashingContext<'a>, _hasher: &mut StableHasher) {
        // `IntVid` values are confined to an inference context and hence
        // should not be hashed.
        bug!("ty::TyKind::hash_stable() - can't hash an IntVid {:?}.", *self)
    }
}

impl<'a> HashStable<StableHashingContext<'a>> for ty::FloatVid {
    fn hash_stable(&self, _hcx: &mut StableHashingContext<'a>, _hasher: &mut StableHasher) {
        // `FloatVid` values are confined to an inference context and hence
        // should not be hashed.
        bug!("ty::TyKind::hash_stable() - can't hash a FloatVid {:?}.", *self)
    }
}

impl<'a> HashStable<StableHashingContext<'a>> for crate::middle::privacy::AccessLevels {
    fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) {
        hcx.with_node_id_hashing_mode(NodeIdHashingMode::HashDefPath, |hcx| {
            let crate::middle::privacy::AccessLevels { ref map } = *self;

            map.hash_stable(hcx, hasher);
        });
    }
}
<|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 dom::attr::Attr;
use dom::attr::AttrValue;
use dom::bindings::cell::DOMRefCell;
use dom::bindings::codegen::Bindings::HTMLImageElementBinding;
use dom::bindings::codegen::Bindings::HTMLImageElementBinding::HTMLImageElementMethods;
use dom::bindings::codegen::Bindings::WindowBinding::WindowMethods;
use dom::bindings::error::Fallible;
use dom::bindings::global::GlobalRef;
use dom::bindings::inheritance::Castable;
use dom::bindings::js::{LayoutJS, Root};
use dom::bindings::refcounted::Trusted;
use dom::document::Document;
use dom::element::AttributeMutation;
use dom::event::{Event, EventBubbles, EventCancelable};
use dom::htmlelement::HTMLElement;
use dom::node::{Node, NodeDamage, document_from_node, window_from_node};
use dom::virtualmethods::VirtualMethods;
use ipc_channel::ipc;
use ipc_channel::router::ROUTER;
use net_traits::image::base::Image;
use net_traits::image_cache_task::{ImageResponder, ImageResponse};
use script_task::ScriptTaskEventCategory::UpdateReplacedElement;
use script_task::{CommonScriptMsg, Runnable, ScriptChan};
use std::sync::Arc;
use string_cache::Atom;
use url::{Url, UrlParser};
use util::str::DOMString;

#[dom_struct]
pub struct HTMLImageElement {
    htmlelement: HTMLElement,
    url: DOMRefCell<Option<Url>>,
    image: DOMRefCell<Option<Arc<Image>>>,
}

impl HTMLImageElement {
    pub fn get_url(&self) -> Option<Url>{
        self.url.borrow().clone()
    }
}

struct ImageResponseHandlerRunnable {
    element: Trusted<HTMLImageElement>,
    image: ImageResponse,
}

impl ImageResponseHandlerRunnable {
    fn new(element: Trusted<HTMLImageElement>, image: ImageResponse)
           -> ImageResponseHandlerRunnable {
        ImageResponseHandlerRunnable {
            element: element,
            image: image,
        }
    }
}

impl Runnable for ImageResponseHandlerRunnable {
    fn handler(self: Box<Self>) {
        // Update the image field
        let element = self.element.root();
        let element_ref = element.r();
        *element_ref.image.borrow_mut() = match self.image {
            ImageResponse::Loaded(image) | ImageResponse::PlaceholderLoaded(image) => {
                Some(image)
            }
            ImageResponse::None => None,
        };

        // Mark the node dirty
        let document = document_from_node(&*element);
        document.content_changed(element.upcast(), NodeDamage::OtherNodeDamage);

        // Fire image.onload
        let window = window_from_node(document.r());
        let event = Event::new(GlobalRef::Window(window.r()),
                               DOMString::from("load"),
                               EventBubbles::DoesNotBubble,
                               EventCancelable::NotCancelable);
        event.fire(element.upcast());

        // Trigger reflow
        window.add_pending_reflow();
    }
}

impl HTMLImageElement {
    /// Makes the local `image` member match the status of the `src` attribute and starts
    /// prefetching the image. This method must be called after `src` is changed.
    fn update_image(&self, value: Option<(DOMString, Url)>) {
        let document = document_from_node(self);
        let window = document.window();
        let image_cache = window.image_cache_task();
        match value {
            None => {
                *self.url.borrow_mut() = None;
                *self.image.borrow_mut() = None;
            }
            Some((src, base_url)) => {
                let img_url = UrlParser::new().base_url(&base_url).parse(&src);
                // FIXME: handle URL parse errors more gracefully.
                let img_url = img_url.unwrap();
                *self.url.borrow_mut() = Some(img_url.clone());

                let trusted_node = Trusted::new(window.get_cx(), self, window.script_chan());
                let (responder_sender, responder_receiver) = ipc::channel().unwrap();
                let script_chan = window.script_chan();
                let wrapper = window.get_runnable_wrapper();
                ROUTER.add_route(responder_receiver.to_opaque(), box move |message| {
                    // Return the image via a message to the script task, which marks the element
                    // as dirty and triggers a reflow.
                    let image_response = message.to().unwrap();
                    let runnable = ImageResponseHandlerRunnable::new(
                        trusted_node.clone(), image_response);
                    let runnable = wrapper.wrap_runnable(runnable);
                    script_chan.send(CommonScriptMsg::RunnableMsg(
                        UpdateReplacedElement, runnable)).unwrap();
                });

                image_cache.request_image(img_url,
                                          window.image_cache_chan(),
                                          Some(ImageResponder::new(responder_sender)));
            }
        }
    }

    fn new_inherited(localName: Atom, prefix: Option<DOMString>, document: &Document) -> HTMLImageElement {
        HTMLImageElement {
            htmlelement: HTMLElement::new_inherited(localName, prefix, document),
            url: DOMRefCell::new(None),
            image: DOMRefCell::new(None),
        }
    }

    #[allow(unrooted_must_root)]
    pub fn new(localName: Atom,
               prefix: Option<DOMString>,
               document: &Document) -> Root<HTMLImageElement> {
        let element = HTMLImageElement::new_inherited(localName, prefix, document);
        Node::reflect_node(box element, document, HTMLImageElementBinding::Wrap)
    }

    pub fn Image(global: GlobalRef,
                 width: Option<u32>,
                 height: Option<u32>) -> Fallible<Root<HTMLImageElement>> {
        let document = global.as_window().Document();
        let image = HTMLImageElement::new(atom!("img"), None, document.r());
        if let Some(w) = width {
            image.SetWidth(w);
        }
        if let Some(h) = height {
            image.SetHeight(h);
        }

        Ok(image)
    }
}

pub trait LayoutHTMLImageElementHelpers {
    #[allow(unsafe_code)]
    unsafe fn image(&self) -> Option<Arc<Image>>;

    #[allow(unsafe_code)]
    unsafe fn image_url(&self) -> Option<Url>;
}

impl LayoutHTMLImageElementHelpers for LayoutJS<HTMLImageElement> {
    #[allow(unsafe_code)]
    unsafe fn image(&self) -> Option<Arc<Image>> {
        (*self.unsafe_get()).image.borrow_for_layout().clone()
    }

    #[allow(unsafe_code)]
    unsafe fn image_url(&self) -> Option<Url> {
        (*self.unsafe_get()).url.borrow_for_layout().clone()
    }
}

impl HTMLImageElementMethods for HTMLImageElement {
    // https://html.spec.whatwg.org/multipage/#dom-img-alt
    make_getter!(Alt, "alt");
    // https://html.spec.whatwg.org/multipage/#dom-img-alt
    make_setter!(SetAlt, "alt");

    // https://html.spec.whatwg.org/multipage/#dom-img-src
    make_url_getter!(Src, "src");
    // https://html.spec.whatwg.org/multipage/#dom-img-src
    make_setter!(SetSrc, "src");

    // https://html.spec.whatwg.org/multipage/#dom-img-usemap
    make_getter!(UseMap, "usemap");
    // https://html.spec.whatwg.org/multipage/#dom-img-usemap
    make_setter!(SetUseMap, "usemap");

    // https://html.spec.whatwg.org/multipage/#dom-img-ismap
    make_bool_getter!(IsMap, "ismap");
    // https://html.spec.whatwg.org/multipage/#dom-img-ismap
    make_bool_setter!(SetIsMap, "ismap");

    // https://html.spec.whatwg.org/multipage/#dom-img-width
    fn Width(&self) -> u32 {
        let node = self.upcast::<Node>();
        let rect = node.get_bounding_content_box();
        rect.size.width.to_px() as u32
    }

    // https://html.spec.whatwg.org/multipage/#dom-img-width
    make_uint_setter!(SetWidth, "width");

    // https://html.spec.whatwg.org/multipage/#dom-img-height
    fn Height(&self) -> u32 {
        let node = self.upcast::<Node>();
        let rect = node.get_bounding_content_box();
        rect.size.height.to_px() as u32
    }

    // https://html.spec.whatwg.org/multipage/#dom-img-height
    make_uint_setter!(SetHeight, "height");

    // https://html.spec.whatwg.org/multipage/#dom-img-naturalwidth
    fn NaturalWidth(&self) -> u32 {
        let image = self.image.borrow();

        match *image {
            Some(ref image) => image.width,
            None => 0,
        }
    }

    // https://html.spec.whatwg.org/multipage/#dom-img-naturalheight
    fn NaturalHeight(&self) -> u32 {
        let image = self.image.borrow();

        match *image {
            Some(ref image) => image.height,
            None => 0,
        }
    }

    // https://html.spec.whatwg.org/multipage/#dom-img-complete
    fn Complete(&self) -> bool {
        let image = self.image.borrow();
        image.is_some()
    }

    // https://html.spec.whatwg.org/multipage/#dom-img-name
    make_getter!(Name, "name");

    // https://html.spec.whatwg.org/multipage/#dom-img-name
    make_atomic_setter!(SetName, "name");

    // https://html.spec.whatwg.org/multipage/#dom-img-align
    make_getter!(Align, "align");

    // https://html.spec.whatwg.org/multipage/#dom-img-align
    make_setter!(SetAlign, "align");

    // https://html.spec.whatwg.org/multipage/#dom-img-hspace
    make_uint_getter!(Hspace, "hspace");

    // https://html.spec.whatwg.org/multipage/#dom-img-hspace
    make_uint_setter!(SetHspace, "hspace");

    // https://html.spec.whatwg.org/multipage/#dom-img-vspace
    make_uint_getter!(Vspace, "vspace");

    // https://html.spec.whatwg.org/multipage/#dom-img-vspace
    make_uint_setter!(SetVspace, "vspace");

    // https://html.spec.whatwg.org/multipage/#dom-img-longdesc
    make_getter!(LongDesc, "longdesc");

    // https://html.spec.whatwg.org/multipage/#dom-img-longdesc
    make_setter!(SetLongDesc, "longdesc");

    // https://html.spec.whatwg.org/multipage/#dom-img-border
    make_getter!(Border, "border");

    // https://html.spec.whatwg.org/multipage/#dom-img-border
    make_setter!(SetBorder, "border");
}

impl VirtualMethods for HTMLImageElement {
    fn super_type(&self) -> Option<&VirtualMethods> {
        Some(self.upcast::<HTMLElement>() as &VirtualMethods)
    }

    fn attribute_mutated(&self, attr: &Attr, mutation: AttributeMutation) {
        self.super_type().unwrap().attribute_mutated(attr, mutation);
        match attr.local_name() {
            &atom!("src") => {
                self.update_image(mutation.new_value(attr).map(|value| {
                    // FIXME(ajeffrey): convert directly from AttrValue to DOMString
                    (DOMString::from(&**value), window_from_node(self).get_url())
                }));
            },
            _ => {},
        }
    }

    fn parse_plain_attribute(&self, name: &Atom, value: DOMString) -> AttrValue {
        match name {
            &atom!("name") => AttrValue::from_atomic(value),
            &atom!("width") | &atom!("height") |
            &atom!("hspace") | &atom!("vspace") => AttrValue::from_u32(value, 0),
            _ => self.super_type().unwrap().parse_plain_attribute(name, value),
        }
    }
}
<commit_msg>Auto merge of #8818 - jdm:imagepanic, r=Manishearth<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 dom::attr::Attr;
use dom::attr::AttrValue;
use dom::bindings::cell::DOMRefCell;
use dom::bindings::codegen::Bindings::HTMLImageElementBinding;
use dom::bindings::codegen::Bindings::HTMLImageElementBinding::HTMLImageElementMethods;
use dom::bindings::codegen::Bindings::WindowBinding::WindowMethods;
use dom::bindings::error::Fallible;
use dom::bindings::global::GlobalRef;
use dom::bindings::inheritance::Castable;
use dom::bindings::js::{LayoutJS, Root};
use dom::bindings::refcounted::Trusted;
use dom::document::Document;
use dom::element::AttributeMutation;
use dom::event::{Event, EventBubbles, EventCancelable};
use dom::htmlelement::HTMLElement;
use dom::node::{Node, NodeDamage, document_from_node, window_from_node};
use dom::virtualmethods::VirtualMethods;
use ipc_channel::ipc;
use ipc_channel::router::ROUTER;
use net_traits::image::base::Image;
use net_traits::image_cache_task::{ImageResponder, ImageResponse};
use script_task::ScriptTaskEventCategory::UpdateReplacedElement;
use script_task::{CommonScriptMsg, Runnable, ScriptChan};
use std::sync::Arc;
use string_cache::Atom;
use url::{Url, UrlParser};
use util::str::DOMString;

#[dom_struct]
pub struct HTMLImageElement {
    htmlelement: HTMLElement,
    url: DOMRefCell<Option<Url>>,
    image: DOMRefCell<Option<Arc<Image>>>,
}

impl HTMLImageElement {
    pub fn get_url(&self) -> Option<Url>{
        self.url.borrow().clone()
    }
}

struct ImageResponseHandlerRunnable {
    element: Trusted<HTMLImageElement>,
    image: ImageResponse,
}

impl ImageResponseHandlerRunnable {
    fn new(element: Trusted<HTMLImageElement>, image: ImageResponse)
           -> ImageResponseHandlerRunnable {
        ImageResponseHandlerRunnable {
            element: element,
            image: image,
        }
    }
}

impl Runnable for ImageResponseHandlerRunnable {
    fn handler(self: Box<Self>) {
        // Update the image field
        let element = self.element.root();
        let element_ref = element.r();
        *element_ref.image.borrow_mut() = match self.image {
            ImageResponse::Loaded(image) | ImageResponse::PlaceholderLoaded(image) => {
                Some(image)
            }
            ImageResponse::None => None,
        };

        // Mark the node dirty
        let document = document_from_node(&*element);
        document.content_changed(element.upcast(), NodeDamage::OtherNodeDamage);

        // Fire image.onload
        let window = window_from_node(document.r());
        let event = Event::new(GlobalRef::Window(window.r()),
                               DOMString::from("load"),
                               EventBubbles::DoesNotBubble,
                               EventCancelable::NotCancelable);
        event.fire(element.upcast());

        // Trigger reflow
        window.add_pending_reflow();
    }
}

impl HTMLImageElement {
    /// Makes the local `image` member match the status of the `src` attribute and starts
    /// prefetching the image. This method must be called after `src` is changed.
    fn update_image(&self, value: Option<(DOMString, Url)>) {
        let document = document_from_node(self);
        let window = document.window();
        let image_cache = window.image_cache_task();
        match value {
            None => {
                *self.url.borrow_mut() = None;
                *self.image.borrow_mut() = None;
            }
            Some((src, base_url)) => {
                let img_url = UrlParser::new().base_url(&base_url).parse(&src);
                // FIXME: handle URL parse errors more gracefully.
                let img_url = img_url.unwrap();
                *self.url.borrow_mut() = Some(img_url.clone());

                let trusted_node = Trusted::new(window.get_cx(), self, window.script_chan());
                let (responder_sender, responder_receiver) = ipc::channel().unwrap();
                let script_chan = window.script_chan();
                let wrapper = window.get_runnable_wrapper();
                ROUTER.add_route(responder_receiver.to_opaque(), box move |message| {
                    // Return the image via a message to the script task, which marks the element
                    // as dirty and triggers a reflow.
                    let image_response = message.to().unwrap();
                    let runnable = ImageResponseHandlerRunnable::new(
                        trusted_node.clone(), image_response);
                    let runnable = wrapper.wrap_runnable(runnable);
                    let _ = script_chan.send(CommonScriptMsg::RunnableMsg(
                        UpdateReplacedElement, runnable));
                });

                image_cache.request_image(img_url,
                                          window.image_cache_chan(),
                                          Some(ImageResponder::new(responder_sender)));
            }
        }
    }

    fn new_inherited(localName: Atom, prefix: Option<DOMString>, document: &Document) -> HTMLImageElement {
        HTMLImageElement {
            htmlelement: HTMLElement::new_inherited(localName, prefix, document),
            url: DOMRefCell::new(None),
            image: DOMRefCell::new(None),
        }
    }

    #[allow(unrooted_must_root)]
    pub fn new(localName: Atom,
               prefix: Option<DOMString>,
               document: &Document) -> Root<HTMLImageElement> {
        let element = HTMLImageElement::new_inherited(localName, prefix, document);
        Node::reflect_node(box element, document, HTMLImageElementBinding::Wrap)
    }

    pub fn Image(global: GlobalRef,
                 width: Option<u32>,
                 height: Option<u32>) -> Fallible<Root<HTMLImageElement>> {
        let document = global.as_window().Document();
        let image = HTMLImageElement::new(atom!("img"), None, document.r());
        if let Some(w) = width {
            image.SetWidth(w);
        }
        if let Some(h) = height {
            image.SetHeight(h);
        }

        Ok(image)
    }
}

pub trait LayoutHTMLImageElementHelpers {
    #[allow(unsafe_code)]
    unsafe fn image(&self) -> Option<Arc<Image>>;

    #[allow(unsafe_code)]
    unsafe fn image_url(&self) -> Option<Url>;
}

impl LayoutHTMLImageElementHelpers for LayoutJS<HTMLImageElement> {
    #[allow(unsafe_code)]
    unsafe fn image(&self) -> Option<Arc<Image>> {
        (*self.unsafe_get()).image.borrow_for_layout().clone()
    }

    #[allow(unsafe_code)]
    unsafe fn image_url(&self) -> Option<Url> {
        (*self.unsafe_get()).url.borrow_for_layout().clone()
    }
}

impl HTMLImageElementMethods for HTMLImageElement {
    // https://html.spec.whatwg.org/multipage/#dom-img-alt
    make_getter!(Alt, "alt");
    // https://html.spec.whatwg.org/multipage/#dom-img-alt
    make_setter!(SetAlt, "alt");

    // https://html.spec.whatwg.org/multipage/#dom-img-src
    make_url_getter!(Src, "src");
    // https://html.spec.whatwg.org/multipage/#dom-img-src
    make_setter!(SetSrc, "src");

    // https://html.spec.whatwg.org/multipage/#dom-img-usemap
    make_getter!(UseMap, "usemap");
    // https://html.spec.whatwg.org/multipage/#dom-img-usemap
    make_setter!(SetUseMap, "usemap");

    // https://html.spec.whatwg.org/multipage/#dom-img-ismap
    make_bool_getter!(IsMap, "ismap");
    // https://html.spec.whatwg.org/multipage/#dom-img-ismap
    make_bool_setter!(SetIsMap, "ismap");

    // https://html.spec.whatwg.org/multipage/#dom-img-width
    fn Width(&self) -> u32 {
        let node = self.upcast::<Node>();
        let rect = node.get_bounding_content_box();
        rect.size.width.to_px() as u32
    }

    // https://html.spec.whatwg.org/multipage/#dom-img-width
    make_uint_setter!(SetWidth, "width");

    // https://html.spec.whatwg.org/multipage/#dom-img-height
    fn Height(&self) -> u32 {
        let node = self.upcast::<Node>();
        let rect = node.get_bounding_content_box();
        rect.size.height.to_px() as u32
    }

    // https://html.spec.whatwg.org/multipage/#dom-img-height
    make_uint_setter!(SetHeight, "height");

    // https://html.spec.whatwg.org/multipage/#dom-img-naturalwidth
    fn NaturalWidth(&self) -> u32 {
        let image = self.image.borrow();

        match *image {
            Some(ref image) => image.width,
            None => 0,
        }
    }

    // https://html.spec.whatwg.org/multipage/#dom-img-naturalheight
    fn NaturalHeight(&self) -> u32 {
        let image = self.image.borrow();

        match *image {
            Some(ref image) => image.height,
            None => 0,
        }
    }

    // https://html.spec.whatwg.org/multipage/#dom-img-complete
    fn Complete(&self) -> bool {
        let image = self.image.borrow();
        image.is_some()
    }

    // https://html.spec.whatwg.org/multipage/#dom-img-name
    make_getter!(Name, "name");

    // https://html.spec.whatwg.org/multipage/#dom-img-name
    make_atomic_setter!(SetName, "name");

    // https://html.spec.whatwg.org/multipage/#dom-img-align
    make_getter!(Align, "align");

    // https://html.spec.whatwg.org/multipage/#dom-img-align
    make_setter!(SetAlign, "align");

    // https://html.spec.whatwg.org/multipage/#dom-img-hspace
    make_uint_getter!(Hspace, "hspace");

    // https://html.spec.whatwg.org/multipage/#dom-img-hspace
    make_uint_setter!(SetHspace, "hspace");

    // https://html.spec.whatwg.org/multipage/#dom-img-vspace
    make_uint_getter!(Vspace, "vspace");

    // https://html.spec.whatwg.org/multipage/#dom-img-vspace
    make_uint_setter!(SetVspace, "vspace");

    // https://html.spec.whatwg.org/multipage/#dom-img-longdesc
    make_getter!(LongDesc, "longdesc");

    // https://html.spec.whatwg.org/multipage/#dom-img-longdesc
    make_setter!(SetLongDesc, "longdesc");

    // https://html.spec.whatwg.org/multipage/#dom-img-border
    make_getter!(Border, "border");

    // https://html.spec.whatwg.org/multipage/#dom-img-border
    make_setter!(SetBorder, "border");
}

impl VirtualMethods for HTMLImageElement {
    fn super_type(&self) -> Option<&VirtualMethods> {
        Some(self.upcast::<HTMLElement>() as &VirtualMethods)
    }

    fn attribute_mutated(&self, attr: &Attr, mutation: AttributeMutation) {
        self.super_type().unwrap().attribute_mutated(attr, mutation);
        match attr.local_name() {
            &atom!("src") => {
                self.update_image(mutation.new_value(attr).map(|value| {
                    // FIXME(ajeffrey): convert directly from AttrValue to DOMString
                    (DOMString::from(&**value), window_from_node(self).get_url())
                }));
            },
            _ => {},
        }
    }

    fn parse_plain_attribute(&self, name: &Atom, value: DOMString) -> AttrValue {
        match name {
            &atom!("name") => AttrValue::from_atomic(value),
            &atom!("width") | &atom!("height") |
            &atom!("hspace") | &atom!("vspace") => AttrValue::from_u32(value, 0),
            _ => self.super_type().unwrap().parse_plain_attribute(name, value),
        }
    }
}
<|endoftext|>