<commit_before>// Copyright (c) 2016 The vulkano developers
// Licensed under the Apache License, Version 2.0
// <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT
// license <LICENSE-MIT or http://opensource.org/licenses/MIT>,
// at your option. All files in the project carrying such
// notice may not be copied, modified, or distributed except
// according to those terms.

use std::ops::Range;

use buffer::BufferSlice;
use buffer::sys::UnsafeBuffer;
use device::DeviceOwned;
use device::Queue;
use image::ImageAccess;
use memory::Content;
use sync::AccessError;

use SafeDeref;

/// Trait for objects that represent a way for the GPU to have access to a buffer or a slice of a
/// buffer.
///
/// See also `TypedBufferAccess`.
pub unsafe trait BufferAccess: DeviceOwned {
    /// Returns the inner information about this buffer.
    fn inner(&self) -> BufferInner;

    /// Returns the size of the buffer in bytes.
    fn size(&self) -> usize;

    /// Returns the length of the buffer in number of elements.
    ///
    /// This method can only be called for buffers whose type is known to be an array.
    #[inline]
    fn len(&self) -> usize
        where Self: TypedBufferAccess,
              Self::Content: Content
    {
        self.size() / <Self::Content as Content>::indiv_size()
    }

    /// Builds a `BufferSlice` object holding the buffer by reference.
    #[inline]
    fn as_buffer_slice(&self) -> BufferSlice<Self::Content, &Self>
        where Self: Sized + TypedBufferAccess
    {
        BufferSlice::from_typed_buffer_access(self)
    }

    /// Builds a `BufferSlice` object holding part of the buffer by reference.
    ///
    /// This method can only be called for buffers whose type is known to be an array.
    ///
    /// This method can be used when you want to perform an operation on some part of the buffer
    /// and not on the whole buffer.
    ///
    /// Returns `None` if out of range.
    #[inline]
    fn slice<T>(&self, range: Range<usize>) -> Option<BufferSlice<[T], &Self>>
        where Self: Sized + TypedBufferAccess<Content = [T]>
    {
        BufferSlice::slice(self.as_buffer_slice(), range)
    }

    /// Builds a `BufferSlice` object holding the buffer by value.
    #[inline]
    fn into_buffer_slice(self) -> BufferSlice<Self::Content, Self>
        where Self: Sized + TypedBufferAccess
    {
        BufferSlice::from_typed_buffer_access(self)
    }

    /// Builds a `BufferSlice` object holding part of the buffer by reference.
    ///
    /// This method can only be called for buffers whose type is known to be an array.
    ///
    /// This method can be used when you want to perform an operation on a specific element of the
    /// buffer and not on the whole buffer.
    ///
    /// Returns `None` if out of range.
    #[inline]
    fn index<T>(&self, index: usize) -> Option<BufferSlice<[T], &Self>>
        where Self: Sized + TypedBufferAccess<Content = [T]>
    {
        self.slice(index .. (index + 1))
    }

    /// Returns true if an access to `self` potentially overlaps the same memory as an access to
    /// `other`.
    ///
    /// If this function returns `false`, this means that we are allowed to mutably access the
    /// content of `self` at the same time as the content of `other` without causing a data
    /// race.
    ///
    /// Note that the function must be transitive. In other words if `conflicts(a, b)` is true and
    /// `conflicts(b, c)` is true, then `conflicts(a, c)` must be true as well.
    fn conflicts_buffer(&self, other: &BufferAccess) -> bool;

    /// Returns true if an access to `self` potentially overlaps the same memory as an access to
    /// `other`.
    ///
    /// If this function returns `false`, this means that we are allowed to mutably access the
    /// content of `self` at the same time as the content of `other` without causing a data
    /// race.
    ///
    /// Note that the function must be transitive. In other words if `conflicts(a, b)` is true and
    /// `conflicts(b, c)` is true, then `conflicts(a, c)` must be true as well.
    fn conflicts_image(&self, other: &ImageAccess) -> bool;

    /// Returns a key that uniquely identifies the buffer. Two buffers or images that potentially
    /// overlap in memory must return the same key.
    ///
    /// The key is shared amongst all buffers and images, which means that you can make several
    /// different buffer objects share the same memory, or make some buffer objects share memory
    /// with images, as long as they return the same key.
    ///
    /// Since it is possible to accidentally return the same key for memory ranges that don't
    /// overlap, the `conflicts_buffer` or `conflicts_image` function should always be called to
    /// verify whether they actually overlap.
    fn conflict_key(&self) -> u64;

    /// Locks the resource for usage on the GPU. Returns an error if the lock can't be acquired.
    ///
    /// This function exists to prevent the user from causing a data race by reading and writing
    /// to the same resource at the same time.
    ///
    /// If you call this function, you should call `unlock()` once the resource is no longer in use
    /// by the GPU. The implementation is not expected to automatically perform any unlocking and
    /// can rely on the fact that `unlock()` is going to be called.
    fn try_gpu_lock(&self, exclusive_access: bool, queue: &Queue) -> Result<(), AccessError>;

    /// Locks the resource for usage on the GPU. Supposes that the resource is already locked, and
    /// simply increases the lock by one.
    ///
    /// Must only be called after `try_gpu_lock()` succeeded.
    ///
    /// If you call this function, you should call `unlock()` once the resource is no longer in use
    /// by the GPU. The implementation is not expected to automatically perform any unlocking and
    /// can rely on the fact that `unlock()` is going to be called.
    unsafe fn increase_gpu_lock(&self);

    /// Unlocks the resource previously acquired with `try_gpu_lock` or `increase_gpu_lock`.
    ///
    /// # Safety
    ///
    /// Must only be called once per previous lock.
    unsafe fn unlock(&self);
}

/// Inner information about a buffer.
#[derive(Copy, Clone, Debug)]
pub struct BufferInner<'a> {
    /// The underlying buffer object.
    pub buffer: &'a UnsafeBuffer,
    /// The offset in bytes from the start of the underlying buffer object to the start of the
    /// buffer we're describing.
    pub offset: usize,
}

unsafe impl<T> BufferAccess for T
    where T: SafeDeref,
          T::Target: BufferAccess
{
    #[inline]
    fn inner(&self) -> BufferInner {
        (**self).inner()
    }

    #[inline]
    fn size(&self) -> usize {
        (**self).size()
    }

    #[inline]
    fn conflicts_buffer(&self, other: &BufferAccess) -> bool {
        (**self).conflicts_buffer(other)
    }

    #[inline]
    fn conflicts_image(&self, other: &ImageAccess) -> bool {
        (**self).conflicts_image(other)
    }

    #[inline]
    fn conflict_key(&self) -> u64 {
        (**self).conflict_key()
    }

    #[inline]
    fn try_gpu_lock(&self, exclusive_access: bool, queue: &Queue) -> Result<(), AccessError> {
        (**self).try_gpu_lock(exclusive_access, queue)
    }

    #[inline]
    unsafe fn increase_gpu_lock(&self) {
        (**self).increase_gpu_lock()
    }

    #[inline]
    unsafe fn unlock(&self) {
        (**self).unlock()
    }
}

/// Extension trait for `BufferAccess`. Indicates the type of the content of the buffer.
pub unsafe trait TypedBufferAccess: BufferAccess {
    /// The type of the content.
    type Content: ?Sized;
}

unsafe impl<T> TypedBufferAccess for T
    where T: SafeDeref,
          T::Target: TypedBufferAccess
{
    type Content = <T::Target as TypedBufferAccess>::Content;
}
<commit_msg>Fix future object safety of BufferAccess (#966)<commit_after>// Copyright (c) 2016 The vulkano developers
// Licensed under the Apache License, Version 2.0
// <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT
// license <LICENSE-MIT or http://opensource.org/licenses/MIT>,
// at your option. All files in the project carrying such
// notice may not be copied, modified, or distributed except
// according to those terms.

use std::ops::Range;

use buffer::BufferSlice;
use buffer::sys::UnsafeBuffer;
use device::DeviceOwned;
use device::Queue;
use image::ImageAccess;
use memory::Content;
use sync::AccessError;

use SafeDeref;

/// Trait for objects that represent a way for the GPU to have access to a buffer or a slice of a
/// buffer.
///
/// See also `TypedBufferAccess`.
pub unsafe trait BufferAccess: DeviceOwned {
    /// Returns the inner information about this buffer.
    fn inner(&self) -> BufferInner;

    /// Returns the size of the buffer in bytes.
    fn size(&self) -> usize;

    /// Builds a `BufferSlice` object holding the buffer by reference.
    #[inline]
    fn as_buffer_slice(&self) -> BufferSlice<Self::Content, &Self>
        where Self: Sized + TypedBufferAccess
    {
        BufferSlice::from_typed_buffer_access(self)
    }

    /// Builds a `BufferSlice` object holding part of the buffer by reference.
    ///
    /// This method can only be called for buffers whose type is known to be an array.
    ///
    /// This method can be used when you want to perform an operation on some part of the buffer
    /// and not on the whole buffer.
    ///
    /// Returns `None` if out of range.
    #[inline]
    fn slice<T>(&self, range: Range<usize>) -> Option<BufferSlice<[T], &Self>>
        where Self: Sized + TypedBufferAccess<Content = [T]>
    {
        BufferSlice::slice(self.as_buffer_slice(), range)
    }

    /// Builds a `BufferSlice` object holding the buffer by value.
    #[inline]
    fn into_buffer_slice(self) -> BufferSlice<Self::Content, Self>
        where Self: Sized + TypedBufferAccess
    {
        BufferSlice::from_typed_buffer_access(self)
    }

    /// Builds a `BufferSlice` object holding part of the buffer by reference.
    ///
    /// This method can only be called for buffers whose type is known to be an array.
    ///
    /// This method can be used when you want to perform an operation on a specific element of the
    /// buffer and not on the whole buffer.
    ///
    /// Returns `None` if out of range.
    #[inline]
    fn index<T>(&self, index: usize) -> Option<BufferSlice<[T], &Self>>
        where Self: Sized + TypedBufferAccess<Content = [T]>
    {
        self.slice(index .. (index + 1))
    }

    /// Returns true if an access to `self` potentially overlaps the same memory as an access to
    /// `other`.
    ///
    /// If this function returns `false`, this means that we are allowed to mutably access the
    /// content of `self` at the same time as the content of `other` without causing a data
    /// race.
    ///
    /// Note that the function must be transitive. In other words if `conflicts(a, b)` is true and
    /// `conflicts(b, c)` is true, then `conflicts(a, c)` must be true as well.
    fn conflicts_buffer(&self, other: &BufferAccess) -> bool;

    /// Returns true if an access to `self` potentially overlaps the same memory as an access to
    /// `other`.
    ///
    /// If this function returns `false`, this means that we are allowed to mutably access the
    /// content of `self` at the same time as the content of `other` without causing a data
    /// race.
    ///
    /// Note that the function must be transitive. In other words if `conflicts(a, b)` is true and
    /// `conflicts(b, c)` is true, then `conflicts(a, c)` must be true as well.
    fn conflicts_image(&self, other: &ImageAccess) -> bool;

    /// Returns a key that uniquely identifies the buffer. Two buffers or images that potentially
    /// overlap in memory must return the same key.
    ///
    /// The key is shared amongst all buffers and images, which means that you can make several
    /// different buffer objects share the same memory, or make some buffer objects share memory
    /// with images, as long as they return the same key.
    ///
    /// Since it is possible to accidentally return the same key for memory ranges that don't
    /// overlap, the `conflicts_buffer` or `conflicts_image` function should always be called to
    /// verify whether they actually overlap.
    fn conflict_key(&self) -> u64;

    /// Locks the resource for usage on the GPU. Returns an error if the lock can't be acquired.
    ///
    /// This function exists to prevent the user from causing a data race by reading and writing
    /// to the same resource at the same time.
    ///
    /// If you call this function, you should call `unlock()` once the resource is no longer in use
    /// by the GPU. The implementation is not expected to automatically perform any unlocking and
    /// can rely on the fact that `unlock()` is going to be called.
    fn try_gpu_lock(&self, exclusive_access: bool, queue: &Queue) -> Result<(), AccessError>;

    /// Locks the resource for usage on the GPU. Supposes that the resource is already locked, and
    /// simply increases the lock by one.
    ///
    /// Must only be called after `try_gpu_lock()` succeeded.
    ///
    /// If you call this function, you should call `unlock()` once the resource is no longer in use
    /// by the GPU. The implementation is not expected to automatically perform any unlocking and
    /// can rely on the fact that `unlock()` is going to be called.
    unsafe fn increase_gpu_lock(&self);

    /// Unlocks the resource previously acquired with `try_gpu_lock` or `increase_gpu_lock`.
    ///
    /// # Safety
    ///
    /// Must only be called once per previous lock.
    unsafe fn unlock(&self);
}

/// Inner information about a buffer.
#[derive(Copy, Clone, Debug)]
pub struct BufferInner<'a> {
    /// The underlying buffer object.
    pub buffer: &'a UnsafeBuffer,
    /// The offset in bytes from the start of the underlying buffer object to the start of the
    /// buffer we're describing.
    pub offset: usize,
}

unsafe impl<T> BufferAccess for T
    where T: SafeDeref,
          T::Target: BufferAccess
{
    #[inline]
    fn inner(&self) -> BufferInner {
        (**self).inner()
    }

    #[inline]
    fn size(&self) -> usize {
        (**self).size()
    }

    #[inline]
    fn conflicts_buffer(&self, other: &BufferAccess) -> bool {
        (**self).conflicts_buffer(other)
    }

    #[inline]
    fn conflicts_image(&self, other: &ImageAccess) -> bool {
        (**self).conflicts_image(other)
    }

    #[inline]
    fn conflict_key(&self) -> u64 {
        (**self).conflict_key()
    }

    #[inline]
    fn try_gpu_lock(&self, exclusive_access: bool, queue: &Queue) -> Result<(), AccessError> {
        (**self).try_gpu_lock(exclusive_access, queue)
    }

    #[inline]
    unsafe fn increase_gpu_lock(&self) {
        (**self).increase_gpu_lock()
    }

    #[inline]
    unsafe fn unlock(&self) {
        (**self).unlock()
    }
}

/// Extension trait for `BufferAccess`. Indicates the type of the content of the buffer.
pub unsafe trait TypedBufferAccess: BufferAccess {
    /// The type of the content.
    type Content: ?Sized;

    /// Returns the length of the buffer in number of elements.
    ///
    /// This method can only be called for buffers whose type is known to be an array.
    #[inline]
    fn len(&self) -> usize where Self::Content: Content {
        self.size() / <Self::Content as Content>::indiv_size()
    }
}

unsafe impl<T> TypedBufferAccess for T
    where T: SafeDeref,
          T::Target: TypedBufferAccess
{
    type Content = <T::Target as TypedBufferAccess>::Content;
}
<|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 cssparser::{Parser, Token, SourcePosition};
use properties::DeclaredValue;
use std::collections::{HashMap, HashSet};
use std::sync::Arc;
use string_cache::Atom;

#[derive(Clone, PartialEq)]
pub struct Value {
    /// In CSS syntax
    value: String,

    /// Custom property names in var() functions. Do not include the `--` prefix.
    references: HashSet<Atom>,
}

pub struct BorrowedValue<'a> {
    value: &'a str,
    references: Option<&'a HashSet<Atom>>,
}

pub fn parse(input: &mut Parser) -> Result<Value, ()> {
    let start = input.position();
    let mut references = HashSet::new();
    try!(parse_declaration_value(input, &mut references));
    Ok(Value {
        value: input.slice_from(start).to_owned(),
        references: references,
    })
}

/// https://drafts.csswg.org/css-syntax-3/#typedef-declaration-value
fn parse_declaration_value(input: &mut Parser, references: &mut HashSet<Atom>) -> Result<(), ()> {
    if input.is_exhausted() {
        // Need at least one token
        return Err(())
    }
    while let Ok(token) = input.next() {
        match token {
            Token::BadUrl |
            Token::BadString |
            Token::CloseParenthesis |
            Token::CloseSquareBracket |
            Token::CloseCurlyBracket |

            Token::Semicolon |
            Token::Delim('!') => {
                return Err(())
            }

            Token::Function(ref name) if name == "var" => {
                try!(input.parse_nested_block(|input| {
                    parse_var_function(input, references)
                }));
            }

            Token::Function(_) |
            Token::ParenthesisBlock |
            Token::CurlyBracketBlock |
            Token::SquareBracketBlock => {
                try!(input.parse_nested_block(|input| {
                    parse_declaration_value_block(input, references)
                }));
            }

            _ => {}
        }
    }
    Ok(())
}

/// Like parse_declaration_value,
/// but accept `!` and `;` since they are only invalid at the top level
fn parse_declaration_value_block(input: &mut Parser, references: &mut HashSet<Atom>)
                                 -> Result<(), ()> {
    while let Ok(token) = input.next() {
        match token {
            Token::BadUrl |
            Token::BadString |
            Token::CloseParenthesis |
            Token::CloseSquareBracket |
            Token::CloseCurlyBracket => {
                return Err(())
            }

            Token::Function(ref name) if name == "var" => {
                try!(input.parse_nested_block(|input| {
                    parse_var_function(input, references)
                }));
            }

            Token::Function(_) |
            Token::ParenthesisBlock |
            Token::CurlyBracketBlock |
            Token::SquareBracketBlock => {
                try!(input.parse_nested_block(|input| {
                    parse_declaration_value_block(input, references)
                }));
            }

            _ => {}
        }
    }
    Ok(())
}

// If the var function is valid, return Ok((custom_property_name, fallback))
fn parse_var_function<'i, 't>(input: &mut Parser<'i, 't>, references: &mut HashSet<Atom>)
                              -> Result<(), ()> {
    // https://drafts.csswg.org/css-variables/#typedef-custom-property-name
    let name = try!(input.expect_ident());
    let name = if name.starts_with("--") {
        &name[2..]
    } else {
        return Err(())
    };
    if input.expect_comma().is_ok() {
        try!(parse_declaration_value(input, references));
    }
    references.insert(Atom::from_slice(name));
    Ok(())
}

/// Add one custom property declaration to a map,
/// unless another with the same name was already there.
pub fn cascade<'a>(custom_properties: &mut Option<HashMap<&'a Atom, BorrowedValue<'a>>>,
                   inherited_custom_properties: &'a Option<Arc<HashMap<Atom, String>>>,
                   seen: &mut HashSet<&'a Atom>,
                   name: &'a Atom,
                   value: &'a DeclaredValue<Value>) {
    let was_not_already_present = seen.insert(name);
    if was_not_already_present {
        let map = match *custom_properties {
            Some(ref mut map) => map,
            None => {
                *custom_properties = Some(match *inherited_custom_properties {
                    Some(ref inherited) => inherited.iter().map(|(key, value)| {
                        (key, BorrowedValue { value: &value, references: None })
                    }).collect(),
                    None => HashMap::new(),
                });
                custom_properties.as_mut().unwrap()
            }
        };
        match *value {
            DeclaredValue::Value(ref value) => {
                map.insert(name, BorrowedValue {
                    value: &value.value,
                    references: Some(&value.references),
                });
            }
            DeclaredValue::Initial => {
                map.remove(&name);
            }
            DeclaredValue::Inherit => {}  // The inherited value is what we already have.
        }
    }
}

pub fn finish_cascade(custom_properties: Option<HashMap<&Atom, BorrowedValue>>,
                      inherited_custom_properties: &Option<Arc<HashMap<Atom, String>>>)
                      -> Option<Arc<HashMap<Atom, String>>> {
    if let Some(mut map) = custom_properties {
        remove_cycles(&mut map);
        Some(Arc::new(substitute_all(map)))
    } else {
        inherited_custom_properties.clone()
    }
}

/// https://drafts.csswg.org/css-variables/#cycles
fn remove_cycles(map: &mut HashMap<&Atom, BorrowedValue>) {
    let mut to_remove = HashSet::new();
    {
        let mut visited = HashSet::new();
        let mut stack = Vec::new();
        for name in map.keys() {
            walk(map, name, &mut stack, &mut visited, &mut to_remove);

            fn walk<'a>(map: &HashMap<&'a Atom, BorrowedValue<'a>>,
                        name: &'a Atom,
                        stack: &mut Vec<&'a Atom>,
                        visited: &mut HashSet<&'a Atom>,
                        to_remove: &mut HashSet<Atom>) {
                let was_not_already_present = visited.insert(name);
                if !was_not_already_present {
                    return
                }
                if let Some(value) = map.get(name) {
                    if let Some(references) = value.references {
                        stack.push(name);
                        for next in references {
                            if let Some(position) = stack.position_elem(&next) {
                                // Found a cycle
                                for in_cycle in &stack[position..] {
                                    to_remove.insert((**in_cycle).clone());
                                }
                            } else {
                                walk(map, next, stack, visited, to_remove);
                            }
                        }
                        stack.pop();
                    }
                }
            }
        }
    }
    for name in &to_remove {
        map.remove(name);
    }
}

fn substitute_all(custom_properties: HashMap<&Atom, BorrowedValue>) -> HashMap<Atom, String> {
    let mut substituted_map = HashMap::new();
    let mut invalid = HashSet::new();
    for (&name, value) in &custom_properties {
        // If this value is invalid at computed time it won’t be inserted in substituted_map.
        // Nothing else to do.
        let _ = substitute_one(
            name, value, &custom_properties, None, &mut substituted_map, &mut invalid);
    }
    substituted_map
}

fn substitute_one(name: &Atom,
                  value: &BorrowedValue,
                  custom_properties: &HashMap<&Atom, BorrowedValue>,
                  substituted: Option<&mut String>,
                  substituted_map: &mut HashMap<Atom, String>,
                  invalid: &mut HashSet<Atom>)
                  -> Result<(), ()> {
    if let Some(value) = substituted_map.get(name) {
        if let Some(substituted) = substituted {
            substituted.push_str(value)
        }
        return Ok(())
    }

    if invalid.contains(name) {
        return Err(());
    }
    let value = if let Some(references) = value.references {
        if !references.is_empty() {
            let mut substituted = String::new();
            let mut input = Parser::new(&value.value);
            let mut start = input.position();
            if substitute_block(
                custom_properties, &mut input, &mut start, &mut substituted,
                substituted_map, invalid,
            ).is_err() {
                invalid.insert(name.clone());
                return Err(())
            }
            substituted.push_str(input.slice_from(start));
            substituted
        } else {
            value.value.to_owned()
        }
    } else {
        value.value.to_owned()
    };
    if let Some(substituted) = substituted {
        substituted.push_str(&value)
    }
    substituted_map.insert(name.clone(), value);
    Ok(())
}

fn substitute_block(custom_properties: &HashMap<&Atom, BorrowedValue>,
                    input: &mut Parser,
                    start: &mut SourcePosition,
                    substituted: &mut String,
                    substituted_map: &mut HashMap<Atom, String>,
                    invalid: &mut HashSet<Atom>)
                    -> Result<(), ()> {
    while let Ok(token) = input.next() {
        match token {
            Token::Function(ref name) if name == "var" => {
                substituted.push_str(input.slice_from(*start));
                try!(input.parse_nested_block(|input| {
                    let name = input.expect_ident().unwrap();
                    debug_assert!(name.starts_with("--"));
                    let name = Atom::from_slice(&name[2..]);

                    if let Some(value) = custom_properties.get(&name) {
                        try!(substitute_one(
                            &name, value, custom_properties,
                            Some(substituted), substituted_map, invalid));
                        // Skip over the fallback, as `parse_nested_block` would return `Err`
                        // if we don’t consume all of `input`.
                        // FIXME: Add a specialized method to cssparser to do this with less work.
                        while let Ok(_) = input.next() {}
                    } else {
                        try!(input.expect_comma());
                        let mut start = input.position();
                        try!(substitute_block(
                            custom_properties, input, &mut start, substituted,
                            substituted_map, invalid));
                        substituted.push_str(input.slice_from(start));
                    }
                    Ok(())
                }));
                *start = input.position();
            }

            Token::Function(_) |
            Token::ParenthesisBlock |
            Token::CurlyBracketBlock |
            Token::SquareBracketBlock => {
                try!(input.parse_nested_block(|input| substitute_block(
                    custom_properties, input, start, substituted, substituted_map, invalid)));
            }

            _ => {}
        }
    }
    Ok(())
}
<commit_msg>Add a FIXME comment of EOF handling in custom properties.<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 cssparser::{Parser, Token, SourcePosition};
use properties::DeclaredValue;
use std::collections::{HashMap, HashSet};
use std::sync::Arc;
use string_cache::Atom;

#[derive(Clone, PartialEq)]
pub struct Value {
    /// In CSS syntax
    value: String,

    /// Custom property names in var() functions. Do not include the `--` prefix.
    references: HashSet<Atom>,
}

pub struct BorrowedValue<'a> {
    value: &'a str,
    references: Option<&'a HashSet<Atom>>,
}

pub fn parse(input: &mut Parser) -> Result<Value, ()> {
    let start = input.position();
    let mut references = HashSet::new();
    try!(parse_declaration_value(input, &mut references));
    Ok(Value {
        value: input.slice_from(start).to_owned(),
        references: references,
    })
}

/// https://drafts.csswg.org/css-syntax-3/#typedef-declaration-value
fn parse_declaration_value(input: &mut Parser, references: &mut HashSet<Atom>) -> Result<(), ()> {
    if input.is_exhausted() {
        // Need at least one token
        return Err(())
    }
    while let Ok(token) = input.next() {
        match token {
            Token::BadUrl |
            Token::BadString |
            Token::CloseParenthesis |
            Token::CloseSquareBracket |
            Token::CloseCurlyBracket |

            Token::Semicolon |
            Token::Delim('!') => {
                return Err(())
            }

            Token::Function(ref name) if name == "var" => {
                try!(input.parse_nested_block(|input| {
                    parse_var_function(input, references)
                }));
            }

            Token::Function(_) |
            Token::ParenthesisBlock |
            Token::CurlyBracketBlock |
            Token::SquareBracketBlock => {
                try!(input.parse_nested_block(|input| {
                    parse_declaration_value_block(input, references)
                }));
            }

            _ => {}
        }
    }
    Ok(())
}

/// Like parse_declaration_value,
/// but accept `!` and `;` since they are only invalid at the top level
fn parse_declaration_value_block(input: &mut Parser, references: &mut HashSet<Atom>)
                                 -> Result<(), ()> {
    while let Ok(token) = input.next() {
        match token {
            Token::BadUrl |
            Token::BadString |
            Token::CloseParenthesis |
            Token::CloseSquareBracket |
            Token::CloseCurlyBracket => {
                return Err(())
            }

            Token::Function(ref name) if name == "var" => {
                try!(input.parse_nested_block(|input| {
                    parse_var_function(input, references)
                }));
            }

            Token::Function(_) |
            Token::ParenthesisBlock |
            Token::CurlyBracketBlock |
            Token::SquareBracketBlock => {
                try!(input.parse_nested_block(|input| {
                    parse_declaration_value_block(input, references)
                }));
            }

            _ => {}
        }
    }
    Ok(())
}

// If the var function is valid, return Ok((custom_property_name, fallback))
fn parse_var_function<'i, 't>(input: &mut Parser<'i, 't>, references: &mut HashSet<Atom>)
                              -> Result<(), ()> {
    // https://drafts.csswg.org/css-variables/#typedef-custom-property-name
    let name = try!(input.expect_ident());
    let name = if name.starts_with("--") {
        &name[2..]
    } else {
        return Err(())
    };
    if input.expect_comma().is_ok() {
        try!(parse_declaration_value(input, references));
    }
    references.insert(Atom::from_slice(name));
    Ok(())
}

/// Add one custom property declaration to a map,
/// unless another with the same name was already there.
pub fn cascade<'a>(custom_properties: &mut Option<HashMap<&'a Atom, BorrowedValue<'a>>>,
                   inherited_custom_properties: &'a Option<Arc<HashMap<Atom, String>>>,
                   seen: &mut HashSet<&'a Atom>,
                   name: &'a Atom,
                   value: &'a DeclaredValue<Value>) {
    let was_not_already_present = seen.insert(name);
    if was_not_already_present {
        let map = match *custom_properties {
            Some(ref mut map) => map,
            None => {
                *custom_properties = Some(match *inherited_custom_properties {
                    Some(ref inherited) => inherited.iter().map(|(key, value)| {
                        (key, BorrowedValue { value: &value, references: None })
                    }).collect(),
                    None => HashMap::new(),
                });
                custom_properties.as_mut().unwrap()
            }
        };
        match *value {
            DeclaredValue::Value(ref value) => {
                map.insert(name, BorrowedValue {
                    value: &value.value,
                    references: Some(&value.references),
                });
            }
            DeclaredValue::Initial => {
                map.remove(&name);
            }
            DeclaredValue::Inherit => {}  // The inherited value is what we already have.
        }
    }
}

pub fn finish_cascade(custom_properties: Option<HashMap<&Atom, BorrowedValue>>,
                      inherited_custom_properties: &Option<Arc<HashMap<Atom, String>>>)
                      -> Option<Arc<HashMap<Atom, String>>> {
    if let Some(mut map) = custom_properties {
        remove_cycles(&mut map);
        Some(Arc::new(substitute_all(map)))
    } else {
        inherited_custom_properties.clone()
    }
}

/// https://drafts.csswg.org/css-variables/#cycles
fn remove_cycles(map: &mut HashMap<&Atom, BorrowedValue>) {
    let mut to_remove = HashSet::new();
    {
        let mut visited = HashSet::new();
        let mut stack = Vec::new();
        for name in map.keys() {
            walk(map, name, &mut stack, &mut visited, &mut to_remove);

            fn walk<'a>(map: &HashMap<&'a Atom, BorrowedValue<'a>>,
                        name: &'a Atom,
                        stack: &mut Vec<&'a Atom>,
                        visited: &mut HashSet<&'a Atom>,
                        to_remove: &mut HashSet<Atom>) {
                let was_not_already_present = visited.insert(name);
                if !was_not_already_present {
                    return
                }
                if let Some(value) = map.get(name) {
                    if let Some(references) = value.references {
                        stack.push(name);
                        for next in references {
                            if let Some(position) = stack.position_elem(&next) {
                                // Found a cycle
                                for in_cycle in &stack[position..] {
                                    to_remove.insert((**in_cycle).clone());
                                }
                            } else {
                                walk(map, next, stack, visited, to_remove);
                            }
                        }
                        stack.pop();
                    }
                }
            }
        }
    }
    for name in &to_remove {
        map.remove(name);
    }
}

fn substitute_all(custom_properties: HashMap<&Atom, BorrowedValue>) -> HashMap<Atom, String> {
    let mut substituted_map = HashMap::new();
    let mut invalid = HashSet::new();
    for (&name, value) in &custom_properties {
        // If this value is invalid at computed time it won’t be inserted in substituted_map.
        // Nothing else to do.
        let _ = substitute_one(
            name, value, &custom_properties, None, &mut substituted_map, &mut invalid);
    }
    substituted_map
}

fn substitute_one(name: &Atom,
                  value: &BorrowedValue,
                  custom_properties: &HashMap<&Atom, BorrowedValue>,
                  substituted: Option<&mut String>,
                  substituted_map: &mut HashMap<Atom, String>,
                  invalid: &mut HashSet<Atom>)
                  -> Result<(), ()> {
    if let Some(value) = substituted_map.get(name) {
        if let Some(substituted) = substituted {
            substituted.push_str(value)
        }
        return Ok(())
    }

    if invalid.contains(name) {
        return Err(());
    }
    let value = if let Some(references) = value.references {
        if !references.is_empty() {
            let mut substituted = String::new();
            let mut input = Parser::new(&value.value);
            let mut start = input.position();
            if substitute_block(
                custom_properties, &mut input, &mut start, &mut substituted,
                substituted_map, invalid,
            ).is_err() {
                invalid.insert(name.clone());
                return Err(())
            }
            substituted.push_str(input.slice_from(start));
            substituted
        } else {
            value.value.to_owned()
        }
    } else {
        value.value.to_owned()
    };
    if let Some(substituted) = substituted {
        substituted.push_str(&value)
    }
    substituted_map.insert(name.clone(), value);
    Ok(())
}

fn substitute_block(custom_properties: &HashMap<&Atom, BorrowedValue>,
                    input: &mut Parser,
                    start: &mut SourcePosition,
                    substituted: &mut String,
                    substituted_map: &mut HashMap<Atom, String>,
                    invalid: &mut HashSet<Atom>)
                    -> Result<(), ()> {
    while let Ok(token) = input.next() {
        match token {
            Token::Function(ref name) if name == "var" => {
                substituted.push_str(input.slice_from(*start));
                try!(input.parse_nested_block(|input| {
                    let name = input.expect_ident().unwrap();
                    debug_assert!(name.starts_with("--"));
                    let name = Atom::from_slice(&name[2..]);

                    if let Some(value) = custom_properties.get(&name) {
                        try!(substitute_one(
                            &name, value, custom_properties,
                            Some(substituted), substituted_map, invalid));
                        // Skip over the fallback, as `parse_nested_block` would return `Err`
                        // if we don’t consume all of `input`.
                        // FIXME: Add a specialized method to cssparser to do this with less work.
                        while let Ok(_) = input.next() {}
                    } else {
                        try!(input.expect_comma());
                        let mut start = input.position();
                        try!(substitute_block(
                            custom_properties, input, &mut start, substituted,
                            substituted_map, invalid));
                        substituted.push_str(input.slice_from(start));
                    }
                    Ok(())
                }));
                *start = input.position();
            }

            Token::Function(_) |
            Token::ParenthesisBlock |
            Token::CurlyBracketBlock |
            Token::SquareBracketBlock => {
                try!(input.parse_nested_block(|input| substitute_block(
                    custom_properties, input, start, substituted, substituted_map, invalid)));
            }

            _ => {}
        }
    }
    // FIXME: deal with things being implicitly closed at the end of the input. E.g.
    // ```html
    // <div style="--color: rgb(0,0,0">
    // <p style="background: var(--color) var(--image) top left; --image: url('a.png"></p>
    // </div>
    // ```
    Ok(())
}
<|endoftext|>


<commit_before>export writeinstr;
export writer;
export writer_factory;
export writer_util;
export make_writer_factory;
export future_writer_factory;

enum writeinstr {
    write(str),
    done
}

type writer = fn~(+writeinstr);
type writer_factory = fn~(page: doc::page) -> writer;

impl writer_util for writer {
    fn write_str(str: str) {
        self(write(str));
    }

    fn write_line(str: str) {
        self.write_str(str + "\n");
    }

    fn write_done() {
        self(done)
    }
}

fn make_writer_factory(config: config::config) -> writer_factory {
    alt config.output_format {
      config::markdown {
        markdown_writer_factory(config)
      }
      config::pandoc_html {
        pandoc_writer_factory(config)
      }
    }
}

fn markdown_writer_factory(config: config::config) -> writer_factory {
    fn~(page: doc::page) -> writer {
        markdown_writer(config, page)
    }
}

fn pandoc_writer_factory(config: config::config) -> writer_factory {
    fn~(page: doc::page) -> writer {
        pandoc_writer(config, page)
    }
}

fn markdown_writer(
    config: config::config,
    page: doc::page
) -> writer {
    let filename = make_filename(config, "md", page);
    generic_writer {|markdown|
        write_file(filename, markdown);
    }
}

fn pandoc_writer(
    config: config::config,
    page: doc::page
) -> writer {
    assert option::is_some(config.pandoc_cmd);
    let pandoc_cmd = option::get(config.pandoc_cmd);
    let filename = make_filename(config, "html", page);

    let pandoc_args = [
        "--standalone",
        "--section-divs",
        "--from=markdown",
        "--to=html",
        "--css=rust.css",
        "--output=" + filename
    ];

    generic_writer {|markdown|
        import std::run;
        import std::os;
        import std::io;
        import std::io::writer_util;

        #debug("pandoc cmd: %s", pandoc_cmd);
        #debug("pandoc args: %s", str::connect(pandoc_args, " "));

        let pipe_in = os::pipe();
        let pipe_out = os::pipe();
        let pipe_err = os::pipe();
        let pid = run::spawn_process(
            pandoc_cmd, pandoc_args, none, none,
            pipe_in.in, pipe_out.out, pipe_err.out);

        let writer = io::fd_writer(pipe_in.out, false);
        writer.write_str(markdown);

        os::close(pipe_in.in);
        os::close(pipe_out.out);
        os::close(pipe_err.out);
        os::close(pipe_in.out);
        os::close(pipe_out.in);
        os::close(pipe_err.in);

        let status = run::waitpid(pid);
        #debug("pandoc result: %i", status);
        if status != 0 {
            fail "pandoc failed";
        }
    }
}

fn generic_writer(process: fn~(markdown: str)) -> writer {
    let ch = task::spawn_listener {|po: comm::port<writeinstr>|
        let markdown = "";
        let keep_going = true;
        while keep_going {
            alt comm::recv(po) {
              write(s) { markdown += s; }
              done { keep_going = false; }
            }
        }
        process(markdown);
    };

    fn~(+instr: writeinstr) {
        comm::send(ch, instr);
    }
}

fn make_filename(
    config: config::config,
    ext: str,
    _page: doc::page
) -> str {
    import std::fs;
    let cratefile = fs::basename(config.input_crate);
    let cratename = tuple::first(fs::splitext(cratefile));
    fs::connect(config.output_dir, cratename + "." + ext)
}

fn write_file(path: str, s: str) {
    import std::io;
    import std::io::writer_util;

    alt io::file_writer(path, [io::create, io::truncate]) {
      result::ok(writer) {
        writer.write_str(s);
      }
      result::err(e) { fail e }
    }
}

#[test]
fn should_use_markdown_file_name_based_off_crate() {
    let config = {
        output_dir: "output/dir"
        with config::default_config("input/test.rc")
    };
    let doc = test::mk_doc("");
    let page = doc::cratepage(doc.cratedoc());
    assert make_filename(config, "md", page) == "output/dir/test.md";
}

#[cfg(test)]
mod test {
    fn mk_doc(source: str) -> doc::doc {
        astsrv::from_str(source) {|srv|
            extract::from_srv(srv, "")
        }
    }
}

fn future_writer_factory(
) -> (writer_factory, comm::port<(doc::page, str)>) {
    let markdown_po = comm::port();
    let markdown_ch = comm::chan(markdown_po);
    let writer_factory = fn~(page: doc::page) -> writer {
        let writer_po = comm::port();
        let writer_ch = comm::chan(writer_po);
        task::spawn {||
            let (writer, future) = future_writer();
            comm::send(writer_ch, writer);
            let s = future::get(future);
            comm::send(markdown_ch, (page, s));
        }
        comm::recv(writer_po)
    };

    (writer_factory, markdown_po)
}

fn future_writer() -> (writer, future::future<str>) {
    let port = comm::port();
    let chan = comm::chan(port);
    let writer = fn~(+instr: writeinstr) {
        comm::send(chan, copy instr);
    };
    let future = future::from_fn {||
        let res = "";
        while true {
            alt comm::recv(port) {
              write(s) { res += s }
              done { break }
            }
        }
        res
    };
    (writer, future)
}
<commit_msg>rustdoc: Build appropriate filenames for pages<commit_after>export writeinstr;
export writer;
export writer_factory;
export writer_util;
export make_writer_factory;
export future_writer_factory;

enum writeinstr {
    write(str),
    done
}

type writer = fn~(+writeinstr);
type writer_factory = fn~(page: doc::page) -> writer;

impl writer_util for writer {
    fn write_str(str: str) {
        self(write(str));
    }

    fn write_line(str: str) {
        self.write_str(str + "\n");
    }

    fn write_done() {
        self(done)
    }
}

fn make_writer_factory(config: config::config) -> writer_factory {
    alt config.output_format {
      config::markdown {
        markdown_writer_factory(config)
      }
      config::pandoc_html {
        pandoc_writer_factory(config)
      }
    }
}

fn markdown_writer_factory(config: config::config) -> writer_factory {
    fn~(page: doc::page) -> writer {
        markdown_writer(config, page)
    }
}

fn pandoc_writer_factory(config: config::config) -> writer_factory {
    fn~(page: doc::page) -> writer {
        pandoc_writer(config, page)
    }
}

fn markdown_writer(
    config: config::config,
    page: doc::page
) -> writer {
    let filename = make_filename(config, page);
    generic_writer {|markdown|
        write_file(filename, markdown);
    }
}

fn pandoc_writer(
    config: config::config,
    page: doc::page
) -> writer {
    assert option::is_some(config.pandoc_cmd);
    let pandoc_cmd = option::get(config.pandoc_cmd);
    let filename = make_filename(config, page);

    let pandoc_args = [
        "--standalone",
        "--section-divs",
        "--from=markdown",
        "--to=html",
        "--css=rust.css",
        "--output=" + filename
    ];

    generic_writer {|markdown|
        import std::run;
        import std::os;
        import std::io;
        import std::io::writer_util;

        #debug("pandoc cmd: %s", pandoc_cmd);
        #debug("pandoc args: %s", str::connect(pandoc_args, " "));

        let pipe_in = os::pipe();
        let pipe_out = os::pipe();
        let pipe_err = os::pipe();
        let pid = run::spawn_process(
            pandoc_cmd, pandoc_args, none, none,
            pipe_in.in, pipe_out.out, pipe_err.out);

        let writer = io::fd_writer(pipe_in.out, false);
        writer.write_str(markdown);

        os::close(pipe_in.in);
        os::close(pipe_out.out);
        os::close(pipe_err.out);
        os::close(pipe_in.out);
        os::close(pipe_out.in);
        os::close(pipe_err.in);

        let status = run::waitpid(pid);
        #debug("pandoc result: %i", status);
        if status != 0 {
            fail "pandoc failed";
        }
    }
}

fn generic_writer(process: fn~(markdown: str)) -> writer {
    let ch = task::spawn_listener {|po: comm::port<writeinstr>|
        let markdown = "";
        let keep_going = true;
        while keep_going {
            alt comm::recv(po) {
              write(s) { markdown += s; }
              done { keep_going = false; }
            }
        }
        process(markdown);
    };

    fn~(+instr: writeinstr) {
        comm::send(ch, instr);
    }
}

fn make_filename(
    config: config::config,
    page: doc::page
) -> str {
    import std::fs;

    let filename = {
        alt page {
          doc::cratepage(doc) {
            if config.output_format == config::pandoc_html &&
                config.output_style == config::doc_per_mod {
                "index"
            } else {
                assert doc.topmod.name() != "";
                doc.topmod.name()
            }
          }
          doc::itempage(doc) {
            str::connect(doc.path() + [doc.name()], "_")
          }
        }
    };
    let ext = alt config.output_format {
      config::markdown { "md" }
      config::pandoc_html { "html" }
    };
    fs::connect(config.output_dir, filename + "." + ext)
}

#[test]
fn should_use_markdown_file_name_based_off_crate() {
    let config = {
        output_dir: "output/dir",
        output_format: config::markdown,
        output_style: config::doc_per_crate
        with config::default_config("input/test.rc")
    };
    let doc = test::mk_doc("test", "");
    let page = doc::cratepage(doc.cratedoc());
    let filename = make_filename(config, page);
    assert filename == "output/dir/test.md";
}

#[test]
fn should_name_html_crate_file_name_index_html_when_doc_per_mod() {
    let config = {
        output_dir: "output/dir",
        output_format: config::pandoc_html,
        output_style: config::doc_per_mod
        with config::default_config("input/test.rc")
    };
    let doc = test::mk_doc("", "");
    let page = doc::cratepage(doc.cratedoc());
    let filename = make_filename(config, page);
    assert filename == "output/dir/index.html";
}

#[test]
fn should_name_mod_file_names_by_path() {
    let config = {
        output_dir: "output/dir",
        output_format: config::pandoc_html,
        output_style: config::doc_per_mod
        with config::default_config("input/test.rc")
    };
    let doc = test::mk_doc("", "mod a { mod b { } }");
    let modb = doc.cratemod().mods()[0].mods()[0];
    let page = doc::itempage(doc::modtag(modb));
    let filename = make_filename(config, page);
    assert  filename == "output/dir/a_b.html";
}

#[cfg(test)]
mod test {
    fn mk_doc(name: str, source: str) -> doc::doc {
        astsrv::from_str(source) {|srv|
            let doc = extract::from_srv(srv, name);
            let doc = path_pass::mk_pass().f(srv, doc);
            doc
        }
    }
}

fn write_file(path: str, s: str) {
    import std::io;
    import std::io::writer_util;

    alt io::file_writer(path, [io::create, io::truncate]) {
      result::ok(writer) {
        writer.write_str(s);
      }
      result::err(e) { fail e }
    }
}

fn future_writer_factory(
) -> (writer_factory, comm::port<(doc::page, str)>) {
    let markdown_po = comm::port();
    let markdown_ch = comm::chan(markdown_po);
    let writer_factory = fn~(page: doc::page) -> writer {
        let writer_po = comm::port();
        let writer_ch = comm::chan(writer_po);
        task::spawn {||
            let (writer, future) = future_writer();
            comm::send(writer_ch, writer);
            let s = future::get(future);
            comm::send(markdown_ch, (page, s));
        }
        comm::recv(writer_po)
    };

    (writer_factory, markdown_po)
}

fn future_writer() -> (writer, future::future<str>) {
    let port = comm::port();
    let chan = comm::chan(port);
    let writer = fn~(+instr: writeinstr) {
        comm::send(chan, copy instr);
    };
    let future = future::from_fn {||
        let res = "";
        while true {
            alt comm::recv(port) {
              write(s) { res += s }
              done { break }
            }
        }
        res
    };
    (writer, future)
}
<|endoftext|>


<commit_before>use std::env;

use dotenv::dotenv;
use diesel;
use diesel::result::Error as DieselErr;
use diesel::prelude::*;
use diesel::pg::PgConnection;

use super::models::*;

pub struct Backend {
    connection: PgConnection
}

impl Backend {
    pub fn new() -> Self {
        dotenv().ok();

        let database_url = env::var("DATABASE_URL")
            .expect("DATABASE_URL must be set");
        let connection = PgConnection::establish(&database_url)
            .expect(&format!("Error connecting to {}", database_url));

        Self {
            connection
        }
    }

    pub fn vote(&self, user: &str, entity: &str, up: i32, down: i32) {
        use super::schema::{users, voteables, votes};

        use super::schema::users::dsl as us;
        let mut res: Vec<User> = us::users.filter(us::user_id.eq(user)).load(&self.connection).unwrap();
        let user = match res.len() {
            0 => {
                let new_user = NewUser { user_id: user };
                diesel::insert(&new_user).into(users::table)
                    .get_result(&self.connection)
                    .expect("Error creating new user")
            },
            _ => res.pop().unwrap(),
        };

        use super::schema::voteables::dsl::*;
        let mut res: Vec<Voteable> = voteables.filter(value.eq(entity)).load(&self.connection).unwrap();
        let mut voteable= match res.len() {
            0 => {
                let new_voteable = NewVoteable {
                    value: entity,
                    total_up: 0,
                    total_down: 0,
                };

                diesel::insert(&new_voteable).into(voteables::table)
                    .get_result(&self.connection)
                    .expect("Error creating new voteable")
            },

            _ => res.pop().unwrap(),
        };

        voteable.total_up += up;
        voteable.total_down += down;
        voteable.save_changes::<Voteable>(&self.connection);

        use super::schema::votes::dsl as vts;
        let mut res: Vec<Vote> = vts::votes.filter(vts::user_id.eq(user.id))
                                           .filter(vts::voteable_id.eq(voteable.id))
                                           .load(&self.connection).unwrap();
        let mut vote = match res.len() {
            0 => {
                let new_vote = NewVote{
                    user_id: user.id,
                    voteable_id: voteable.id,
                    up: 0,
                    down: 0,
                };

                diesel::insert(&new_vote).into(votes::table)
                    .get_result(&self.connection)
                    .expect("Error creating new vote")
            },
            _ => res.pop().unwrap(),
        };

        vote.up += up;
        vote.down += down;
        vote.save_changes::<Vote>(&self.connection);
    }

    pub fn get_upvotes(&self, entity: &str) -> Option<Voteable> {
        use super::schema::voteables::dsl::*;

        let mut res = voteables.filter(value.eq(entity)).load(&self.connection).unwrap();
        match res.len() {
            0 => None,
            _ => Some(res.pop().unwrap()),
        }
    }
}
<commit_msg>Convert karma entities to all lower-case<commit_after>use std::env;

use dotenv::dotenv;
use diesel;
use diesel::result::Error as DieselErr;
use diesel::prelude::*;
use diesel::pg::PgConnection;

use super::models::*;

pub struct Backend {
    connection: PgConnection
}

impl Backend {
    pub fn new() -> Self {
        dotenv().ok();

        let database_url = env::var("DATABASE_URL")
            .expect("DATABASE_URL must be set");
        let connection = PgConnection::establish(&database_url)
            .expect(&format!("Error connecting to {}", database_url));

        Self {
            connection
        }
    }

    pub fn vote(&self, user: &str, entity: &str, up: i32, down: i32) {
        use super::schema::{users, voteables, votes};

        let entity = &entity.to_lowercase();

        use super::schema::users::dsl as us;
        let mut res: Vec<User> = us::users.filter(us::user_id.eq(user))
                                          .load(&self.connection).unwrap();
        let user = match res.len() {
            0 => {
                let new_user = NewUser { user_id: user };
                diesel::insert(&new_user).into(users::table)
                    .get_result(&self.connection)
                    .expect("Error creating new user")
            },
            _ => res.pop().unwrap(),
        };

        use super::schema::voteables::dsl::*;
        let mut res: Vec<Voteable> = voteables.filter(value.eq(entity))
                                              .load(&self.connection).unwrap();
        let mut voteable= match res.len() {
            0 => {
                let new_voteable = NewVoteable {
                    value: entity,
                    total_up: 0,
                    total_down: 0,
                };

                diesel::insert(&new_voteable).into(voteables::table)
                    .get_result(&self.connection)
                    .expect("Error creating new voteable")
            },

            _ => res.pop().unwrap(),
        };

        voteable.total_up += up;
        voteable.total_down += down;
        voteable.save_changes::<Voteable>(&self.connection);

        use super::schema::votes::dsl as vts;
        let mut res: Vec<Vote> = vts::votes.filter(vts::user_id.eq(user.id))
                                           .filter(vts::voteable_id.eq(voteable.id))
                                           .load(&self.connection).unwrap();
        let mut vote = match res.len() {
            0 => {
                let new_vote = NewVote{
                    user_id: user.id,
                    voteable_id: voteable.id,
                    up: 0,
                    down: 0,
                };

                diesel::insert(&new_vote).into(votes::table)
                    .get_result(&self.connection)
                    .expect("Error creating new vote")
            },
            _ => res.pop().unwrap(),
        };

        vote.up += up;
        vote.down += down;
        vote.save_changes::<Vote>(&self.connection);
    }

    pub fn get_upvotes(&self, entity: &str) -> Option<Voteable> {
        use super::schema::voteables::dsl::*;

        let entity = &entity.to_lowercase();

        let mut res = voteables.filter(value.eq(entity))
                               .load(&self.connection).unwrap();
        match res.len() {
            0 => None,
            _ => Some(res.pop().unwrap()),
        }
    }
}
<|endoftext|>


<commit_before>// run-pass

#![allow(unused_must_use)]
// Makes sure that zero-initializing large types is reasonably fast,
// Doing it incorrectly causes massive slowdown in LLVM during
// optimisation.

// pretty-expanded FIXME #23616
// ignore-emscripten no threads support

#![feature(intrinsics)]

use std::{mem, thread};

const SIZE: usize = 1024 * 1024;

fn main() {
    // do the test in a new thread to avoid (spurious?) stack overflows
    thread::spawn(|| {
        let _memory: [u8; SIZE] = unsafe { mem::zeroed() };
    }).join();
}
<commit_msg>init-large-type test needs optimizations<commit_after>// compile-flags: -O
// run-pass

#![allow(unused_must_use)]
// Makes sure that zero-initializing large types is reasonably fast,
// Doing it incorrectly causes massive slowdown in LLVM during
// optimisation.

// pretty-expanded FIXME #23616
// ignore-emscripten no threads support

#![feature(intrinsics)]

use std::{mem, thread};

const SIZE: usize = 1024 * 1024;

fn main() {
    // do the test in a new thread to avoid (spurious?) stack overflows
    thread::spawn(|| {
        let _memory: [u8; SIZE] = unsafe { mem::zeroed() };
    }).join();
}
<|endoftext|>


<commit_before>use std::borrow::Cow;
use std::f32;
use std::f64;

use super::test_default_values::*;
use quick_protobuf::*;

#[test]
fn test_default_value_simple() {
    let bytes = &[];
    let mut reader = BytesReader::from_bytes(bytes);
    let d = TestDefaultValues::from_reader(&mut reader, bytes).unwrap();
    assert_eq!(1.0, d.double_field);
    assert_eq!(2.0, d.float_field);
    assert_eq!(3, d.int32_field);
    assert_eq!(4, d.int64_field);
    assert_eq!(5, d.uint32_field);
    assert_eq!(6, d.uint64_field);
    assert_eq!(7, d.sint32_field);
    assert_eq!(8, d.sint64_field);
    assert_eq!(9, d.fixed32_field);
    assert_eq!(10, d.fixed64_field);
    assert_eq!(11, d.sfixed32_field);
    assert_eq!(12, d.sfixed64_field);
    assert_eq!(true, d.bool_field);
    assert_eq!("abc\n22", d.string_field);
    assert_eq!(b"cde\n33".to_vec(), d.bytes_field.to_vec()); // if error - change to Cow::Borrowed(...)
    assert!(EnumForDefaultValue::TWO.eq(&d.enum_field));
    assert!(d
        .enum_field_without_default
        .map_or(true, |e| e.eq(&EnumForDefaultValue::ONE)));
}

#[test]
fn test_default_value_extreme() {
    let bytes = &[];
    let mut reader = BytesReader::from_bytes(bytes);
    let d = TestExtremeDefaultValues::from_reader(&mut reader, bytes).unwrap();
    assert_eq!(f64::INFINITY, d.inf_double);
    assert_eq!(f64::NEG_INFINITY, d.neg_inf_double);
    assert!(d.nan_double.is_nan());
    assert_eq!(f32::INFINITY, d.inf_float);
    assert_eq!(f32::NEG_INFINITY, d.neg_inf_float);
    assert!(d.nan_float.is_nan());
}
<commit_msg>Removed unnecessary comment<commit_after>use std::borrow::Cow;
use std::f32;
use std::f64;

use super::test_default_values::*;
use quick_protobuf::*;

#[test]
fn test_default_value_simple() {
    let bytes = &[];
    let mut reader = BytesReader::from_bytes(bytes);
    let d = TestDefaultValues::from_reader(&mut reader, bytes).unwrap();
    assert_eq!(1.0, d.double_field);
    assert_eq!(2.0, d.float_field);
    assert_eq!(3, d.int32_field);
    assert_eq!(4, d.int64_field);
    assert_eq!(5, d.uint32_field);
    assert_eq!(6, d.uint64_field);
    assert_eq!(7, d.sint32_field);
    assert_eq!(8, d.sint64_field);
    assert_eq!(9, d.fixed32_field);
    assert_eq!(10, d.fixed64_field);
    assert_eq!(11, d.sfixed32_field);
    assert_eq!(12, d.sfixed64_field);
    assert_eq!(true, d.bool_field);
    assert_eq!("abc\n22", d.string_field);
    assert_eq!(b"cde\n33".to_vec(), d.bytes_field.to_vec());
    assert!(EnumForDefaultValue::TWO.eq(&d.enum_field));
    assert!(d
        .enum_field_without_default
        .map_or(true, |e| e.eq(&EnumForDefaultValue::ONE)));
}

#[test]
fn test_default_value_extreme() {
    let bytes = &[];
    let mut reader = BytesReader::from_bytes(bytes);
    let d = TestExtremeDefaultValues::from_reader(&mut reader, bytes).unwrap();
    assert_eq!(f64::INFINITY, d.inf_double);
    assert_eq!(f64::NEG_INFINITY, d.neg_inf_double);
    assert!(d.nan_double.is_nan());
    assert_eq!(f32::INFINITY, d.inf_float);
    assert_eq!(f32::NEG_INFINITY, d.neg_inf_float);
    assert!(d.nan_float.is_nan());
}
<|endoftext|>


<commit_before>use crate::utils::{get_item_name, snippet_with_applicability, span_lint, span_lint_and_sugg};
use rustc_ast::ast::LitKind;
use rustc_data_structures::fx::FxHashSet;
use rustc_errors::Applicability;
use rustc_hir::def_id::DefId;
use rustc_hir::{AssocItemKind, BinOpKind, Expr, ExprKind, ImplItemRef, Item, ItemKind, TraitItemRef};
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::ty;
use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::source_map::{Span, Spanned, Symbol};

declare_clippy_lint! {
    /// **What it does:** Checks for getting the length of something via `.len()`
    /// just to compare to zero, and suggests using `.is_empty()` where applicable.
    ///
    /// **Why is this bad?** Some structures can answer `.is_empty()` much faster
    /// than calculating their length. So it is good to get into the habit of using
    /// `.is_empty()`, and having it is cheap.
    /// Besides, it makes the intent clearer than a manual comparison in some contexts.
    ///
    /// **Known problems:** None.
    ///
    /// **Example:**
    /// ```ignore
    /// if x.len() == 0 {
    ///     ..
    /// }
    /// if y.len() != 0 {
    ///     ..
    /// }
    /// ```
    /// instead use
    /// ```ignore
    /// if x.is_empty() {
    ///     ..
    /// }
    /// if !y.is_empty() {
    ///     ..
    /// }
    /// ```
    pub LEN_ZERO,
    style,
    "checking `.len() == 0` or `.len() > 0` (or similar) when `.is_empty()` could be used instead"
}

declare_clippy_lint! {
    /// **What it does:** Checks for items that implement `.len()` but not
    /// `.is_empty()`.
    ///
    /// **Why is this bad?** It is good custom to have both methods, because for
    /// some data structures, asking about the length will be a costly operation,
    /// whereas `.is_empty()` can usually answer in constant time. Also it used to
    /// lead to false positives on the [`len_zero`](#len_zero) lint – currently that
    /// lint will ignore such entities.
    ///
    /// **Known problems:** None.
    ///
    /// **Example:**
    /// ```ignore
    /// impl X {
    ///     pub fn len(&self) -> usize {
    ///         ..
    ///     }
    /// }
    /// ```
    pub LEN_WITHOUT_IS_EMPTY,
    style,
    "traits or impls with a public `len` method but no corresponding `is_empty` method"
}

declare_clippy_lint! {
    /// **What it does:** Checks for comparing to an empty slice such as "" or [],`
    /// and suggests using `.is_empty()` where applicable.
    ///
    /// **Why is this bad?** Some structures can answer `.is_empty()` much faster
    /// than checking for equality. So it is good to get into the habit of using
    /// `.is_empty()`, and having it is cheap.
    /// Besides, it makes the intent clearer than a manual comparison in some contexts.
    ///
    /// **Known problems:** None.
    ///
    /// **Example:**
    ///
    /// ```ignore
    /// if s == "" {
    ///     ..
    /// }

    /// if arr == [] {
    ///     ..
    /// }
    /// ```
    /// Use instead:
    /// ```ignore
    /// if s.is_empty() {
    ///     ..
    /// }

    /// if arr.is_empty() {
    ///     ..
    /// }
    /// ```
    pub COMPARISON_TO_EMPTY,
    style,
    "checking `x == \"\"` or `x == []` (or similar) when `.is_empty()` could be used instead"
}

declare_lint_pass!(LenZero => [LEN_ZERO, LEN_WITHOUT_IS_EMPTY, COMPARISON_TO_EMPTY]);

impl<'tcx> LateLintPass<'tcx> for LenZero {
    fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) {
        if item.span.from_expansion() {
            return;
        }

        match item.kind {
            ItemKind::Trait(_, _, _, _, ref trait_items) => check_trait_items(cx, item, trait_items),
            ItemKind::Impl {
                of_trait: None,
                items: ref impl_items,
                ..
            } => check_impl_items(cx, item, impl_items),
            _ => (),
        }
    }

    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
        if expr.span.from_expansion() {
            return;
        }

        if let ExprKind::Binary(Spanned { node: cmp, .. }, ref left, ref right) = expr.kind {
            match cmp {
                BinOpKind::Eq => {
                    check_cmp(cx, expr.span, left, right, "", 0); // len == 0
                    check_cmp(cx, expr.span, right, left, "", 0); // 0 == len
                },
                BinOpKind::Ne => {
                    check_cmp(cx, expr.span, left, right, "!", 0); // len != 0
                    check_cmp(cx, expr.span, right, left, "!", 0); // 0 != len
                },
                BinOpKind::Gt => {
                    check_cmp(cx, expr.span, left, right, "!", 0); // len > 0
                    check_cmp(cx, expr.span, right, left, "", 1); // 1 > len
                },
                BinOpKind::Lt => {
                    check_cmp(cx, expr.span, left, right, "", 1); // len < 1
                    check_cmp(cx, expr.span, right, left, "!", 0); // 0 < len
                },
                BinOpKind::Ge => check_cmp(cx, expr.span, left, right, "!", 1), // len >= 1
                BinOpKind::Le => check_cmp(cx, expr.span, right, left, "!", 1), // 1 <= len
                _ => (),
            }
        }
    }
}

fn check_trait_items(cx: &LateContext<'_>, visited_trait: &Item<'_>, trait_items: &[TraitItemRef]) {
    fn is_named_self(cx: &LateContext<'_>, item: &TraitItemRef, name: &str) -> bool {
        item.ident.name.as_str() == name
            && if let AssocItemKind::Fn { has_self } = item.kind {
                has_self && {
                    let did = cx.tcx.hir().local_def_id(item.id.hir_id);
                    cx.tcx.fn_sig(did).inputs().skip_binder().len() == 1
                }
            } else {
                false
            }
    }

    // fill the set with current and super traits
    fn fill_trait_set(traitt: DefId, set: &mut FxHashSet<DefId>, cx: &LateContext<'_>) {
        if set.insert(traitt) {
            for supertrait in rustc_trait_selection::traits::supertrait_def_ids(cx.tcx, traitt) {
                fill_trait_set(supertrait, set, cx);
            }
        }
    }

    if cx.access_levels.is_exported(visited_trait.hir_id) && trait_items.iter().any(|i| is_named_self(cx, i, "len")) {
        let mut current_and_super_traits = FxHashSet::default();
        let visited_trait_def_id = cx.tcx.hir().local_def_id(visited_trait.hir_id);
        fill_trait_set(visited_trait_def_id.to_def_id(), &mut current_and_super_traits, cx);

        let is_empty_method_found = current_and_super_traits
            .iter()
            .flat_map(|&i| cx.tcx.associated_items(i).in_definition_order())
            .any(|i| {
                i.kind == ty::AssocKind::Fn
                    && i.fn_has_self_parameter
                    && i.ident.name == sym!(is_empty)
                    && cx.tcx.fn_sig(i.def_id).inputs().skip_binder().len() == 1
            });

        if !is_empty_method_found {
            span_lint(
                cx,
                LEN_WITHOUT_IS_EMPTY,
                visited_trait.span,
                &format!(
                    "trait `{}` has a `len` method but no (possibly inherited) `is_empty` method",
                    visited_trait.ident.name
                ),
            );
        }
    }
}

fn check_impl_items(cx: &LateContext<'_>, item: &Item<'_>, impl_items: &[ImplItemRef<'_>]) {
    fn is_named_self(cx: &LateContext<'_>, item: &ImplItemRef<'_>, name: &str) -> bool {
        item.ident.name.as_str() == name
            && if let AssocItemKind::Fn { has_self } = item.kind {
                has_self && {
                    let did = cx.tcx.hir().local_def_id(item.id.hir_id);
                    cx.tcx.fn_sig(did).inputs().skip_binder().len() == 1
                }
            } else {
                false
            }
    }

    let is_empty = if let Some(is_empty) = impl_items.iter().find(|i| is_named_self(cx, i, "is_empty")) {
        if cx.access_levels.is_exported(is_empty.id.hir_id) {
            return;
        } else {
            "a private"
        }
    } else {
        "no corresponding"
    };

    if let Some(i) = impl_items.iter().find(|i| is_named_self(cx, i, "len")) {
        if cx.access_levels.is_exported(i.id.hir_id) {
            let def_id = cx.tcx.hir().local_def_id(item.hir_id);
            let ty = cx.tcx.type_of(def_id);

            span_lint(
                cx,
                LEN_WITHOUT_IS_EMPTY,
                item.span,
                &format!(
                    "item `{}` has a public `len` method but {} `is_empty` method",
                    ty, is_empty
                ),
            );
        }
    }
}

fn check_cmp(cx: &LateContext<'_>, span: Span, method: &Expr<'_>, lit: &Expr<'_>, op: &str, compare_to: u32) {
    if let (&ExprKind::MethodCall(ref method_path, _, ref args, _), &ExprKind::Lit(ref lit)) = (&method.kind, &lit.kind)
    {
        // check if we are in an is_empty() method
        if let Some(name) = get_item_name(cx, method) {
            if name.as_str() == "is_empty" {
                return;
            }
        }

        check_len(cx, span, method_path.ident.name, args, &lit.node, op, compare_to)
    } else {
        check_empty_expr(cx, span, method, lit, op)
    }
}

fn check_len(
    cx: &LateContext<'_>,
    span: Span,
    method_name: Symbol,
    args: &[Expr<'_>],
    lit: &LitKind,
    op: &str,
    compare_to: u32,
) {
    if let LitKind::Int(lit, _) = *lit {
        // check if length is compared to the specified number
        if lit != u128::from(compare_to) {
            return;
        }

        if method_name.as_str() == "len" && args.len() == 1 && has_is_empty(cx, &args[0]) {
            let mut applicability = Applicability::MachineApplicable;
            span_lint_and_sugg(
                cx,
                LEN_ZERO,
                span,
                &format!("length comparison to {}", if compare_to == 0 { "zero" } else { "one" }),
                &format!("using `{}is_empty` is clearer and more explicit", op),
                format!(
                    "{}{}.is_empty()",
                    op,
                    snippet_with_applicability(cx, args[0].span, "_", &mut applicability)
                ),
                applicability,
            );
        }
    }
}

fn check_empty_expr(
    cx: &LateContext<'_>,
    span: Span,
    lit1: &Expr<'_>,
    lit2: &Expr<'_>,
    op: &str
) {
    if (is_empty_array(lit2) || is_empty_string(lit2)) && has_is_empty(cx, lit1) {
        let mut applicability = Applicability::MachineApplicable;
        span_lint_and_sugg(
            cx,
            COMPARISON_TO_EMPTY,
            span,
            &format!("comparison to empty slice"),
            &format!("using `{}is_empty` is clearer and more explicit", op),
            format!(
                "{}{}.is_empty()",
                op,
                snippet_with_applicability(cx, lit1.span, "_", &mut applicability)
            ),
            applicability,
        );
    }
}

fn is_empty_string(expr: &Expr<'_>) -> bool {
    if let ExprKind::Lit(ref lit) = expr.kind {
        if let LitKind::Str(lit, _) = lit.node {
            let lit = lit.as_str();
            return lit == "";
        }
    }
    false
}

fn is_empty_array(expr: &Expr<'_>) -> bool {
    if let ExprKind::Array(ref arr) = expr.kind {
        return arr.is_empty();
    }
    false
}

/// Checks if this type has an `is_empty` method.
fn has_is_empty(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
    /// Gets an `AssocItem` and return true if it matches `is_empty(self)`.
    fn is_is_empty(cx: &LateContext<'_>, item: &ty::AssocItem) -> bool {
        if let ty::AssocKind::Fn = item.kind {
            if item.ident.name.as_str() == "is_empty" {
                let sig = cx.tcx.fn_sig(item.def_id);
                let ty = sig.skip_binder();
                ty.inputs().len() == 1
            } else {
                false
            }
        } else {
            false
        }
    }

    /// Checks the inherent impl's items for an `is_empty(self)` method.
    fn has_is_empty_impl(cx: &LateContext<'_>, id: DefId) -> bool {
        cx.tcx.inherent_impls(id).iter().any(|imp| {
            cx.tcx
                .associated_items(*imp)
                .in_definition_order()
                .any(|item| is_is_empty(cx, &item))
        })
    }

    let ty = &cx.typeck_results().expr_ty(expr).peel_refs();
    match ty.kind() {
        ty::Dynamic(ref tt, ..) => tt.principal().map_or(false, |principal| {
            cx.tcx
                .associated_items(principal.def_id())
                .in_definition_order()
                .any(|item| is_is_empty(cx, &item))
        }),
        ty::Projection(ref proj) => has_is_empty_impl(cx, proj.item_def_id),
        ty::Adt(id, _) => has_is_empty_impl(cx, id.did),
        ty::Array(..) | ty::Slice(..) | ty::Str => true,
        _ => false,
    }
}
<commit_msg>run cargo fmt<commit_after>use crate::utils::{get_item_name, snippet_with_applicability, span_lint, span_lint_and_sugg};
use rustc_ast::ast::LitKind;
use rustc_data_structures::fx::FxHashSet;
use rustc_errors::Applicability;
use rustc_hir::def_id::DefId;
use rustc_hir::{AssocItemKind, BinOpKind, Expr, ExprKind, ImplItemRef, Item, ItemKind, TraitItemRef};
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::ty;
use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::source_map::{Span, Spanned, Symbol};

declare_clippy_lint! {
    /// **What it does:** Checks for getting the length of something via `.len()`
    /// just to compare to zero, and suggests using `.is_empty()` where applicable.
    ///
    /// **Why is this bad?** Some structures can answer `.is_empty()` much faster
    /// than calculating their length. So it is good to get into the habit of using
    /// `.is_empty()`, and having it is cheap.
    /// Besides, it makes the intent clearer than a manual comparison in some contexts.
    ///
    /// **Known problems:** None.
    ///
    /// **Example:**
    /// ```ignore
    /// if x.len() == 0 {
    ///     ..
    /// }
    /// if y.len() != 0 {
    ///     ..
    /// }
    /// ```
    /// instead use
    /// ```ignore
    /// if x.is_empty() {
    ///     ..
    /// }
    /// if !y.is_empty() {
    ///     ..
    /// }
    /// ```
    pub LEN_ZERO,
    style,
    "checking `.len() == 0` or `.len() > 0` (or similar) when `.is_empty()` could be used instead"
}

declare_clippy_lint! {
    /// **What it does:** Checks for items that implement `.len()` but not
    /// `.is_empty()`.
    ///
    /// **Why is this bad?** It is good custom to have both methods, because for
    /// some data structures, asking about the length will be a costly operation,
    /// whereas `.is_empty()` can usually answer in constant time. Also it used to
    /// lead to false positives on the [`len_zero`](#len_zero) lint – currently that
    /// lint will ignore such entities.
    ///
    /// **Known problems:** None.
    ///
    /// **Example:**
    /// ```ignore
    /// impl X {
    ///     pub fn len(&self) -> usize {
    ///         ..
    ///     }
    /// }
    /// ```
    pub LEN_WITHOUT_IS_EMPTY,
    style,
    "traits or impls with a public `len` method but no corresponding `is_empty` method"
}

declare_clippy_lint! {
    /// **What it does:** Checks for comparing to an empty slice such as "" or [],`
    /// and suggests using `.is_empty()` where applicable.
    ///
    /// **Why is this bad?** Some structures can answer `.is_empty()` much faster
    /// than checking for equality. So it is good to get into the habit of using
    /// `.is_empty()`, and having it is cheap.
    /// Besides, it makes the intent clearer than a manual comparison in some contexts.
    ///
    /// **Known problems:** None.
    ///
    /// **Example:**
    ///
    /// ```ignore
    /// if s == "" {
    ///     ..
    /// }

    /// if arr == [] {
    ///     ..
    /// }
    /// ```
    /// Use instead:
    /// ```ignore
    /// if s.is_empty() {
    ///     ..
    /// }

    /// if arr.is_empty() {
    ///     ..
    /// }
    /// ```
    pub COMPARISON_TO_EMPTY,
    style,
    "checking `x == \"\"` or `x == []` (or similar) when `.is_empty()` could be used instead"
}

declare_lint_pass!(LenZero => [LEN_ZERO, LEN_WITHOUT_IS_EMPTY, COMPARISON_TO_EMPTY]);

impl<'tcx> LateLintPass<'tcx> for LenZero {
    fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) {
        if item.span.from_expansion() {
            return;
        }

        match item.kind {
            ItemKind::Trait(_, _, _, _, ref trait_items) => check_trait_items(cx, item, trait_items),
            ItemKind::Impl {
                of_trait: None,
                items: ref impl_items,
                ..
            } => check_impl_items(cx, item, impl_items),
            _ => (),
        }
    }

    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
        if expr.span.from_expansion() {
            return;
        }

        if let ExprKind::Binary(Spanned { node: cmp, .. }, ref left, ref right) = expr.kind {
            match cmp {
                BinOpKind::Eq => {
                    check_cmp(cx, expr.span, left, right, "", 0); // len == 0
                    check_cmp(cx, expr.span, right, left, "", 0); // 0 == len
                },
                BinOpKind::Ne => {
                    check_cmp(cx, expr.span, left, right, "!", 0); // len != 0
                    check_cmp(cx, expr.span, right, left, "!", 0); // 0 != len
                },
                BinOpKind::Gt => {
                    check_cmp(cx, expr.span, left, right, "!", 0); // len > 0
                    check_cmp(cx, expr.span, right, left, "", 1); // 1 > len
                },
                BinOpKind::Lt => {
                    check_cmp(cx, expr.span, left, right, "", 1); // len < 1
                    check_cmp(cx, expr.span, right, left, "!", 0); // 0 < len
                },
                BinOpKind::Ge => check_cmp(cx, expr.span, left, right, "!", 1), // len >= 1
                BinOpKind::Le => check_cmp(cx, expr.span, right, left, "!", 1), // 1 <= len
                _ => (),
            }
        }
    }
}

fn check_trait_items(cx: &LateContext<'_>, visited_trait: &Item<'_>, trait_items: &[TraitItemRef]) {
    fn is_named_self(cx: &LateContext<'_>, item: &TraitItemRef, name: &str) -> bool {
        item.ident.name.as_str() == name
            && if let AssocItemKind::Fn { has_self } = item.kind {
                has_self && {
                    let did = cx.tcx.hir().local_def_id(item.id.hir_id);
                    cx.tcx.fn_sig(did).inputs().skip_binder().len() == 1
                }
            } else {
                false
            }
    }

    // fill the set with current and super traits
    fn fill_trait_set(traitt: DefId, set: &mut FxHashSet<DefId>, cx: &LateContext<'_>) {
        if set.insert(traitt) {
            for supertrait in rustc_trait_selection::traits::supertrait_def_ids(cx.tcx, traitt) {
                fill_trait_set(supertrait, set, cx);
            }
        }
    }

    if cx.access_levels.is_exported(visited_trait.hir_id) && trait_items.iter().any(|i| is_named_self(cx, i, "len")) {
        let mut current_and_super_traits = FxHashSet::default();
        let visited_trait_def_id = cx.tcx.hir().local_def_id(visited_trait.hir_id);
        fill_trait_set(visited_trait_def_id.to_def_id(), &mut current_and_super_traits, cx);

        let is_empty_method_found = current_and_super_traits
            .iter()
            .flat_map(|&i| cx.tcx.associated_items(i).in_definition_order())
            .any(|i| {
                i.kind == ty::AssocKind::Fn
                    && i.fn_has_self_parameter
                    && i.ident.name == sym!(is_empty)
                    && cx.tcx.fn_sig(i.def_id).inputs().skip_binder().len() == 1
            });

        if !is_empty_method_found {
            span_lint(
                cx,
                LEN_WITHOUT_IS_EMPTY,
                visited_trait.span,
                &format!(
                    "trait `{}` has a `len` method but no (possibly inherited) `is_empty` method",
                    visited_trait.ident.name
                ),
            );
        }
    }
}

fn check_impl_items(cx: &LateContext<'_>, item: &Item<'_>, impl_items: &[ImplItemRef<'_>]) {
    fn is_named_self(cx: &LateContext<'_>, item: &ImplItemRef<'_>, name: &str) -> bool {
        item.ident.name.as_str() == name
            && if let AssocItemKind::Fn { has_self } = item.kind {
                has_self && {
                    let did = cx.tcx.hir().local_def_id(item.id.hir_id);
                    cx.tcx.fn_sig(did).inputs().skip_binder().len() == 1
                }
            } else {
                false
            }
    }

    let is_empty = if let Some(is_empty) = impl_items.iter().find(|i| is_named_self(cx, i, "is_empty")) {
        if cx.access_levels.is_exported(is_empty.id.hir_id) {
            return;
        } else {
            "a private"
        }
    } else {
        "no corresponding"
    };

    if let Some(i) = impl_items.iter().find(|i| is_named_self(cx, i, "len")) {
        if cx.access_levels.is_exported(i.id.hir_id) {
            let def_id = cx.tcx.hir().local_def_id(item.hir_id);
            let ty = cx.tcx.type_of(def_id);

            span_lint(
                cx,
                LEN_WITHOUT_IS_EMPTY,
                item.span,
                &format!(
                    "item `{}` has a public `len` method but {} `is_empty` method",
                    ty, is_empty
                ),
            );
        }
    }
}

fn check_cmp(cx: &LateContext<'_>, span: Span, method: &Expr<'_>, lit: &Expr<'_>, op: &str, compare_to: u32) {
    if let (&ExprKind::MethodCall(ref method_path, _, ref args, _), &ExprKind::Lit(ref lit)) = (&method.kind, &lit.kind)
    {
        // check if we are in an is_empty() method
        if let Some(name) = get_item_name(cx, method) {
            if name.as_str() == "is_empty" {
                return;
            }
        }

        check_len(cx, span, method_path.ident.name, args, &lit.node, op, compare_to)
    } else {
        check_empty_expr(cx, span, method, lit, op)
    }
}

fn check_len(
    cx: &LateContext<'_>,
    span: Span,
    method_name: Symbol,
    args: &[Expr<'_>],
    lit: &LitKind,
    op: &str,
    compare_to: u32,
) {
    if let LitKind::Int(lit, _) = *lit {
        // check if length is compared to the specified number
        if lit != u128::from(compare_to) {
            return;
        }

        if method_name.as_str() == "len" && args.len() == 1 && has_is_empty(cx, &args[0]) {
            let mut applicability = Applicability::MachineApplicable;
            span_lint_and_sugg(
                cx,
                LEN_ZERO,
                span,
                &format!("length comparison to {}", if compare_to == 0 { "zero" } else { "one" }),
                &format!("using `{}is_empty` is clearer and more explicit", op),
                format!(
                    "{}{}.is_empty()",
                    op,
                    snippet_with_applicability(cx, args[0].span, "_", &mut applicability)
                ),
                applicability,
            );
        }
    }
}

fn check_empty_expr(cx: &LateContext<'_>, span: Span, lit1: &Expr<'_>, lit2: &Expr<'_>, op: &str) {
    if (is_empty_array(lit2) || is_empty_string(lit2)) && has_is_empty(cx, lit1) {
        let mut applicability = Applicability::MachineApplicable;
        span_lint_and_sugg(
            cx,
            COMPARISON_TO_EMPTY,
            span,
            &format!("comparison to empty slice"),
            &format!("using `{}is_empty` is clearer and more explicit", op),
            format!(
                "{}{}.is_empty()",
                op,
                snippet_with_applicability(cx, lit1.span, "_", &mut applicability)
            ),
            applicability,
        );
    }
}

fn is_empty_string(expr: &Expr<'_>) -> bool {
    if let ExprKind::Lit(ref lit) = expr.kind {
        if let LitKind::Str(lit, _) = lit.node {
            let lit = lit.as_str();
            return lit == "";
        }
    }
    false
}

fn is_empty_array(expr: &Expr<'_>) -> bool {
    if let ExprKind::Array(ref arr) = expr.kind {
        return arr.is_empty();
    }
    false
}

/// Checks if this type has an `is_empty` method.
fn has_is_empty(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
    /// Gets an `AssocItem` and return true if it matches `is_empty(self)`.
    fn is_is_empty(cx: &LateContext<'_>, item: &ty::AssocItem) -> bool {
        if let ty::AssocKind::Fn = item.kind {
            if item.ident.name.as_str() == "is_empty" {
                let sig = cx.tcx.fn_sig(item.def_id);
                let ty = sig.skip_binder();
                ty.inputs().len() == 1
            } else {
                false
            }
        } else {
            false
        }
    }

    /// Checks the inherent impl's items for an `is_empty(self)` method.
    fn has_is_empty_impl(cx: &LateContext<'_>, id: DefId) -> bool {
        cx.tcx.inherent_impls(id).iter().any(|imp| {
            cx.tcx
                .associated_items(*imp)
                .in_definition_order()
                .any(|item| is_is_empty(cx, &item))
        })
    }

    let ty = &cx.typeck_results().expr_ty(expr).peel_refs();
    match ty.kind() {
        ty::Dynamic(ref tt, ..) => tt.principal().map_or(false, |principal| {
            cx.tcx
                .associated_items(principal.def_id())
                .in_definition_order()
                .any(|item| is_is_empty(cx, &item))
        }),
        ty::Projection(ref proj) => has_is_empty_impl(cx, proj.item_def_id),
        ty::Adt(id, _) => has_is_empty_impl(cx, id.did),
        ty::Array(..) | ty::Slice(..) | ty::Str => true,
        _ => false,
    }
}
<|endoftext|>